summaryrefslogtreecommitdiffstats
path: root/appengine_django/auth
diff options
context:
space:
mode:
Diffstat (limited to 'appengine_django/auth')
-rw-r--r--appengine_django/auth/__init__.py25
-rw-r--r--appengine_django/auth/__init__.pycbin0 -> 478 bytes
-rw-r--r--appengine_django/auth/decorators.py31
-rw-r--r--appengine_django/auth/decorators.pycbin0 -> 1008 bytes
-rw-r--r--appengine_django/auth/middleware.py36
-rw-r--r--appengine_django/auth/middleware.pycbin0 -> 1428 bytes
-rw-r--r--appengine_django/auth/models.py172
-rw-r--r--appengine_django/auth/models.pycbin0 -> 7999 bytes
-rw-r--r--appengine_django/auth/templatetags.py62
-rw-r--r--appengine_django/auth/templatetags.pycbin0 -> 2124 bytes
-rw-r--r--appengine_django/auth/tests.py58
11 files changed, 384 insertions, 0 deletions
diff --git a/appengine_django/auth/__init__.py b/appengine_django/auth/__init__.py
new file mode 100644
index 0000000..d2db207
--- /dev/null
+++ b/appengine_django/auth/__init__.py
@@ -0,0 +1,25 @@
+# Copyright 2008 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+Authentication module that mimics the behavior of Django's authentication
+implementation.
+
+Limitations:
+ - all user permissions methods are not available (requires contenttypes)
+"""
+
+from django.template import add_to_builtins
+
+add_to_builtins('appengine_django.auth.templatetags')
diff --git a/appengine_django/auth/__init__.pyc b/appengine_django/auth/__init__.pyc
new file mode 100644
index 0000000..4d1edc3
--- /dev/null
+++ b/appengine_django/auth/__init__.pyc
Binary files differ
diff --git a/appengine_django/auth/decorators.py b/appengine_django/auth/decorators.py
new file mode 100644
index 0000000..d897c24
--- /dev/null
+++ b/appengine_django/auth/decorators.py
@@ -0,0 +1,31 @@
+# Copyright 2008 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Decorators for the authentication framework."""
+
+from django.http import HttpResponseRedirect
+
+from google.appengine.api import users
+
+
+def login_required(function):
+ """Implementation of Django's login_required decorator.
+
+ The login redirect URL is always set to request.path
+ """
+ def login_required_wrapper(request, *args, **kw):
+ if request.user.is_authenticated():
+ return function(request, *args, **kw)
+ return HttpResponseRedirect(users.create_login_url(request.path))
+ return login_required_wrapper
diff --git a/appengine_django/auth/decorators.pyc b/appengine_django/auth/decorators.pyc
new file mode 100644
index 0000000..477c819
--- /dev/null
+++ b/appengine_django/auth/decorators.pyc
Binary files differ
diff --git a/appengine_django/auth/middleware.py b/appengine_django/auth/middleware.py
new file mode 100644
index 0000000..a727e47
--- /dev/null
+++ b/appengine_django/auth/middleware.py
@@ -0,0 +1,36 @@
+# Copyright 2008 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# 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
+
+
+class LazyUser(object):
+ def __get__(self, request, obj_type=None):
+ if not hasattr(request, '_cached_user'):
+ user = users.get_current_user()
+ if user:
+ request._cached_user = User.get_djangouser_for_user(user)
+ else:
+ request._cached_user = AnonymousUser()
+ return request._cached_user
+
+
+class AuthenticationMiddleware(object):
+ def process_request(self, request):
+ request.__class__.user = LazyUser()
+ return None
diff --git a/appengine_django/auth/middleware.pyc b/appengine_django/auth/middleware.pyc
new file mode 100644
index 0000000..ff36de8
--- /dev/null
+++ b/appengine_django/auth/middleware.pyc
Binary files differ
diff --git a/appengine_django/auth/models.py b/appengine_django/auth/models.py
new file mode 100644
index 0000000..d93e240
--- /dev/null
+++ b/appengine_django/auth/models.py
@@ -0,0 +1,172 @@
+# Copyright 2008 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+App Engine compatible models for the Django authentication framework.
+"""
+
+from django.core import mail
+from django.core.exceptions import ImproperlyConfigured
+from django.db import models
+from django.utils.encoding import smart_str
+import urllib
+
+from django.db.models.manager import EmptyManager
+
+from google.appengine.api import users
+from google.appengine.ext import db
+
+from appengine_django.models import BaseModel
+
+
+class User(BaseModel):
+ """A model with the same attributes and methods as a Django user model.
+
+ The model has two additions. The first addition is a 'user' attribute which
+ references a App Engine user. The second is the 'get_djangouser_for_user'
+ 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)
+ first_name = db.StringProperty()
+ last_name = db.StringProperty()
+ email = db.EmailProperty()
+ password = db.StringProperty()
+ is_staff = db.BooleanProperty(default=False, required=True)
+ is_active = db.BooleanProperty(default=True, required=True)
+ is_superuser = db.BooleanProperty(default=False, required=True)
+ last_login = db.DateTimeProperty(auto_now_add=True, required=True)
+ date_joined = db.DateTimeProperty(auto_now_add=True, required=True)
+ groups = EmptyManager()
+ user_permissions = EmptyManager()
+
+ def __unicode__(self):
+ return self.username
+
+ def __str__(self):
+ return unicode(self).encode('utf-8')
+
+ @classmethod
+ def get_djangouser_for_user(cls, user):
+ query = cls.all().filter("user =", user)
+ if query.count() == 0:
+ django_user = cls(user=user, email=user.email(), username=user.nickname())
+ django_user.save()
+ else:
+ django_user = query.get()
+ return django_user
+
+ def set_password(self, raw_password):
+ raise NotImplementedError
+
+ def check_password(self, raw_password):
+ raise NotImplementedError
+
+ def set_unusable_password(self):
+ raise NotImplementedError
+
+ def has_usable_password(self):
+ raise NotImplementedError
+
+ def get_group_permissions(self):
+ return self.user_permissions
+
+ def get_all_permissions(self):
+ return self.user_permissions
+
+ def has_perm(self, perm):
+ return False
+
+ def has_perms(self, perm_list):
+ return False
+
+ def has_module_perms(self, module):
+ return False
+
+ def get_and_delete_messages(self):
+ """Gets and deletes messages for this user"""
+ msgs = []
+ for msg in self.message_set:
+ msgs.append(msg)
+ msg.delete()
+ return msgs
+
+ def is_anonymous(self):
+ """Always return False"""
+ return False
+
+ def is_authenticated(self):
+ """Always return True"""
+ return True
+
+ def get_absolute_url(self):
+ return "/users/%s/" % urllib.quote(smart_str(self.username))
+
+ def get_full_name(self):
+ full_name = u'%s %s' % (self.first_name, self.last_name)
+ return full_name.strip()
+
+ def email_user(self, subject, message, from_email):
+ """Sends an email to this user.
+
+ According to the App Engine email API the from_email must be the
+ email address of a registered administrator for the application.
+ """
+ mail.send_mail(subject,
+ message,
+ from_email,
+ [self.email])
+
+ def get_profile(self):
+ """
+ Returns site-specific profile for this user. Raises
+ SiteProfileNotAvailable if this site does not allow profiles.
+
+ When using the App Engine authentication framework, users are created
+ automatically.
+ """
+ from django.contrib.auth.models import SiteProfileNotAvailable
+ if not hasattr(self, '_profile_cache'):
+ from django.conf import settings
+ if not hasattr(settings, "AUTH_PROFILE_MODULE"):
+ raise SiteProfileNotAvailable
+ try:
+ app_label, model_name = settings.AUTH_PROFILE_MODULE.split('.')
+ model = models.get_model(app_label, model_name)
+ self._profile_cache = model.all().filter("user =", self).get()
+ if not self._profile_cache:
+ raise model.DoesNotExist
+ except (ImportError, ImproperlyConfigured):
+ raise SiteProfileNotAvailable
+ return self._profile_cache
+
+
+class Group(BaseModel):
+ """Group model not fully implemented yet."""
+ # TODO: Implement this model, requires contenttypes
+ name = db.StringProperty()
+ permissions = EmptyManager()
+
+
+class Message(BaseModel):
+ """User message model"""
+ user = db.ReferenceProperty(User)
+ message = db.TextProperty()
+
+
+class Permission(BaseModel):
+ """Permission model not fully implemented yet."""
+ # TODO: Implement this model, requires contenttypes
+ name = db.StringProperty()
diff --git a/appengine_django/auth/models.pyc b/appengine_django/auth/models.pyc
new file mode 100644
index 0000000..0a73b9c
--- /dev/null
+++ b/appengine_django/auth/models.pyc
Binary files differ
diff --git a/appengine_django/auth/templatetags.py b/appengine_django/auth/templatetags.py
new file mode 100644
index 0000000..8237890
--- /dev/null
+++ b/appengine_django/auth/templatetags.py
@@ -0,0 +1,62 @@
+# Copyright 2008 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+Template tags for the auth module. These are inserted into Django as "built-in"
+tags so you do not need to use the load statement in your template to get
+access to them.
+"""
+
+from django.template import Library
+from django.template import Node
+
+from google.appengine.api import users
+
+
+class AuthLoginUrlsNode(Node):
+ """Template node that creates an App Engine login or logout URL.
+
+ If create_login_url is True the App Engine's login URL is rendered into
+ the template, otherwise the logout URL.
+ """
+ def __init__(self, create_login_url, redirect):
+ self.redirect = redirect
+ self.create_login_url = create_login_url
+
+ def render(self, context):
+ if self.create_login_url:
+ return users.create_login_url(self.redirect)
+ else:
+ return users.create_logout_url(self.redirect)
+
+
+def auth_login_urls(parser, token):
+ """Template tag registered as 'auth_login_url' and 'auth_logout_url'
+ when the module is imported.
+
+ Both tags take an optional argument that specifies the redirect URL and
+ defaults to '/'.
+ """
+ bits = list(token.split_contents())
+ if len(bits) == 2:
+ redirect = bits[1]
+ else:
+ redirect = "/"
+ login = bits[0] == "auth_login_url"
+ return AuthLoginUrlsNode(login, redirect)
+
+
+register = Library()
+register.tag("auth_login_url", auth_login_urls)
+register.tag("auth_logout_url", auth_login_urls)
diff --git a/appengine_django/auth/templatetags.pyc b/appengine_django/auth/templatetags.pyc
new file mode 100644
index 0000000..7121f59
--- /dev/null
+++ b/appengine_django/auth/templatetags.pyc
Binary files differ
diff --git a/appengine_django/auth/tests.py b/appengine_django/auth/tests.py
new file mode 100644
index 0000000..20aecfa
--- /dev/null
+++ b/appengine_django/auth/tests.py
@@ -0,0 +1,58 @@
+# Copyright 2008 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+BASIC_TESTS = """
+>>> from google.appengine.api import users
+>>> from models import User, AnonymousUser
+>>> appengine_user = users.User("test@example.com")
+>>> django_user = User.get_djangouser_for_user(appengine_user)
+>>> django_user.email == appengine_user.email()
+True
+>>> django_user.username == appengine_user.nickname()
+True
+>>> django_user.user == appengine_user
+True
+
+>>> django_user.username = 'test2'
+>>> key = django_user.save()
+>>> django_user.username == 'test2'
+True
+
+>>> django_user2 = User.get_djangouser_for_user(appengine_user)
+>>> django_user2 == django_user
+True
+
+>>> django_user.is_authenticated()
+True
+>>> django_user.is_staff
+False
+>>> django_user.is_active
+True
+
+>>> a = AnonymousUser()
+>>> a.is_authenticated()
+False
+>>> a.is_staff
+False
+>>> a.is_active
+False
+>>> a.groups.all()
+[]
+>>> a.user_permissions.all()
+[]
+
+
+"""
+
+__test__ = {'BASIC_TESTS': BASIC_TESTS}