blob: 3ef1bd3f7da2ec2608fb8d111b14fc289065d253 [file] [log] [blame]
Tri Voa3141f52016-09-30 17:32:04 -07001#!/usr/bin/env python
2#
3# Copyright 2016 - The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16"""Module for handling Authentication.
17
18Possible cases of authentication are noted below.
19
20--------------------------------------------------------
21 account | authentcation
22--------------------------------------------------------
23
24google account (e.g. gmail)* | normal oauth2
25
26
27service account* | oauth2 + private key
28
29--------------------------------------------------------
30
31* For now, non-google employees (i.e. non @google.com account) or
32 non-google-owned service account can not access Android Build API.
33 Only local build artifact can be used.
34
35* Google-owned service account, if used, needs to be whitelisted by
36 Android Build team so that acloud can access build api.
37"""
38
39import logging
40import os
Tri Voa3141f52016-09-30 17:32:04 -070041
42import httplib2
43
Kevin Chenga9dac952018-07-11 10:42:02 -070044# pylint: disable=import-error
Tri Voa3141f52016-09-30 17:32:04 -070045from oauth2client import client as oauth2_client
Kevin Chenga9dac952018-07-11 10:42:02 -070046from oauth2client import service_account as oauth2_service_account
Tri Voa3141f52016-09-30 17:32:04 -070047from oauth2client.contrib import multistore_file
48from oauth2client import tools as oauth2_tools
49
50from acloud.public import errors
51
52logger = logging.getLogger(__name__)
53HOME_FOLDER = os.path.expanduser("~")
54
55
56def _CreateOauthServiceAccountCreds(email, private_key_path, scopes):
57 """Create credentials with a normal service account.
58
59 Args:
60 email: email address as the account.
61 private_key_path: Path to the service account P12 key.
62 scopes: string, multiple scopes should be saperated by space.
63 Api scopes to request for the oauth token.
64
65 Returns:
66 An oauth2client.OAuth2Credentials instance.
67
68 Raises:
69 errors.AuthentcationError: if failed to authenticate.
70 """
71 try:
Kevin Chenga9dac952018-07-11 10:42:02 -070072 credentials = oauth2_service_account.ServiceAccountCredentials.from_p12_keyfile(
73 email, private_key_path, scopes=scopes)
Tri Voa3141f52016-09-30 17:32:04 -070074 except EnvironmentError as e:
75 raise errors.AuthentcationError(
Kevin Chenga9dac952018-07-11 10:42:02 -070076 "Could not authenticate using private key file (%s) "
77 " error message: %s" % (private_key_path, str(e)))
Tri Voa3141f52016-09-30 17:32:04 -070078 return credentials
79
80
81class RunFlowFlags(object):
82 """Flags for oauth2client.tools.run_flow."""
83
84 def __init__(self, browser_auth):
85 self.auth_host_port = [8080, 8090]
86 self.auth_host_name = "localhost"
87 self.logging_level = "ERROR"
88 self.noauth_local_webserver = not browser_auth
89
90
91def _RunAuthFlow(storage, client_id, client_secret, user_agent, scopes):
92 """Get user oauth2 credentials.
93
94 Args:
95 client_id: String, client id from the cloud project.
96 client_secret: String, client secret for the client_id.
97 user_agent: The user agent for the credential, e.g. "acloud"
98 scopes: String, scopes separated by space.
99
100 Returns:
101 An oauth2client.OAuth2Credentials instance.
102 """
103 flags = RunFlowFlags(browser_auth=False)
104 flow = oauth2_client.OAuth2WebServerFlow(
105 client_id=client_id,
106 client_secret=client_secret,
107 scope=scopes,
108 user_agent=user_agent)
109 credentials = oauth2_tools.run_flow(
110 flow=flow, storage=storage, flags=flags)
111 return credentials
112
113
114def _CreateOauthUserCreds(creds_cache_file, client_id, client_secret,
115 user_agent, scopes):
116 """Get user oauth2 credentials.
117
118 Args:
119 creds_cache_file: String, file name for the credential cache.
120 e.g. .acloud_oauth2.dat
121 Will be created at home folder.
122 client_id: String, client id from the cloud project.
123 client_secret: String, client secret for the client_id.
124 user_agent: The user agent for the credential, e.g. "acloud"
125 scopes: String, scopes separated by space.
126
127 Returns:
128 An oauth2client.OAuth2Credentials instance.
129 """
130 if not client_id or not client_secret:
131 raise errors.AuthentcationError(
132 "Could not authenticate using Oauth2 flow, please set client_id "
133 "and client_secret in your config file. Contact the cloud project's "
134 "admin if you don't have the client_id and client_secret.")
135 storage = multistore_file.get_credential_storage(
136 filename=os.path.abspath(creds_cache_file),
137 client_id=client_id,
138 user_agent=user_agent,
139 scope=scopes)
140 credentials = storage.get()
141 if credentials is not None:
142 try:
143 credentials.refresh(httplib2.Http())
144 except oauth2_client.AccessTokenRefreshError:
145 pass
146 if not credentials.invalid:
147 return credentials
148 return _RunAuthFlow(storage, client_id, client_secret, user_agent, scopes)
149
150
151def CreateCredentials(acloud_config, scopes):
152 """Create credentials.
153
154 Args:
155 acloud_config: An AcloudConfig object.
156 scopes: A string representing for scopes, separted by space,
157 like "SCOPE_1 SCOPE_2 SCOPE_3"
158
159 Returns:
160 An oauth2client.OAuth2Credentials instance.
161 """
162 if acloud_config.service_account_private_key_path:
163 return _CreateOauthServiceAccountCreds(
164 acloud_config.service_account_name,
165 acloud_config.service_account_private_key_path,
166 scopes=scopes)
167
168 creds_cache_file = os.path.join(HOME_FOLDER,
169 acloud_config.creds_cache_file)
170 return _CreateOauthUserCreds(
171 creds_cache_file=creds_cache_file,
172 client_id=acloud_config.client_id,
173 client_secret=acloud_config.client_secret,
174 user_agent=acloud_config.user_agent,
175 scopes=scopes)