Index: appengine_django/auth/__init__.py =================================================================== --- appengine_django/auth/__init__.py (Revision 0) +++ appengine_django/auth/__init__.py (Revision 0) @@ -0,0 +1,215 @@ +# 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. + +To use this authentication module in an Appengine/Django project: + - add 'appengine_django.auth.middleware.AuthenticationMiddleware' + to MIDDLEWARE_CLASSES + - add 'appengine_django.auth.context_processors.auth' + to TEMPLATE_CONTEXT_PROCESSORS + +Limitations: + - all user manipulation methods are not available + - all user permissions methods are not available + - user.get_profile method not implemented + - db.UserProperty returns user.User instead of DjangoUser +""" + +from datetime import datetime +import urllib + +from django.core import mail +from django.db.models.manager import EmptyManager +from django.http import HttpResponseRedirect +from django.template import Node +from django.utils.encoding import smart_str + +from google.appengine.api import datastore_types +from google.appengine.api import users +from google.appengine.ext.webapp import template + +from appengine_django import models + +register = template.create_template_register() +template.register_template_library("appengine_django.auth") + + +class CallableString(str): + """String subclass that returns the string if it's called as a function. + This class is required for the user's email attribute. + """ + def __call__(self): + return str(self) + + +class DjangoUser(users.User): + """App Engine User subclass that mimics the behavior of an Django user. + + This class is added to datastore_types when importing this module + to make it usable for db.UserProperty(). + """ + id = None + is_active = True + first_name = None + last_name = None + password = None + _groups = EmptyManager() + _user_permissions = EmptyManager() + + def __init__(self, *args, **kw): + super(DjangoUser, self).__init__(*args, **kw) + self.email = CallableString(self._User__email) + + def __unicode__(self): + return self.username + + def __str__(self): + return unicode(self).encode('utf-8') + + def _get_username(self): + return self.nickname() + username = property(fget=_get_username) + + def _is_superuser(self): + return users.is_current_user_admin() + is_superuser = property(fget=_is_superuser) + is_staff = property(fget=_is_superuser) + + def _default_now(self): + return datetime.now() + last_login = property(fget=_default_now) + date_joined = property(fget=_default_now) + + 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 set_unusable_password(self): + raise NotImplementedError + + def has_usable_password(self): + 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 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): + return [] + + 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): + return "" + + 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): + raise NotImplementedError +datastore_types._PROPERTY_TYPES.append(DjangoUser) + + +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 + + +class AuthLoginUrlsNode(Node): + """Template node that creates an appengine login or logout URL. + + If create_login_url is True the appengine'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.tag("auth_login_url", auth_login_urls) +register.tag("auth_logout_url", auth_login_urls)