blob: 66d1c2c8975198bb973744bf39c099128879f764 [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
41import sys
42
43import httplib2
44
45from oauth2client import client as oauth2_client
46from oauth2client.contrib import multistore_file
47from oauth2client import tools as oauth2_tools
48
49from acloud.public import errors
50
51logger = logging.getLogger(__name__)
52HOME_FOLDER = os.path.expanduser("~")
53
54
55def _CreateOauthServiceAccountCreds(email, private_key_path, scopes):
56 """Create credentials with a normal service account.
57
58 Args:
59 email: email address as the account.
60 private_key_path: Path to the service account P12 key.
61 scopes: string, multiple scopes should be saperated by space.
62 Api scopes to request for the oauth token.
63
64 Returns:
65 An oauth2client.OAuth2Credentials instance.
66
67 Raises:
68 errors.AuthentcationError: if failed to authenticate.
69 """
70 try:
71 with open(private_key_path) as f:
72 private_key = f.read()
73 credentials = oauth2_client.SignedJwtAssertionCredentials(
74 email, private_key, scopes)
75 except EnvironmentError as e:
76 raise errors.AuthentcationError(
77 "Could not authenticate using private key file %s, error message: %s",
78 private_key_path, str(e))
79 return credentials
80
81
82class RunFlowFlags(object):
83 """Flags for oauth2client.tools.run_flow."""
84
85 def __init__(self, browser_auth):
86 self.auth_host_port = [8080, 8090]
87 self.auth_host_name = "localhost"
88 self.logging_level = "ERROR"
89 self.noauth_local_webserver = not browser_auth
90
91
92def _RunAuthFlow(storage, client_id, client_secret, user_agent, scopes):
93 """Get user oauth2 credentials.
94
95 Args:
96 client_id: String, client id from the cloud project.
97 client_secret: String, client secret for the client_id.
98 user_agent: The user agent for the credential, e.g. "acloud"
99 scopes: String, scopes separated by space.
100
101 Returns:
102 An oauth2client.OAuth2Credentials instance.
103 """
104 flags = RunFlowFlags(browser_auth=False)
105 flow = oauth2_client.OAuth2WebServerFlow(
106 client_id=client_id,
107 client_secret=client_secret,
108 scope=scopes,
109 user_agent=user_agent)
110 credentials = oauth2_tools.run_flow(
111 flow=flow, storage=storage, flags=flags)
112 return credentials
113
114
115def _CreateOauthUserCreds(creds_cache_file, client_id, client_secret,
116 user_agent, scopes):
117 """Get user oauth2 credentials.
118
119 Args:
120 creds_cache_file: String, file name for the credential cache.
121 e.g. .acloud_oauth2.dat
122 Will be created at home folder.
123 client_id: String, client id from the cloud project.
124 client_secret: String, client secret for the client_id.
125 user_agent: The user agent for the credential, e.g. "acloud"
126 scopes: String, scopes separated by space.
127
128 Returns:
129 An oauth2client.OAuth2Credentials instance.
130 """
131 if not client_id or not client_secret:
132 raise errors.AuthentcationError(
133 "Could not authenticate using Oauth2 flow, please set client_id "
134 "and client_secret in your config file. Contact the cloud project's "
135 "admin if you don't have the client_id and client_secret.")
136 storage = multistore_file.get_credential_storage(
137 filename=os.path.abspath(creds_cache_file),
138 client_id=client_id,
139 user_agent=user_agent,
140 scope=scopes)
141 credentials = storage.get()
142 if credentials is not None:
143 try:
144 credentials.refresh(httplib2.Http())
145 except oauth2_client.AccessTokenRefreshError:
146 pass
147 if not credentials.invalid:
148 return credentials
149 return _RunAuthFlow(storage, client_id, client_secret, user_agent, scopes)
150
151
152def CreateCredentials(acloud_config, scopes):
153 """Create credentials.
154
155 Args:
156 acloud_config: An AcloudConfig object.
157 scopes: A string representing for scopes, separted by space,
158 like "SCOPE_1 SCOPE_2 SCOPE_3"
159
160 Returns:
161 An oauth2client.OAuth2Credentials instance.
162 """
163 if acloud_config.service_account_private_key_path:
164 return _CreateOauthServiceAccountCreds(
165 acloud_config.service_account_name,
166 acloud_config.service_account_private_key_path,
167 scopes=scopes)
168
169 creds_cache_file = os.path.join(HOME_FOLDER,
170 acloud_config.creds_cache_file)
171 return _CreateOauthUserCreds(
172 creds_cache_file=creds_cache_file,
173 client_id=acloud_config.client_id,
174 client_secret=acloud_config.client_secret,
175 user_agent=acloud_config.user_agent,
176 scopes=scopes)