Moving away from session storage for user email confirmation process

This commit is contained in:
Adolfo Fitoria 2013-06-18 15:36:15 -06:00
parent 48c8d730c2
commit c4151b7752
6 changed files with 188 additions and 33 deletions

View File

@ -36,7 +36,8 @@ REQUIREMENTS = {
'pytz': 'pytz', 'pytz': 'pytz',
'tinymce': 'django-tinymce==1.5.1b2', 'tinymce': 'django-tinymce==1.5.1b2',
'longerusername': 'longerusername', 'longerusername': 'longerusername',
'bs4': 'beautifulsoup4' 'bs4': 'beautifulsoup4',
'picklefield': 'django-picklefield==0.3.0',
} }
if platform.system() != 'Windows': if platform.system() != 'Windows':

View File

@ -0,0 +1,134 @@
# -*- 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 'UserEmailVerifier'
db.create_table('django_authopenid_useremailverifier', (
('key', self.gf('django.db.models.fields.CharField')(unique=True, max_length=255, primary_key=True)),
('value', self.gf('picklefield.fields.PickledObjectField')()),
('verified', self.gf('django.db.models.fields.BooleanField')(default=False)),
))
db.send_create_signal('django_authopenid', ['UserEmailVerifier'])
def backwards(self, orm):
# Deleting model 'UserEmailVerifier'
db.delete_table('django_authopenid_useremailverifier')
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'},
'about': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'avatar_type': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '1'}),
'bronze': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
'consecutive_days_visit_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'country': ('django_countries.fields.CountryField', [], {'max_length': '2', 'blank': 'True'}),
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'date_of_birth': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
'display_tag_filter_strategy': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'email_isvalid': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'email_key': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}),
'email_signature': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'email_tag_filter_strategy': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'gold': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
'gravatar': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'ignored_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'interesting_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_fake': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'languages': ('django.db.models.fields.CharField', [], {'default': "'es-NI'", 'max_length': '128'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'last_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'location': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
'new_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'questions_per_page': ('django.db.models.fields.SmallIntegerField', [], {'default': '10'}),
'real_name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
'reputation': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}),
'seen_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'show_country': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'show_marked_tags': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'silver': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
'social_sharing_mode': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'status': ('django.db.models.fields.CharField', [], {'default': "'w'", 'max_length': '2'}),
'subscribed_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'twitter_access_token': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256'}),
'twitter_handle': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '32'}),
'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': '255'}),
'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'})
},
'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'})
},
'django_authopenid.association': {
'Meta': {'object_name': 'Association'},
'assoc_type': ('django.db.models.fields.TextField', [], {'max_length': '64'}),
'handle': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'issued': ('django.db.models.fields.IntegerField', [], {}),
'lifetime': ('django.db.models.fields.IntegerField', [], {}),
'secret': ('django.db.models.fields.TextField', [], {'max_length': '255'}),
'server_url': ('django.db.models.fields.TextField', [], {'max_length': '2047'})
},
'django_authopenid.nonce': {
'Meta': {'object_name': 'Nonce'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'salt': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
'server_url': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'timestamp': ('django.db.models.fields.IntegerField', [], {})
},
'django_authopenid.userassociation': {
'Meta': {'unique_together': "(('user', 'provider_name'), ('openid_url', 'provider_name'))", 'object_name': 'UserAssociation'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'last_used_timestamp': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
'openid_url': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'provider_name': ('django.db.models.fields.CharField', [], {'default': "'unknown'", 'max_length': '64'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
},
'django_authopenid.useremailverifier': {
'Meta': {'object_name': 'UserEmailVerifier'},
'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'primary_key': 'True'}),
'value': ('picklefield.fields.PickledObjectField', [], {})
},
'django_authopenid.userpasswordqueue': {
'Meta': {'object_name': 'UserPasswordQueue'},
'confirm_key': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'new_password': ('django.db.models.fields.CharField', [], {'max_length': '30'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'unique': 'True'})
}
}
complete_apps = ['django_authopenid']

View File

@ -3,21 +3,24 @@ from django.conf import settings
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.db import models from django.db import models
from picklefield.fields import PickledObjectField
import hashlib, random, sys, os, time import hashlib, random, sys, os, time
__all__ = ['Nonce', 'Association', 'UserAssociation', __all__ = ['Nonce', 'Association', 'UserAssociation',
'UserPasswordQueueManager', 'UserPasswordQueue'] 'UserPasswordQueueManager', 'UserPasswordQueue',
'UserEmailVerifier']
class Nonce(models.Model): class Nonce(models.Model):
""" openid nonce """ """ openid nonce """
server_url = models.CharField(max_length=255) server_url = models.CharField(max_length=255)
timestamp = models.IntegerField() timestamp = models.IntegerField()
salt = models.CharField(max_length=40) salt = models.CharField(max_length=40)
def __unicode__(self): def __unicode__(self):
return u"Nonce: %s" % self.id return u"Nonce: %s" % self.id
class Association(models.Model): class Association(models.Model):
""" association openid url and lifetime """ """ association openid url and lifetime """
server_url = models.TextField(max_length=2047) server_url = models.TextField(max_length=2047)
@ -26,19 +29,19 @@ class Association(models.Model):
issued = models.IntegerField() issued = models.IntegerField()
lifetime = models.IntegerField() lifetime = models.IntegerField()
assoc_type = models.TextField(max_length=64) assoc_type = models.TextField(max_length=64)
def __unicode__(self): def __unicode__(self):
return u"Association: %s, %s" % (self.server_url, self.handle) return u"Association: %s, %s" % (self.server_url, self.handle)
class UserAssociation(models.Model): class UserAssociation(models.Model):
""" """
model to manage association between openid and user model to manage association between openid and user
""" """
#todo: rename this field so that it sounds good for other methods #todo: rename this field so that it sounds good for other methods
#for exaple, for password provider this will hold password #for exaple, for password provider this will hold password
openid_url = models.CharField(blank=False, max_length=255) openid_url = models.CharField(blank=False, max_length=255)
user = models.ForeignKey(User) user = models.ForeignKey(User)
#in the future this must be turned into an #in the future this must be turned into an
#association with a Provider record #association with a Provider record
#to hold things like login badge, etc #to hold things like login badge, etc
provider_name = models.CharField(max_length=64, default='unknown') provider_name = models.CharField(max_length=64, default='unknown')
@ -49,7 +52,7 @@ class UserAssociation(models.Model):
('user','provider_name'), ('user','provider_name'),
('openid_url', 'provider_name') ('openid_url', 'provider_name')
) )
def __unicode__(self): def __unicode__(self):
return "Openid %s with user %s" % (self.openid_url, self.user) return "Openid %s with user %s" % (self.openid_url, self.user)
@ -82,3 +85,13 @@ class UserPasswordQueue(models.Model):
def __unicode__(self): def __unicode__(self):
return self.user.username return self.user.username
class UserEmailVerifier(models.Model):
'''Model that stores the required values to verify an email
address'''
key = models.CharField(max_length=255, unique=True, primary_key=True)
value = PickledObjectField()
verified = models.BooleanField(default=False)
def __unicode__(self):
return self.key

View File

@ -81,7 +81,7 @@ import urllib
from askbot import forms as askbot_forms from askbot import forms as askbot_forms
from askbot.deps.django_authopenid import util from askbot.deps.django_authopenid import util
from askbot.deps.django_authopenid import decorators from askbot.deps.django_authopenid import decorators
from askbot.deps.django_authopenid.models import UserAssociation from askbot.deps.django_authopenid.models import UserAssociation, UserEmailVerifier
from askbot.deps.django_authopenid import forms from askbot.deps.django_authopenid import forms
from askbot.deps.django_authopenid.backends import AuthBackend from askbot.deps.django_authopenid.backends import AuthBackend
import logging import logging
@ -1019,12 +1019,13 @@ def register(request, login_provider_name=None, user_identifier=None):
cleanup_post_register_session(request) cleanup_post_register_session(request)
return HttpResponseRedirect(next_url) return HttpResponseRedirect(next_url)
else: else:
request.session['username'] = username email_verifier = UserEmailVerifier(key=generate_random_key())
request.session['email'] = email email_verifier.value = {'username': username, 'email': email,
key = generate_random_key() 'user_identifier': user_identifier,
email = request.session['email'] 'login_provider_name': login_provider_name}
send_email_key(email, key, handler_url_name='verify_email_and_register') email_verifier.save()
request.session['validation_code'] = key send_email_key(email, email_verifier.key,
handler_url_name='verify_email_and_register')
redirect_url = reverse('verify_email_and_register') + '?next=' + next_url redirect_url = reverse('verify_email_and_register') + '?next=' + next_url
return HttpResponseRedirect(redirect_url) return HttpResponseRedirect(redirect_url)
@ -1073,14 +1074,16 @@ def verify_email_and_register(request):
try: try:
#we get here with post if button is pushed #we get here with post if button is pushed
#or with "get" if emailed link is clicked #or with "get" if emailed link is clicked
expected_code = request.session['validation_code'] email_verifier = UserEmailVerifier.objects.get(key=presented_code)
assert(presented_code == expected_code) #verifies that the code has not been used already
#create an account! assert(email_verifier.verified == False)
username = request.session['username']
email = request.session['email'] username = email_verifier.value['username']
password = request.session.get('password', None) email = email_verifier.value['email']
user_identifier = request.session.get('user_identifier', None) password = email_verifier.value.get('password', None)
login_provider_name = request.session.get('login_provider_name', None) user_identifier = email_verifier.value.get('user_identifier', None)
login_provider_name = email_verifier.value.get('login_provider_name', None)
if password: if password:
user = create_authenticated_user_account( user = create_authenticated_user_account(
username=username, username=username,
@ -1098,7 +1101,10 @@ def verify_email_and_register(request):
raise NotImplementedError() raise NotImplementedError()
login(request, user) login(request, user)
email_verifier.verified = True
email_verifier.save()
cleanup_post_register_session(request) cleanup_post_register_session(request)
return HttpResponseRedirect(get_next_url(request)) return HttpResponseRedirect(get_next_url(request))
except Exception, e: except Exception, e:
message = _( message = _(
@ -1159,14 +1165,13 @@ def signup_with_password(request):
cleanup_post_register_session(request) cleanup_post_register_session(request)
return HttpResponseRedirect(get_next_url(request)) return HttpResponseRedirect(get_next_url(request))
else: else:
request.session['username'] = username email_verifier = UserEmailVerifier(key=generate_random_key())
request.session['email'] = email email_verifier.value = {'username': username,
request.session['password'] = password 'login_provider_name': provider_name,
#todo: generate a key and save it in the session 'email': email, 'password': password}
key = generate_random_key() email_verifier.save()
email = request.session['email'] send_email_key(email, email_verifier.key,
send_email_key(email, key, handler_url_name='verify_email_and_register') handler_url_name='verify_email_and_register')
request.session['validation_code'] = key
redirect_url = reverse('verify_email_and_register') + \ redirect_url = reverse('verify_email_and_register') + \
'?next=' + get_next_url(request) '?next=' + get_next_url(request)
return HttpResponseRedirect(redirect_url) return HttpResponseRedirect(redirect_url)

View File

@ -23,3 +23,4 @@ sanction
django-tinymce==1.5.1b2 django-tinymce==1.5.1b2
longerusername longerusername
beautifulsoup4 beautifulsoup4
django-picklefield==0.3.0

View File

@ -27,3 +27,4 @@ sanction
django-tinymce django-tinymce
longerusername longerusername
beautifulsoup4 beautifulsoup4
django-picklefield==0.3.0