def do_fetch(): # client_secret is not really a secret in that case. So an attacker can # impersonate service's identity in OAuth2 flow. But that's generally # fine as long as a list of allowed redirect_uri's associated with client_id # is limited to 'localhost' or 'urn:ietf:wg:oauth:2.0:oob'. In that case # attacker needs some process running on user's machine to successfully # complete the flow and grab access_token. When you have malicious code # running on your machine you're screwed anyway. response = requests.get( '%s/auth/api/v1/server/oauth_config' % urlhost.rstrip('/'), verify=tools.get_cacerts_bundle()) if response.status_code == 200: try: config = response.json() if not isinstance(config, dict): raise ValueError() return _ServiceConfig( config['client_id'], config['client_not_so_secret'], config.get('primary_url')) except (KeyError, ValueError) as err: logging.error('Invalid response from the service: %s', err) else: logging.warning( 'Error when fetching oauth_config, HTTP status code %d', response.status_code) return None
def __init__(self): super(RequestsLibEngine, self).__init__() self.session = requests.Session() # Configure session. self.session.trust_env = False self.session.verify = tools.get_cacerts_bundle() # Configure connection pools. for protocol in ('https://', 'http://'): self.session.mount(protocol, adapters.HTTPAdapter( pool_connections=64, pool_maxsize=64, max_retries=0, pool_block=False))
def authenticated_http_request(service_account, *args, **kwargs): """Sends an OAuth2-authenticated HTTP request. Args: service_account: Service account to use. For GCE, the name of the service account, otherwise the path to the service account JSON file. Raises: AuthenticatedHttpRequestFailure """ scopes = kwargs.pop('scopes', []) kwargs['headers'] = kwargs.get('headers', {}).copy() http = httplib2.Http(ca_certs=tools.get_cacerts_bundle()) # Authorize the request. In general, we need to produce an OAuth2 bearer token # using a service account JSON file. However on GCE there is a shortcut: it # can fetch the current bearer token right from the instance metadata without # the need for the oauth2client.client library. if platforms.is_gce(): try: gce_bearer_token, _ = platforms.gce.oauth2_access_token_with_expiration( account=service_account) except (IOError, urllib2.HTTPError) as e: raise AuthenticatedHttpRequestFailure(e) kwargs['headers']['Authorization'] = 'Bearer %s' % gce_bearer_token else: try: oauth2client = get_oauth2_client(service_account, scopes) except (IOError, OSError, ValueError) as e: raise AuthenticatedHttpRequestFailure(e) http = oauth2client.authorize(http) try: return http.request(*args, **kwargs) except client.Error as e: raise AuthenticatedHttpRequestFailure(e)
def authenticated_http_request(service_account, *args, **kwargs): """Sends an OAuth2-authenticated HTTP request. Args: service_account: Service account to use. For GCE, the name of the service account, otherwise the path to the service account JSON file. Raises: AuthenticatedHttpRequestFailure """ scopes = kwargs.pop('scopes', []) kwargs['headers'] = kwargs.get('headers', {}).copy() http = httplib2.Http(ca_certs=tools.get_cacerts_bundle()) # Authorize the request. In general, we need to produce an OAuth2 bearer token # using a service account JSON file. However on GCE there is a shortcut: it # can fetch the current bearer token right from the instance metadata without # the need for the oauth2client.client library. if platforms.is_gce(): try: gce_bearer_token = platforms.gce.oauth2_access_token( account=service_account) except (IOError, urllib2.HTTPError) as e: raise AuthenticatedHttpRequestFailure(e) kwargs['headers']['Authorization'] = 'Bearer %s' % gce_bearer_token else: try: oauth2client = get_oauth2_client(service_account, scopes) except (IOError, OSError, ValueError) as e: raise AuthenticatedHttpRequestFailure(e) http = oauth2client.authorize(http) try: return http.request(*args, **kwargs) except client.Error as e: raise AuthenticatedHttpRequestFailure(e)
def create_access_token(urlhost, config, allow_user_interaction): """Mints and caches new access_token, launching OAuth2 dance if necessary. Args: urlhost: base URL of a host to make OAuth2 token for. config: OAuthConfig instance. allow_user_interaction: if False, do not use interactive browser based flow (return None instead if it is required). Returns: AccessToken on success. None on error or if OAuth2 flow was interrupted. """ assert isinstance(config, OAuthConfig) if config.disabled: return None auth_service_url = _fetch_auth_service_url(urlhost) if not auth_service_url: return None storage = _get_storage(auth_service_url, config) credentials = None if config.service_account_json: # 2-legged flow that uses service account credentials. try: service_account = _load_service_account_json(config.service_account_json) except BadServiceAccountCredentials as e: logging.error('Bad service account credentials: %s', e) return None # Body of token refresh request (with JWT assertion signed with secret key). body = urllib.urlencode({ 'assertion': _make_assertion_jwt(service_account), 'grant_type': 'urn:ietf:params:oauth:grant-type:jwt-bearer', }) # Exchange it for access_token. http = httplib2.Http(ca_certs=tools.get_cacerts_bundle()) resp, content = http.request( uri=OAUTH_TOKEN_ENDPOINT, method='POST', body=body, headers={'Content-Type': 'application/x-www-form-urlencoded'}) if resp.status != 200: logging.error( 'Failed to grab access token for service account: %r', content) return None try: token = json.loads(content) access_token = token['access_token'] expires_at = None if 'expires_in' in token: expires_at = datetime.datetime.utcnow() expires_at += datetime.timedelta(seconds=int(token['expires_in'])) except (KeyError, ValueError) as e: logging.error('Unexpected access token response format: %s', e) return None credentials = client.OAuth2Credentials( access_token=access_token, client_id=service_account.client_id, client_secret=None, refresh_token=None, token_expiry=expires_at, token_uri=None, user_agent=None) else: # 3-legged flow with (perhaps cached) refresh token. credentials = storage.get() refreshed = False if credentials and not credentials.invalid: try: credentials.refresh(httplib2.Http(ca_certs=tools.get_cacerts_bundle())) refreshed = True except client.Error as err: logging.error('OAuth error: %s', err) # Refresh token is missing or invalid, go through full flow. if not refreshed: if not allow_user_interaction: return None credentials = _run_oauth_dance(auth_service_url, config) if not credentials: return None # Success. logging.info('OAuth access_token refreshed. Expires in %s.', credentials.token_expiry - datetime.datetime.utcnow()) credentials.set_store(storage) storage.put(credentials) return AccessToken(credentials.access_token, credentials.token_expiry)