| Index: sitescripts/extensions/bin/createNightlies.py |
| diff --git a/sitescripts/extensions/bin/createNightlies.py b/sitescripts/extensions/bin/createNightlies.py |
| index 4bd0f7fe3792361c1211c7049bf6f09ac0acb264..3a1d3e2b91e219dae19693d46bf7b470eb680725 100644 |
| --- a/sitescripts/extensions/bin/createNightlies.py |
| +++ b/sitescripts/extensions/bin/createNightlies.py |
| @@ -25,7 +25,9 @@ Nightly builds generation script |
| import argparse |
| import ConfigParser |
| +import binascii |
| import base64 |
| +import datetime |
| import hashlib |
| import hmac |
| import json |
| @@ -39,12 +41,17 @@ import subprocess |
| import sys |
| import tempfile |
| import time |
| +import uuid |
| from urllib import urlencode |
| import urllib2 |
| import urlparse |
| import zipfile |
| import contextlib |
| +from Crypto.PublicKey import RSA |
| +from Crypto.Signature import PKCS1_v1_5 |
| +import Crypto.Hash.SHA256 |
| + |
| from xml.dom.minidom import parse as parseXml |
| from sitescripts.extensions.utils import ( |
| @@ -350,7 +357,7 @@ class NightlyBuild(object): |
| env = dict(env, SPIDERMONKEY_BINARY=spiderMonkeyBinary) |
| command = [os.path.join(self.tempdir, 'build.py')] |
| - if self.config.type == 'safari': |
| + if self.config.type in {'safari', 'edge'}: |
|
tlucas
2018/04/13 13:06:04
The branch 'edge' is built from still uses the old
Sebastian Noack
2018/04/14 02:47:30
I would rather see a dependency update landing in
tlucas
2018/04/14 08:55:46
Yeah, i agree.
@Ollie - what do you think about th
Sebastian Noack
2018/04/14 09:40:54
Alternatively, we could add a hack to build.py:
Oleksandr
2018/04/16 04:37:07
I would rather merge `master` into `edge` first. T
Sebastian Noack
2018/04/16 05:45:50
Wouldn't this rather be a reason to go with my sug
Sebastian Noack
2018/04/16 05:47:19
Sorry, I meant "edge" (not "next").
tlucas
2018/04/16 10:06:07
All of the above does IMHO encourage a workaround
tlucas
2018/04/16 10:25:14
https://codereview.adblockplus.org/29753557/
https
tlucas
2018/04/16 14:50:09
Done.
|
| command.extend(['-t', self.config.type, 'build']) |
| else: |
| command.extend(['build', '-t', self.config.type]) |
| @@ -649,21 +656,72 @@ class NightlyBuild(object): |
| if any(status not in ('OK', 'ITEM_PENDING_REVIEW') for status in response['status']): |
| raise Exception({'status': response['status'], 'statusDetail': response['statusDetail']}) |
| + def generate_certificate_token_request(self, url, private_key): |
| + # Construct the token request according to |
| + # https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-certificate-credentials |
| + def base64url_encode(data): |
| + return base64.urlsafe_b64encode(data).replace(b'=', b'') |
|
Sebastian Noack
2018/04/14 02:47:30
How about .rstrip(b'=')?
tlucas
2018/04/14 08:55:46
see below
tlucas
2018/04/16 14:50:09
FWIW, there's no stripping / replacing done anymor
|
| + |
| + segments = [] |
| + |
| + hex_val = binascii.a2b_hex(self.config.thumbprint) |
| + x5t = base64.urlsafe_b64encode(hex_val).decode() |
| + |
| + now = datetime.datetime.now() |
|
Sebastian Noack
2018/04/14 02:47:30
It seems you are relying on the fact that our serv
tlucas
2018/04/14 08:55:46
I'm actually thinking about refactoring parts of t
tlucas
2018/04/16 14:50:09
Done, almost: mktime() expects a time_struct in lo
Sebastian Noack
2018/04/16 16:13:29
You are right. But time.time() gives the same resu
tlucas
2018/04/16 16:50:24
Done.
|
| + minutes = datetime.timedelta(0, 0, 0, 0, 10) |
| + expires = now + minutes |
| + |
| + # generate the full jwt body |
| + jwt_payload = { |
| + 'aud': url, |
| + 'iss': self.config.clientID, |
| + 'sub': self.config.clientID, |
| + 'nbf': int(time.mktime(now.timetuple())), |
| + 'exp': int(time.mktime(expires.timetuple())), |
| + 'jti': str(uuid.uuid4()), |
| + } |
| + |
| + jwt_headers = {'typ': 'JWT', 'alg': 'RS256', 'x5t': x5t} |
| + |
| + # sign the jwt body with the given private key |
| + key = RSA.importKey(private_key) |
| + |
| + segments.append(base64url_encode(json.dumps(jwt_headers))) |
| + segments.append(base64url_encode(json.dumps(jwt_payload))) |
|
Sebastian Noack
2018/04/14 02:47:30
Since we don't append to segments above, how about
tlucas
2018/04/14 08:55:46
Acknowledged.
tlucas
2018/04/16 14:50:09
Done.
|
| + |
| + body = b'.'.join(segments) |
| + signature = PKCS1_v1_5.new(key).sign(Crypto.Hash.SHA256.new(body)) |
| + |
| + segments.append(base64url_encode(signature)) |
| + signed_jwt = b'.'.join(segments) |
| + |
| + # generate oauth parameters for login.microsoft.com |
| + oauth_params = { |
| + 'grant_type': 'client_credentials', |
| + 'client_id': self.config.clientID, |
| + 'resource': 'https://graph.windows.net', |
| + 'client_assertion_type': 'urn:ietf:params:oauth:client-assertion-' |
| + 'type:jwt-bearer', |
| + 'client_assertion': signed_jwt, |
| + } |
| + |
| + request = urllib2.Request(url, urlencode(oauth_params)) |
| + request.get_method = lambda: 'POST' |
| + |
| + return request |
| + |
| def get_windows_store_access_token(self): |
| - # use refresh token to obtain a valid access token |
| - # https://docs.microsoft.com/en-us/azure/active-directory/active-directory-protocols-oauth-code#refreshing-the-access-tokens |
| - server = 'https://login.microsoftonline.com' |
| - token_path = '{}/{}/oauth2/token'.format(server, self.config.tenantID) |
| + # use client certificate to obtain a valid access token |
| + url = 'https://login.microsoftonline.com/{}/oauth2/token'.format( |
| + self.config.tenantID |
| + ) |
| + |
| + with open(self.config.privateKey, 'r') as fp: |
| + private_key = fp.read() |
| opener = urllib2.build_opener(HTTPErrorBodyHandler) |
| - post_data = urlencode([ |
| - ('refresh_token', self.config.refreshToken), |
| - ('client_id', self.config.clientID), |
| - ('client_secret', self.config.clientSecret), |
| - ('grant_type', 'refresh_token'), |
| - ('resource', 'https://graph.windows.net') |
| - ]) |
| - request = urllib2.Request(token_path, post_data) |
| + request = self.generate_certificate_token_request(url, private_key) |
| + |
| with contextlib.closing(opener.open(request)) as response: |
| data = json.load(response) |
| auth_token = '{0[token_type]} {0[access_token]}'.format(data) |
| @@ -814,7 +872,7 @@ class NightlyBuild(object): |
| self.uploadToMozillaAddons() |
| elif self.config.type == 'chrome' and self.config.clientID and self.config.clientSecret and self.config.refreshToken: |
| self.uploadToChromeWebStore() |
| - elif self.config.type == 'edge' and self.config.clientID and self.config.clientSecret and self.config.refreshToken and self.config.tenantID: |
| + elif self.config.type == 'edge' and self.config.clientID and self.config.tenantID and self.config.privateKey and self.config.thumbprint: |
| self.upload_to_windows_store() |
| finally: |