Snippets

Penn DB Group Prov Create and delete token for signature generation test

Created by John Frommeyer last modified
#!/usr/bin/env python3

# Will get SSL warnings if python 2 is used. Still works though.

"""Testing web service"""

import sys
from datetime import datetime
import hashlib
import hmac
import base64
import argparse
import getpass
import requests

SCHEME = 'https://'
HOST = 'pennprovenance.net'
BASE_URL = SCHEME + HOST
TOKENS_PATH = '/prov/tokens'


def hash_and_encode(payload):
    """hash and base64 encode the string payload"""
    payload_bytes = payload.encode('utf-8')
    hasher = hashlib.sha256()
    hasher.update(payload_bytes)
    hashed_bytes = hasher.digest()
    encoded_hashed_bytes = base64.standard_b64encode(hashed_bytes)
    encoded_hashed = encoded_hashed_bytes.decode('utf-8')
    return encoded_hashed


def sign_string(token, string_to_sign):
    """return base64 encoding of signature for string_to_sign signed by token"""
    key_bytes = token.encode('utf-8')
    msg_bytes = string_to_sign.encode('utf-8')
    signature_bytes = hmac.new(
        key_bytes, msg=msg_bytes, digestmod=hashlib.sha256).digest()
    encoded_signature_bytes = base64.standard_b64encode(signature_bytes)
    encoded_signature = encoded_signature_bytes.decode('utf-8')
    return encoded_signature


def get_auth_headers(
        session_token,
        http_method,
        request_path,
        query_string,
        payload):
    """return authentication headers for the request signed with sessionToken"""
    session_key = session_token['key']
    now = datetime.now().isoformat()
    payload_for_signing = hash_and_encode(payload)

    string_to_sign = '\n'.join((
        session_key,
        http_method,
        HOST,
        request_path,
        query_string,
        now,
        payload_for_signing))

    token = session_token['token']
    signature = sign_string(token, string_to_sign)
    auth_headers = {'sessionKey': session_key,
                    'timestamp': now,
                    'signature': signature}
    return auth_headers


def create_token(url, username, password):
    """create token and return sessionToken dict"""
    post_payload = {
        'credentials': {
            'name': username,
            'password': password
        }
    }

    post_headers = {'Accept': 'application/json'}

    post_response = requests.post(url,
                                  headers=post_headers, json=post_payload)
    if post_response.status_code != 200:
        sys.exit('POST error:' + post_response.text)

    post_response_json = post_response.json()

    session_token = post_response_json['sessionToken']
    print('successfully created session token: ' + session_token['key'])
    return session_token


def delete_token(session_token):
    """delete token"""
    session_key = session_token['key']
    method = 'DELETE'
    query_string = ''
    delete_path = TOKENS_PATH + '/' + session_key
    payload = ''

    delete_headers = get_auth_headers(
        session_token,
        method,
        delete_path,
        query_string,
        payload)

    # Even though we don't expect a body in response add Accept header in case
    # we get error back. Otherwise we'd get XML version.
    delete_headers['Accept'] = 'application/json'
    delete = requests.delete(
        BASE_URL + delete_path,
        headers=delete_headers)
    if delete.status_code != 204:
        sys.exit('DELETE error:' + delete.text)
    print('successfully deleted session token: ' + session_key)


def main():
    """create and delete a token for signature testing"""
    parser = argparse.ArgumentParser(
        description='Create and delete token to test request signing')
    parser.add_argument(
        '--username',
        dest='username',
        required=True,
        help='the user\'s username')
    parser.add_argument(
        '--password',
        dest='password',
        help="""The user\'s password.
        If not supplied on command line, the user will be prompted for password.""")

    args = parser.parse_args()

    username = args.username
    password = args.password
    if password is None:
        password = getpass.getpass(
            prompt='Enter password for ' + username + ': ')

    session_token = create_token(
        BASE_URL + TOKENS_PATH,
        username,
        password)

    delete_token(session_token)


if __name__ == '__main__':
    main()

Comments (0)

HTTPS SSH

You can clone a snippet to your computer for local editing. Learn more.