Skip to content

Instantly share code, notes, and snippets.

@ghinch
Created December 27, 2009 21:36
Show Gist options
  • Save ghinch/264408 to your computer and use it in GitHub Desktop.
Save ghinch/264408 to your computer and use it in GitHub Desktop.
Index: appengine_django/auth/middleware.py
===================================================================
--- appengine_django/auth/middleware.py (revision 100)
+++ appengine_django/auth/middleware.py (working copy)
@@ -12,8 +12,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-from django.contrib.auth.models import AnonymousUser
-
from google.appengine.api import users
from appengine_django.auth.models import User
@@ -26,7 +24,8 @@
if user:
request._cached_user = User.get_djangouser_for_user(user)
else:
- request._cached_user = AnonymousUser()
+ from django.contrib.auth import get_user
+ request._cached_user = get_user(request)
return request._cached_user
Index: appengine_django/auth/models.py
===================================================================
--- appengine_django/auth/models.py (revision 100)
+++ appengine_django/auth/models.py (working copy)
@@ -20,6 +20,7 @@
from django.core.exceptions import ImproperlyConfigured
from django.db import models
from django.utils.encoding import smart_str
+from django.utils.hashcompat import md5_constructor, sha_constructor
import urllib
from django.db.models.manager import EmptyManager
@@ -27,9 +28,87 @@
from google.appengine.api import users
from google.appengine.ext import db
-from appengine_django.models import BaseModel
+from appengine_django.models import BaseModel, ModelManager
+UNUSABLE_PASSWORD = '!' # This will never be a valid hash
+class UserNotUniqueError(Exception):
+ pass
+
+def get_hexdigest(algorithm, salt, raw_password):
+ """
+ Returns a string of the hexdigest of the given plaintext password and salt
+ using the given algorithm ('md5', 'sha1' or 'crypt').
+ """
+ raw_password, salt = smart_str(raw_password), smart_str(salt)
+ if algorithm == 'crypt':
+ try:
+ import crypt
+ except ImportError:
+ raise ValueError('"crypt" password algorithm not supported in this environment')
+ return crypt.crypt(raw_password, salt)
+
+ if algorithm == 'md5':
+ return md5_constructor(salt + raw_password).hexdigest()
+ elif algorithm == 'sha1':
+ return sha_constructor(salt + raw_password).hexdigest()
+ raise ValueError("Got unknown password algorithm type in password.")
+
+def check_password(raw_password, enc_password):
+ """
+ Returns a boolean of whether the raw_password was correct. Handles
+ encryption formats behind the scenes.
+ """
+ algo, salt, hsh = enc_password.split('$')
+ return hsh == get_hexdigest(algo, salt, raw_password)
+
+def check_unique(username):
+ query = User.all().filter('username=', username)
+ user = query.get()
+ if user:
+ raise UserNotUniqueError
+
+class UserManager(ModelManager):
+ def get(self, *args, **kwargs):
+ if 'pk' in kwargs:
+ uname = kwargs['pk']
+ elif 'username' in kwargs:
+ uname = kwargs['username']
+ else:
+ return False
+ query = self.owner.all().filter('username =', uname)
+ user = query.get()
+ if user:
+ user.id = user.username
+ else:
+ user = AnonymousUser
+ return user
+
+ def create_user(self, username, email, key_name, aeuser=None, password=None):
+ "Creates and saves a User with the given username, e-mail and password."
+ user = self.owner(username=username, email= email.strip().lower(), key_name=key_name, user=aeuser)
+ if password:
+ user.set_password(password)
+ else:
+ user.set_unusable_password()
+ user.put()
+ return user
+
+ def create_superuser(self, username, email, password):
+ u = self.create_user(username, email, password)
+ u.is_staff = True
+ u.is_active = True
+ u.is_superuser = True
+ u.put()
+ return u
+
+ def make_random_password(self, length=10, allowed_chars='abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789'):
+ "Generates a random password with the given length and given allowed_chars"
+ # Note that default value of allowed_chars does not have "I" or letters
+ # that look like it -- just to avoid confusion.
+ from random import choice
+ return ''.join([choice(allowed_chars) for i in range(length)])
+
class User(BaseModel):
"""A model with the same attributes and methods as a Django user model.
@@ -38,11 +117,11 @@
classmethod that should be used to retrieve a DjangoUser instance from a App
Engine user object.
"""
- user = db.UserProperty(required=True)
- username = db.StringProperty(required=True)
+ user = db.UserProperty()
+ username = db.StringProperty(required=True, validator=check_unique)
first_name = db.StringProperty()
last_name = db.StringProperty()
- email = db.EmailProperty()
+ email = db.EmailProperty(required=True)
password = db.StringProperty()
is_staff = db.BooleanProperty(default=False, required=True)
is_active = db.BooleanProperty(default=True, required=True)
@@ -69,18 +148,27 @@
return django_user
def set_password(self, raw_password):
- raise NotImplementedError
+ import random
+ algo = 'sha1'
+ salt = get_hexdigest(algo, str(random.random()), str(random.random()))[:5]
+ hsh = get_hexdigest(algo, salt, raw_password)
+ self.password = '%s$%s$%s' % (algo, salt, hsh)
def check_password(self, raw_password):
- raise NotImplementedError
+ """
+ Returns a boolean of whether the raw_password was correct. Handles
+ encryption formats behind the scenes.
+ """
+ return check_password(raw_password, self.password)
def set_unusable_password(self):
- raise NotImplementedError
+ # Sets a value that will never be a valid hash
+ self.password = UNUSABLE_PASSWORD
def has_usable_password(self):
- raise NotImplementedError
+ return self.password != UNUSABLE_PASSWORD
- def get_group_permissions(self):
+ def gee_grnup_permissions(self):
return self.user_permissions
def get_all_permissions(self):
@@ -152,6 +240,7 @@
raise SiteProfileNotAvailable
return self._profile_cache
+User.objects = UserManager(User)
class Group(BaseModel):
"""Group model not fully implemented yet."""
@@ -170,3 +259,68 @@
"""Permission model not fully implemented yet."""
# TODO: Implement this model, requires contenttypes
name = db.StringProperty()
+
+class AnonymousUser(object):
+ id = None
+ username = ''
+ is_staff = False
+ is_active = False
+ is_superuser = False
+ _groups = EmptyManager()
+ _user_permissions = EmptyManager()
+
+ def __init__(self):
+ pass
+
+ def __unicode__(self):
+ return 'AnonymousUser'
+
+ def __str__(self):
+ return unicode(self).encode('utf-8')
+
+ def __eq__(self, other):
+ return isinstance(other, self.__class__)
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+ def __hash__(self):
+ return 1 # instances always return the same hash value
+
+ def save(self):
+ raise NotImplementedError
+
+ def delete(self):
+ raise NotImplementedError
+
+ def set_password(self, raw_password):
+ raise NotImplementedError
+
+ def check_password(self, raw_password):
+ raise NotImplementedError
+
+ def _get_groups(self):
+ return self._groups
+ groups = property(_get_groups)
+
+ def _get_user_permissions(self):
+ return self._user_permissions
+ user_permissions = property(_get_user_permissions)
+
+ def has_perm(self, perm, obj=None):
+ return False
+
+ def has_perms(self, perm_list, obj=None):
+ return False
+
+ def has_module_perms(self, module):
+ return False
+
+ def get_and_delete_messages(self):
+ return []
+
+ def is_anonymous(self):
+ return True
+
+ def is_authenticated(self):
+ return False
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment