Chromium Code Reviews| Index: tools/experiments/generate_token.py |
| diff --git a/tools/experiments/generate_token.py b/tools/experiments/generate_token.py |
| new file mode 100755 |
| index 0000000000000000000000000000000000000000..79bfb61cfba136155c1f8194420695f84cc0ef88 |
| --- /dev/null |
| +++ b/tools/experiments/generate_token.py |
| @@ -0,0 +1,124 @@ |
| +#!/usr/bin/env python |
| +# Copyright (c) 2016 The Chromium Authors. All rights reserved. |
| +# Use of this source code is governed by a BSD-style license that can be |
| +# found in the LICENSE file. |
| + |
| +"""Utility for generating experimental API tokens |
| + |
| +usage: generate_token.py [-h] [--key-file KEY_FILE] |
| + [--expire-days EXPIRE_DAYS | |
| + --expire-timestamp EXPIRE_TIMESTAMP] |
| + hostname api_name |
| + |
| +Generate tokens for enabling experimental APIs |
| + |
| +positional arguments: |
| + hostname Host for which to enable the API. This can be a bare |
| + hostname (in which case https will be assumed,) or a |
| + valid origin URL (hostname, protocol and port can all |
| + be included.) |
| + api_name API to enable. The current list of experimental APIs |
| + can be found in RuntimeFeatures.in |
| + |
| +optional arguments: |
| + -h, --help show this help message and exit |
| + --key-file KEY_FILE Ed25519 private key file to sign the token with |
| + --expire-days EXPIRE_DAYS |
| + Days from now when the token should exipire |
| + --expire-timestamp EXPIRE_TIMESTAMP |
| + Exact time (milliseconds since 1970-01-01 00:00:00 |
| + UTC) when the token should exipire |
|
miket_OOO
2016/01/21 21:54:56
This is up to you, but if this is just a rote copy
iclelland
2016/01/22 20:31:57
It is a rote copy, just for anyone reading the cod
|
| +""" |
| +import argparse |
| +import base64 |
| +import re |
| +import os |
| +import sys |
| +import time |
| +import urlparse |
| + |
| +from third_party import ed25519 |
| + |
| +def OriginFromArgs(arg): |
| + """Constructs the origin for the token from the command line arguments. |
| + |
| + Returns None if this is not possible (neither a valid hostname nor a |
| + valid origin URL was provided.) |
| + """ |
| + # Does it look like a hostname? |
| + if re.match('^[a-z0-9.-]*$', arg): |
|
miket_OOO
2016/01/21 21:54:56
Make sure we (this tool and dev-console server cod
iclelland
2016/01/22 20:31:57
It looks like your regex disallows a '-' as a comp
|
| + return 'https://'+arg |
| + # Try to construct an origin URL from the argument |
| + origin = urlparse.urlparse(arg) |
| + if not origin or not origin.scheme or not origin.netloc: |
| + raise argparse.ArgumentTypeError("%s is not a hostname or a URL" % arg) |
| + # Strip any other components and return the URL: |
| + return urlparse.urlunparse(origin[:2]+('',)*4) |
|
miket_OOO
2016/01/21 21:54:56
whitespace after comma and around binary operator
iclelland
2016/01/22 20:31:57
Done.
|
| + |
| +def ExpiryFromArgs(args): |
| + if args.expire_timestamp: |
| + return int(args.expire_timestamp) |
| + return ((int(time.time()) + (int(args.expire_days) * 86400)) * 1000) |
| + |
| +def GenerateTokenData(origin, apiName, expiry): |
| + return "{0}|{1}|{2}".format(origin, apiName, expiry) |
| + |
| +def Sign(private_key, data): |
| + return ed25519.signature(data, private_key[:32], private_key[32:]) |
| + |
| +def FormatToken(signature, data): |
| + return base64.b64encode(signature) + "|" + data |
| + |
| +def main(): |
| + parser = argparse.ArgumentParser( |
| + description="Generate tokens for enabling experimental APIs") |
| + parser.add_argument('hostname', |
| + help="Host for which to enable the API. This can be a " |
| + "bare hostname (in which case https will be " |
| + "assumed,) or a valid origin URL (hostname, " |
| + "protocol and port can all be included.)", |
| + type=OriginFromArgs) |
| + parser.add_argument('api_name', |
| + help="API to enable. The current list of experimental " |
| + "APIs can be found in RuntimeFeatures.in") |
| + parser.add_argument('--key-file', |
| + help='Ed25519 private key file to sign the token with', |
| + default="eftest.key") |
| + expiry_group = parser.add_mutually_exclusive_group() |
| + expiry_group.add_argument('--expire-days', |
| + help='Days from now when the token should exipire', |
| + type=int, |
| + default=42) |
| + expiry_group.add_argument('--expire-timestamp', |
| + help="Exact time (milliseconds since 1970-01-01 " |
| + "00:00:00 UTC) when the token should exipire", |
| + type=int) |
| + |
| + args = parser.parse_args() |
| + expiry = ExpiryFromArgs(args) |
| + |
| + key_file = open(os.path.expanduser(args.key_file)) |
| + private_key = key_file.read() |
| + |
| + # Validate that the key file read was a proper Ed25519 key -- running the |
| + # publickey method on the first half of the key should return the second |
| + # half. |
| + if (len(private_key) != 64 or |
| + ed25519.publickey(private_key[:32]) != private_key[32:]): |
| + print("Unable to use the specified private key file.") |
| + sys.exit(1) |
| + |
| + token_data = GenerateTokenData(args.hostname, args.api_name, expiry) |
| + signature = Sign(private_key, token_data) |
| + |
| + # Verify that that the signature is correct before printing it. |
| + try: |
| + ed25519.checkvalid(signature, token_data, private_key[32:]) |
| + except Exception: |
| + print "There was an error generating the signature." |
| + sys.exit(1) |
| + |
| + print FormatToken(signature, token_data) |
| + |
| +if __name__ == "__main__": |
| + main() |