| OLD | NEW | 
|---|
| (Empty) |  | 
|  | 1 # Copyright (c) 2013 The Chromium Authors. All rights reserved. | 
|  | 2 # Use of this source code is governed by a BSD-style license that can be | 
|  | 3 # found in the LICENSE file. | 
|  | 4 | 
|  | 5 """AuthHandler plugin for gsutil's boto to support LOAS based auth.""" | 
|  | 6 | 
|  | 7 import getpass | 
|  | 8 import json | 
|  | 9 import os | 
|  | 10 import re | 
|  | 11 import subprocess | 
|  | 12 import time | 
|  | 13 import urllib2 | 
|  | 14 | 
|  | 15 from boto.auth_handler import AuthHandler | 
|  | 16 from boto.auth_handler import NotReadyToAuthenticate | 
|  | 17 | 
|  | 18 CMD = ['stubby', '--proto2', 'call', 'blade:sso', 'CorpLogin.Exchange'] | 
|  | 19 | 
|  | 20 STUBBY_CMD = """target: { | 
|  | 21   scope: GAIA_USER | 
|  | 22   name: "%s" | 
|  | 23 } | 
|  | 24 target_credential: { | 
|  | 25   type: OAUTH2_TOKEN | 
|  | 26   oauth2_attributes: { | 
|  | 27     scope: 'https://www.googleapis.com/auth/devstorage.read_only' | 
|  | 28   } | 
|  | 29 }""" | 
|  | 30 | 
|  | 31 COOKIE_LOCATION = os.path.expanduser('~/.devstore_token') | 
|  | 32 | 
|  | 33 TOKEN_EXPIRY = 300 | 
|  | 34 | 
|  | 35 | 
|  | 36 class SSOAuthError(Exception): | 
|  | 37   pass | 
|  | 38 | 
|  | 39 | 
|  | 40 class SSOAuth(AuthHandler): | 
|  | 41   """SSO based auth handler.""" | 
|  | 42 | 
|  | 43   capability = ['google-oauth2', 's3'] | 
|  | 44 | 
|  | 45   def __init__(self, path, config, provider): | 
|  | 46     if provider.name == 'google' and self.has_prodaccess(): | 
|  | 47       # If we don't have a loas token, then bypass this auth handler. | 
|  | 48       if subprocess.call('loas_check', | 
|  | 49                          stdout=subprocess.PIPE, | 
|  | 50                          stderr=subprocess.PIPE): | 
|  | 51         raise NotReadyToAuthenticate() | 
|  | 52     else: | 
|  | 53       raise NotReadyToAuthenticate() | 
|  | 54     self.token = None | 
|  | 55     self.expire = 0 | 
|  | 56 | 
|  | 57   def GetAccessToken(self): | 
|  | 58     """Returns a valid devstore access token. | 
|  | 59 | 
|  | 60     This will return from an in-memory cache if the token is there already, | 
|  | 61     then try a filesystem cache, and then runs a stubby call if none of the | 
|  | 62     caches have a valid token. | 
|  | 63     """ | 
|  | 64     if self.token and self.expire > time.time(): | 
|  | 65       return self.token | 
|  | 66 | 
|  | 67     # Try to retrieve token from filesystem cache. | 
|  | 68     if os.path.exists(COOKIE_LOCATION): | 
|  | 69       last_modified = os.path.getmtime(COOKIE_LOCATION) | 
|  | 70       if time.time() - last_modified < TOKEN_EXPIRY: | 
|  | 71         with open(COOKIE_LOCATION, 'rb') as f: | 
|  | 72           self.token = f.read() | 
|  | 73         self.expire = last_modified + TOKEN_EXPIRY | 
|  | 74         return self.token | 
|  | 75 | 
|  | 76     # If the token is not in either caches, or has expired, then fetch token. | 
|  | 77     username = '%s@google.com' % getpass.getuser() | 
|  | 78     proc = subprocess.Popen(CMD, stdin=subprocess.PIPE, stdout=subprocess.PIPE) | 
|  | 79     out, err = proc.communicate(STUBBY_CMD % username) | 
|  | 80     if proc.returncode: | 
|  | 81       raise SSOAuthError('Stubby returned %d\n%s' % (proc.returncode, err)) | 
|  | 82     token_match = re.search(r'oauth2_token: "(.*)"$', out) | 
|  | 83 | 
|  | 84     if not token_match: | 
|  | 85       raise SSOAuthError('Oauth2 token not found in %s' % out) | 
|  | 86 | 
|  | 87     token = token_match.group(1) | 
|  | 88     self.token = token | 
|  | 89     self.expire = time.time() + TOKEN_EXPIRY | 
|  | 90     with os.fdopen(os.open(COOKIE_LOCATION, | 
|  | 91                            os.O_WRONLY | os.O_CREAT, | 
|  | 92                            0600), 'wb') as f: | 
|  | 93       f.write(token) | 
|  | 94     return token | 
|  | 95 | 
|  | 96   def add_auth(self, http_request): | 
|  | 97     http_request.headers['Authorization'] = 'OAuth %s' % self.GetAccessToken() | 
|  | 98 | 
|  | 99   @staticmethod | 
|  | 100   def has_prodaccess(): | 
|  | 101     for path in os.environ['PATH'].split(os.pathsep): | 
|  | 102       exe_file = os.path.join(path, 'prodaccess') | 
|  | 103       if os.path.exists(exe_file) and os.access(exe_file, os.X_OK): | 
|  | 104         return True | 
|  | 105     return False | 
| OLD | NEW | 
|---|