def setUp(self): self._transport = MagicMock(spec=JOSETransport) self._transport.decrypt_response.return_value = '{"public_key_id":"' + self.PUBLIC_KEY_ID + '", "auth": null}' self._service_client = ServiceClient(self._subject_id, self._transport) self._headers = {"X-IOV-JWT": "jwt", "Other Header": "jwt"} self._issuer_private_key = MagicMock() self._transport.loaded_issuer_private_keys = { self.PUBLIC_KEY_ID: self._issuer_private_key } patcher = patch( "launchkey.entities.validation.AuthorizationResponsePackageValidator.to_python" ) self._authorization_response_validator_patch = patcher.start() self.addCleanup(patcher.stop) self._authorization_response_validator_patch.return_value = MagicMock( spec=dict) patcher = patch("launchkey.clients.service.loads") self._service_client_loads_patch = patcher.start() self.addCleanup(patcher.stop) self._service_client_loads_patch.side_effect = json.loads patcher = patch("launchkey.entities.service.loads") self._service_entity_loads_patch = patcher.start() self.addCleanup(patcher.stop) self._service_entity_loads_patch.return_value = MagicMock(spec=dict) patcher = patch("launchkey.entities.validation.AuthorizeSSEValidator") self._authorize_sse_validator_patch = patcher.start() self.addCleanup(patcher.stop) self._authorize_sse_validator_patch.return_value = MagicMock(spec=dict)
def setUp(self): self._transport = MagicMock(spec=JOSETransport) self._transport.decrypt_response.return_value = '{"public_key_id":"' + self.PUBLIC_KEY_ID + '", "auth": null}' self._service_client = ServiceClient(self._subject_id, self._transport) self._headers = {"X-IOV-JWT": "jwt", "Other Header": "jwt"} self._issuer_private_key = MagicMock() self._transport.loaded_issuer_private_keys = {self.PUBLIC_KEY_ID: self._issuer_private_key} patcher = patch("launchkey.entities.validation.AuthorizationResponsePackageValidator.to_python") self._authorization_response_validator_patch = patcher.start() self._authorization_response_validator_patch.return_value = MagicMock(spec=dict) patcher = patch("launchkey.clients.service.loads") self._service_client_loads_patch = patcher.start() self.addCleanup(patcher.stop) self._service_client_loads_patch.side_effect = json.loads patcher = patch("launchkey.entities.service.loads") self._service_entity_loads_patch = patcher.start() self.addCleanup(patcher.stop) self._service_entity_loads_patch.return_value = MagicMock(spec=dict) patcher = patch("launchkey.entities.validation.AuthorizeSSEValidator") self._authorize_sse_validator_patch = patcher.start() self.addCleanup(patcher.stop) self._authorize_sse_validator_patch.return_value = MagicMock(spec=dict)
def make_service_client(self, service_id): """ Retrieves a client to make service calls. :param service_id: Service id :return: launchkey.clients.ServiceClient """ return ServiceClient(service_id, self._transport)
def setUp(self): self._transport = MagicMock() self._response = APIResponse({}, {}, 200) self._transport.post.return_value = self._response self._transport.get.return_value = self._response self._transport.put.return_value = self._response self._transport.delete.return_value = self._response self._device_response = { "auth_request": str(uuid4()), "response": True, "device_id": str(uuid4()), "service_pins": ["1234", "3456", "5678"] } self._transport.loaded_issuer_private_key.decrypt.return_value = dumps( self._device_response) self._service_client = ServiceClient(uuid4(), self._transport) self._service_client._transport._verify_jwt_response = MagicMock()
def setUp(self): self._transport = MagicMock() self._response = APIResponse({}, {}, 200) self._transport.post.return_value = self._response self._transport.get.return_value = self._response self._transport.put.return_value = self._response self._transport.delete.return_value = self._response self._device_response = {"auth_request": str(uuid4()), "response": True, "device_id": str(uuid4()), "service_pins": ["1234", "3456", "5678"]} self._transport.loaded_issuer_private_key.decrypt.return_value = dumps(self._device_response) self._service_id = uuid4() self._issuer = "svc:{}".format(self._service_id) self._service_client = ServiceClient(self._service_id, self._transport) self._service_client._transport._verify_jwt_response = MagicMock()
class TestServiceClient(unittest.TestCase): def setUp(self): self._transport = MagicMock() self._response = APIResponse({}, {}, 200) self._transport.post.return_value = self._response self._transport.get.return_value = self._response self._transport.put.return_value = self._response self._transport.delete.return_value = self._response self._device_response = { "auth_request": str(uuid4()), "response": True, "device_id": str(uuid4()), "service_pins": ["1234", "3456", "5678"] } self._transport.loaded_issuer_private_key.decrypt.return_value = dumps( self._device_response) self._service_client = ServiceClient(uuid4(), self._transport) self._service_client._transport._verify_jwt_response = MagicMock() def test_authorize_success(self): self._response.data = {"auth_request": ANY} self._service_client.authorize(ANY, ANY, MagicMock(spec=AuthPolicy)) def test_authorize_invalid_policy_input(self): self._response.data = {"auth_request": ANY} with self.assertRaises(InvalidParameters): self._service_client.authorize(ANY, ANY, ANY) def test_authorize_unexpected_result(self): self._response.data = {MagicMock(spec=str): ANY} with self.assertRaises(UnexpectedAPIResponse): self._service_client.authorize(ANY) def test_authorize_invalid_params(self): self._transport.post.side_effect = LaunchKeyAPIException( { "error_code": "ARG-001", "error_detail": "" }, 400) with self.assertRaises(InvalidParameters): self._service_client.authorize(ANY) def test_authorize_invalid_policy(self): self._transport.post.side_effect = LaunchKeyAPIException( { "error_code": "SVC-002", "error_detail": "" }, 400) with self.assertRaises(InvalidPolicyInput): self._service_client.authorize(ANY) def test_authorize_policy_failure(self): self._transport.post.side_effect = LaunchKeyAPIException( { "error_code": "SVC-003", "error_detail": "" }, 400) with self.assertRaises(PolicyFailure): self._service_client.authorize(ANY) def test_authorize_entity_not_found(self): self._transport.post.side_effect = LaunchKeyAPIException({}, 404) with self.assertRaises(EntityNotFound): self._service_client.authorize(ANY) def test_authorize_rate_limited(self): self._transport.post.side_effect = LaunchKeyAPIException({}, 429) with self.assertRaises(RateLimited): self._service_client.authorize(ANY) @patch("launchkey.entities.service.b64decode") @patch("launchkey.entities.service.loads") @patch("launchkey.entities.service.AuthorizationResponsePackageValidator") def test_get_authorization_response_success( self, b64decode_patch, json_loads_patch, auth_response_package_validator_patch): b64decode_patch.return_value = MagicMock(spec=str) json_loads_patch.return_value = MagicMock(spec=dict) auth_response_package_validator_patch.return_value = MagicMock( spec=dict) public_key_id = str(uuid4()) self._service_client._transport.loaded_issuer_private_keys = { public_key_id: MagicMock() } self._response.data = { "auth": ANY, "service_user_hash": ANY, "user_push_id": ANY, "org_user_hash": ANY, "public_key_id": public_key_id } self.assertIsInstance( self._service_client.get_authorization_response(ANY), AuthorizationResponse) def test_get_authorization_response_unexpected_response(self): self._response.data = {MagicMock(spec=str): ANY} with self.assertRaises(UnexpectedAPIResponse): self._service_client.get_authorization_response(ANY) def test_get_authorization_response_no_response(self): self._response.status_code = 204 self.assertIsNone(self._service_client.get_authorization_response(ANY)) def test_get_authorization_response_invalid_params(self): self._transport.get.side_effect = LaunchKeyAPIException( { "error_code": "ARG-001", "error_detail": "" }, 400) with self.assertRaises(InvalidParameters): self._service_client.get_authorization_response(ANY) def test_get_authorization_response_timeout(self): self._transport.get.side_effect = LaunchKeyAPIException({}, 408) with self.assertRaises(RequestTimedOut): self._service_client.get_authorization_response(ANY) def test_session_start_success(self): self.assertIsNone(self._service_client.session_start(ANY, ANY)) def test_session_start_invalid_params(self): self._transport.post.side_effect = LaunchKeyAPIException( { "error_code": "ARG-001", "error_detail": "" }, 400) with self.assertRaises(InvalidParameters): self._service_client.session_start(ANY, ANY) def test_session_start_entity_not_found(self): self._transport.post.side_effect = LaunchKeyAPIException({}, 404) with self.assertRaises(EntityNotFound): self._service_client.session_start(ANY, ANY) def test_session_end_success(self): self.assertIsNone(self._service_client.session_end(ANY)) def test_session_end_invalid_params(self): self._transport.delete.side_effect = LaunchKeyAPIException( { "error_code": "ARG-001", "error_detail": "" }, 400) with self.assertRaises(InvalidParameters): self._service_client.session_end(ANY) def test_session_end_entity_not_found(self): self._transport.delete.side_effect = LaunchKeyAPIException({}, 404) with self.assertRaises(EntityNotFound): self._service_client.session_end(ANY) def test_webhook_session_end(self): request = dumps({ "service_user_hash": str(uuid4()), "api_time": str(datetime.utcnow())[:19].replace(" ", "T") + "Z" }) self.assertIsInstance( self._service_client.handle_webhook(request, ANY), SessionEndRequest) def test_webhook_session_end_invalid_input(self): request = dumps({"service_user_hash": str(uuid4())}) with self.assertRaises(UnexpectedAPIResponse): self.assertIsInstance( self._service_client.handle_webhook(request, ANY), SessionEndRequest) @patch("launchkey.entities.service.b64decode") @patch("launchkey.clients.service.loads") @patch("launchkey.entities.service.loads") @patch("launchkey.entities.validation.AuthorizeSSEValidator") @patch("launchkey.entities.service.AuthorizationResponsePackageValidator") def test_webhook_authorization_response( self, auth_response_package_validator_patch, auth_sse_validator_patch, json_loads_patch, json_loads_patch_2, b64decode_patch): b64decode_patch.return_value = MagicMock(spec=str) json_loads_patch.return_value = MagicMock(spec=dict) json_loads_patch_2.return_value = MagicMock(spec=dict) auth_sse_validator_patch.return_value = MagicMock(spec=dict) auth_response_package_validator_patch.return_value = MagicMock( spec=dict) self._transport.loaded_issuer_private_keys = { json_loads_patch_2().get(): MagicMock() } self.assertIsInstance( self._service_client.handle_webhook(MagicMock(), ANY), AuthorizationResponse)
class TestHandleWebhook(unittest.TestCase): _subject_id = uuid4() PUBLIC_KEY_ID = "Public Key ID" def setUp(self): patcher = patch("launchkey.clients.service.XiovJWTService") self._x_iov_jwt_service_patch = patcher.start().return_value self.addCleanup(patcher.stop) self._x_iov_jwt_service_patch.decrypt_jwe.return_value = '{"public_key_id":"' + self.PUBLIC_KEY_ID + '", "auth": null}' self._transport = MagicMock(spec=JOSETransport) self._service_client = ServiceClient(self._subject_id, self._transport) self._headers = {"X-IOV-JWT": "jwt", "Other Header": "jwt"} self._issuer_private_key = MagicMock() self._transport.loaded_issuer_private_keys = { self.PUBLIC_KEY_ID: self._issuer_private_key } patcher = patch( "launchkey.entities.validation.AuthorizationResponsePackageValidator.to_python" ) self._authorization_response_validator_patch = patcher.start() self.addCleanup(patcher.stop) self._authorization_response_validator_patch.return_value = MagicMock( spec=dict) patcher = patch("launchkey.clients.service.loads") self._service_client_loads_patch = patcher.start() self.addCleanup(patcher.stop) self._service_client_loads_patch.side_effect = json.loads patcher = patch("launchkey.entities.service.loads") self._service_entity_loads_patch = patcher.start() self.addCleanup(patcher.stop) self._service_entity_loads_patch.return_value = MagicMock(spec=dict) patcher = patch("launchkey.entities.validation.AuthorizeSSEValidator") self._authorize_sse_validator_patch = patcher.start() self.addCleanup(patcher.stop) self._authorize_sse_validator_patch.return_value = MagicMock(spec=dict) def test_webhook_session_end(self): body = dumps({ "service_user_hash": str(uuid4()), "api_time": str(datetime.utcnow())[:19].replace(" ", "T") + "Z" }) self._x_iov_jwt_service_patch.verify_jwt_request.return_value = body self.assertIsInstance( self._service_client.handle_webhook(body, self._headers), SessionEndRequest) def test_webhook_session_end_invalid_input(self): body = dumps({"service_user_hash": str(uuid4())}) self._x_iov_jwt_service_patch.verify_jwt_request.return_value = body with self.assertRaises(UnexpectedWebhookRequest): self.assertIsInstance( self._service_client.handle_webhook(body, self._headers), SessionEndRequest) def test_webhook_authorization_response_returns_authorization_response( self): self.assertIsInstance( self._service_client.handle_webhook(MagicMock(), self._headers), AuthorizationResponse) def test_calls_decrypt_jwe_request_with_expected_parameters(self): self._service_client.handle_webhook('body', self._headers, 'method', 'path') self._x_iov_jwt_service_patch.decrypt_jwe.assert_called_with( "body", self._headers, "method", "path") def test_handle_webhook_handles_jwt_validation_errors(self): self._x_iov_jwt_service_patch.decrypt_jwe.side_effect = XiovJWTValidationFailure with self.assertRaises(UnexpectedWebhookRequest): self._service_client.handle_webhook(MagicMock(), self._headers) def test_handle_webhook_session_end_requests_handles_data_validation_errors( self): self._authorize_sse_validator_patch.to_python.side_effect = Invalid body = dumps({"service_user_hash": str(uuid4())}) self._x_iov_jwt_service_patch.verify_jwt_request.return_value = body with self.assertRaises(UnexpectedWebhookRequest): self._service_client.handle_webhook(body, self._headers) def test_handle_webhook_auth_response_handles_json_loads_errors(self): self._transport.decrypt_response.return_value = '{"public_key_id":"' + self.PUBLIC_KEY_ID + '","auth":null}' self._service_entity_loads_patch.side_effect = ValueError with self.assertRaises(UnexpectedDeviceResponse): self._service_client.handle_webhook(MagicMock(), self._headers) def test_handle_webhook_auth_response_requests_handles_unexpected_key( self, ): self._x_iov_jwt_service_patch.decrypt_jwe.side_effect = UnexpectedKeyID with self.assertRaises(UnexpectedKeyID): self._service_client.handle_webhook(MagicMock(), self._headers) def test_handle_webhook_for_authorization_response_handles_jwe_decryption_errors( self): self._x_iov_jwt_service_patch.decrypt_jwe.side_effect = XiovJWTDecryptionFailure with self.assertRaises(UnableToDecryptWebhookRequest): self._service_client.handle_webhook(MagicMock(), self._headers) def test_handle_webhook_for_invalid_response_when_validating_auth_response( self): self._authorization_response_validator_patch.side_effect = Invalid with self.assertRaises(UnexpectedDeviceResponse): self._service_client.handle_webhook(MagicMock(), self._headers) def test_handle_webhook_for_response_without_auth_package_parsing_auth_response( self): self._x_iov_jwt_service_patch.decrypt_jwe.return_value = '{"public_key_id":"' + self.PUBLIC_KEY_ID + '"}' with self.assertRaises(UnexpectedAuthorizationResponse): self._service_client.handle_webhook(MagicMock(), self._headers)
class TestServiceClient(unittest.TestCase): def setUp(self): self._transport = MagicMock() self._response = APIResponse({}, {}, 200) self._transport.post.return_value = self._response self._transport.get.return_value = self._response self._transport.put.return_value = self._response self._transport.delete.return_value = self._response self._device_response = { "auth_request": str(uuid4()), "response": True, "device_id": str(uuid4()), "service_pins": ["1234", "3456", "5678"] } self._transport.loaded_issuer_private_key.decrypt.return_value = dumps( self._device_response) self._service_id = uuid4() self._issuer = "svc:{}".format(self._service_id) self._service_client = ServiceClient(self._service_id, self._transport) self._service_client._transport._verify_jwt_response = MagicMock() def test_authorize_calls_authorization_request(self): policy = AuthPolicy() auth_response = AuthorizationRequest(str(uuid4()), None) self._service_client.authorization_request = MagicMock( return_value=auth_response) self._service_client.authorize('user', 'context', policy, 'title', 30, 'push_title', 'push_body') self._service_client.authorization_request.assert_called_once_with( 'user', 'context', policy, 'title', 30, 'push_title', 'push_body') def test_authorize_returns_auth_request_id_from_authorization_request_response( self): expected = str(uuid4()) self._response.data = {"auth_request": expected} actual = self._service_client.authorize('user', 'context', AuthPolicy()) self.assertEqual(actual, expected) def test_authorization_request_success(self): self._response.data = {"auth_request": "value"} policy = MagicMock(spec=AuthPolicy) policy.get_policy.return_value = "policy" self._service_client.authorization_request("user", "context", policy) self._transport.post.assert_called_once_with('/service/v3/auths', self._issuer, username="******", context="context", policy="policy") def test_authorization_request_response_has_auth_request(self): self._response.data = {"auth_request": "expected value"} self.assertEqual( 'expected value', self._service_client.authorization_request(ANY).auth_request) 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_invalid_policy_input(self): self._response.data = {"auth_request": ANY} with self.assertRaises(InvalidParameters): self._service_client.authorization_request(ANY, ANY, ANY) def test_authorization_request_unexpected_result(self): self._response.data = {MagicMock(spec=str): ANY} with self.assertRaises(UnexpectedAPIResponse): self._service_client.authorization_request(ANY) def test_authorization_request_invalid_params(self): self._transport.post.side_effect = LaunchKeyAPIException( { "error_code": "ARG-001", "error_detail": "" }, 400) with self.assertRaises(InvalidParameters): self._service_client.authorization_request(ANY) def test_authorization_request_invalid_policy(self): self._transport.post.side_effect = LaunchKeyAPIException( { "error_code": "SVC-002", "error_detail": "" }, 400) with self.assertRaises(InvalidPolicyInput): self._service_client.authorization_request(ANY) def test_authorization_request_policy_failure(self): self._transport.post.side_effect = LaunchKeyAPIException( { "error_code": "SVC-003", "error_detail": "" }, 400) with self.assertRaises(PolicyFailure): self._service_client.authorization_request(ANY) def test_authorization_request_entity_not_found(self): self._transport.post.side_effect = LaunchKeyAPIException({}, 404) with self.assertRaises(EntityNotFound): self._service_client.authorization_request(ANY) def test_authorization_request_rate_limited(self): self._transport.post.side_effect = LaunchKeyAPIException({}, 429) with self.assertRaises(RateLimited): self._service_client.authorization_request(ANY) def test_authorization_request_default(self): self._response.data = {"auth_request": "expected value"} self._service_client.authorization_request("my_user") self._transport.post.assert_called_with(ANY, ANY, username="******") def test_authorization_request_context(self): self._response.data = {"auth_request": "expected value"} self._service_client.authorization_request( "my_user", context="Here's some context!") self._transport.post.assert_called_with(ANY, ANY, username="******", context="Here's some context!") def test_authorization_request_title(self): self._response.data = {"auth_request": "expected value"} self._service_client.authorization_request("my_user", title="Here's a title!") self._transport.post.assert_called_with(ANY, ANY, username="******", title="Here's a title!") def test_authorization_request_push_title(self): self._response.data = {"auth_request": "expected value"} self._service_client.authorization_request("my_user", push_title="A Push Title") self._transport.post.assert_called_with(ANY, ANY, username="******", push_title="A Push Title") def test_authorization_request_push_body(self): self._response.data = {"auth_request": "expected value"} self._service_client.authorization_request("my_user", push_body="Push Body") self._transport.post.assert_called_with(ANY, ANY, username="******", push_body="Push Body") def test_authorization_request_ttl(self): self._response.data = {"auth_request": "expected value"} self._service_client.authorization_request("my_user", ttl=336) self._transport.post.assert_called_with(ANY, ANY, username="******", ttl=336) def test_authorization_request_denial_reasons_as_list(self): self._response.data = {"auth_request": "expected value"} self._service_client.authorization_request( "my_user", denial_reasons=[ DenialReason('fraud', 'Fraud Reason', True), DenialReason('not', 'Not Fraud Reason', False) ]) self._transport.post.assert_called_with(ANY, ANY, username="******", denial_reasons=[{ "id": 'fraud', "reason": 'Fraud Reason', "fraud": True }, { "id": 'not', "reason": 'Not Fraud Reason', "fraud": False }]) def test_authorization_request_denial_reasons_as_set(self): self._response.data = {"auth_request": "expected value"} self._service_client.authorization_request( "my_user", denial_reasons={ DenialReason('fraud', 'Fraud Reason', True), DenialReason('not', 'Not Fraud Reason', False) }) denial_reasons = self._transport.post.call_args[1]['denial_reasons'] self.assertEqual(len(denial_reasons), 2) self.assertIn({ "id": 'fraud', "reason": 'Fraud Reason', "fraud": True }, denial_reasons) self.assertIn( { "id": 'not', "reason": 'Not Fraud Reason', "fraud": False }, denial_reasons) @data("e6e809ab-9e83-47a2-924a-64ae3d424a45", True, False, {"Test": "Data"}, DenialReason(1, 2, 3)) def test_authorization_request_denial_reasons_invalid_input(self, reasons): with self.assertRaises(InvalidParameters): self._service_client.authorization_request("my_user", denial_reasons=reasons) @data("e6e809ab-9e83-47a2-924a-64ae3d424a45", True, False, {"Test": "Data"}) def test_authorization_request_denial_reasons_invalid_reason(self, reason): with self.assertRaises(InvalidParameters): self._service_client.authorization_request("my_user", denial_reasons=[reason]) @patch("launchkey.entities.service.loads") @patch("launchkey.entities.service.AuthorizationResponsePackageValidator") def test_get_authorization_response_success( self, json_loads_patch, auth_response_package_validator_patch): json_loads_patch.return_value = MagicMock(spec=dict) auth_response_package_validator_patch.return_value = MagicMock( spec=dict) public_key_id = str(uuid4()) self._service_client._transport.loaded_issuer_private_keys = { public_key_id: MagicMock() } self._response.data = { "auth": ANY, "service_user_hash": ANY, "user_push_id": ANY, "org_user_hash": ANY, "public_key_id": public_key_id } actual = self._service_client.get_authorization_response( "auth-request-id") self._transport.get.assert_called_once_with( "/service/v3/auths/auth-request-id", self._issuer) self.assertIsInstance(actual, AuthorizationResponse) def test_get_authorization_response_unexpected_response(self): self._response.data = {MagicMock(spec=str): ANY} with self.assertRaises(UnexpectedAPIResponse): self._service_client.get_authorization_response(ANY) def test_get_authorization_response_no_response(self): self._response.status_code = 204 self.assertIsNone(self._service_client.get_authorization_response(ANY)) def test_get_authorization_response_invalid_params(self): self._transport.get.side_effect = LaunchKeyAPIException( { "error_code": "ARG-001", "error_detail": "" }, 400) with self.assertRaises(InvalidParameters): self._service_client.get_authorization_response(ANY) def test_get_authorization_response_response_exists(self): self._transport.get.side_effect = LaunchKeyAPIException( { "error_code": "SVC-006", "error_detail": "" }, 400) with self.assertRaises(AuthorizationResponseExists): self._service_client.get_authorization_response(ANY) def test_get_authorization_response_timeout(self): self._transport.get.side_effect = LaunchKeyAPIException({}, 408) with self.assertRaises(RequestTimedOut): self._service_client.get_authorization_response(ANY) def test_cancel_authorization_request_success(self): self._service_client.cancel_authorization_request("auth-request-id") self._transport.delete.assert_called_once_with( "/service/v3/auths/auth-request-id", self._issuer) def test_cancel_authorization_request_invalid_params(self): self._transport.delete.side_effect = LaunchKeyAPIException( { "error_code": "ARG-001", "error_detail": "" }, 400) with self.assertRaises(InvalidParameters): self._service_client.cancel_authorization_request( "auth-request-id") def test_cancel_authorization_request_authorization_response_exists(self): self._transport.delete.side_effect = LaunchKeyAPIException( { "error_code": "SVC-006", "error_detail": "" }, 400) with self.assertRaises(AuthorizationResponseExists): self._service_client.cancel_authorization_request( "auth-request-id") def test_cancel_authorization_request_authorization_request_canceled(self): self._transport.delete.side_effect = LaunchKeyAPIException( { "error_code": "SVC-007", "error_detail": "" }, 400) with self.assertRaises(AuthorizationRequestCanceled): self._service_client.cancel_authorization_request( "auth-request-id") def test_cancel_authorization_request_invalid_params(self): self._transport.delete.side_effect = LaunchKeyAPIException( { "error_code": "ARG-001", "error_detail": "" }, 400) with self.assertRaises(InvalidParameters): self._service_client.cancel_authorization_request( "auth-request-id") def test_session_start_success(self): self._service_client.session_start("user-id", "auth-request-id") self._transport.post.assert_called_once_with( "/service/v3/sessions", self._issuer, username="******", auth_request="auth-request-id") def test_session_start_invalid_params(self): self._transport.post.side_effect = LaunchKeyAPIException( { "error_code": "ARG-001", "error_detail": "" }, 400) with self.assertRaises(InvalidParameters): self._service_client.session_start(ANY, ANY) def test_session_start_entity_not_found(self): self._transport.post.side_effect = LaunchKeyAPIException({}, 404) with self.assertRaises(EntityNotFound): self._service_client.session_start(ANY, ANY) def test_session_end_success(self): self._service_client.session_end("user-id") self._transport.delete.assert_called_once_with("/service/v3/sessions", self._issuer, username="******") def test_session_end_invalid_params(self): self._transport.delete.side_effect = LaunchKeyAPIException( { "error_code": "ARG-001", "error_detail": "" }, 400) with self.assertRaises(InvalidParameters): self._service_client.session_end(ANY) def test_session_end_entity_not_found(self): self._transport.delete.side_effect = LaunchKeyAPIException({}, 404) with self.assertRaises(EntityNotFound): self._service_client.session_end(ANY)
def make_service_client(self): """ Retrieves a client to make service calls. :return: launchkey.clients.ServiceClient """ return ServiceClient(self._issuer_id, self._transport)
class TestHandleWebhook(unittest.TestCase): _subject_id = uuid4() PUBLIC_KEY_ID = "Public Key ID" def setUp(self): self._transport = MagicMock(spec=JOSETransport) self._transport.decrypt_response.return_value = '{"public_key_id":"' + self.PUBLIC_KEY_ID + '", "auth": null}' self._service_client = ServiceClient(self._subject_id, self._transport) self._headers = {"X-IOV-JWT": "jwt", "Other Header": "jwt"} self._issuer_private_key = MagicMock() self._transport.loaded_issuer_private_keys = {self.PUBLIC_KEY_ID: self._issuer_private_key} patcher = patch("launchkey.entities.validation.AuthorizationResponsePackageValidator.to_python") self._authorization_response_validator_patch = patcher.start() self._authorization_response_validator_patch.return_value = MagicMock(spec=dict) patcher = patch("launchkey.clients.service.loads") self._service_client_loads_patch = patcher.start() self.addCleanup(patcher.stop) self._service_client_loads_patch.side_effect = json.loads patcher = patch("launchkey.entities.service.loads") self._service_entity_loads_patch = patcher.start() self.addCleanup(patcher.stop) self._service_entity_loads_patch.return_value = MagicMock(spec=dict) patcher = patch("launchkey.entities.validation.AuthorizeSSEValidator") self._authorize_sse_validator_patch = patcher.start() self.addCleanup(patcher.stop) self._authorize_sse_validator_patch.return_value = MagicMock(spec=dict) def test_webhook_session_end(self): request = dumps({"service_user_hash": str(uuid4()), "api_time": str(datetime.utcnow())[:19].replace(" ", "T") + "Z"}) self.assertIsInstance(self._service_client.handle_webhook(request, self._headers), SessionEndRequest) def test_webhook_session_end_invalid_input(self): request = dumps({"service_user_hash": str(uuid4())}) with self.assertRaises(UnexpectedWebhookRequest): self.assertIsInstance(self._service_client.handle_webhook(request, self._headers), SessionEndRequest) def test_webhook_authorization_response_returns_authorization_response(self): self.assertIsInstance(self._service_client.handle_webhook(MagicMock(), self._headers), AuthorizationResponse) def test_calls_verify_jwt_request_with_expected_parameters(self): self._headers['X-IOV-JWT'] = 'compact.jwt.string' self._service_client.handle_webhook('body', self._headers, 'method', 'path') self._transport.verify_jwt_request.assert_called_with("compact.jwt.string", 'svc:' + str(self._subject_id), 'method', 'path', 'body') def test_handle_webhook_handles_jwt_validation_errors(self): self._transport.verify_jwt_request.side_effect = InvalidJWTResponse with self.assertRaises(UnexpectedWebhookRequest): self._service_client.handle_webhook(MagicMock(), self._headers) def test_handle_webhook_session_end_requests_handles_data_validation_errors(self): self._authorize_sse_validator_patch.to_python.side_effect = Invalid with self.assertRaises(UnexpectedWebhookRequest): self._service_client.handle_webhook(dumps({"service_user_hash": str(uuid4())}), self._headers) def test_handle_webhook_auth_response_handles_json_loads_errors(self): self._transport.decrypt_response.return_value = '{"public_key_id":"' + self.PUBLIC_KEY_ID + '","auth":null}' self._service_entity_loads_patch.side_effect = ValueError with self.assertRaises(UnexpectedDeviceResponse): self._service_client.handle_webhook(MagicMock(), self._headers) def test_handle_webhook_auth_response_requests_handles_unexpected_key(self,): self._transport.decrypt_response.side_effect = UnexpectedKeyID with self.assertRaises(UnexpectedKeyID): self._service_client.handle_webhook(MagicMock(), self._headers) def test_handle_webhook_for_authorization_response_handles_jwe_decryption_errors(self): self._transport.decrypt_response.side_effect = JWKESTException with self.assertRaises(UnableToDecryptWebhookRequest): self._service_client.handle_webhook(MagicMock(), self._headers) def test_handle_webhook_for_invalid_response_when_validating_auth_response(self): self._authorization_response_validator_patch.side_effect = Invalid with self.assertRaises(UnexpectedDeviceResponse): self._service_client.handle_webhook(MagicMock(), self._headers) def test_handle_webhook_for_response_without_auth_package_parsing_auth_response(self): self._transport.decrypt_response.return_value = '{"public_key_id":"' + self.PUBLIC_KEY_ID + '"}' with self.assertRaises(UnexpectedAuthorizationResponse): self._service_client.handle_webhook(MagicMock(), self._headers) def test_no_jwt_header_raises_webhook_authorization_error(self): del self._headers['X-IOV-JWT'] with self.assertRaises(WebhookAuthorizationError): self._service_client.handle_webhook(MagicMock(), self._headers)
class TestServiceClient(unittest.TestCase): def setUp(self): self._transport = MagicMock() self._response = APIResponse({}, {}, 200) self._transport.post.return_value = self._response self._transport.get.return_value = self._response self._transport.put.return_value = self._response self._transport.delete.return_value = self._response self._device_response = {"auth_request": str(uuid4()), "response": True, "device_id": str(uuid4()), "service_pins": ["1234", "3456", "5678"]} self._transport.loaded_issuer_private_key.decrypt.return_value = dumps(self._device_response) self._service_id = uuid4() self._issuer = "svc:{}".format(self._service_id) self._service_client = ServiceClient(self._service_id, self._transport) self._service_client._transport._verify_jwt_response = MagicMock() def test_authorize_calls_authorization_request(self): policy = AuthPolicy() auth_response = AuthorizationRequest(str(uuid4()), None) self._service_client.authorization_request = MagicMock(return_value=auth_response) self._service_client.authorize('user', 'context', policy, 'title', 30, 'push_title', 'push_body') self._service_client.authorization_request.assert_called_once_with( 'user', 'context', policy, 'title', 30, 'push_title', 'push_body') def test_authorize_returns_auth_request_id_from_authorization_request_response(self): expected = str(uuid4()) self._response.data = {"auth_request": expected} actual = self._service_client.authorize('user', 'context', AuthPolicy()) self.assertEqual(actual, expected) def test_authorization_request_success(self): self._response.data = {"auth_request": "value"} policy = MagicMock(spec=AuthPolicy) policy.get_policy.return_value = "policy" self._service_client.authorization_request("user", "context", policy) self._transport.post.assert_called_once_with( '/service/v3/auths', self._issuer, username="******", context="context", policy="policy" ) def test_authorization_request_response_has_auth_request(self): self._response.data = {"auth_request": "expected value"} self.assertEqual('expected value', self._service_client.authorization_request(ANY).auth_request) 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_invalid_policy_input(self): self._response.data = {"auth_request": ANY} with self.assertRaises(InvalidParameters): self._service_client.authorization_request(ANY, ANY, ANY) def test_authorization_request_unexpected_result(self): self._response.data = {MagicMock(spec=str): ANY} with self.assertRaises(UnexpectedAPIResponse): self._service_client.authorization_request(ANY) def test_authorization_request_invalid_params(self): self._transport.post.side_effect = LaunchKeyAPIException({"error_code": "ARG-001", "error_detail": ""}, 400) with self.assertRaises(InvalidParameters): self._service_client.authorization_request(ANY) def test_authorization_request_invalid_policy(self): self._transport.post.side_effect = LaunchKeyAPIException({"error_code": "SVC-002", "error_detail": ""}, 400) with self.assertRaises(InvalidPolicyInput): self._service_client.authorization_request(ANY) def test_authorization_request_policy_failure(self): self._transport.post.side_effect = LaunchKeyAPIException({"error_code": "SVC-003", "error_detail": ""}, 400) with self.assertRaises(PolicyFailure): self._service_client.authorization_request(ANY) def test_authorization_request_entity_not_found(self): self._transport.post.side_effect = LaunchKeyAPIException({}, 404) with self.assertRaises(EntityNotFound): self._service_client.authorization_request(ANY) def test_authorization_request_rate_limited(self): self._transport.post.side_effect = LaunchKeyAPIException({}, 429) with self.assertRaises(RateLimited): self._service_client.authorization_request(ANY) def test_authorization_request_default(self): self._response.data = {"auth_request": "expected value"} self._service_client.authorization_request("my_user") self._transport.post.assert_called_with(ANY, ANY, username="******") def test_authorization_request_context(self): self._response.data = {"auth_request": "expected value"} self._service_client.authorization_request("my_user", context="Here's some context!") self._transport.post.assert_called_with(ANY, ANY, username="******", context="Here's some context!") def test_authorization_request_title(self): self._response.data = {"auth_request": "expected value"} self._service_client.authorization_request("my_user", title="Here's a title!") self._transport.post.assert_called_with(ANY, ANY, username="******", title="Here's a title!") def test_authorization_request_push_title(self): self._response.data = {"auth_request": "expected value"} self._service_client.authorization_request("my_user", push_title="A Push Title") self._transport.post.assert_called_with(ANY, ANY, username="******", push_title="A Push Title") def test_authorization_request_push_body(self): self._response.data = {"auth_request": "expected value"} self._service_client.authorization_request("my_user", push_body="Push Body") self._transport.post.assert_called_with(ANY, ANY, username="******", push_body="Push Body") def test_authorization_request_ttl(self): self._response.data = {"auth_request": "expected value"} self._service_client.authorization_request("my_user", ttl=336) self._transport.post.assert_called_with(ANY, ANY, username="******", ttl=336) def test_authorization_request_denial_reasons_as_list(self): self._response.data = {"auth_request": "expected value"} self._service_client.authorization_request("my_user", denial_reasons=[ DenialReason('fraud', 'Fraud Reason', True), DenialReason('not', 'Not Fraud Reason', False) ]) self._transport.post.assert_called_with( ANY, ANY, username="******", denial_reasons=[ {"id": 'fraud', "reason": 'Fraud Reason', "fraud": True}, {"id": 'not', "reason": 'Not Fraud Reason', "fraud": False} ] ) def test_authorization_request_denial_reasons_as_set(self): self._response.data = {"auth_request": "expected value"} self._service_client.authorization_request("my_user", denial_reasons={ DenialReason('fraud', 'Fraud Reason', True), DenialReason('not', 'Not Fraud Reason', False) }) denial_reasons = self._transport.post.call_args[1]['denial_reasons'] self.assertEqual(len(denial_reasons), 2) self.assertIn( {"id": 'fraud', "reason": 'Fraud Reason', "fraud": True}, denial_reasons ) self.assertIn( {"id": 'not', "reason": 'Not Fraud Reason', "fraud": False}, denial_reasons ) @data( "e6e809ab-9e83-47a2-924a-64ae3d424a45", True, False, {"Test": "Data"}, DenialReason(1, 2, 3) ) def test_authorization_request_denial_reasons_invalid_input(self, reasons): with self.assertRaises(InvalidParameters): self._service_client.authorization_request( "my_user", denial_reasons=reasons ) @data( "e6e809ab-9e83-47a2-924a-64ae3d424a45", True, False, {"Test": "Data"} ) def test_authorization_request_denial_reasons_invalid_reason(self, reason): with self.assertRaises(InvalidParameters): self._service_client.authorization_request( "my_user", denial_reasons=[reason] ) @patch("launchkey.entities.service.loads") @patch("launchkey.entities.service.AuthorizationResponsePackageValidator") def test_get_authorization_response_success( self, json_loads_patch, auth_response_package_validator_patch): json_loads_patch.return_value = MagicMock(spec=dict) auth_response_package_validator_patch.return_value = MagicMock(spec=dict) public_key_id = str(uuid4()) self._service_client._transport.loaded_issuer_private_keys = {public_key_id: MagicMock()} self._response.data = { "auth": ANY, "service_user_hash": ANY, "user_push_id": ANY, "org_user_hash": ANY, "public_key_id": public_key_id } actual = self._service_client.get_authorization_response( "auth-request-id") self._transport.get.assert_called_once_with( "/service/v3/auths/auth-request-id", self._issuer) self.assertIsInstance(actual, AuthorizationResponse) def test_get_authorization_response_unexpected_response(self): self._response.data = {MagicMock(spec=str): ANY} with self.assertRaises(UnexpectedAPIResponse): self._service_client.get_authorization_response(ANY) def test_get_authorization_response_no_response(self): self._response.status_code = 204 self.assertIsNone(self._service_client.get_authorization_response(ANY)) def test_get_authorization_response_invalid_params(self): self._transport.get.side_effect = LaunchKeyAPIException({"error_code": "ARG-001", "error_detail": ""}, 400) with self.assertRaises(InvalidParameters): self._service_client.get_authorization_response(ANY) def test_get_authorization_response_timeout(self): self._transport.get.side_effect = LaunchKeyAPIException({}, 408) with self.assertRaises(RequestTimedOut): self._service_client.get_authorization_response(ANY) def test_session_start_success(self): self._service_client.session_start("user-id", "auth-request-id") self._transport.post.assert_called_once_with( "/service/v3/sessions", self._issuer, username="******", auth_request="auth-request-id") def test_session_start_invalid_params(self): self._transport.post.side_effect = LaunchKeyAPIException({"error_code": "ARG-001", "error_detail": ""}, 400) with self.assertRaises(InvalidParameters): self._service_client.session_start(ANY, ANY) def test_session_start_entity_not_found(self): self._transport.post.side_effect = LaunchKeyAPIException({}, 404) with self.assertRaises(EntityNotFound): self._service_client.session_start(ANY, ANY) def test_session_end_success(self): self._service_client.session_end("user-id") self._transport.delete.assert_called_once_with( "/service/v3/sessions", self._issuer, username="******") def test_session_end_invalid_params(self): self._transport.delete.side_effect = LaunchKeyAPIException({"error_code": "ARG-001", "error_detail": ""}, 400) with self.assertRaises(InvalidParameters): self._service_client.session_end(ANY) def test_session_end_entity_not_found(self): self._transport.delete.side_effect = LaunchKeyAPIException({}, 404) with self.assertRaises(EntityNotFound): self._service_client.session_end(ANY)