Exemplo n.º 1
0
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)
Exemplo n.º 2
0
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):
        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)