|
1 #!/usr/bin/env python |
|
2 # |
|
3 # Copyright 2007 Google Inc. |
|
4 # |
|
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 |
|
8 # |
|
9 # http://www.apache.org/licenses/LICENSE-2.0 |
|
10 # |
|
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. |
|
16 # |
|
17 |
|
18 """Python datastore class User to be used as a datastore data type. |
|
19 |
|
20 Classes defined here: |
|
21 User: object representing a user. |
|
22 Error: base exception type |
|
23 BadRequestError: UserService exception |
|
24 UserNotFoundError: UserService exception |
|
25 BackendError: UserService exception |
|
26 """ |
|
27 |
|
28 |
|
29 |
|
30 |
|
31 |
|
32 |
|
33 import os |
|
34 from google.appengine.api import apiproxy_stub_map |
|
35 from google.appengine.api import user_service_pb |
|
36 from google.appengine.api import api_base_pb |
|
37 from google.appengine.runtime import apiproxy_errors |
|
38 |
|
39 |
|
40 class Error(Exception): |
|
41 """Base User error type.""" |
|
42 |
|
43 |
|
44 class UserNotFoundError(Error): |
|
45 """Raised by User.__init__() when there's no email argument and no user is |
|
46 logged in.""" |
|
47 |
|
48 |
|
49 class RedirectTooLongError(Error): |
|
50 """Raised by UserService calls if the generated redirect URL was too long. |
|
51 """ |
|
52 |
|
53 |
|
54 class User(object): |
|
55 """A user. |
|
56 |
|
57 We provide here the email address, nickname, and auth domain for a user. |
|
58 |
|
59 A nickname is a human-readable string which uniquely identifies a Google |
|
60 user, akin to a username. It will be an email address for some users, but |
|
61 not all. |
|
62 """ |
|
63 |
|
64 |
|
65 def __init__(self, email=None, _auth_domain=None): |
|
66 """Constructor. |
|
67 |
|
68 Args: |
|
69 # email is optional. it defaults to the current user. |
|
70 email: string |
|
71 """ |
|
72 if _auth_domain is None: |
|
73 _auth_domain = os.environ.get('AUTH_DOMAIN') |
|
74 else: |
|
75 assert email is not None |
|
76 |
|
77 assert _auth_domain |
|
78 |
|
79 if email is None: |
|
80 assert 'USER_EMAIL' in os.environ |
|
81 email = os.environ['USER_EMAIL'] |
|
82 |
|
83 if not email: |
|
84 raise UserNotFoundError |
|
85 |
|
86 self.__email = email |
|
87 self.__auth_domain = _auth_domain |
|
88 |
|
89 def nickname(self): |
|
90 """Return this user's nickname. |
|
91 |
|
92 The nickname will be a unique, human readable identifier for this user |
|
93 with respect to this application. It will be an email address for some |
|
94 users, but not all. |
|
95 """ |
|
96 if (self.__email and self.__auth_domain and |
|
97 self.__email.endswith('@' + self.__auth_domain)): |
|
98 suffix_len = len(self.__auth_domain) + 1 |
|
99 return self.__email[:-suffix_len] |
|
100 else: |
|
101 return self.__email |
|
102 |
|
103 def email(self): |
|
104 """Return this user's email address.""" |
|
105 return self.__email |
|
106 |
|
107 def auth_domain(self): |
|
108 """Return this user's auth domain.""" |
|
109 return self.__auth_domain |
|
110 |
|
111 def __unicode__(self): |
|
112 return unicode(self.nickname()) |
|
113 |
|
114 def __str__(self): |
|
115 return str(self.nickname()) |
|
116 |
|
117 def __repr__(self): |
|
118 return "users.User(email='%s')" % self.email() |
|
119 |
|
120 def __hash__(self): |
|
121 return hash((self.__email, self.__auth_domain)) |
|
122 |
|
123 def __cmp__(self, other): |
|
124 if not isinstance(other, User): |
|
125 return NotImplemented |
|
126 return cmp((self.__email, self.__auth_domain), |
|
127 (other.__email, other.__auth_domain)) |
|
128 |
|
129 |
|
130 def create_login_url(dest_url): |
|
131 """Computes the login URL for this request and specified destination URL. |
|
132 |
|
133 Args: |
|
134 dest_url: String that is the desired final destination URL for the user |
|
135 once login is complete. If 'dest_url' does not have a host |
|
136 specified, we will use the host from the current request. |
|
137 |
|
138 Returns: |
|
139 string |
|
140 """ |
|
141 req = user_service_pb.StringProto() |
|
142 resp = user_service_pb.StringProto() |
|
143 req.set_value(dest_url) |
|
144 try: |
|
145 apiproxy_stub_map.MakeSyncCall('user', 'CreateLoginURL', req, resp) |
|
146 except apiproxy_errors.ApplicationError, e: |
|
147 if (e.application_error == |
|
148 user_service_pb.UserServiceError.REDIRECT_URL_TOO_LONG): |
|
149 raise RedirectTooLongError |
|
150 else: |
|
151 raise e |
|
152 return resp.value() |
|
153 |
|
154 CreateLoginURL = create_login_url |
|
155 |
|
156 |
|
157 def create_logout_url(dest_url): |
|
158 """Computes the logout URL for this request and specified destination URL. |
|
159 |
|
160 Args: |
|
161 dest_url: String that is the desired final destination URL for the user |
|
162 once logout is complete. If 'dest_url' does not have a host |
|
163 specified, we will use the host from the current request. |
|
164 |
|
165 Returns: |
|
166 string |
|
167 """ |
|
168 req = user_service_pb.StringProto() |
|
169 resp = user_service_pb.StringProto() |
|
170 req.set_value(dest_url) |
|
171 try: |
|
172 apiproxy_stub_map.MakeSyncCall('user', 'CreateLogoutURL', req, resp) |
|
173 except apiproxy_errors.ApplicationError, e: |
|
174 if (e.application_error == |
|
175 user_service_pb.UserServiceError.REDIRECT_URL_TOO_LONG): |
|
176 raise RedirectTooLongError |
|
177 else: |
|
178 raise e |
|
179 return resp.value() |
|
180 |
|
181 CreateLogoutURL = create_logout_url |
|
182 |
|
183 |
|
184 def get_current_user(): |
|
185 try: |
|
186 return User() |
|
187 except UserNotFoundError: |
|
188 return None |
|
189 |
|
190 GetCurrentUser = get_current_user |
|
191 |
|
192 |
|
193 def is_current_user_admin(): |
|
194 """Return true if the user making this request is an admin for this |
|
195 application, false otherwise. |
|
196 |
|
197 We specifically make this a separate function, and not a member function of |
|
198 the User class, because admin status is not persisted in the datastore. It |
|
199 only exists for the user making this request right now. |
|
200 """ |
|
201 return (os.environ.get('USER_IS_ADMIN', '0')) == "1" |
|
202 |
|
203 IsCurrentUserAdmin = is_current_user_admin |