model + setup form
authorOlivier Larchevêque <olivier.larcheveque@auf.org>
Tue, 6 Aug 2013 15:32:05 +0000 (11:32 -0400)
committerOlivier Larchevêque <olivier.larcheveque@auf.org>
Tue, 6 Aug 2013 15:32:05 +0000 (11:32 -0400)
12 files changed:
.gitignore [new file with mode: 0644]
secretquestions/__init__.py [new file with mode: 0644]
secretquestions/admin.py [new file with mode: 0644]
secretquestions/forms.py [new file with mode: 0644]
secretquestions/migrations/0001_initial.py [new file with mode: 0644]
secretquestions/migrations/__init__.py [new file with mode: 0644]
secretquestions/models.py [new file with mode: 0644]
secretquestions/templates/secretquestions/setup_form.html [new file with mode: 0644]
secretquestions/urls.py [new file with mode: 0644]
secretquestions/views.py [new file with mode: 0644]
setup.cfg [new file with mode: 0644]
setup.py [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..3ab10f0
--- /dev/null
@@ -0,0 +1,2 @@
+*egg-info
+*.pyc
diff --git a/secretquestions/__init__.py b/secretquestions/__init__.py
new file mode 100644 (file)
index 0000000..792d600
--- /dev/null
@@ -0,0 +1 @@
+#
diff --git a/secretquestions/admin.py b/secretquestions/admin.py
new file mode 100644 (file)
index 0000000..f1c282b
--- /dev/null
@@ -0,0 +1,7 @@
+# -*- coding: utf-8 -*-
+
+from django.contrib import admin
+
+from .models import Question
+
+admin.site.register(Question)
diff --git a/secretquestions/forms.py b/secretquestions/forms.py
new file mode 100644 (file)
index 0000000..2a13145
--- /dev/null
@@ -0,0 +1,64 @@
+# -*- coding: utf-8 -*-
+
+from django.conf import settings
+from django import forms
+from django.forms.models import modelformset_factory, ModelForm
+from django.utils.translation import ugettext as _
+
+from .models import Answer, crypt_answer
+
+
+MAX_SECRET_QUESTIONS = getattr(settings, 'MAX_SECRET_QUESTIONS', 3)
+
+
+class AnswerForm(ModelForm):
+
+    class Meta:
+        model = Answer
+
+    def __init__(self, *args, **kwargs):
+        if 'instance' in kwargs:
+            kwargs['instance'].secret = ""
+        super(AnswerForm, self).__init__(*args, **kwargs)
+
+    def clean_secret(self):
+        data = self.cleaned_data['secret']
+        return crypt_answer(data)
+
+
+_FreeAnswerFormSet = modelformset_factory(Answer, form=AnswerForm,
+        fields=("question", "secret"),
+        extra=MAX_SECRET_QUESTIONS,
+        max_num=MAX_SECRET_QUESTIONS,
+        can_delete=False)
+
+
+class AnswerFormSet(_FreeAnswerFormSet):
+
+    def __init__(self, *args, **kwargs):
+        self.user = kwargs.pop('user')
+        super(AnswerFormSet, self).__init__(*args, **kwargs)
+
+    def save_all(self):
+        instances = self.save(commit=False)
+        for instance in instances:
+            instance.user = self.user
+            instance.save()
+
+    def clean(self):
+        questions = []
+        for i in range(0, self.total_form_count()):
+            form = self.forms[i]
+            try:
+                question = form.cleaned_data.get('question')
+            except:
+                question = None
+            if question is None:
+                raise forms.ValidationError(
+                _("All questions have to be selected."))
+            if question in questions:
+                raise forms.ValidationError(
+                _("Each question has to be different."))
+            questions.append(question)
+
+        return super(AnswerFormSet, self).clean()
diff --git a/secretquestions/migrations/0001_initial.py b/secretquestions/migrations/0001_initial.py
new file mode 100644 (file)
index 0000000..02ce945
--- /dev/null
@@ -0,0 +1,87 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+        # Adding model 'Question'
+        db.create_table('secretquestions_question', (
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('text', self.gf('django.db.models.fields.CharField')(max_length=255)),
+        ))
+        db.send_create_signal('secretquestions', ['Question'])
+
+        # Adding model 'Answer'
+        db.create_table('secretquestions_answer', (
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])),
+            ('question', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['secretquestions.Question'])),
+            ('secret', self.gf('django.db.models.fields.CharField')(max_length=255)),
+        ))
+        db.send_create_signal('secretquestions', ['Answer'])
+
+
+    def backwards(self, orm):
+        # Deleting model 'Question'
+        db.delete_table('secretquestions_question')
+
+        # Deleting model 'Answer'
+        db.delete_table('secretquestions_answer')
+
+
+    models = {
+        'auth.group': {
+            'Meta': {'object_name': 'Group'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+            'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        'auth.permission': {
+            'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+        },
+        'auth.user': {
+            'Meta': {'object_name': 'User'},
+            'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        },
+        'contenttypes.contenttype': {
+            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+            'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        'secretquestions.answer': {
+            'Meta': {'object_name': 'Answer'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['secretquestions.Question']"}),
+            'secret': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+        },
+        'secretquestions.question': {
+            'Meta': {'object_name': 'Question'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'text': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        }
+    }
+
+    complete_apps = ['secretquestions']
\ No newline at end of file
diff --git a/secretquestions/migrations/__init__.py b/secretquestions/migrations/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/secretquestions/models.py b/secretquestions/models.py
new file mode 100644 (file)
index 0000000..f567ed4
--- /dev/null
@@ -0,0 +1,29 @@
+# -*- coding: utf-8 -*-
+
+from django.db import models
+
+from django.contrib.auth.models import get_hexdigest
+
+
+def crypt_answer(raw):
+    import random
+    algo = 'sha1'
+    salt = get_hexdigest(algo, str(random.random()), str(random.random()))[:5]
+    hsh = get_hexdigest(algo, salt, raw)
+    return '%s$%s$%s' % (algo, salt, hsh)
+
+
+class Question(models.Model):
+    text = models.CharField(max_length=255)
+
+    def __unicode__(self):
+        return self.text
+
+
+class Answer(models.Model):
+    user = models.ForeignKey('auth.User')
+    question = models.ForeignKey('secretquestions.Question')
+    secret = models.CharField(max_length=255)
+
+    class Meta:
+        unique_together = ('question', 'user')
diff --git a/secretquestions/templates/secretquestions/setup_form.html b/secretquestions/templates/secretquestions/setup_form.html
new file mode 100644 (file)
index 0000000..c76e9d6
--- /dev/null
@@ -0,0 +1,23 @@
+{% extends "base.html" %}
+{% load i18n %}
+
+{% block content %}
+
+<p>
+{% blocktrans %}
+Select several questions and register your secret answers.
+These one will be asked to you for some critical features which required more
+security to ensure that is you.
+{% endblocktrans %}
+</p>
+
+{{ formset.non_form_errors.as_ul }}
+
+<form action="" method="post">{% csrf_token %}
+    <table>
+        {{ formset }}
+    </table>
+    <input type="submit" value="{% trans "Save all" %}" />
+</form>
+
+{% endblock content %}
diff --git a/secretquestions/urls.py b/secretquestions/urls.py
new file mode 100644 (file)
index 0000000..1dbcc54
--- /dev/null
@@ -0,0 +1,8 @@
+# -*- coding: utf-8 -*-
+
+from django.conf.urls.defaults import patterns
+
+urlpatterns = patterns('',
+    (r'questions/setup$', 'secretquestions.views.setup_form'),
+    (r'questions/ask$','secretquestions.views.ask_form'),
+)
diff --git a/secretquestions/views.py b/secretquestions/views.py
new file mode 100644 (file)
index 0000000..386db09
--- /dev/null
@@ -0,0 +1,31 @@
+# -*- coding: utf-8 -*-
+
+from django.template import RequestContext
+from django.shortcuts import render_to_response, redirect
+from django.conf import settings
+from django.utils.translation import ugettext as _
+
+from django.contrib.auth.decorators import login_required
+from django.contrib import messages
+
+from .forms import AnswerFormSet
+
+
+@login_required
+def setup_form(request):
+    if request.method == 'POST':
+        formset = AnswerFormSet(request.POST, user=request.user)
+        if formset.is_valid():
+            formset.save_all()
+            messages.info(request, _("Your secret answers were successfully saved."))
+            return redirect(settings.LOGIN_REDIRECT_URL)
+    else:
+        formset = AnswerFormSet(user=request.user)
+    return render_to_response("secretquestions/setup_form.html", {
+        "formset": formset,
+        },
+        context_instance=RequestContext(request))
+
+
+def ask_form(request):
+    pass
diff --git a/setup.cfg b/setup.cfg
new file mode 100644 (file)
index 0000000..01bb954
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,3 @@
+[egg_info]
+tag_build = dev
+tag_svn_revision = true
diff --git a/setup.py b/setup.py
new file mode 100644 (file)
index 0000000..5061f71
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,26 @@
+from setuptools import setup, find_packages
+import sys, os
+
+version = '0.0'
+
+setup(name='django-secretquestions',
+      version=version,
+      description="Provides secret questions toolkit",
+      long_description="""\
+""",
+      classifiers=[], # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers
+      keywords='django secretquestions authentication security',
+      author='Olivier Larchev\xc3\xaaque',
+      author_email='olivier.larcheveque@auf.org',
+      url='',
+      license='',
+      packages=find_packages(exclude=['ez_setup', 'examples', 'tests']),
+      include_package_data=True,
+      zip_safe=False,
+      install_requires=[
+          # -*- Extra requirements: -*-
+      ],
+      entry_points="""
+      # -*- Entry points: -*-
+      """,
+      )