Skip to content

Commit 36a5f12

Browse files
committed
SN-612 Adding unit and integration tests
1 parent 399b477 commit 36a5f12

File tree

12 files changed

+285
-27
lines changed

12 files changed

+285
-27
lines changed

securenative/__init__.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
from securenative.event_options import Event
2+
from securenative.sdk_options import SecureNativeOptions
3+
from securenative.securenative import SecureNative
4+
import securenative.event_types
5+
6+
_sn_sdk: SecureNative = None
7+
8+
9+
def init(api_key, options=SecureNativeOptions()):
10+
global _sn_sdk
11+
if _sn_sdk is None:
12+
_sn_sdk = SecureNative(api_key, options)
13+
14+
15+
def track(event: Event):
16+
sdk = _get_or_throw()
17+
sdk.track(event)
18+
19+
20+
def verify(event: Event):
21+
sdk = _get_or_throw()
22+
return sdk.verify(event)
23+
24+
25+
def verify_webhook(hmac_header: str, body: str):
26+
sdk = _get_or_throw()
27+
sdk.verify_webhook(hmac_header=hmac_header, body=body)
28+
29+
30+
def flush():
31+
sdk = _get_or_throw()
32+
sdk.flush()
33+
34+
35+
def _get_or_throw():
36+
if _sn_sdk is None:
37+
raise ValueError(
38+
u'You should call securenative.init(api_key, options) before making any other sdk function call.')
39+
return _sn_sdk

