From 7397b16f4cc1a4a5315139fe2c89f275f4612f85 Mon Sep 17 00:00:00 2001 From: Cody Moncur Date: Thu, 5 Dec 2019 15:22:50 -0800 Subject: [PATCH 1/5] Add Device IDs to Auth Request Object, Update CLI to Reflect (#94) * Add device IDs to Auth Request object, update CLI to reflect --- CHANGES.rst | 6 ++++ examples/cli/cli.py | 3 +- .../service-client-auth-request-send.feature | 7 +++++ .../steps/managers/directory_service_auths.py | 16 +++++++--- features/steps/service_auth_steps.py | 29 ++++++++++++++++++- launchkey/clients/service.py | 3 +- launchkey/entities/service/__init__.py | 11 ++++--- launchkey/entities/validation.py | 1 + tests/test_entities_service.py | 18 +++++++++++- tests/test_service_client.py | 12 ++++++++ tests/test_validation.py | 11 ++++++- 11 files changed, 104 insertions(+), 13 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 7639f49..b410987 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,6 +1,12 @@ CHANGELOG for LaunchKey Python SDK ================================== +3.7.0 +_____ + +* Add device ID list to `AuthorizationRequest` object +* Update CLI to display device ID list upon authorization request + 3.6.0 ----- diff --git a/examples/cli/cli.py b/examples/cli/cli.py index c39adc4..8a15c83 100644 --- a/examples/cli/cli.py +++ b/examples/cli/cli.py @@ -133,7 +133,8 @@ def authorize(ctx, service_id, username, context, title, ttl, push_title, "Authorization request successful", { "Auth Request": auth.auth_request, - "Push Package": auth.push_package + "Push Package": auth.push_package, + "Device IDs": auth.device_ids }, color=SUCCESS_COLOR ) diff --git a/features/service_client/auth_request/service-client-auth-request-send.feature b/features/service_client/auth_request/service-client-auth-request-send.feature index ce570cb..aad500c 100755 --- a/features/service_client/auth_request/service-client-auth-request-send.feature +++ b/features/service_client/auth_request/service-client-auth-request-send.feature @@ -8,6 +8,7 @@ Feature: Service Client Authorization Request: Can Send Request Background: Given I created a Directory + And I have added an SDK Key to the Directory And I created a Directory Service Scenario: Making a request with a valid User an no linked Devices raises EntityNotFound @@ -22,3 +23,9 @@ Feature: Service Client Authorization Request: Can Send Request Scenario: Making a request including context with an invalid User Throws EntityNotFound When I attempt to make an Authorization request with the context value "Hello iovation!" Then a EntityNotFound error occurs + + @device_testing + Scenario: Making a request with a valid user device parses the device IDs in the response + Given I have a linked Device + When I make an Authorization request + Then the Authorization Request response Device IDs matches the current Devices list diff --git a/features/steps/managers/directory_service_auths.py b/features/steps/managers/directory_service_auths.py index a056a42..a8614ff 100644 --- a/features/steps/managers/directory_service_auths.py +++ b/features/steps/managers/directory_service_auths.py @@ -13,6 +13,7 @@ class DirectoryServiceAuthsManager(BaseManager): def __init__(self, organization_factory): self.current_auth_response = None self.previous_auth_response = None + self.current_auth_request = None self.current_auth_request_id = None self.previous_auth_request_id = None super(DirectoryServiceAuthsManager, self, ).__init__( @@ -43,7 +44,7 @@ def create_auth_request(self, service_id, user, context=None, policy=None, push_body=None, denial_reasons=None): client = self._get_service_client(service_id) try: - self.current_auth_request_id = client.authorization_request( + current_auth_request = client.authorization_request( user, context=context, policy=policy, @@ -52,10 +53,14 @@ def create_auth_request(self, service_id, user, context=None, policy=None, push_title=push_title, push_body=push_body, denial_reasons=denial_reasons - ).auth_request + ) + + self.current_auth_request = current_auth_request + self.current_auth_request_id = current_auth_request.auth_request + except EntityNotFound: sleep(2) - self.current_auth_request_id = client.authorization_request( + current_auth_request = client.authorization_request( user, context=context, policy=policy, @@ -64,7 +69,10 @@ def create_auth_request(self, service_id, user, context=None, policy=None, push_title=push_title, push_body=push_body, denial_reasons=denial_reasons - ).auth_request + ) + + self.current_auth_request = current_auth_request + self.current_auth_request_id = current_auth_request.auth_request def get_auth_response(self, service_id, auth_request): client = self._get_service_client(service_id) diff --git a/features/steps/service_auth_steps.py b/features/steps/service_auth_steps.py index ce990e6..891946a 100644 --- a/features/steps/service_auth_steps.py +++ b/features/steps/service_auth_steps.py @@ -1,5 +1,7 @@ from behave import given, when, then +from hamcrest import assert_that, equal_to + from launchkey.entities.service import GeoFence, AuthMethod, AuthMethodType, \ Requirement, AuthResponseReason @@ -263,4 +265,29 @@ def parse_input(value): "is not supported on all devices, so this may fail." % (expected_method, methods[i], expected_method.method)) else: - raise Exception("Expected %s but got %s" % (expected_method, methods[i])) + raise Exception("Expected %s but got %s" % (expected_method, + methods[i])) + + +@then("the Authorization Request response Device IDs matches the current " + "Devices list") +def verify_device_ids_match_device_list(context): + request = context.directory_service_auths_manager.current_auth_request + + if not request: + raise Exception("Expected an auth request to be present but got none.") + + request_device_ids = request.device_ids + + if not request_device_ids: + raise Exception("Expected device IDs to be present in auth request " + "but got %s instead." % request_device_ids) + + current_directory = context.entity_manager.get_current_directory() + current_user_identifier = context.directory_device_manager. \ + current_user_identifier + current_user_devices = context.directory_device_manager \ + .retrieve_user_devices(current_user_identifier, current_directory.id) + current_user_device_ids = map(lambda d: d.id, current_user_devices) + + assert_that(request_device_ids, equal_to(current_user_device_ids)) diff --git a/launchkey/clients/service.py b/launchkey/clients/service.py index 085f769..452dafa 100644 --- a/launchkey/clients/service.py +++ b/launchkey/clients/service.py @@ -171,7 +171,8 @@ def authorization_request(self, user, context=None, policy=None, self._subject, **kwargs) data = self._validate_response(response, AuthorizeValidator) return AuthorizationRequest(data.get('auth_request'), - data.get('push_package')) + data.get('push_package'), + data.get('device_ids')) @api_call def get_advanced_authorization_response(self, authorization_request_id): diff --git a/launchkey/entities/service/__init__.py b/launchkey/entities/service/__init__.py index 9d42a6e..27b7aff 100644 --- a/launchkey/entities/service/__init__.py +++ b/launchkey/entities/service/__init__.py @@ -479,21 +479,24 @@ def __repr__(self): class AuthorizationRequest(object): """ - Authorization Response object containing decrypted auth response + Authorization Request object containing decrypted auth response and other related information """ - def __init__(self, auth_request, push_package): + def __init__(self, auth_request, push_package, device_ids=None): self.auth_request = auth_request self.push_package = push_package + self.device_ids = device_ids def __repr__(self): return "AuthorizationRequest <" \ "auth_request=\"{auth_request}\", " \ - "push_package=\"{push_package}\">".\ + "push_package=\"{push_package}\", " \ + "device_ids=\"{device_ids}\">".\ format( auth_request=self.auth_request, - push_package=self.push_package + push_package=self.push_package, + device_ids=self.device_ids ) diff --git a/launchkey/entities/validation.py b/launchkey/entities/validation.py index 334f0f0..9047eb3 100644 --- a/launchkey/entities/validation.py +++ b/launchkey/entities/validation.py @@ -270,6 +270,7 @@ class AuthorizeValidator(Schema): """Authorize entity validator""" auth_request = validators.String(not_empty=True) push_package = validators.String(if_missing=None, not_empty=True) + device_ids = ForEach(validators.String(), if_missing=None) allow_extra_fields = True diff --git a/tests/test_entities_service.py b/tests/test_entities_service.py index 1bce1b4..24ca3e2 100644 --- a/tests/test_entities_service.py +++ b/tests/test_entities_service.py @@ -1,6 +1,8 @@ import unittest import json +from uuid import uuid4 + from mock import MagicMock, patch from ddt import data, unpack, ddt from formencode import Invalid @@ -1583,7 +1585,21 @@ def test_repr(self): self.assertEqual( str(auth_request), 'AuthorizationRequest ' + 'push_package="package", ' + 'device_ids="None">' + ) + + faux_device_id = str(uuid4()) + auth_request_with_device_ids = AuthorizationRequest( + auth_request='auth', + push_package='package', + device_ids=[faux_device_id]) + + self.assertEqual( + str(auth_request_with_device_ids), + 'AuthorizationRequest ' % faux_device_id ) diff --git a/tests/test_service_client.py b/tests/test_service_client.py index 3889360..c81a995 100644 --- a/tests/test_service_client.py +++ b/tests/test_service_client.py @@ -83,6 +83,18 @@ def test_authorization_request_response_has_push_package(self): self._response.data = {"auth_request": "auth", "push_package": "expected package"} self.assertEqual('expected package', self._service_client.authorization_request(ANY).push_package) + def test_authorization_request_response_has_device_ids(self): + expected_device_ids = ["expected_device_id"] + self._response.data = { + "auth_request": "auth", + "push_package": "expected package", + "device_ids": expected_device_ids + } + + self.assertEqual( + expected_device_ids, + self._service_client.authorization_request(ANY).device_ids) + def test_authorization_request_invalid_policy_input(self): self._response.data = {"auth_request": ANY} with self.assertRaises(InvalidParameters): diff --git a/tests/test_validation.py b/tests/test_validation.py index f16075a..575e4ab 100644 --- a/tests/test_validation.py +++ b/tests/test_validation.py @@ -15,9 +15,11 @@ class TestAuthorizeValidator(TestCase): def setUp(self): + self._expected_device_ids = ['expected_device_id'] self._data = { 'auth_request': 'Expected Auth Request', - 'push_package': 'Expected Push Package' + 'push_package': 'Expected Push Package', + 'device_ids': self._expected_device_ids } self._validator = AuthorizeValidator() @@ -62,6 +64,13 @@ def test_push_package_returns_unchanged_string(self): actual = self._validator.to_python(self._data) self.assertIn('push_package', actual) self.assertEqual(actual['push_package'], expected) + self.assertEqual(actual["device_ids"], self._expected_device_ids) + + def test_device_ids_may_be_missing(self): + del self._data["device_ids"] + actual = self._validator.to_python(self._data) + self.assertIn('device_ids', actual) + self.assertIsNone(actual['device_ids']) @ddt From e927676098994871be1519ec1543d59bdcf87bf6 Mon Sep 17 00:00:00 2001 From: Juan Rodriguez Date: Wed, 11 Dec 2019 14:00:20 -0800 Subject: [PATCH 2/5] changes within service_auth_steps.py to address map function --- features/steps/service_auth_steps.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/steps/service_auth_steps.py b/features/steps/service_auth_steps.py index 891946a..858a797 100644 --- a/features/steps/service_auth_steps.py +++ b/features/steps/service_auth_steps.py @@ -288,6 +288,6 @@ def verify_device_ids_match_device_list(context): current_user_identifier current_user_devices = context.directory_device_manager \ .retrieve_user_devices(current_user_identifier, current_directory.id) - current_user_device_ids = map(lambda d: d.id, current_user_devices) + current_user_device_ids = [d.id for d in current_user_devices] assert_that(request_device_ids, equal_to(current_user_device_ids)) From b025a039fd557498a4593d05ba868500a83771d0 Mon Sep 17 00:00:00 2001 From: Brad Porter Date: Thu, 12 Dec 2019 15:12:02 -0800 Subject: [PATCH 3/5] Bumped version to 3.7-rc.1 --- launchkey/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launchkey/__init__.py b/launchkey/__init__.py index b330407..702944f 100644 --- a/launchkey/__init__.py +++ b/launchkey/__init__.py @@ -1,5 +1,5 @@ """LaunchKey Service SDK module""" -SDK_VERSION = '3.6.0' +SDK_VERSION = '3.7.0-rc.1' LAUNCHKEY_PRODUCTION = "https://api.launchkey.com" VALID_JWT_ISSUER_LIST = ["svc", "dir", "org"] JOSE_SUPPORTED_JWE_ALGS = ['RSA-OAEP'] From 99094981233de76fe15af64e2d1113a797e31c7f Mon Sep 17 00:00:00 2001 From: Brad Porter Date: Thu, 12 Dec 2019 16:28:56 -0800 Subject: [PATCH 4/5] Updated changelog 3.7.0 subheader to use the proper definition --- CHANGES.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index b410987..55fde48 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,7 +2,7 @@ CHANGELOG for LaunchKey Python SDK ================================== 3.7.0 -_____ +----- * Add device ID list to `AuthorizationRequest` object * Update CLI to display device ID list upon authorization request From 7f0414d95d3ac38105244977481d7288d3bbff70 Mon Sep 17 00:00:00 2001 From: Brad Porter Date: Tue, 7 Jan 2020 12:08:54 -0800 Subject: [PATCH 5/5] Updated version to be 3.7.0 in preperation for release --- launchkey/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launchkey/__init__.py b/launchkey/__init__.py index 702944f..dc598f2 100644 --- a/launchkey/__init__.py +++ b/launchkey/__init__.py @@ -1,5 +1,5 @@ """LaunchKey Service SDK module""" -SDK_VERSION = '3.7.0-rc.1' +SDK_VERSION = '3.7.0' LAUNCHKEY_PRODUCTION = "https://api.launchkey.com" VALID_JWT_ISSUER_LIST = ["svc", "dir", "org"] JOSE_SUPPORTED_JWE_ALGS = ['RSA-OAEP']