1.9.30 sync.
[gae.git] / python / google / appengine / api / users.py
blob2401598241b395a02748c801e19655d428ca9c41
1 #!/usr/bin/env python
3 # Copyright 2007 Google Inc.
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
22 """Python datastore class User to be used as a datastore data type.
24 Classes defined here:
25 User: object representing a user. A user could be a Google Accounts user
26 or a federated user.
27 Error: base exception type
28 UserNotFoundError: UserService exception
29 RedirectTooLongError: UserService exception
30 NotAllowedError: UserService exception
31 """
44 import os
45 from google.appengine.api import apiproxy_stub_map
46 from google.appengine.api import user_service_pb
47 from google.appengine.runtime import apiproxy_errors
50 class Error(Exception):
51 """Base User error type."""
54 class UserNotFoundError(Error):
55 """Raised by User.__init__() when there's no email argument and no user is
56 logged in."""
59 class RedirectTooLongError(Error):
60 """Raised by UserService calls if the generated redirect URL was too long.
61 """
64 class NotAllowedError(Error):
65 """Raised by UserService calls if the requested redirect URL is not allowed.
66 """
69 class User(object):
70 """A user.
72 We provide the email address, nickname, and id for a user.
74 A nickname is a human-readable string which uniquely identifies a Google
75 user, akin to a username. It will be an email address for some users, but
76 not all.
78 A user could be a Google Accounts user or a federated login user.
80 federated_identity and federated_provider are only avaliable for
81 federated users.
82 """
88 __user_id = None
89 __federated_identity = None
90 __federated_provider = None
92 def __init__(self, email=None, _auth_domain=None,
93 _user_id=None, federated_identity=None, federated_provider=None,
94 _strict_mode=True):
95 """Constructor.
97 Args:
98 email: An optional string of the user's email address. It defaults to
99 the current user's email address.
100 federated_identity: federated identity of user. It defaults to the current
101 user's federated identity.
102 federated_provider: federated provider url of user.
104 Raises:
105 UserNotFoundError: Raised if the user is not logged in and both email
106 and federated identity are empty.
115 if _auth_domain is None:
116 _auth_domain = os.environ.get('AUTH_DOMAIN')
117 assert _auth_domain
119 if email is None and federated_identity is None:
120 email = os.environ.get('USER_EMAIL', email)
121 _user_id = os.environ.get('USER_ID', _user_id)
122 federated_identity = os.environ.get('FEDERATED_IDENTITY',
123 federated_identity)
124 federated_provider = os.environ.get('FEDERATED_PROVIDER',
125 federated_provider)
131 if email is None:
132 email = ''
134 if not email and not federated_identity and _strict_mode:
137 raise UserNotFoundError
139 self.__email = email
140 self.__federated_identity = federated_identity
141 self.__federated_provider = federated_provider
142 self.__auth_domain = _auth_domain
143 self.__user_id = _user_id or None
146 def nickname(self):
147 """Return this user's nickname.
149 The nickname will be a unique, human readable identifier for this user
150 with respect to this application. It will be an email address for some
151 users, part of the email address for some users, and the federated identity
152 for federated users who have not asserted an email address.
154 if (self.__email and self.__auth_domain and
155 self.__email.endswith('@' + self.__auth_domain)):
156 suffix_len = len(self.__auth_domain) + 1
157 return self.__email[:-suffix_len]
158 elif self.__federated_identity:
159 return self.__federated_identity
160 else:
161 return self.__email
163 def email(self):
164 """Return this user's email address."""
165 return self.__email
167 def user_id(self):
168 """Return either a permanent unique identifying string or None.
170 If the email address was set explicity, this will return None.
172 return self.__user_id
174 def auth_domain(self):
175 """Return this user's auth domain.
177 This method is internal and should not be used by client applications.
179 return self.__auth_domain
181 def federated_identity(self):
182 """Return this user's federated identity, None if not a federated user."""
183 return self.__federated_identity
185 def federated_provider(self):
186 """Return this user's federated provider, None if not a federated user."""
187 return self.__federated_provider
189 def __unicode__(self):
190 return unicode(self.nickname())
192 def __str__(self):
193 return str(self.nickname())
195 def __repr__(self):
196 values = []
197 if self.__email:
198 values.append("email='%s'" % self.__email)
199 if self.__federated_identity:
200 values.append("federated_identity='%s'" % self.__federated_identity)
201 if self.__user_id:
202 values.append("_user_id='%s'" % self.__user_id)
203 return 'users.User(%s)' % ','.join(values)
205 def __hash__(self):
206 if self.__federated_identity:
207 return hash((self.__federated_identity, self.__auth_domain))
208 else:
209 return hash((self.__email, self.__auth_domain))
211 def __cmp__(self, other):
212 if not isinstance(other, User):
213 return NotImplemented
214 if self.__federated_identity:
215 return cmp((self.__federated_identity, self.__auth_domain),
216 (other.__federated_identity, other.__auth_domain))
217 else:
218 return cmp((self.__email, self.__auth_domain),
219 (other.__email, other.__auth_domain))
222 def create_login_url(dest_url=None, _auth_domain=None,
223 federated_identity=None):
224 """Computes the login URL for redirection.
226 Args:
227 dest_url: String that is the desired final destination URL for the user
228 once login is complete. If 'dest_url' does not have a host
229 specified, we will use the host from the current request.
230 federated_identity: federated_identity is used to trigger OpenId Login
231 flow, an empty value will trigger Google OpenID Login
232 by default.
234 Returns:
235 Login URL as a string. If federated_identity is set, this will be
236 a federated login using the specified identity. If not, this
237 will use Google Accounts.
239 req = user_service_pb.CreateLoginURLRequest()
240 resp = user_service_pb.CreateLoginURLResponse()
241 if dest_url:
242 req.set_destination_url(dest_url)
243 else:
244 req.set_destination_url('')
245 if _auth_domain:
246 req.set_auth_domain(_auth_domain)
247 if federated_identity:
248 req.set_federated_identity(federated_identity)
250 try:
251 apiproxy_stub_map.MakeSyncCall('user', 'CreateLoginURL', req, resp)
252 except apiproxy_errors.ApplicationError, e:
253 if (e.application_error ==
254 user_service_pb.UserServiceError.REDIRECT_URL_TOO_LONG):
255 raise RedirectTooLongError
256 elif (e.application_error ==
257 user_service_pb.UserServiceError.NOT_ALLOWED):
258 raise NotAllowedError
259 else:
260 raise e
261 return resp.login_url()
264 CreateLoginURL = create_login_url
267 def create_logout_url(dest_url, _auth_domain=None):
268 """Computes the logout URL for this request and specified destination URL,
269 for both federated login App and Google Accounts App.
271 Args:
272 dest_url: String that is the desired final destination URL for the user
273 once logout is complete. If 'dest_url' does not have a host
274 specified, we will use the host from the current request.
276 Returns:
277 Logout URL as a string
279 req = user_service_pb.CreateLogoutURLRequest()
280 resp = user_service_pb.CreateLogoutURLResponse()
281 req.set_destination_url(dest_url)
282 if _auth_domain:
283 req.set_auth_domain(_auth_domain)
285 try:
286 apiproxy_stub_map.MakeSyncCall('user', 'CreateLogoutURL', req, resp)
287 except apiproxy_errors.ApplicationError, e:
288 if (e.application_error ==
289 user_service_pb.UserServiceError.REDIRECT_URL_TOO_LONG):
290 raise RedirectTooLongError
291 else:
292 raise e
293 return resp.logout_url()
296 CreateLogoutURL = create_logout_url
299 def get_current_user():
300 try:
301 return User()
302 except UserNotFoundError:
303 return None
306 GetCurrentUser = get_current_user
309 def is_current_user_admin():
310 """Return true if the user making this request is an admin for this
311 application, false otherwise.
313 We specifically make this a separate function, and not a member function of
314 the User class, because admin status is not persisted in the datastore. It
315 only exists for the user making this request right now.
317 return (os.environ.get('USER_IS_ADMIN', '0')) == '1'
320 IsCurrentUserAdmin = is_current_user_admin