Permalink
Browse files

Implemented URL obfuscation for “secret” forms

  • Loading branch information...
1 parent 0f86cf0 commit 685fad77f911ac83adf8b553dadfa99ab68990df @samluescher committed Jul 19, 2011
View
@@ -35,7 +35,7 @@ class FormDefinitionFieldInline(admin.StackedInline):
class FormDefinitionAdmin(admin.ModelAdmin):
fieldsets = [
- (_('Basic'), {'fields': ['name', 'method', 'action', 'title', 'allow_get_initial', 'log_data', 'success_redirect', 'success_clear', 'display_logged']}),
+ (_('Basic'), {'fields': ['name', 'require_hash', 'method', 'action', 'title', 'allow_get_initial', 'log_data', 'success_redirect', 'success_clear', 'display_logged']}),
(_('Mail form'), {'fields': ['mail_to', 'mail_from', 'mail_subject'], 'classes': ['collapse']}),
(_('Templates'), {'fields': ['message_template', 'form_template_name'], 'classes': ['collapse']}),
(_('Messages'), {'fields': ['success_message', 'error_message', 'submit_label'], 'classes': ['collapse']}),
@@ -0,0 +1,99 @@
+# encoding: 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 field 'FormDefinition.require_hash'
+ db.add_column('form_designer_formdefinition', 'require_hash', self.gf('django.db.models.fields.BooleanField')(default=False, blank=True), keep_default=False)
+
+ # Adding field 'FormDefinition.private_hash'
+ db.add_column('form_designer_formdefinition', 'private_hash', self.gf('django.db.models.fields.CharField')(default='', max_length=40), keep_default=False)
+
+ # Adding field 'FormDefinition.public_hash'
+ db.add_column('form_designer_formdefinition', 'public_hash', self.gf('django.db.models.fields.CharField')(default='', max_length=40), keep_default=False)
+
+ # Changing field 'FormDefinitionField.regex'
+ db.alter_column('form_designer_formdefinitionfield', 'regex', self.gf('form_designer.fields.RegexpExpressionField')(max_length=255, null=True, blank=True))
+
+
+ def backwards(self, orm):
+
+ # Deleting field 'FormDefinition.require_hash'
+ db.delete_column('form_designer_formdefinition', 'require_hash')
+
+ # Deleting field 'FormDefinition.private_hash'
+ db.delete_column('form_designer_formdefinition', 'private_hash')
+
+ # Deleting field 'FormDefinition.public_hash'
+ db.delete_column('form_designer_formdefinition', 'public_hash')
+
+ # Changing field 'FormDefinitionField.regex'
+ db.alter_column('form_designer_formdefinitionfield', 'regex', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True))
+
+
+ models = {
+ 'form_designer.formdefinition': {
+ 'Meta': {'object_name': 'FormDefinition'},
+ 'action': ('django.db.models.fields.URLField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'allow_get_initial': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
+ 'body': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'display_logged': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+ 'error_message': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'form_template_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'log_data': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
+ 'mail_from': ('form_designer.fields.TemplateCharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'mail_subject': ('form_designer.fields.TemplateCharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'mail_to': ('form_designer.fields.TemplateCharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'message_template': ('form_designer.fields.TemplateTextField', [], {'null': 'True', 'blank': 'True'}),
+ 'method': ('django.db.models.fields.CharField', [], {'default': "'POST'", 'max_length': '10'}),
+ 'name': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}),
+ 'private_hash': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '40'}),
+ 'public_hash': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '40'}),
+ 'require_hash': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+ 'submit_label': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'success_clear': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
+ 'success_message': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'success_redirect': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'})
+ },
+ 'form_designer.formdefinitionfield': {
+ 'Meta': {'object_name': 'FormDefinitionField'},
+ 'choice_labels': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'choice_model': ('form_designer.fields.ModelNameField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'choice_model_empty_label': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'choice_values': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'decimal_places': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'field_class': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
+ 'form_definition': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['form_designer.FormDefinition']"}),
+ 'help_text': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'include_result': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
+ 'initial': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'label': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'max_digits': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'max_length': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'max_value': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}),
+ 'min_length': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'min_value': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}),
+ 'name': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'position': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'regex': ('form_designer.fields.RegexpExpressionField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'required': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
+ 'widget': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255', 'null': 'True', 'blank': 'True'})
+ },
+ 'form_designer.formlog': {
+ 'Meta': {'object_name': 'FormLog'},
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+ 'data': ('picklefield.fields.PickledObjectField', [], {'null': 'True', 'blank': 'True'}),
+ 'form_definition': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['form_designer.FormDefinition']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
+ }
+ }
+
+ complete_apps = ['form_designer']
View
@@ -1,4 +1,5 @@
import re
+import hashlib, uuid
from django.db import models
from django.utils.translation import ugettext, ugettext_lazy as _
@@ -32,6 +33,9 @@ def get_class(import_path):
class FormDefinition(models.Model):
name = models.SlugField(_('Name'), max_length=255, unique=True)
+ require_hash = models.BooleanField(_('Obfuscate URL to this form'), default=False, help_text=_('If enabled, the form can only be reached via a secret URL.'))
+ private_hash = models.CharField(editable=False, max_length=40, default='')
+ public_hash = models.CharField(editable=False, max_length=40, default='')
title = models.CharField(_('Title'), max_length=255, blank=True, null=True)
body = models.TextField(_('Body'), blank=True, null=True)
action = models.URLField(_('Target URL'), help_text=_('If you leave this empty, the page where the form resides will be requested, and you can use the mail form and logging features. You can also send data to external sites: For instance, enter "http://www.google.ch/search" to create a search form.'), max_length=255, blank=True, null=True)
@@ -54,6 +58,13 @@ class Meta:
verbose_name = _('Form')
verbose_name_plural = _('Forms')
+ def save(self, *args, **kwargs):
+ if not self.private_hash:
+ self.private_hash = hashlib.sha1(str(uuid.uuid4())).hexdigest()
+ if not self.public_hash:
+ self.public_hash = hashlib.sha1(str(uuid.uuid4())).hexdigest()
+ super(FormDefinition, self).save()
+
def get_field_dict(self):
dict = {}
for field in self.formdefinitionfield_set.all():
@@ -62,6 +73,8 @@ def get_field_dict(self):
@models.permalink
def get_absolute_url(self):
+ if self.require_hash:
+ return ('form_designer.views.detail_by_hash', [str(self.public_hash)])
return ('form_designer.views.detail', [str(self.name)])
def get_form_data(self, form):
@@ -198,10 +211,10 @@ class Meta:
verbose_name = _('Field')
verbose_name_plural = _('Fields')
- def save(self):
+ def save(self, *args, **kwargs):
if self.position == None:
self.position = 0
- super(FormDefinitionField, self).save()
+ super(FormDefinitionField, self).save(*args, **kwargs)
def ____init__(self, field_class=None, name=None, required=None, widget=None, label=None, initial=None, help_text=None, *args, **kwargs):
super(FormDefinitionField, self).__init__(*args, **kwargs)
View
@@ -2,4 +2,5 @@
urlpatterns = patterns('',
url(r'^(?P<object_name>[-\w]+)/$', 'form_designer.views.detail', name='form_designer_detail'),
+ url(r'^h/(?P<public_hash>[-\w]+)/$', 'form_designer.views.detail_by_hash', name='form_designer_detail_by_hash'),
)
View
@@ -86,8 +86,7 @@ def process_form(request, form_definition, context={}, is_cms_plugin=False):
return context
-def detail(request, object_name):
- form_definition = get_object_or_404(FormDefinition, name=object_name)
+def _form_detail_view(request, form_definition):
result = process_form(request, form_definition)
if isinstance(result, HttpResponseRedirect):
return result
@@ -96,3 +95,11 @@ def detail(request, object_name):
})
return render_to_response('html/formdefinition/detail.html', result,
context_instance=RequestContext(request))
+
+def detail(request, object_name):
+ form_definition = get_object_or_404(FormDefinition, name=object_name, require_hash=False)
+ return _form_detail_view(request, form_definition)
+
+def detail_by_hash(request, public_hash):
+ form_definition = get_object_or_404(FormDefinition, public_hash=public_hash)
+ return _form_detail_view(request, form_definition)

0 comments on commit 685fad7

Please sign in to comment.