diff options
author | Tri Vo <trong@google.com> | 2016-09-30 17:32:04 -0700 |
---|---|---|
committer | Tri Vo <trong@google.com> | 2016-10-06 12:22:39 -0700 |
commit | a3141f5641f2d2129850de827150d4f6e7ce703c (patch) | |
tree | 1d47a6df7699f9663a2499cd229b75dfddaefb76 | |
parent | f6a7217f80d4e43c7479e411056e162c28999376 (diff) | |
download | acloud-a3141f5641f2d2129850de827150d4f6e7ce703c.tar.gz |
Add auth.py that depends on open-source tools only.
Change-Id: Ibf093cac4bb624a4d6b697760ccc7063215ff5be
-rw-r--r-- | internal/lib/auth.py | 176 |
1 files changed, 176 insertions, 0 deletions
diff --git a/internal/lib/auth.py b/internal/lib/auth.py new file mode 100644 index 00000000..66d1c2c8 --- /dev/null +++ b/internal/lib/auth.py @@ -0,0 +1,176 @@ +#!/usr/bin/env python +# +# Copyright 2016 - The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Module for handling Authentication. + +Possible cases of authentication are noted below. + +-------------------------------------------------------- + account | authentcation +-------------------------------------------------------- + +google account (e.g. gmail)* | normal oauth2 + + +service account* | oauth2 + private key + +-------------------------------------------------------- + +* For now, non-google employees (i.e. non @google.com account) or + non-google-owned service account can not access Android Build API. + Only local build artifact can be used. + +* Google-owned service account, if used, needs to be whitelisted by + Android Build team so that acloud can access build api. +""" + +import logging +import os +import sys + +import httplib2 + +from oauth2client import client as oauth2_client +from oauth2client.contrib import multistore_file +from oauth2client import tools as oauth2_tools + +from acloud.public import errors + +logger = logging.getLogger(__name__) +HOME_FOLDER = os.path.expanduser("~") + + +def _CreateOauthServiceAccountCreds(email, private_key_path, scopes): + """Create credentials with a normal service account. + + Args: + email: email address as the account. + private_key_path: Path to the service account P12 key. + scopes: string, multiple scopes should be saperated by space. + Api scopes to request for the oauth token. + + Returns: + An oauth2client.OAuth2Credentials instance. + + Raises: + errors.AuthentcationError: if failed to authenticate. + """ + try: + with open(private_key_path) as f: + private_key = f.read() + credentials = oauth2_client.SignedJwtAssertionCredentials( + email, private_key, scopes) + except EnvironmentError as e: + raise errors.AuthentcationError( + "Could not authenticate using private key file %s, error message: %s", + private_key_path, str(e)) + return credentials + + +class RunFlowFlags(object): + """Flags for oauth2client.tools.run_flow.""" + + def __init__(self, browser_auth): + self.auth_host_port = [8080, 8090] + self.auth_host_name = "localhost" + self.logging_level = "ERROR" + self.noauth_local_webserver = not browser_auth + + +def _RunAuthFlow(storage, client_id, client_secret, user_agent, scopes): + """Get user oauth2 credentials. + + Args: + client_id: String, client id from the cloud project. + client_secret: String, client secret for the client_id. + user_agent: The user agent for the credential, e.g. "acloud" + scopes: String, scopes separated by space. + + Returns: + An oauth2client.OAuth2Credentials instance. + """ + flags = RunFlowFlags(browser_auth=False) + flow = oauth2_client.OAuth2WebServerFlow( + client_id=client_id, + client_secret=client_secret, + scope=scopes, + user_agent=user_agent) + credentials = oauth2_tools.run_flow( + flow=flow, storage=storage, flags=flags) + return credentials + + +def _CreateOauthUserCreds(creds_cache_file, client_id, client_secret, + user_agent, scopes): + """Get user oauth2 credentials. + + Args: + creds_cache_file: String, file name for the credential cache. + e.g. .acloud_oauth2.dat + Will be created at home folder. + client_id: String, client id from the cloud project. + client_secret: String, client secret for the client_id. + user_agent: The user agent for the credential, e.g. "acloud" + scopes: String, scopes separated by space. + + Returns: + An oauth2client.OAuth2Credentials instance. + """ + if not client_id or not client_secret: + raise errors.AuthentcationError( + "Could not authenticate using Oauth2 flow, please set client_id " + "and client_secret in your config file. Contact the cloud project's " + "admin if you don't have the client_id and client_secret.") + storage = multistore_file.get_credential_storage( + filename=os.path.abspath(creds_cache_file), + client_id=client_id, + user_agent=user_agent, + scope=scopes) + credentials = storage.get() + if credentials is not None: + try: + credentials.refresh(httplib2.Http()) + except oauth2_client.AccessTokenRefreshError: + pass + if not credentials.invalid: + return credentials + return _RunAuthFlow(storage, client_id, client_secret, user_agent, scopes) + + +def CreateCredentials(acloud_config, scopes): + """Create credentials. + + Args: + acloud_config: An AcloudConfig object. + scopes: A string representing for scopes, separted by space, + like "SCOPE_1 SCOPE_2 SCOPE_3" + + Returns: + An oauth2client.OAuth2Credentials instance. + """ + if acloud_config.service_account_private_key_path: + return _CreateOauthServiceAccountCreds( + acloud_config.service_account_name, + acloud_config.service_account_private_key_path, + scopes=scopes) + + creds_cache_file = os.path.join(HOME_FOLDER, + acloud_config.creds_cache_file) + return _CreateOauthUserCreds( + creds_cache_file=creds_cache_file, + client_id=acloud_config.client_id, + client_secret=acloud_config.client_secret, + user_agent=acloud_config.user_agent, + scopes=scopes) |