Skip to content

Commit

Permalink
migrate openid changes
Browse files Browse the repository at this point in the history
  • Loading branch information
numerodix committed Jun 23, 2011
1 parent 69763bf commit baa3b2d
Show file tree
Hide file tree
Showing 10 changed files with 162 additions and 162 deletions.
@@ -0,0 +1,5 @@
function(doc) {
if (doc.doc_type == 'Association') {
emit(doc.issued + doc.lifetime, doc);
}
}
@@ -0,0 +1,5 @@
function(doc) {
if (doc.doc_type == 'UserOpenidAssociation') {
emit(doc.openid, doc);
}
}
@@ -0,0 +1,5 @@
function(doc) {
if (doc.doc_type == 'Nonce') {
emit(doc.timestamp, doc);
}
}
@@ -0,0 +1,5 @@
function(doc) {
if (doc.doc_type == 'Association') {
emit([doc.server_url, doc.handle], doc);
}
}
@@ -0,0 +1,5 @@
function(doc) {
if (doc.doc_type == 'Nonce') {
emit([doc.server_url, doc.timestamp, doc.salt], doc);
}
}
@@ -0,0 +1,5 @@
function(doc) {
if (doc.doc_type == 'Association') {
emit(doc.server_url, doc);
}
}
63 changes: 28 additions & 35 deletions django_couchdb_utils/openid_consumer/consumer.py
@@ -1,15 +1,17 @@
import couchdb, datetime
import datetime
from django.conf import settings
from openid.consumer import consumer
from django_openid.consumer import signed
from openid_consumer.models import server_uri, DB_PREFIX
from django_openid.auth import AuthConsumer as DjangoOpenidAuthConsumer
# I know this long naming sucks! But couldn't think of anything better...
from django_openid.consumer import Consumer as DjangoOpenidConsumer, \
LoginConsumer as DjangoOpenidLoginConsumer, \
SessionConsumer as DjangoOpenidSessionConsumer, \
CookieConsumer as DjangoOpenidCookieConsumer
from openid_consumer.models import DjangoCouchDBOpenIDStore, get_or_create, get_values
from .models import DjangoCouchDBOpenIDStore, UserOpenidAssociation
from django_couchdb_utils.auth.models import User
from django.contrib.auth import login
from couchdbkit.exceptions import ResourceNotFound

class Consumer(DjangoOpenidConsumer):
def get_consumer(self, request, session_store):
Expand All @@ -25,18 +27,12 @@ class CookieConsumer(LoginConsumer, DjangoOpenidCookieConsumer):
pass

class AuthConsumer(SessionConsumer, DjangoOpenidAuthConsumer):
def __init__(self):
auth_db_prefix = getattr(settings, "COUCHDB_AUTH_PREFIX", "")
self.auth_db = get_or_create(server_uri, "%s%s" %(auth_db_prefix, "auth"))
super(AuthConsumer, self).__init__()

def user_can_login(self, request, user):
"Over-ride for things like user bans or account e-mail validation"
return user.is_active

def log_in_user(self, request, user):
from auth import login
user.backend = 'auth.backends.CouchDBAuthBackend'
user.backend = 'django_couchdb_utils.auth.backends.CouchDBAuthBackend'
login(request, user)

def do_associate(self, request):
Expand All @@ -49,13 +45,13 @@ def do_associate(self, request):
except signed.BadSignature:
return self.show_error(request, self.csrf_failed_message)
# Associate openid with their account, if it isn't already
temp_db = get_or_create(server_uri, "%s%s" %(DB_PREFIX, "user_openid"))
if not len(get_values(temp_db.view('openid_view/all', key = openid))):
from openid_consumer.models import UserOpenidAssociation
if not len(UserOpenidAssociation.view('%s/openid_view' % UserOpenidAssociation._meta.app_label,
key = openid), include_docs=True):
uoa = UserOpenidAssociation(user_id = request.user.id,
openid = openid,
created = datetime.datetime.now())
uoa.store(temp_db)
uoa["temp"] = True
uoa.store()
return self.show_associate_done(request, openid)

