Source release 19.2.0

This commit is contained in:
Alex Dale
2024-06-25 14:03:53 -07:00
parent b8bdfccebe
commit cd8256726f
89 changed files with 2747 additions and 35949 deletions

View File

@@ -20,16 +20,21 @@ import urllib.parse
import urllib.request
import uuid
import webbrowser
from google.auth.transport import requests
from google.oauth2 import service_account
DEFAULT_BASE = 'https://widevine.googleapis.com/v1beta1'
UPLOAD_PATH = '/uniqueDeviceInfo:batchUpload'
TOKEN_CACHE_FILE = os.path.join(
os.path.expanduser('~'), '.device_info_uploader.token')
os.path.expanduser('~'), '.device_info_uploader.token'
)
OAUTH_SERVICE_BASE = 'https://accounts.google.com/o/oauth2'
OAUTH_AUTHN_URL = OAUTH_SERVICE_BASE + '/auth'
OAUTH_TOKEN_URL = OAUTH_SERVICE_BASE + '/token'
OAUTH_SCOPES = ['https://www.googleapis.com/auth/widevine/frontend']
class OAuthHTTPRequestHandler(http.server.BaseHTTPRequestHandler):
"""HTTP Handler used to accept the oauth response when the user logs in."""
@@ -41,17 +46,24 @@ class OAuthHTTPRequestHandler(http.server.BaseHTTPRequestHandler):
params = dict(urllib.parse.parse_qsl(parsed_path.query))
if 'error' in params:
error = params['error']
self.respond(400, error,
f'Error received from the OAuth server: {error}.')
self.respond(
400, error, f'Error received from the OAuth server: {error}.'
)
sys.exit(-1)
elif 'code' not in params:
self.respond(400, 'ERROR',
('Response from OAuth server is missing the authorization '
f'code. Full response: "{self.path}"'))
self.respond(
400,
'ERROR',
(
'Response from OAuth server is missing the authorization '
f'code. Full response: "{self.path}"'
),
)
sys.exit(-1)
else:
self.respond(200, 'Success!',
'Success! You may close this browser window.')
self.respond(
200, 'Success!', 'Success! You may close this browser window.'
)
self.server.code = params['code']
def do_POST(self): # pylint: disable=invalid-name
@@ -70,20 +82,25 @@ class OAuthHTTPRequestHandler(http.server.BaseHTTPRequestHandler):
self.send_response(code)
self.send_header('Content-type', 'text/html')
self.end_headers()
self.wfile.write(('<html>'
f' <title>{title}</title>'
f' <body>'
f' <p style="font-size:24px;">{message}</p>'
f' </body>'
'</html>').encode('utf-8'))
self.wfile.write(
(
'<html>'
f' <title>{title}</title>'
' <body>'
f' <p style="font-size:24px;">{message}</p>'
' </body>'
'</html>'
).encode('utf-8')
)
class LocalOAuthReceiver(http.server.HTTPServer):
"""HTTP server that will wait for an OAuth authorization code."""
def __init__(self):
super(LocalOAuthReceiver, self).__init__(('127.0.0.1', 0),
OAuthHTTPRequestHandler)
super(LocalOAuthReceiver, self).__init__(
('127.0.0.1', 0), OAuthHTTPRequestHandler
)
self.code = None
def port(self):
@@ -116,20 +133,34 @@ def parse_args():
'--json-csr',
nargs='+',
required=True,
help='list of files containing JSON output from rkp_factory_extraction_tool'
help=(
'list of files containing JSON output from'
' rkp_factory_extraction_tool'
),
)
parser.add_argument(
'--credentials', required=True, help='JSON credentials file')
parser.add_argument('--credentials', help='JSON credentials file')
parser.add_argument(
'--endpoint', default=DEFAULT_BASE, help='destination server URL')
'--endpoint', default=DEFAULT_BASE, help='destination server URL'
)
parser.add_argument('--org-name', required=True, help='orgnization name')
parser.add_argument(
'--cache-token',
action='store_true',
help='Use a locally cached a refresh token')
help='Use a locally cached a refresh token',
)
parser.add_argument(
'--service-credentials', help='JSON credentials file for service account'
)
parser.add_argument(
'--die-on-error',
action='store_true',
help='exit on error and stop uploading more CSRs',
)
return parser.parse_args()
@@ -159,7 +190,7 @@ def parse_json_csrs(filename, batches):
'name': obj['name'],
'model': obj['model'],
'product': obj['product'],
'build_info': obj['build_info']
'build_info': obj['build_info'],
})
except KeyError as e:
die(f'Invalid object at {filename}:{line_count}, missing {e}')
@@ -258,7 +289,8 @@ def load_and_validate_creds(credfile):
' The given credentials do not appear to be for a locally installed\n'
' application. Please navigate to the credentials dashboard and\n'
' ensure that the "Type" of your client is "Desktop":\n'
' https://console.cloud.google.com/apis/credentials')
' https://console.cloud.google.com/apis/credentials'
)
if 'installed' not in credmap:
die(not_local_app_creds_error)
@@ -267,10 +299,12 @@ def load_and_validate_creds(credfile):
expected_keys = set(['client_id', 'client_secret', 'redirect_uris'])
if not expected_keys.issubset(creds.keys()):
die(('ERROR: Invalid credential file.\n'
' The given credentials do not appear to be valid. Please\n'
' re-download the client credentials file from the dashboard:\n'
' https://console.cloud.google.com/apis/credentials'))
die((
'ERROR: Invalid credential file.\n'
' The given credentials do not appear to be valid. Please\n'
' re-download the client credentials file from the dashboard:\n'
' https://console.cloud.google.com/apis/credentials'
))
if 'http://localhost' not in creds['redirect_uris']:
die(not_local_app_creds_error)
@@ -279,7 +313,23 @@ def load_and_validate_creds(credfile):
def authenticate_and_fetch_token(args):
"""Authenticate the user and fetch an OAUTH2 access token."""
"""Authenticate and fetch an OAUTH2 access token."""
# Auth for service account
if args.service_credentials:
if not os.path.exists(args.service_credentials):
die('Service account credentials file does not exist.')
svc_account = service_account.Credentials.from_service_account_file(
args.service_credentials,
scopes=OAUTH_SCOPES,
)
svc_account.refresh(requests.Request())
return svc_account.token
# Auth for user account
if args.credentials is None:
die('User credentials is not provided.')
if not os.path.exists(args.credentials):
die('User credentials file does not exist.')
creds = load_and_validate_creds(args.credentials)
access_type = 'online'
@@ -292,10 +342,17 @@ def authenticate_and_fetch_token(args):
httpd = LocalOAuthReceiver()
redirect_uri = f'http://127.0.0.1:{httpd.port()}'
url = (
OAUTH_AUTHN_URL + '?response_type=code' + '&client_id=' +
creds['client_id'] + '&redirect_uri=' + redirect_uri +
'&scope=https://www.googleapis.com/auth/widevine/frontend' +
'&access_type=' + access_type + '&prompt=select_account')
OAUTH_AUTHN_URL
+ '?response_type=code'
+ '&client_id='
+ creds['client_id']
+ '&redirect_uri='
+ redirect_uri
+ '&scope=https://www.googleapis.com/auth/widevine/frontend'
+ '&access_type='
+ access_type
+ '&prompt=select_account'
)
print('Opening your web browser to authenticate...')
if not webbrowser.open(url, new=1, autoraise=True):
print('Error opening the browser. Please open this link in a browser')
@@ -319,8 +376,9 @@ def upload_batch(args, device_metadata, bccs):
request = urllib.request.Request(args.endpoint + UPLOAD_PATH)
request.add_header('Content-Type', 'application/json')
request.add_header('X-GFE-SSL', 'yes')
request.add_header('Authorization',
'Bearer ' + authenticate_and_fetch_token(args))
request.add_header(
'Authorization', 'Bearer ' + authenticate_and_fetch_token(args)
)
try:
response = urllib.request.urlopen(request, body)
except urllib.error.HTTPError as e:
@@ -329,8 +387,11 @@ def upload_batch(args, device_metadata, bccs):
eprint(line.decode('utf-8').rstrip())
sys.exit(1)
while chunk := response.read(1024):
print(chunk.decode('utf-8'))
response_body = response.read().decode('utf-8')
print(response_body)
res = json.loads(response_body)
if 'failedDeviceInfo' in res and args.die_on_error:
sys.exit(1)
def main():