load_key()   A
last analyzed

Complexity

Conditions 2

Size

Total Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
c 1
b 0
f 0
dl 0
loc 13
rs 9.4285
1
#!/usr/bin/env python
2
# -*- coding: utf-8 -*-
3
"""Update encrypted deploy password in Travis config file
4
"""
5
6
7
from __future__ import print_function
8
import base64
9
import json
10
import os
11
from getpass import getpass
12
import yaml
13
from cryptography.hazmat.primitives.serialization import load_pem_public_key
14
from cryptography.hazmat.backends import default_backend
15
from cryptography.hazmat.primitives.asymmetric.padding import PKCS1v15
16
17
18
try:
19
    from urllib import urlopen
20
except:
21
    from urllib.request import urlopen
22
23
24
GITHUB_REPO = 'sh4r3m4n/couchdb_download_token'
25
TRAVIS_CONFIG_FILE = os.path.join(
26
    os.path.dirname(os.path.abspath(__file__)), '.travis.yml')
27
28
29
def load_key(pubkey):
30
    """Load public RSA key, with work-around for keys using
31
    incorrect header/footer format.
32
33
    Read more about RSA encryption with cryptography:
34
    https://cryptography.io/latest/hazmat/primitives/asymmetric/rsa/
35
    """
36
    try:
37
        return load_pem_public_key(pubkey.encode(), default_backend())
38
    except ValueError:
39
        # workaround for https://github.com/travis-ci/travis-api/issues/196
40
        pubkey = pubkey.replace('BEGIN RSA', 'BEGIN').replace('END RSA', 'END')
41
        return load_pem_public_key(pubkey.encode(), default_backend())
42
43
44
def encrypt(pubkey, password):
45
    """Encrypt password using given RSA public key and encode it with base64.
46
47
    The encrypted password can only be decrypted by someone with the
48
    private key (in this case, only Travis).
49
    """
50
    key = load_key(pubkey)
51
    encrypted_password = key.encrypt(password, PKCS1v15())
52
    return base64.b64encode(encrypted_password)
53
54
55
def fetch_public_key(repo):
56
    """Download RSA public key Travis will use for this repo.
57
58
    Travis API docs: http://docs.travis-ci.com/api/#repository-keys
59
    """
60
    keyurl = 'https://api.travis-ci.org/repos/{0}/key'.format(repo)
61
    data = json.loads(urlopen(keyurl).read().decode())
62
    if 'key' not in data:
63
        errmsg = "Could not find public key for repo: {}.\n".format(repo)
64
        errmsg += "Have you already added your GitHub repo to Travis?"
65
        raise ValueError(errmsg)
66
    return data['key']
67
68
69
def prepend_line(filepath, line):
70
    """Rewrite a file adding a line to its beginning.
71
    """
72
    with open(filepath) as f:
73
        lines = f.readlines()
74
75
    lines.insert(0, line)
76
77
    with open(filepath, 'w') as f:
78
        f.writelines(lines)
79
80
81
def load_yaml_config(filepath):
82
    with open(filepath) as f:
83
        return yaml.load(f)
84
85
86
def save_yaml_config(filepath, config):
87
    with open(filepath, 'w') as f:
88
        yaml.dump(config, f, default_flow_style=False)
89
90
91
def update_travis_deploy_password(encrypted_password):
92
    """Update the deploy section of the .travis.yml file
93
    to use the given encrypted password.
94
    """
95
    config = load_yaml_config(TRAVIS_CONFIG_FILE)
96
97
    config['deploy']['password'] = dict(secure=encrypted_password)
98
99
    save_yaml_config(TRAVIS_CONFIG_FILE, config)
100
101
    line = ('# This file was autogenerated and will overwrite'
102
            ' each time you run travis_pypi_setup.py\n')
103
    prepend_line(TRAVIS_CONFIG_FILE, line)
104
105
106
def main(args):
107
    public_key = fetch_public_key(args.repo)
108
    password = args.password or getpass('PyPI password: ')
109
    update_travis_deploy_password(encrypt(public_key, password.encode()))
110
    print("Wrote encrypted password to .travis.yml -- you're ready to deploy")
111
112
113
if '__main__' == __name__:
114
    import argparse
115
    parser = argparse.ArgumentParser(description=__doc__)
116
    parser.add_argument('--repo', default=GITHUB_REPO,
117
                        help='GitHub repo (default: %s)' % GITHUB_REPO)
118
    parser.add_argument('--password',
119
                        help='PyPI password (will prompt if not provided)')
120
121
    args = parser.parse_args()
122
    main(args)
123