Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(156)

Side by Side Diff: client/utils/auth_server.py

Issue 2963103002: Always treat TokenError as fatal error. (Closed)
Patch Set: Created 3 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « client/tests/auth_server_test.py ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright 2016 The LUCI Authors. All rights reserved. 2 # Copyright 2016 The LUCI Authors. All rights reserved.
3 # Use of this source code is governed under the Apache License, Version 2.0 3 # Use of this source code is governed under the Apache License, Version 2.0
4 # that can be found in the LICENSE file. 4 # that can be found in the LICENSE file.
5 5
6 import base64 6 import base64
7 import BaseHTTPServer 7 import BaseHTTPServer
8 import collections 8 import collections
9 import json 9 import json
10 import logging 10 import logging
11 import os 11 import os
12 import re 12 import re
13 import SocketServer 13 import SocketServer
14 import sys 14 import sys
15 import threading 15 import threading
16 import time 16 import time
17 17
18 18
19 # OAuth access token with its expiration time. 19 # OAuth access token with its expiration time.
20 AccessToken = collections.namedtuple('AccessToken', [ 20 AccessToken = collections.namedtuple('AccessToken', [
21 'access_token', # urlsafe str with the token 21 'access_token', # urlsafe str with the token
22 'expiry', # expiration time as unix timestamp in seconds 22 'expiry', # expiration time as unix timestamp in seconds
23 ]) 23 ])
24 24
25 25
26 class TokenError(Exception): 26 class TokenError(Exception):
27 """Raised by TokenProvider if the token can't be created. 27 """Raised by TokenProvider if the token can't be created (fatal error).
28 28
29 See TokenProvider docs for more info. 29 See TokenProvider docs for more info.
30 """ 30 """
31 31
32 def __init__(self, code, msg, fatal=False): 32 def __init__(self, code, msg):
33 super(TokenError, self).__init__(msg) 33 super(TokenError, self).__init__(msg)
34 self.code = code 34 self.code = code
35 self.fatal = fatal
36 35
37 36
38 class RPCError(Exception): 37 class RPCError(Exception):
39 """Raised by LocalAuthServer RPC handlers to reply with HTTP error status.""" 38 """Raised by LocalAuthServer RPC handlers to reply with HTTP error status."""
40 39
41 def __init__(self, code, msg): 40 def __init__(self, code, msg):
42 super(RPCError, self).__init__(msg) 41 super(RPCError, self).__init__(msg)
43 self.code = code 42 self.code = code
44 43
45 44
46 class TokenProvider(object): 45 class TokenProvider(object):
47 """Interface for an object that can create OAuth tokens on demand. 46 """Interface for an object that can create OAuth tokens on demand.
48 47
49 Defined as a concrete class only for documentation purposes. 48 Defined as a concrete class only for documentation purposes.
50 """ 49 """
51 50
52 def generate_token(self, account_id, scopes): 51 def generate_token(self, account_id, scopes):
53 """Generates a new access token with given scopes. 52 """Generates a new access token with given scopes.
54 53
55 Will be called from multiple threads (possibly concurrently) whenever 54 Will be called from multiple threads (possibly concurrently) whenever
56 LocalAuthServer needs to refresh a token with particular scopes. 55 LocalAuthServer needs to refresh a token with particular scopes.
57 56
58 Can rise RPCError exceptions. They will be immediately converted to 57 Can rise RPCError exceptions. They will be immediately converted to
59 corresponding RPC error replies (e.g. HTTP 500). This is appropriate for 58 corresponding RPC error replies (e.g. HTTP 500). This is appropriate for
60 low-level or transient errors. 59 low-level or transient errors.
61 60
62 Can also raise TokenError. It will be converted to GetOAuthToken reply with 61 Can also raise TokenError. It will be converted to GetOAuthToken reply with
63 non-zero error_code. It will also optionally be cached, so that the provider 62 non-zero error_code. It will also be cached, so that the provider would
64 would never be called again for the same set of scopes. This is appropriate 63 never be called again for the same set of scopes. This is appropriate for
65 for high-level or fatal errors. 64 high-level fatal errors.
66 65
67 Returns AccessToken on success. 66 Returns AccessToken on success.
68 """ 67 """
69 raise NotImplementedError() 68 raise NotImplementedError()
70 69
71 70
72 class LocalAuthServer(object): 71 class LocalAuthServer(object):
73 """LocalAuthServer handles /rpc/LuciLocalAuthService.* requests. 72 """LocalAuthServer handles /rpc/LuciLocalAuthService.* requests.
74 73
75 It exposes an HTTP JSON RPC API that is used by task processes to grab an 74 It exposes an HTTP JSON RPC API that is used by task processes to grab an
(...skipping 168 matching lines...) Expand 10 before | Expand all | Expand 10 after
244 # Do the refresh outside of the RPC server lock to unblock other clients 243 # Do the refresh outside of the RPC server lock to unblock other clients
245 # that are hitting the cache. The token provider should implement its own 244 # that are hitting the cache. The token provider should implement its own
246 # synchronization. 245 # synchronization.
247 if need_refresh: 246 if need_refresh:
248 try: 247 try:
249 tok_or_err = token_provider.generate_token(account_id, scopes) 248 tok_or_err = token_provider.generate_token(account_id, scopes)
250 assert isinstance(tok_or_err, AccessToken), tok_or_err 249 assert isinstance(tok_or_err, AccessToken), tok_or_err
251 except TokenError as exc: 250 except TokenError as exc:
252 tok_or_err = exc 251 tok_or_err = exc
253 # Cache the token or fatal errors (to avoid useless retry later). 252 # Cache the token or fatal errors (to avoid useless retry later).
254 if isinstance(tok_or_err, AccessToken) or tok_or_err.fatal: 253 with self._lock:
255 with self._lock: 254 if not self._server:
256 if not self._server: 255 raise RPCError(503, 'Stopped already.')
257 raise RPCError(503, 'Stopped already.') 256 self._cache[cache_key] = tok_or_err
258 self._cache[cache_key] = tok_or_err
259 257
260 # Done. 258 # Done.
261 if isinstance(tok_or_err, AccessToken): 259 if isinstance(tok_or_err, AccessToken):
262 return { 260 return {
263 'access_token': tok_or_err.access_token, 261 'access_token': tok_or_err.access_token,
264 'expiry': int(tok_or_err.expiry), 262 'expiry': int(tok_or_err.expiry),
265 } 263 }
266 if isinstance(tok_or_err, TokenError): 264 if isinstance(tok_or_err, TokenError):
267 return { 265 return {
268 'error_code': tok_or_err.code, 266 'error_code': tok_or_err.code,
(...skipping 153 matching lines...) Expand 10 before | Expand all | Expand 10 after
422 while True: 420 while True:
423 time.sleep(1) 421 time.sleep(1)
424 except KeyboardInterrupt: 422 except KeyboardInterrupt:
425 pass 423 pass
426 finally: 424 finally:
427 server.stop() 425 server.stop()
428 426
429 427
430 if __name__ == '__main__': 428 if __name__ == '__main__':
431 testing_main() 429 testing_main()
OLDNEW
« no previous file with comments | « client/tests/auth_server_test.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698