| OLD | NEW |
| (Empty) | |
| 1 # Copyright 2008 Google Inc. |
| 2 # |
| 3 # Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 # you may not use this file except in compliance with the License. |
| 5 # You may obtain a copy of the License at |
| 6 # |
| 7 # http://www.apache.org/licenses/LICENSE-2.0 |
| 8 # |
| 9 # Unless required by applicable law or agreed to in writing, software |
| 10 # distributed under the License is distributed on an "AS IS" BASIS, |
| 11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 # See the License for the specific language governing permissions and |
| 13 # limitations under the License. |
| 14 |
| 15 """ |
| 16 Authentication module that mimics the behavior of Django's authentication |
| 17 implementation. |
| 18 |
| 19 To use this authentication module in an Appengine/Django project: |
| 20 - add 'appengine_django.auth.middleware.AuthenticationMiddleware' |
| 21 to MIDDLEWARE_CLASSES |
| 22 - add 'appengine_django.auth.context_processors.auth' |
| 23 to TEMPLATE_CONTEXT_PROCESSORS |
| 24 |
| 25 Limitations: |
| 26 - all user manipulation methods are not available |
| 27 - all user permissions methods are not available |
| 28 - user.get_profile method not implemented |
| 29 - db.UserProperty returns user.User instead of DjangoUser |
| 30 """ |
| 31 |
| 32 from datetime import datetime |
| 33 import urllib |
| 34 |
| 35 from django.core import mail |
| 36 from django.db.models.manager import EmptyManager |
| 37 from django.http import HttpResponseRedirect |
| 38 from django.template import Node |
| 39 from django.utils.encoding import smart_str |
| 40 |
| 41 from google.appengine.api import datastore_types |
| 42 from google.appengine.api import users |
| 43 from google.appengine.ext.webapp import template |
| 44 |
| 45 from appengine_django import models |
| 46 |
| 47 register = template.create_template_register() |
| 48 template.register_template_library("appengine_django.auth") |
| 49 |
| 50 |
| 51 class CallableString(str): |
| 52 """String subclass that returns the string if it's called as a function. |
| 53 This class is required for the user's email attribute. |
| 54 """ |
| 55 def __call__(self): |
| 56 return str(self) |
| 57 |
| 58 |
| 59 class DjangoUser(users.User): |
| 60 """App Engine User subclass that mimics the behavior of an Django user. |
| 61 |
| 62 This class is added to datastore_types when importing this module |
| 63 to make it usable for db.UserProperty(). |
| 64 """ |
| 65 id = None |
| 66 is_active = True |
| 67 first_name = None |
| 68 last_name = None |
| 69 password = None |
| 70 _groups = EmptyManager() |
| 71 _user_permissions = EmptyManager() |
| 72 |
| 73 def __init__(self, *args, **kw): |
| 74 super(DjangoUser, self).__init__(*args, **kw) |
| 75 self.email = CallableString(self._User__email) |
| 76 |
| 77 def __unicode__(self): |
| 78 return self.username |
| 79 |
| 80 def __str__(self): |
| 81 return unicode(self).encode('utf-8') |
| 82 |
| 83 def _get_username(self): |
| 84 return self.nickname() |
| 85 username = property(fget=_get_username) |
| 86 |
| 87 def _is_superuser(self): |
| 88 return users.is_current_user_admin() |
| 89 is_superuser = property(fget=_is_superuser) |
| 90 is_staff = property(fget=_is_superuser) |
| 91 |
| 92 def _default_now(self): |
| 93 return datetime.now() |
| 94 last_login = property(fget=_default_now) |
| 95 date_joined = property(fget=_default_now) |
| 96 |
| 97 def save(self): |
| 98 raise NotImplementedError |
| 99 |
| 100 def delete(self): |
| 101 raise NotImplementedError |
| 102 |
| 103 def set_password(self, raw_password): |
| 104 raise NotImplementedError |
| 105 |
| 106 def check_password(self, raw_password): |
| 107 raise NotImplementedError |
| 108 |
| 109 def set_unusable_password(self): |
| 110 raise NotImplementedError |
| 111 |
| 112 def has_usable_password(self): |
| 113 raise NotImplementedError |
| 114 |
| 115 def _get_groups(self): |
| 116 return self._groups |
| 117 groups = property(_get_groups) |
| 118 |
| 119 def _get_user_permissions(self): |
| 120 return self._user_permissions |
| 121 user_permissions = property(_get_user_permissions) |
| 122 |
| 123 def get_group_permissions(self): |
| 124 return self._user_permissions |
| 125 |
| 126 def get_all_permissions(self): |
| 127 return self._user_permissions |
| 128 |
| 129 def has_perm(self, perm): |
| 130 return False |
| 131 |
| 132 def has_perms(self, perm_list): |
| 133 return False |
| 134 |
| 135 def has_module_perms(self, module): |
| 136 return False |
| 137 |
| 138 def get_and_delete_messages(self): |
| 139 return [] |
| 140 |
| 141 def is_anonymous(self): |
| 142 """Always return False""" |
| 143 return False |
| 144 |
| 145 def is_authenticated(self): |
| 146 """Always return True""" |
| 147 return True |
| 148 |
| 149 def get_absolute_url(self): |
| 150 return "/users/%s/" % urllib.quote(smart_str(self.username)) |
| 151 |
| 152 def get_full_name(self): |
| 153 return "" |
| 154 |
| 155 def email_user(self, subject, message, from_email): |
| 156 """Sends an email to this user. |
| 157 |
| 158 According to the App Engine email API the from_email must be the |
| 159 email address of a registered administrator for the application. |
| 160 """ |
| 161 mail.send_mail(subject, |
| 162 message, |
| 163 from_email, |
| 164 [self.email]) |
| 165 |
| 166 def get_profile(self): |
| 167 raise NotImplementedError |
| 168 datastore_types._PROPERTY_TYPES.append(DjangoUser) |
| 169 |
| 170 |
| 171 def login_required(function): |
| 172 """Implementation of Django's login_required decorator. |
| 173 |
| 174 The login redirect URL is always set to request.path |
| 175 """ |
| 176 def login_required_wrapper(request, *args, **kw): |
| 177 if request.user.is_authenticated(): |
| 178 return function(request, *args, **kw) |
| 179 return HttpResponseRedirect(users.create_login_url(request.path)) |
| 180 return login_required_wrapper |
| 181 |
| 182 |
| 183 class AuthLoginUrlsNode(Node): |
| 184 """Template node that creates an appengine login or logout URL. |
| 185 |
| 186 If create_login_url is True the appengine's login URL is rendered into |
| 187 the template, otherwise the logout URL. |
| 188 """ |
| 189 def __init__(self, create_login_url, redirect): |
| 190 self.redirect = redirect |
| 191 self.create_login_url = create_login_url |
| 192 |
| 193 def render(self, context): |
| 194 if self.create_login_url: |
| 195 return users.create_login_url(self.redirect) |
| 196 else: |
| 197 return users.create_logout_url(self.redirect) |
| 198 |
| 199 |
| 200 def auth_login_urls(parser, token): |
| 201 """Template tag registered as 'auth_login_url' and 'auth_logout_url' |
| 202 when the module is imported. |
| 203 |
| 204 Both tags take an optional argument that specifies the redirect URL and |
| 205 defaults to '/'. |
| 206 """ |
| 207 bits = list(token.split_contents()) |
| 208 if len(bits) == 2: |
| 209 redirect = bits[1] |
| 210 else: |
| 211 redirect = "/" |
| 212 login = bits[0] == "auth_login_url" |
| 213 return AuthLoginUrlsNode(login, redirect) |
| 214 register.tag("auth_login_url", auth_login_urls) |
| 215 register.tag("auth_logout_url", auth_login_urls) |
| OLD | NEW |