You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
238 lines
8.8 KiB
238 lines
8.8 KiB
#!/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 allowed by
|
|
Android Build team so that acloud can access build api.
|
|
"""
|
|
|
|
import logging
|
|
import os
|
|
|
|
import httplib2
|
|
|
|
# pylint: disable=import-error
|
|
from oauth2client import client as oauth2_client
|
|
from oauth2client import service_account as oauth2_service_account
|
|
from oauth2client.contrib import multistore_file
|
|
from oauth2client import tools as oauth2_tools
|
|
|
|
from acloud import errors
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
HOME_FOLDER = os.path.expanduser("~")
|
|
# If there is no specific scope use case, we will always use this default full
|
|
# scopes to run CreateCredentials func and user will only go oauth2 flow once
|
|
# after login with this full scopes credentials.
|
|
_ALL_SCOPES = " ".join(["https://www.googleapis.com/auth/compute",
|
|
"https://www.googleapis.com/auth/logging.write",
|
|
"https://www.googleapis.com/auth/androidbuild.internal",
|
|
"https://www.googleapis.com/auth/devstorage.read_write",
|
|
"https://www.googleapis.com/auth/userinfo.email"])
|
|
|
|
|
|
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.AuthenticationError: if failed to authenticate.
|
|
"""
|
|
try:
|
|
credentials = oauth2_service_account.ServiceAccountCredentials.from_p12_keyfile(
|
|
email, private_key_path, scopes=scopes)
|
|
except EnvironmentError as e:
|
|
raise errors.AuthenticationError(
|
|
"Could not authenticate using private key file (%s) "
|
|
" error message: %s" % (private_key_path, str(e)))
|
|
return credentials
|
|
|
|
|
|
# pylint: disable=invalid-name
|
|
def _CreateOauthServiceAccountCredsWithJsonKey(json_private_key_path, scopes,
|
|
creds_cache_file, user_agent):
|
|
"""Create credentials with a normal service account from json key file.
|
|
|
|
Args:
|
|
json_private_key_path: Path to the service account json key file.
|
|
scopes: string, multiple scopes should be saperated by space.
|
|
Api scopes to request for the oauth token.
|
|
creds_cache_file: String, file name for the credential cache.
|
|
e.g. .acloud_oauth2.dat
|
|
Will be created at home folder.
|
|
user_agent: String, the user agent for the credential, e.g. "acloud"
|
|
|
|
Returns:
|
|
An oauth2client.OAuth2Credentials instance.
|
|
|
|
Raises:
|
|
errors.AuthenticationError: if failed to authenticate.
|
|
"""
|
|
try:
|
|
credentials = oauth2_service_account.ServiceAccountCredentials.from_json_keyfile_name(
|
|
json_private_key_path, scopes=scopes)
|
|
storage = multistore_file.get_credential_storage(
|
|
filename=os.path.abspath(creds_cache_file),
|
|
client_id=credentials.client_id,
|
|
user_agent=user_agent,
|
|
scope=scopes)
|
|
credentials.set_store(storage)
|
|
except EnvironmentError as e:
|
|
raise errors.AuthenticationError(
|
|
"Could not authenticate using json private key file (%s) "
|
|
" error message: %s" % (json_private_key_path, str(e)))
|
|
|
|
return credentials
|
|
|
|
|
|
class RunFlowFlags():
|
|
"""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.AuthenticationError(
|
|
"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:
|
|
if not credentials.access_token_expired and not credentials.invalid:
|
|
return credentials
|
|
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=_ALL_SCOPES):
|
|
"""Create credentials.
|
|
|
|
If no specific scope provided, we create a full scopes credentials for
|
|
authenticating and user will only go oauth2 flow once after login with
|
|
full scopes 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 os.path.isabs(acloud_config.creds_cache_file):
|
|
creds_cache_file = acloud_config.creds_cache_file
|
|
else:
|
|
creds_cache_file = os.path.join(HOME_FOLDER,
|
|
acloud_config.creds_cache_file)
|
|
|
|
if acloud_config.service_account_json_private_key_path:
|
|
return _CreateOauthServiceAccountCredsWithJsonKey(
|
|
acloud_config.service_account_json_private_key_path,
|
|
scopes=scopes,
|
|
creds_cache_file=creds_cache_file,
|
|
user_agent=acloud_config.user_agent)
|
|
if acloud_config.service_account_private_key_path:
|
|
return _CreateOauthServiceAccountCreds(
|
|
acloud_config.service_account_name,
|
|
acloud_config.service_account_private_key_path,
|
|
scopes=scopes)
|
|
|
|
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)
|