App Engine Python SDK version 1.9.12
[gae.git] / python / google / appengine / api / oauth / oauth_api.py
blobf0b730f260198c95033fb84f97003c54c47f6cce
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.
19 """OAuth API.
21 A service that enables App Engine apps to validate OAuth requests.
23 Classes defined here:
24 Error: base exception type
25 NotAllowedError: OAuthService exception
26 OAuthRequestError: OAuthService exception
27 InvalidOAuthParametersError: OAuthService exception
28 InvalidOAuthTokenError: OAuthService exception
29 OAuthServiceFailureError: OAuthService exception
30 """
45 import cPickle
46 import os
48 from google.appengine.api import apiproxy_stub_map
49 from google.appengine.api import user_service_pb
50 from google.appengine.api import users
51 from google.appengine.runtime import apiproxy_errors
54 class Error(Exception):
55 """Base error class for this module."""
58 class OAuthRequestError(Error):
59 """Base error type for invalid OAuth requests."""
62 class NotAllowedError(OAuthRequestError):
63 """Raised if the requested URL does not permit OAuth authentication."""
66 class InvalidOAuthParametersError(OAuthRequestError):
67 """Raised if the request was a malformed OAuth request.
69 For example, the request may have omitted a required parameter, contained
70 an invalid signature, or was made by an unknown consumer.
71 """
74 class InvalidOAuthTokenError(OAuthRequestError):
75 """Raised if the request contained an invalid token.
77 For example, the token may have been revoked by the user.
78 """
81 class OAuthServiceFailureError(Error):
82 """Raised if there was a problem communicating with the OAuth service."""
85 def get_current_user(_scope=None):
86 """Returns the User on whose behalf the request was made.
88 Args:
89 _scope: The custom OAuth scope or an iterable of scopes at least one of
90 which is accepted.
92 Returns:
93 User
95 Raises:
96 OAuthRequestError: The request was not a valid OAuth request.
97 OAuthServiceFailureError: An unknown error occurred.
98 """
100 _maybe_call_get_oauth_user(_scope)
101 return _get_user_from_environ()
104 def is_current_user_admin(_scope=None):
105 """Returns true if the User on whose behalf the request was made is an admin.
107 Args:
108 _scope: The custom OAuth scope or an iterable of scopes at least one of
109 which is accepted.
111 Returns:
112 boolean
114 Raises:
115 OAuthRequestError: The request was not a valid OAuth request.
116 OAuthServiceFailureError: An unknown error occurred.
119 _maybe_call_get_oauth_user(_scope)
120 return os.environ.get('OAUTH_IS_ADMIN', '0') == '1'
124 def get_oauth_consumer_key():
125 """Returns the value of the 'oauth_consumer_key' parameter from the request.
127 Returns:
128 string: The value of the 'oauth_consumer_key' parameter from the request,
129 an identifier for the consumer that signed the request.
131 Raises:
132 OAuthRequestError: The request was not a valid OAuth request.
133 OAuthServiceFailureError: An unknown error occurred.
135 req = user_service_pb.CheckOAuthSignatureRequest()
136 resp = user_service_pb.CheckOAuthSignatureResponse()
137 try:
138 apiproxy_stub_map.MakeSyncCall('user', 'CheckOAuthSignature', req, resp)
139 except apiproxy_errors.ApplicationError, e:
140 if (e.application_error ==
141 user_service_pb.UserServiceError.OAUTH_INVALID_REQUEST):
142 raise InvalidOAuthParametersError(e.error_detail)
143 elif (e.application_error ==
144 user_service_pb.UserServiceError.OAUTH_ERROR):
145 raise OAuthServiceFailureError(e.error_detail)
146 else:
147 raise OAuthServiceFailureError(e.error_detail)
148 return resp.oauth_consumer_key()
151 def get_client_id(_scope):
152 """Returns the value of OAuth2 Client ID from an OAuth2 request.
154 Args:
155 _scope: The custom OAuth scope or an iterable of scopes at least one of
156 which is accepted.
158 Returns:
159 string: The value of Client ID.
161 Raises:
162 OAuthRequestError: The request was not a valid OAuth2 request.
163 OAuthServiceFailureError: An unknow error occurred.
165 _maybe_call_get_oauth_user(_scope)
166 return _get_client_id_from_environ()
169 def get_authorized_scopes(scope):
170 """Returns authorized scopes from input scopes.
172 Args:
173 scope: The custom OAuth scope or an iterable of scopes at least one of
174 which is accepted.
176 Returns:
177 list: A list of authorized OAuth2 scopes
179 Raises:
180 OAuthRequestError: The request was not a valid OAuth2 request.
181 OAuthServiceFailureError: An unknow error occurred
183 _maybe_call_get_oauth_user(scope)
184 return _get_authorized_scopes_from_environ()
187 def _maybe_call_get_oauth_user(scope):
188 """Makes an GetOAuthUser RPC and stores the results in os.environ.
190 This method will only make the RPC if 'OAUTH_ERROR_CODE' has not already
191 been set or 'OAUTH_LAST_SCOPE' is different to str(_scopes).
193 Args:
194 scope: The custom OAuth scope or an iterable of scopes at least one of
195 which is accepted.
198 if not scope:
199 scope_str = ''
200 elif isinstance(scope, basestring):
201 scope_str = scope
202 else:
203 scope_str = str(sorted(scope))
204 if ('OAUTH_ERROR_CODE' not in os.environ or
205 os.environ.get('OAUTH_LAST_SCOPE', None) != scope_str or
206 os.environ.get('TESTONLY_OAUTH_SKIP_CACHE')):
207 req = user_service_pb.GetOAuthUserRequest()
208 if scope:
209 if isinstance(scope, basestring):
210 req.add_scopes(scope)
211 else:
212 req.scopes_list().extend(scope)
214 resp = user_service_pb.GetOAuthUserResponse()
215 try:
216 apiproxy_stub_map.MakeSyncCall('user', 'GetOAuthUser', req, resp)
217 os.environ['OAUTH_EMAIL'] = resp.email()
218 os.environ['OAUTH_AUTH_DOMAIN'] = resp.auth_domain()
219 os.environ['OAUTH_USER_ID'] = resp.user_id()
220 os.environ['OAUTH_CLIENT_ID'] = resp.client_id()
221 os.environ['OAUTH_AUTHORIZED_SCOPES'] = cPickle.dumps(
222 list(resp.scopes_list()), cPickle.HIGHEST_PROTOCOL)
223 if resp.is_admin():
224 os.environ['OAUTH_IS_ADMIN'] = '1'
225 else:
226 os.environ['OAUTH_IS_ADMIN'] = '0'
227 os.environ['OAUTH_ERROR_CODE'] = ''
228 except apiproxy_errors.ApplicationError, e:
229 os.environ['OAUTH_ERROR_CODE'] = str(e.application_error)
230 os.environ['OAUTH_ERROR_DETAIL'] = e.error_detail
231 os.environ['OAUTH_LAST_SCOPE'] = scope_str
232 _maybe_raise_exception()
235 def _maybe_raise_exception():
236 """Raises an error if one has been stored in os.environ.
238 This method requires that 'OAUTH_ERROR_CODE' has already been set (an empty
239 string indicates that there is no actual error).
241 assert 'OAUTH_ERROR_CODE' in os.environ
242 error = os.environ['OAUTH_ERROR_CODE']
243 if error:
244 assert 'OAUTH_ERROR_DETAIL' in os.environ
245 error_detail = os.environ['OAUTH_ERROR_DETAIL']
246 if error == str(user_service_pb.UserServiceError.NOT_ALLOWED):
247 raise NotAllowedError(error_detail)
248 elif error == str(user_service_pb.UserServiceError.OAUTH_INVALID_REQUEST):
249 raise InvalidOAuthParametersError(error_detail)
250 elif error == str(user_service_pb.UserServiceError.OAUTH_INVALID_TOKEN):
251 raise InvalidOAuthTokenError(error_detail)
252 elif error == str(user_service_pb.UserServiceError.OAUTH_ERROR):
253 raise OAuthServiceFailureError(error_detail)
254 else:
255 raise OAuthServiceFailureError(error_detail)
258 def _get_user_from_environ():
259 """Returns a User based on values stored in os.environ.
261 This method requires that 'OAUTH_EMAIL', 'OAUTH_AUTH_DOMAIN', and
262 'OAUTH_USER_ID' have already been set.
264 Returns:
265 User
267 assert 'OAUTH_EMAIL' in os.environ
268 assert 'OAUTH_AUTH_DOMAIN' in os.environ
269 assert 'OAUTH_USER_ID' in os.environ
270 return users.User(email=os.environ['OAUTH_EMAIL'],
271 _auth_domain=os.environ['OAUTH_AUTH_DOMAIN'],
272 _user_id=os.environ['OAUTH_USER_ID'])
275 def _get_client_id_from_environ():
276 """Returns Client ID based on values stored in os.environ.
278 This method requires that 'OAUTH_CLIENT_ID' has already been set.
280 Returns:
281 string: the value of Client ID.
283 assert 'OAUTH_CLIENT_ID' in os.environ
284 return os.environ['OAUTH_CLIENT_ID']
287 def _get_authorized_scopes_from_environ():
288 """Returns authorized scopes based on values stored in os.environ.
290 This method requires that 'OAUTH_AUTHORIZED_SCOPES' has already been set.
292 Returns:
293 list: the list of OAuth scopes.
295 assert 'OAUTH_AUTHORIZED_SCOPES' in os.environ
296 return cPickle.loads(os.environ['OAUTH_AUTHORIZED_SCOPES'])