return self.show_error(request, 'Should POST to here')
Expand All @@ -77,19 +73,19 @@ def do_associations(self, request):
message = self.associate_tampering_message
else:
# It matches! Delete the OpenID relationship
temp_db = get_or_create(server_uri, "%s%s" %(DB_PREFIX, "user_openid"))
from openid_consumer.models import UserOpenidAssociation
rows = get_values(temp_db.view('openid_view/all', key=todelete['openid']))
temp_db.delete(rows[0])
message = self.association_deleted_message % (
todelete['openid']
)
row = UserOpenidAssociation.view('%s/openid_view' % UserOpenidAssociation._meta.app_label,
key=todelete['openid'], include_docs=True).first()
if row.temp == True:
row.delete()
message = self.association_deleted_message % (
todelete['openid']
)
except signed.BadSignature:
message = self.associate_tampering_message
# We construct a button to delete each existing association
openids = []
temp_db = get_or_create(server_uri, "%s%s" %(DB_PREFIX, "user_openid"))
for association in get_values(temp_db.view('openid_view/all')):
for association in UserOpenidAssociation.view('%s/openid_view' % UserOpenidAssociation._meta.app_label,
include_docs=True):
openids.append({
'openid': association['openid'],
'button': signed.dumps({
Expand All @@ -109,22 +105,19 @@ def do_associations(self, request):


def lookup_openid(self, request, identity_url):
from auth.models import User
temp_db = get_or_create(server_uri, "%s%s" %(DB_PREFIX, "user_openid"))
try:
openid = get_values(temp_db.view('openid_view/all', key=identity_url))[0]
except IndexError:
return []
return [User.load(self.auth_db, openid['user_id']),]
openid = UserOpenidAssociation.view('%s/openid_view' % UserOpenidAssociation._meta.app_label,
key=identity_url, include_docs=True).first()
if openid:
return User.view('%s/users_by_username' % User._meta.app_label,
key=openid['user_id'], include_docs=True).all()

def lookup_users_by_email(self, email):
return get_values(self.auth_db.view('auth_email/all', key=email))
return User.view('%s/users_by_email' % User._meta.app_label,
key=email, include_docs=True).first()

def lookup_user_by_username(self, username):
try:
return get_values(self.auth_db.view('auth_id/all', key=username))
except IndexError:
return None
return User.view('%s/users_by_username' % User._meta.app_label,
key=username, include_docs=True).first()

def lookup_user_by_id(self, id):
return self.lookup_user_by_username(id)
16 changes: 8 additions & 8 deletions django_couchdb_utils/openid_consumer/forms.py
@@ -1,24 +1,24 @@
from django import forms
from auth.models import User, DB_PREFIX
from openid_consumer.models import server_uri, get_or_create, get_values
from django_couchdb_utils.auth.models import User
from django_openid.forms import RegistrationForm as DjangoOpenidRegistrationForm, \
RegistrationFormPasswordConfirm as DjangoOpenidRegistrationFormPasswordConfirm
from couchdbkit.exceptions import ResourceNotFound

class RegistrationForm(DjangoOpenidRegistrationForm):
def __init__(self, *args, **kwargs):
super(RegistrationForm, self).__init__(*args, **kwargs)
self.auth_db = get_or_create(server_uri, "%s%s" %(DB_PREFIX, "auth"))
User.id_view.sync(self.auth_db)
User.email_view.sync(self.auth_db)
User.is_active_view.sync(self.auth_db)

def save(self):
user = User(**self.cleaned_data)
return user.store(self.auth_db)
return user.store()

def clean_email(self):
email = self.cleaned_data.get('email', '')
if self.no_duplicate_emails and len(get_values(self.auth_db.view('auth_email/all', key = email))) > 0:
try:
email_count = User.view('%s/users_by_email' % User._meta.app_label, key = email).count()
except ResourceNotFound:
email_count = 0
if self.no_duplicate_emails and email_count > 0:
raise forms.ValidationError, self.duplicate_email_error
return email

Expand Down
153 changes: 56 additions & 97 deletions django_couchdb_utils/openid_consumer/models.py
@@ -1,88 +1,39 @@
from auth.models import User
from couchdb.schema import *
from couchdb.schema import View
from django.conf import settings
from couchdb import Server, Database
from couchdbkit.ext.django.schema import *
import time, base64, openid.store, urlparse
from couchdb.client import PreconditionFailed
from django_openid.models import DjangoOpenIDStore
from django.utils.hashcompat import md5_constructor
from openid.association import Association as OIDAssociation

DEFAULT_COUCHDB_HOST = "http://127.0.0.1:5984"
server_uri = getattr(settings, 'COUCHDB_HOST', DEFAULT_COUCHDB_HOST)
DB_PREFIX = getattr(settings, 'COUCHDB_OPENID_PREFIX', '')
openid_db_uri = getattr(settings, 'COUCHDB_OPENID_DB', '%s%s' %(DB_PREFIX, 'openid'))

def get_or_create(server_uri, db_name):
server = Server(server_uri)
try:
db = server.create(db_name)
except PreconditionFailed, e:
if not e.message[0] == 'file_exists':
raise e
# Database seems to exist. Let's just use it
db = Database(urlparse.urljoin(server_uri, db_name))
return db

def get_values(db_view_result):
return [i['value'] for i in db_view_result.rows]

from couchdbkit.exceptions import ResourceNotFound

class UserOpenidAssociation(Document):
user_id = TextField()
openid = TextField()
created = DateTimeField()
user_id = StringProperty()
openid = StringProperty()
created = DateTimeProperty()

openid_view = View('openid_view',
'''function (doc) { emit(doc.openid, doc); }''',
name='all')
class Meta:
app_label = "django_couchdb_utils_openid_consumer"

class Nonce(Document):
server_url = TextField()
timestamp = IntegerField()
salt = TextField()
server_url = StringProperty()
timestamp = IntegerProperty()
salt = StringProperty()

timestamp_view = View('timestamp_view',
'''function (doc) { emit(doc.timestamp, doc); }''',
name='all')
url_timestamp_salt_view = View('url_timestamp_salt_view',
'''function (doc) { emit([doc.server_url, doc.timestamp, doc.salt], doc); }''',
name='all')
class Meta:
app_label = "django_couchdb_utils_openid_consumer"

class Association(Document):
server_url = TextField()
handle = TextField()
secret = TextField() # Stored base64 encoded
issued = IntegerField()
lifetime = IntegerField()
assoc_type = TextField()
server_url = StringProperty()
handle = StringProperty()
secret = StringProperty() # Stored base64 encoded
issued = IntegerProperty()
lifetime = IntegerProperty()
assoc_type = StringProperty()

url_handle_view = View('url_handle_view',
'''function (doc) { emit([doc.server_url, doc.handle], doc); }''',
name='all')
url_view = View('url_view',
'''function (doc) { emit(doc.server_url, doc); }''',
name='all')
issued_lifetime_view = View('issued_lifetime_view',
'''function (doc) { emit(doc.issued+doc.lifetime, doc); } ''',
name='all')
class Meta:
app_label = "django_couchdb_utils_openid_consumer"

class DjangoCouchDBOpenIDStore(DjangoOpenIDStore):
def __init__(self):
# This constructor argument is specific only to
# the couchdb store. It accepts a couchdb db
# instance
self.nonce_db = get_or_create(server_uri, "%s_nonce" %openid_db_uri)
self.assoc_db = get_or_create(server_uri, "%s_assoc" %openid_db_uri)
self.user_openid_db = get_or_create(server_uri, "%s%s" %(DB_PREFIX, "user_openid"))
UserOpenidAssociation.openid_view.sync(self.user_openid_db)
Nonce.timestamp_view.sync(self.nonce_db)
Nonce.url_timestamp_salt_view.sync(self.nonce_db)
Association.url_handle_view.sync(self.assoc_db)
Association.url_view.sync(self.assoc_db)
Association.issued_lifetime_view.sync(self.assoc_db)

def storeAssociation(self, server_url, association):
assoc = Association(
server_url = server_url,
Expand All @@ -92,62 +43,70 @@ def storeAssociation(self, server_url, association):
lifetime = association.issued,
assoc_type = association.assoc_type
)
assoc.store(self.assoc_db)
assoc.store()

def getAssociation(self, server_url, handle=None):
assocs = []
if handle is not None:
assocs = get_values(self.assoc_db.view('url_handle_view/all', key=[server_url, handle]))
assocs = Association.view('%s/url_handle_view' % Association._meta.app_label,
key=[server_url, handle], include_docs=True).all()
else:
assocs = get_values(self.assoc_db.view('url_view/all', key=server_url))
if not assocs:
return None
assocs = Association.view('%s/url_view' % Association._meta.app_label,
key=server_url, include_docs=True).all()
associations = []
for assoc in assocs:
association = OIDAssociation(
assoc['handle'], base64.decodestring(assoc['secret']), assoc['issued'],
assoc['lifetime'], assoc['assoc_type']
)
if association.getExpiresIn() == 0:
self.removeAssociation(server_url, assoc.handle)
else:
associations.append((association.issued, association))
try:
for assoc in assocs:
association = OIDAssociation(
assoc['handle'], base64.decodestring(assoc['secret']), assoc['issued'],
assoc['lifetime'], assoc['assoc_type']
)
if association.getExpiresIn() == 0:
self.removeAssociation(server_url, assoc.handle)
else:
associations.append((association.issued, association))
except ResourceNotFound:
pass
if not associations:
return None
return associations[-1][1]

def removeAssociation(self, server_url, handle):
assocs = get_values(self.assoc_db.view('url_handle_view/all', key=[server_url, handle]))
assocs_exist = len(assocs) > 0
try:
assocs = Association.view('%s/url_handle_view' % Association._meta.app_label,
key=[server_url, handle], include_docs=True).all()
except ResourceNotFound:
assocs = []
for assoc in assocs:
self.assoc_db.delete(assoc)
return assocs_exist
assoc.delete()
return len(assocs)

def useNonce(self, server_url, timestamp, salt):
# Has nonce expired?
if abs(timestamp - time.time()) > openid.store.nonce.SKEW:
return False
try:
nonce = get_values(self.nonce_db.view('url_timestamp_salt_view/all',
key=[server_url, timestamp, salt]))[0]
except IndexError:
nonce = Nonce.view('%s/url_timestamp_salt_view' % Nonce._meta.app_label,
key=[server_url, timestamp, salt], include_docs=True).first()
if not nonce:
nonce = Nonce(
server_url = server_url,
timestamp = timestamp,
salt = salt
)
nonce.store(self.nonce_db)
nonce.store()
return True
self.nonce_db.delete(nonce)
if nonce:
nonce.delete()
return False

def cleanupNonce(self):
max_key_val = time.time() - openid.store.nonce.SKEW
nonces = get_values(self.nonce_db.view('timestamp_view/all', endkey=max_key_val))
nonces = Nonce.view('%s/timestamp_view' % Nonce._meta.app_label,
endkey=max_key_val, include_docs=True)
for nonce in nonces:
self.nonce_db.delete(nonce)
nonce.delete()

def cleaupAssociations(self):
assocs = get_values(self.assoc_db.view('issued_lifetime_view/all', endkey=time.time()))
assocs = Association.view('%s/issued_lifetime_view' % Association._meta.app_label,
endkey=time.time(), include_docs=True)
for assoc in assocs:
self.assoc_db.delete(assoc)
assoc.delete()

0 comments on commit baa3b2d

Please sign in to comment.