securenative/event_manager.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ def __init__(self, api_key, options=SecureNativeOptions(), http_client=HttpClien
3030
def send_async(self, event, resource_path):
3131
item = QueueItem(
3232
self._build_url(resource_path),
33-
json.dumps(event)
33+
json.dumps(event.as_dict())
3434
)
3535

3636
self.queue.insert(0, item)
@@ -48,7 +48,7 @@ def send_sync(self, event, resources_path):
4848
return self.http_client.post(
4949
self._build_url(resources_path),
5050
self.api_key,
51-
json.dumps(event)
51+
json.dumps(event.as_dict())
5252
)
5353

5454
def _build_url(self, resource_path):

securenative/event_options.py

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,44 @@ def __init__(self, event_type, user=User(), ip=u'127.0.0.1', remote_ip=u'127.0.0
2020
self.ip = ip
2121
self.user_agent = user_agent
2222
self.params = params
23-
if params is None:
24-
params = list()
25-
if isinstance(params, list):
26-
raise ValueError('custom params should be a list of CustomParams, i.e: [CustomParams(key, value), ...])')
23+
self.cid = ''
24+
self.fp = ''
25+
self.params = list()
26+
27+
if params is not None:
28+
if not isinstance(params, list):
29+
raise ValueError(
30+
'custom params should be a list of CustomParams, i.e: [CustomParams(key, value), ...])')
31+
if len(params) > 0 and not isinstance(params[0], CustomParam):
32+
raise ValueError(
33+
'custom params should be a list of CustomParams, i.e: [CustomParams(key, value), ...])')
34+
35+
self.params = params
2736

2837
if sn_cookie_value is not None:
2938
self.cid, self.fp = _parse_cookie(sn_cookie_value)
3039

31-
self.vid = uuid.uuid1()
32-
self.ts = time.time() * 1000
40+
self.vid = str(uuid.uuid4())
41+
self.ts = int(time.time())*1000
42+
43+
def as_dict(self):
44+
return {
45+
"eventType": self.event_type,
46+
"user": {
47+
"id": self.user.user_id,
48+
"email": self.user.user_email,
49+
"name": self.user.user_name
50+
},
51+
"remoteIP": self.remote_ip,
52+
"ip": self.ip,
53+
"cid": self.cid,
54+
"fp": self.fp,
55+
"ts": self.ts,
56+
"vid": self.vid,
57+
"userAgent": self.user_agent,
58+
"device": {},
59+
"params": [{p.key: p.value} for p in self.params]
60+
}
3361

3462

3563
class CustomParam:

securenative/http_client.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ def _headers(self, api_key):
88
return {
99
'Content-Type': 'application/json',
1010
'User-Agent': 'SecureNative-python',
11-
'SN-Version': sdk_version,
11+
'Sn-Version': sdk_version,
1212
'Authorization': api_key
1313
}
1414

1515
def post(self, url, api_key, body):
16-
return requests.post(url=url, body=body, headers=self._headers(api_key))
16+
return requests.post(url=url, data=body, headers=self._headers(api_key))

securenative/securenative.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,28 +12,28 @@ def __init__(self, api_key, options=SecureNativeOptions()):
1212
if api_key is None:
1313
raise MissingApiKeyError()
1414

15-
self.api_key = api_key
16-
self.options = options
17-
self.event_manager = EventManager(self.api_key, self.options)
15+
self._api_key = api_key
16+
self._options = options
17+
self._event_manager = EventManager(self._api_key, self._options)
1818

1919
def track(self, event):
2020
_validate_event(event)
21-
self.event_manager.send_async(event, 'collector/api/v1/track')
21+
self._event_manager.send_async(event, 'collector/api/v1/track')
2222

2323
def verify(self, event):
2424
_validate_event(event)
25-
response = self.event_manager.send_sync(event, 'collector/api/v1/verify')
25+
response = self._event_manager.send_sync(event, 'collector/api/v1/verify')
2626
if response.status_code == 200:
2727
json_result = json.loads(response.text)
2828
return json_result
2929
else:
3030
return None
3131

3232
def verify_webhook(self, hmac_header, body):
33-
return verify_signature(self.api_key, body, hmac_header)
33+
return verify_signature(self._api_key, body, hmac_header)
3434

3535
def flush(self):
36-
self.event_manager.flush()
36+
self._event_manager.flush()
3737

3838

3939
def _validate_event(event):

securenative/utils.py

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,29 @@ def _parse_cookie(cookie_value=None):
1111
if cookie_value is None:
1212
return cid, fp
1313

14-
base64_decoded = base64.b64decode(cookie_value)
15-
if base64_decoded is None:
16-
base64_decoded = u'{}'
14+
try:
15+
base64_decoded = base64.b64decode(cookie_value)
16+
if base64_decoded is None:
17+
base64_decoded = u'{}'
1718

18-
json_obj = json.loads(base64_decoded)
19+
json_obj = json.loads(base64_decoded)
1920

20-
if hasattr(json_obj, 'fp'):
21-
fp = json_obj['fp']
21+
if u'fp' in json_obj:
22+
fp = json_obj['fp']
2223

23-
if hasattr(json_obj, 'cid'):
24-
cid = json_obj['cid']
24+
if u'cid' in json_obj:
25+
cid = json_obj['cid']
26+
except Exception as ex:
27+
pass
2528

2629
return cid, fp
2730

2831

2932
def verify_signature(secret, text_body, header_signature):
30-
comparison_signature = hmac.new(secret, text_body, hashlib.sha3_512).hexdigest()
31-
return comparison_signature == header_signature
33+
try:
34+
key = secret.encode('utf-8')
35+
body = text_body.encode('utf-8')
36+
comparison_signature = hmac.new(key, body, hashlib.sha512).hexdigest()
37+
return hmac.compare_digest(comparison_signature, header_signature)
38+
except Exception as ex:
39+
return False

setup.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
from distutils.core import setup
2+
3+
from securenative.config import sdk_version
4+
5+
setup(
6+
name='securenative-sdk',
7+
packages=['securenative-sdk'],
8+
version=sdk_version,
9+
license='MIT',
10+
description='Secure Native SDK for python',
11+
author='Secure Native',
12+
author_email='[email protected]',
13+
url='http://www.securenative.com',
14+
download_url='https://github.com/user/reponame/archive/v_01.tar.gz', # I explain this later on
15+
keywords=["securenative", 'cyber-security'],
16+
install_requires=[
17+
"requests",
18+
],
19+
classifiers=[
20+
'Development Status :: 5 - Production/Stable',
21+
'Intended Audience :: Developers',
22+
'Topic :: Software Development :: Build Tools',
23+
'License :: OSI Approved :: MIT License',
24+
'Programming Language :: Python :: 3',
25+
'Programming Language :: Python :: 3.4',
26+
'Programming Language :: Python :: 3.5',
27+
'Programming Language :: Python :: 3.6',
28+
],
29+
)

tests/__init__.py

Whitespace-only changes.

tests/event_manager.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import functools
2+
import json
3+
import threading
4+
import unittest
5+
6+
from securenative import Event, event_types
7+
from securenative.event_manager import EventManager
8+
from securenative.event_options import User, CustomParam
9+
10+
11+
class EventManagerTests(unittest.TestCase):
12+
13+
def build_event(self):
14+
return Event(event_type=event_types.login, user=User(user_id='1', user_email='[email protected]', user_name='1 2'),
15+
params=[CustomParam('key', 'val')])
16+
17+
def test_send_sync(self):
18+
api_key = 'key'
19+
resource = 'my/custom/resource'
20+
client = HttpClientMock(None)
21+
event = self.build_event()
22+
manager = EventManager(api_key=api_key, http_client=client)
23+
url, key, body = manager.send_sync(event, resource)
24+
25+
self.assertEqual(url, 'https://api.securenative.com/' + resource)
26+
self.assertEqual(key, api_key)
27+
self.assertEqual(body, json.dumps(event.as_dict()))
28+
29+
def test_send_async(self):
30+
wg = threading.Event()
31+
api_key = 'key'
32+
resource = 'my/custom/resource'
33+
event = self.build_event()
34+
client = HttpClientMock(functools.partial(self.assert_cb, wg, event))
35+
36+
manager = EventManager(api_key=api_key, http_client=client)
37+
manager.send_async(event, resource)
38+
self.assertTrue(wg.wait(manager.options.interval//1000 * 2))
39+
40+
def assert_cb(self, wg, event, url, key, body):
41+
self.assertEqual(url, 'https://api.securenative.com/my/custom/resource')
42+
self.assertEqual(key, 'key')
43+
self.assertEqual(body, json.dumps(event.as_dict()))
44+
wg.set()
45+
46+
47+
class HttpClientMock(object):
48+
def __init__(self, cb):
49+
self.cb = cb
50+
51+
def post(self, url, api_key, body):
52+
if self.cb is not None:
53+
self.cb(url, api_key, body)
54+
return url, api_key, body

tests/http_client.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import json
2+
import unittest
3+
4+
from securenative.config import sdk_version
5+
from securenative.http_client import HttpClient
6+
7+
8+
class HttpClientTests(unittest.TestCase):
9+
def test_post(self):
10+
client = HttpClient()
11+
api_key = u'ABC'
12+
request_body = {"key": "test1", "value": "test2"}
13+
response = client.post(url=u'https://httpbin.org/post', api_key=api_key, body=json.dumps(request_body))
14+
15+
self.assertEqual(response.status_code, 200)
16+
json_body = json.loads(response.text)
17+
self.assertEqual(json_body['headers']['Content-Type'], 'application/json')
18+
self.assertEqual(json_body['headers']['User-Agent'], 'SecureNative-python')
19+
self.assertEqual(json_body['headers']['Sn-Version'], sdk_version)
20+
self.assertEqual(json_body['headers']['Authorization'], api_key)
21+
self.assertEqual(json_body['json'], request_body)

0 commit comments

Comments
 (0)