Skip to content

Commit 55e204b

Browse files
authored
Merge pull request #8 from MastercardDevs/master
Add interceptor and test
2 parents 2f689f7 + 1920702 commit 55e204b

File tree

3 files changed

+123
-1
lines changed

3 files changed

+123
-1
lines changed

oauth1/oauth.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#
2929
from random import randint
3030
import time
31+
import json
3132

3233
import oauth1.coreutils as util
3334
from OpenSSL import crypto
@@ -59,8 +60,11 @@ def get_oauth_parameters(self, uri, method, payload, consumer_key, signing_key):
5960
oauth_parameters.set_oauth_timestamp(OAuth.get_timestamp())
6061
oauth_parameters.set_oauth_signature_method("RSA-SHA256")
6162
oauth_parameters.set_oauth_version("1.0")
63+
64+
payload_str = json.dumps(payload) if type(payload) is dict else payload
65+
6266
if method != "GET" and method != "DELETE" and method != "HEAD":
63-
encoded_hash = util.base64_encode(util.sha256_encode(payload))
67+
encoded_hash = util.base64_encode(util.sha256_encode(payload_str))
6468
oauth_parameters.set_oauth_body_hash(encoded_hash)
6569

6670
# Get the base string

oauth1/signer_interceptor.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import json
2+
from functools import wraps
3+
from oauth1.oauth import OAuth
4+
from oauth1 import authenticationutils
5+
6+
7+
class SignerInterceptor(object):
8+
9+
10+
def __init__(self, key_file, key_password, consumer_key):
11+
"""Load signing key."""
12+
self.signing_key = authenticationutils.load_signing_key(key_file, key_password)
13+
self.consumer_key = consumer_key
14+
15+
16+
def oauth_signing(self, func):
17+
"""Decorator for API request. func is APIClient.request"""
18+
19+
@wraps(func)
20+
def request_function(*args, **kwargs): # pragma: no cover
21+
in_body = kwargs.get("body", None)
22+
23+
auth_header = OAuth().get_authorization_header(args[1], args[0], in_body,
24+
self.consumer_key, self.signing_key)
25+
26+
in_headers = kwargs.get("headers", None)
27+
if not in_headers:
28+
in_headers = dict()
29+
kwargs["headers"] = in_headers
30+
31+
in_headers["Authorization"] = auth_header
32+
33+
return func(*args, **kwargs)
34+
35+
return request_function
36+
37+
38+
39+
def add_signing_layer(self, api_client, key_file, key_password, consumer_key):
40+
"""Create and load configuration. Decorate APIClient.request with header signing"""
41+
42+
api_signer = SignerInterceptor(key_file, key_password, consumer_key)
43+
api_client.request = api_signer.oauth_signing(api_client.request)
44+
45+
def get_signing_layer(self, api_client):
46+
return api_client.request
47+
48+
49+
50+
51+

tests/test_interceptor.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-#
3+
#
4+
#
5+
# Copyright (c) 2019 MasterCard International Incorporated
6+
# All rights reserved.
7+
#
8+
# Redistribution and use in source and binary forms, with or without modification, are
9+
# permitted provided that the following conditions are met:
10+
#
11+
# Redistributions of source code must retain the above copyright notice, this list of
12+
# conditions and the following disclaimer.
13+
# Redistributions in binary form must reproduce the above copyright notice, this list of
14+
# conditions and the following disclaimer in the documentation and/or other materials
15+
# provided with the distribution.
16+
# Neither the name of the MasterCard International Incorporated nor the names of its
17+
# contributors may be used to endorse or promote products derived from this software
18+
# without specific prior written permission.
19+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
20+
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21+
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
22+
# SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23+
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
24+
# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
25+
# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
26+
# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
27+
# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28+
# SUCH DAMAGE.
29+
#
30+
import unittest
31+
import requests
32+
from urllib.parse import urlencode
33+
from oauth1.oauth import OAuth
34+
from oauth1.signer_interceptor import SignerInterceptor
35+
import oauth1.authenticationutils as authenticationutils
36+
from os.path import dirname, realpath, join, os
37+
from oauth1.signer import OAuthSigner
38+
from oauth1.signer_interceptor import add_signing_layer
39+
from oauth1.signer_interceptor import get_signing_layer
40+
41+
42+
class OAuthInterceptorTest(unittest.TestCase):
43+
44+
45+
""" add an interceptor, check api client has changed """
46+
def test_add_interceptor(self):
47+
if os.path.exists('./test_key_container.p12'):
48+
key_file = './test_key_container.p12'
49+
key_password = "Password1"
50+
consumer_key = 'uLXKmWNmIkzIGKfA2injnNQqpZaxaBSKxa3ixEVu2f283c95!33b9b2bd960147e387fa6f3f238f07170000000000000000'
51+
52+
signing_layer1 = get_signing_layer(self, requests)
53+
54+
add_signing_layer(self, requests, key_file, key_password, consumer_key)
55+
56+
signing_layer2 = get_signing_layer(self, requests)
57+
58+
self.assertNotEqual(signing_layer1, signing_layer2)
59+
60+
61+
else:
62+
print("Please add a ./test_key_container.p12 file to enable key tests")
63+
64+
65+
66+
if __name__ == '__main__':
67+
unittest.main()

0 commit comments

Comments
 (0)