class TestJWKESTSupportedAlgs(unittest.TestCase): def setUp(self): self._transport = JOSETransport() self._transport.get = MagicMock(return_value=MagicMock(spec=APIResponse)) public_key = APIResponse(valid_private_key, {"X-IOV-KEY-ID": "59:12:e2:f6:3f:79:d5:1e:18:75:c5:25:ff:b3:b7:f2"}, 200) self._transport.get.return_value = public_key self._transport._server_time_difference = 0, time() def _encrypt_decrypt(self): self._transport.add_issuer_key(valid_private_key) to_encrypt = {"tobe": "encrypted"} encrypted = self._transport._encrypt_request(to_encrypt) self.assertEqual(len(encrypted.split('.')), 5) decrypted = self._transport.decrypt_response(encrypted) self.assertEqual(loads(decrypted), to_encrypt) def test_encrypt_decrypt_defaults(self): self._encrypt_decrypt() def test_supported_jwt_algorithms_success(self): for alg in JOSE_SUPPORTED_JWT_ALGS: self._transport.jwt_algorithm = alg self._encrypt_decrypt() def test_supported_jwe_algorithms_success(self): for alg in JOSE_SUPPORTED_JWE_ALGS: self._transport.jwe_cek_encryption = alg self._encrypt_decrypt() def test_supported_jwe_encryptions_success(self): for enc in JOSE_SUPPORTED_JWE_ENCS: self._transport.jwe_claims_encryption = enc self._encrypt_decrypt()
def setUp(self): self._transport = JOSETransport() self._transport.verify_jwt_response = MagicMock() self._transport._build_jwt_signature = MagicMock() self._transport.content_hash_function = MagicMock() self._transport.decrypt_response = MagicMock() self._transport._encrypt_request = MagicMock()
def setUp(self): self._transport = JOSETransport() self._transport.get = MagicMock(return_value=MagicMock(spec=APIResponse)) public_key = APIResponse(valid_private_key, {"X-IOV-KEY-ID": "59:12:e2:f6:3f:79:d5:1e:18:75:c5:25:ff:b3:b7:f2"}, 200) self._transport.get.return_value = public_key self._transport._server_time_difference = 0, time()
def test_supported_content_hash_algorithm_success_in_get_content_hash( self): for alg in JOSE_SUPPORTED_CONTENT_HASH_ALGS: transport = JOSETransport() to_hash = str(uuid4()) result = transport._get_content_hash(to_hash, alg) self.assertNotEqual(to_hash, result)
def setUp(self): self._http_transport = MagicMock(spec=RequestsTransport) self._transport = JOSETransport(http_client=self._http_transport) self._transport.verify_jwt_response = MagicMock() self._transport._build_jwt_signature = MagicMock() self._transport.decrypt_response = MagicMock(return_value="Decrypted Response") self._transport._encrypt_request = MagicMock(return_value="Encrypted Response")
class TestJOSEProcessJOSERequest(unittest.TestCase): def setUp(self): self._transport = JOSETransport() self._transport.verify_jwt_response = MagicMock() self._transport._build_jwt_signature = MagicMock() self._transport.content_hash_function = MagicMock() self._transport.decrypt_response = MagicMock() self._transport._encrypt_request = MagicMock() @patch('requests.get') def test_process_jose_request_success(self, requests_patch): requests_patch.return_value = MagicMock() self.assertIsInstance( self._transport._process_jose_request('GET', '/path', ANY), APIResponse) @patch('requests.post') @patch('launchkey.transports.jose_auth.json') def test_process_jose_request_data_success(self, requests_patch, json_patch): requests_patch.return_value = MagicMock() json_patch.return_value = MagicMock() self.assertIsInstance( self._transport._process_jose_request('POST', '/path', ANY, ANY), APIResponse) def test_process_jose_request_error_response(self): self._transport._http_client = MagicMock() self._transport._http_client.put.return_value = APIErrorResponse( ANY, ANY, ANY) with self.assertRaises(LaunchKeyAPIException): self.assertIsInstance( self._transport._process_jose_request('PUT', '/path', ANY), APIResponse)
def setUp(self): self._transport = JOSETransport() self._transport.get = MagicMock(return_value=MagicMock( spec=APIResponse)) self._transport._get_jwt_signature = MagicMock(return_value='x.x.x') public_key = APIResponse(valid_public_key, {}, 200) self._transport.get.return_value = public_key self._transport._server_time_difference = 0, time()
def setUp(self): self._transport = JOSETransport() self._transport.get = MagicMock(return_value=MagicMock( spec=APIResponse)) public_key = APIResponse(valid_private_key, transport_request_headers, 200) self._transport.get.return_value = public_key self._transport._server_time_difference = 0, time() self.issuer = "svc" self.issuer_id = uuid4() self._transport.set_issuer(self.issuer, self.issuer_id, valid_private_key) self._body = str(uuid4()) self._content_hash = '16793293daadb5a03b7cbbb9d15a1a705b22e762a1a751bc8625dec666101ff2' self.jwt_payload = { 'aud': '%s:%s' % (self.issuer, self.issuer_id), 'iss': 'lka', 'cty': 'application/json', 'nbf': time(), 'jti': str(uuid4()), 'exp': time() + 30, 'iat': time(), 'request': { 'meth': 'POST', 'path': '/', 'hash': self._content_hash, 'func': 'S256' }, 'sub': '%s:%s' % (self.issuer, self.issuer_id) } self._transport._get_jwt_payload = MagicMock( return_value=self.jwt_payload) self._transport.content_hash_function = MagicMock() self._transport.content_hash_function( ).hexdigest.return_value = self._content_hash self._jwt_patch = patch("launchkey.transports.jose_auth.JWT", return_value=MagicMock(spec=JWT)).start() self._faux_jwt_headers = { "alg": "RS512", "typ": "JWT", # This should be different than the kid from the transport headers "kid": "ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff" } self._jwt_patch.return_value.unpack.return_value.headers = self._faux_jwt_headers patcher = patch('launchkey.transports.jose_auth.sha256') patched = patcher.start() patched.return_value = MagicMock() patched.return_value.hexdigest.return_value = self._content_hash self.addCleanup(patcher.stop)
def setUp(self): self._transport = JOSETransport() self._transport.get = MagicMock(return_value=MagicMock( spec=APIResponse)) public_key = APIResponse(valid_private_key, {}, 200) self._transport.get.return_value = public_key self._transport._server_time_difference = 0, time() self._jwt_patch = patch("launchkey.transports.jose_auth.JWT", return_value=MagicMock(spec=JWT)).start() self._jwt_patch.return_value.unpack.return_value.headers = faux_jwt_headers self.addCleanup(patch.stopall)
def setUp(self): self._transport = JOSETransport() self._transport.get = MagicMock(return_value=MagicMock( spec=APIResponse)) public_key = APIResponse(valid_public_key, transport_request_headers, 200) self._transport.get.return_value = public_key self._transport._server_time_difference = 0, time() self.issuer = "svc" self.issuer_id = uuid4() self._transport.set_issuer(self.issuer, self.issuer_id, valid_private_key) self._body = str(uuid4()) self._content_hash = '16793293daadb5a03b7cbbb9d15a1a705b22e762a1a751bc8625dec666101ff2' self._jwt_patch = patch("launchkey.transports.jose_auth.JWT", return_value=MagicMock(spec=JWT)).start() self._jwt_patch.return_value.unpack.return_value.headers = faux_jwt_headers self.jwt_payload = { 'aud': '%s:%s' % (self.issuer, self.issuer_id), 'iss': 'lka', 'cty': 'application/json', 'nbf': time(), 'jti': str(uuid4()), 'exp': time() + 30, 'iat': time(), 'response': { 'status': 200, 'hash': self._content_hash, 'func': 'S256', 'cache': 'expected cache-control', 'location': 'expected location' }, 'sub': '%s:%s' % (self.issuer, self.issuer_id) } self._headers = { 'Location': 'expected location', 'Cache-Control': 'expected cache-control' } self._transport._get_jwt_payload = MagicMock( return_value=self.jwt_payload) self._transport.content_hash_function = MagicMock() self._transport.content_hash_function.return_value.hexdigest.return_value = self._content_hash patcher = patch('launchkey.transports.jose_auth.sha256') patched = patcher.start() patched.return_value = MagicMock() patched.return_value.hexdigest.return_value = self._content_hash self.addCleanup(patcher.stop)
class TestJOSETransportJWT(unittest.TestCase): def setUp(self): self._transport = JOSETransport() self._transport.get = MagicMock(return_value=MagicMock(spec=APIResponse)) self._transport._get_jwt_signature = MagicMock(return_value='x.x.x') public_key = APIResponse(valid_public_key, {"X-IOV-KEY-ID": "59:12:e2:f6:3f:79:d5:1e:18:75:c5:25:ff:b3:b7:f2"}, 200) self._transport.get.return_value = public_key self._transport._server_time_difference = 0, time() def test_build_jwt_signature(self): self._transport.set_issuer(ANY, uuid4(), valid_private_key) jwt = self._transport._build_jwt_signature("PUT", "/test", str(uuid4()), "test:subject", "ABCDEFG") self.assertIn('IOV-JWT', jwt) jwt = jwt.strip('IOV-JWT ') self.assertEqual(len(jwt.split(".")), 3)
def setUp(self): self._http_client = MagicMock(spec=RequestsTransport) self._transport = JOSETransport(http_client=self._http_client) self._transport.verify_jwt_response = MagicMock() self._transport._build_jwt_signature = MagicMock() self._transport.content_hash_function = MagicMock() self._transport.decrypt_response = MagicMock() self._transport._encrypt_request = MagicMock()
class TestJWKESTSupportedAlgs(unittest.TestCase): def setUp(self): self._transport = JOSETransport() self._transport.get = MagicMock(return_value=MagicMock( spec=APIResponse)) public_key = APIResponse(valid_private_key, transport_request_headers, 200) self._transport.get.return_value = public_key self._transport._server_time_difference = 0, time() self._jwt_patch = patch("launchkey.transports.jose_auth.JWT", return_value=MagicMock(spec=JWT)).start() self._jwt_patch.return_value.unpack.return_value.headers = faux_jwt_headers self.addCleanup(patch.stopall) def _encrypt_decrypt(self): self._transport.add_issuer_key(valid_private_key) to_encrypt = {"tobe": "encrypted"} encrypted = self._transport._encrypt_request(to_encrypt) self.assertEqual(len(encrypted.split('.')), 5) decrypted = self._transport.decrypt_response(encrypted) self.assertEqual(loads(decrypted), to_encrypt) def test_encrypt_decrypt_defaults(self): self._encrypt_decrypt() def test_supported_jwt_algorithms_success(self): for alg in JOSE_SUPPORTED_JWT_ALGS: self._transport.jwt_algorithm = alg self._encrypt_decrypt() def test_supported_jwe_algorithms_success(self): for alg in JOSE_SUPPORTED_JWE_ALGS: self._transport.jwe_cek_encryption = alg self._encrypt_decrypt() def test_supported_jwe_encryptions_success(self): for enc in JOSE_SUPPORTED_JWE_ENCS: self._transport.jwe_claims_encryption = enc self._encrypt_decrypt()
class TestDecryptRSAResponse(unittest.TestCase): def setUp(self): self._transport = JOSETransport() patch.object(self._transport, 'loaded_issuer_private_keys', {"key_id": MagicMock()}).start() self.addCleanup(patch.stopall) def test_decrypt_rsa_response(self): self._transport.loaded_issuer_private_keys['key_id'].decrypt.return_value = "response" resp = self._transport.decrypt_rsa_response('dGVzdGluZw==', 'key_id') self.assertEqual(resp, "response") def test_decrypt_rsa_decrypt_input(self): self._transport.loaded_issuer_private_keys['key_id'].decrypt.return_value = "response" self._transport.decrypt_rsa_response('dGVzdGluZw==', 'key_id') self._transport.loaded_issuer_private_keys['key_id'].decrypt.assert_called_with(b"testing") def test_decrypt_rsa_response_invalid_key(self): with self.assertRaises(UnexpectedKeyID): self._transport.decrypt_rsa_response('response', 'wrong_key')
class TestJOSETransportRESTCalls(unittest.TestCase): def setUp(self): self._http_client = MagicMock(spec=RequestsTransport) self._transport = JOSETransport(http_client=self._http_client) self._transport.verify_jwt_response = MagicMock() self._transport._build_jwt_signature = MagicMock() self._transport.content_hash_function = MagicMock() self._transport.decrypt_response = MagicMock() self._transport._encrypt_request = MagicMock() def test_get(self): self.assertEqual(self._transport.get('/path'), self._http_client.get.return_value) def test_get_with_subject(self): self.assertEqual(self._transport.get('/path', ANY), self._http_client.get.return_value) def test_post(self): self.assertEqual(self._transport.post('/path', ANY), self._http_client.post.return_value) def test_put(self): self.assertEqual(self._transport.put('/path', ANY), self._http_client.put.return_value) def test_delete(self): self.assertEqual(self._transport.delete('/path', ANY), self._http_client.delete.return_value) def test_patch(self): self.assertEqual(self._transport.patch('/path', ANY), self._http_client.patch.return_value)
def setUp(self): self._transport = JOSETransport() self._transport.get = MagicMock(return_value=MagicMock(spec=APIResponse)) public_key = APIResponse(valid_private_key, {"X-IOV-KEY-ID": "59:12:e2:f6:3f:79:d5:1e:18:75:c5:25:ff:b3:b7:f2"}, 200) self._transport.get.return_value = public_key self._transport._server_time_difference = 0, time() self.issuer = "svc" self.issuer_id = uuid4() self._transport.set_issuer(self.issuer, self.issuer_id, valid_private_key) self._body = str(uuid4()) self._content_hash = '16793293daadb5a03b7cbbb9d15a1a705b22e762a1a751bc8625dec666101ff2' self.jwt_payload = { 'aud': '%s:%s' % (self.issuer, self.issuer_id), 'iss': 'lka', 'cty': 'application/json', 'nbf': time(), 'jti': str(uuid4()), 'exp': time() + 30, 'iat': time(), 'request': { 'meth': 'POST', 'path': '/', 'hash': self._content_hash, 'func': 'S256'}, 'sub': '%s:%s' % (self.issuer, self.issuer_id) } self._transport._get_jwt_payload = MagicMock(return_value=self.jwt_payload) self._transport.content_hash_function = MagicMock() self._transport.content_hash_function().hexdigest.return_value = self._content_hash patcher = patch('launchkey.transports.jose_auth.sha256') patched = patcher.start() patched.return_value = MagicMock() patched.return_value.hexdigest.return_value = self._content_hash self.addCleanup(patcher.stop)
def setUp(self): self.jti = str(uuid4()) minified_jwt_payload = {"jti": self.jti, "response": {"status": 200}} self._requests_transport = MagicMock(spec=RequestsTransport) self._requests_transport.get.return_value = APIResponse( valid_private_key, {}, 200) self._transport = JOSETransport(http_client=self._requests_transport) self._import_rsa_key_patch = patch( "launchkey.transports.jose_auth.import_rsa_key", return_value=MagicMock(spec=RsaKey)).start() self._jwt_patch = patch("launchkey.transports.jose_auth.JWT", return_value=MagicMock(spec=JWT)).start() self._jwt_patch.return_value.unpack.return_value.headers = faux_jwt_headers self._jws_patch = patch("launchkey.transports.jose_auth.JWS", return_value=MagicMock(spec=JWS)).start() self._jws_patch.return_value.verify_compact.return_value = minified_jwt_payload patch.object(JOSETransport, "_verify_jwt_payload").start() patch.object(JOSETransport, "_verify_jwt_response_headers").start() patch.object(JOSETransport, "_verify_jwt_content_hash").start() self.addCleanup(patch.stopall)
class TestJOSEProcessJOSERequest(unittest.TestCase): def setUp(self): self._http_transport = MagicMock(spec=RequestsTransport) self._transport = JOSETransport(http_client=self._http_transport) self._transport.verify_jwt_response = MagicMock() self._transport._build_jwt_signature = MagicMock() self._transport.decrypt_response = MagicMock( return_value="Decrypted Response") self._transport._encrypt_request = MagicMock( return_value="Encrypted Response") @patch('requests.get') def test_process_jose_request_success_encrypted_response( self, requests_patch): requests_patch.return_value = MagicMock() response = self._transport._process_jose_request( 'GET', '/path', 'subject', 'body') self._transport.decrypt_response.assert_called_once() self.assertEqual(response.data, self._transport.decrypt_response.return_value) def test_process_jose_request_success_unencrypted_response(self): self._transport._http_client = MagicMock() self._transport._http_client.get.return_value = APIResponse( {"a": "response"}, {}, 200) response = self._transport._process_jose_request('GET', '/path', ANY) self._transport.decrypt_response.assert_not_called() self.assertIsInstance(response, APIResponse) self.assertEqual(response.data, {"a": "response"}) self.assertEqual(response.status_code, 200) def test_process_jose_request_success_empty_response(self): self._transport._http_client = MagicMock() self._transport._http_client.get.return_value = APIResponse( None, {}, 201) response = self._transport._process_jose_request( 'GET', '/path', 'subject') self._transport.decrypt_response.assert_not_called() self.assertIsInstance(response, APIResponse) self.assertIsNone(response.data) self.assertEqual(response.status_code, 201) @patch('launchkey.transports.jose_auth.json') def test_process_jose_request_data_json_success(self, json_patch): json_patch.return_value = "{\"json\": true}" response = self._transport._process_jose_request( 'POST', '/path', 'subject', 'data') json_patch.loads.assert_called_with( self._transport.decrypt_response.return_value) self.assertEqual(response.data, json_patch.loads.return_value) def test_process_jose_request_error_response(self): self._transport._http_client = MagicMock() self._transport._http_client.put.return_value = APIErrorResponse( ANY, ANY, ANY) with self.assertRaises(LaunchKeyAPIException): self._transport._process_jose_request('PUT', '/path', 'subject')
def setUp(self): self._transport = JOSETransport() self._transport.get = MagicMock(return_value=MagicMock( spec=APIResponse)) public_key = APIResponse(valid_private_key, { "X-IOV-KEY-ID": "59:12:e2:f6:3f:79:d5:1e:18:75:c5:25:ff:b3:b7:f2" }, 200) self._transport.get.return_value = public_key self._transport._server_time_difference = 0, time() self.issuer = ANY self.issuer_id = uuid4() self._transport.set_issuer(self.issuer, self.issuer_id, valid_private_key) self._body = str(uuid4()) self._content_hash = '16793293daadb5a03b7cbbb9d15a1a705b22e762a1a751bc8625dec666101ff2' self.jwt_response = { 'aud': '%s:%s' % (self.issuer, self.issuer_id), 'iss': 'lka', 'cty': 'application/json', 'nbf': time(), 'jti': str(uuid4()), 'exp': time() + 30, 'iat': time(), 'response': { 'status': 200, 'hash': self._content_hash, 'func': 'S256' }, 'sub': '%s:%s' % (self.issuer, self.issuer_id) } self._transport._get_jwt_payload = MagicMock( return_value=self.jwt_response) self._transport.content_hash_function = MagicMock() self._transport.content_hash_function( ).hexdigest.return_value = self._content_hash
class TestJOSETransport3rdParty(unittest.TestCase): def setUp(self): self._transport = JOSETransport() self._transport.get = MagicMock(return_value=MagicMock( spec=APIResponse)) public_key = APIResponse(valid_private_key, { "X-IOV-KEY-ID": "59:12:e2:f6:3f:79:d5:1e:18:75:c5:25:ff:b3:b7:f2" }, 200) self._transport.get.return_value = public_key self._transport._server_time_difference = 0, time() def test_public_key_parse(self): self._transport.get.return_value.data = valid_public_key keys = self._transport.api_public_keys self.assertEqual(len(keys), 1) self.assertIsInstance(keys[0], RSAKey) def test_build_jwt_signature_no_key(self): with self.assertRaises(NoIssuerKey): self._transport._build_jwt_signature(MagicMock(spec=str), ANY, ANY, ANY, ANY) def test_build_jwt_signature(self): self._transport.set_issuer(ANY, uuid4(), valid_private_key) jwt = self._transport._build_jwt_signature("PUT", "/test", str(uuid4()), "test:subject", "ABCDEFG") self.assertIn('IOV-JWT', jwt) jwt = jwt.strip('IOV-JWT ') self.assertEqual(len(jwt.split(".")), 3) def test_invalid_jwt_response(self): headers = {"X-IOV-JWT": 'invalid'} with self.assertRaises(InvalidJWTResponse): self._transport.verify_jwt_response(headers, ANY, ANY, ANY) def test_empty_jwt_response(self): headers = {} with self.assertRaises(InvalidJWTResponse): self._transport.verify_jwt_response(headers, ANY, ANY, ANY)
def __init__(self, issuer, issuer_id, private_key, url, testing, transport): """ :param issuer: Issuer type that will be translated directly to the JOSE transport layer as an issuer. IE: svc, dir, org :param issuer_id: UUID of the issuer :param private_key: PEM formatted private key string :param url: URL for the LaunchKey API :param testing: Boolean stating whether testing mode is being used. This will determine whether SSL validation occurs. """ self._issuer_id = UUIDHelper().from_string(issuer_id) self._transport = transport if transport is not None else JOSETransport( ) self._transport.set_url(url, testing) self._transport.set_issuer(issuer, issuer_id, private_key)
def setUp(self): self._transport = JOSETransport() self._transport.get = MagicMock(return_value=MagicMock(spec=APIResponse)) public_key = APIResponse(valid_private_key, {"X-IOV-KEY-ID": "59:12:e2:f6:3f:79:d5:1e:18:75:c5:25:ff:b3:b7:f2"}, 200) self._transport.get.return_value = public_key self._transport._server_time_difference = 0, time() self.issuer = "svc" self.issuer_id = uuid4() self._transport.set_issuer(self.issuer, self.issuer_id, valid_private_key) self._body = str(uuid4()) self._content_hash = '16793293daadb5a03b7cbbb9d15a1a705b22e762a1a751bc8625dec666101ff2' self.jwt_payload = { 'aud': '%s:%s' % (self.issuer, self.issuer_id), 'iss': 'lka', 'cty': 'application/json', 'nbf': time(), 'jti': str(uuid4()), 'exp': time() + 30, 'iat': time(), 'response': { 'status': 200, 'hash': self._content_hash, 'func': 'S256', 'cache': 'expected cache-control', 'location': 'expected location'}, 'sub': '%s:%s' % (self.issuer, self.issuer_id) } self._headers = {'Location': 'expected location', 'Cache-Control': 'expected cache-control'} self._transport._get_jwt_payload = MagicMock(return_value=self.jwt_payload) self._transport.content_hash_function = MagicMock() self._transport.content_hash_function.return_value.hexdigest.return_value = self._content_hash patcher = patch('launchkey.transports.jose_auth.sha256') patched = patcher.start() patched.return_value = MagicMock() patched.return_value.hexdigest.return_value = self._content_hash self.addCleanup(patcher.stop)
class TestJOSEProcessJOSERequest(unittest.TestCase): def setUp(self): self._http_transport = MagicMock(spec=RequestsTransport) self._transport = JOSETransport(http_client=self._http_transport) self._transport.verify_jwt_response = MagicMock() self._transport._build_jwt_signature = MagicMock() self._transport.decrypt_response = MagicMock(return_value="Decrypted Response") self._transport._encrypt_request = MagicMock(return_value="Encrypted Response") @patch('requests.get') def test_process_jose_request_success_encrypted_response(self, requests_patch): requests_patch.return_value = MagicMock() response = self._transport._process_jose_request('GET', '/path', 'subject', 'body') self._transport.decrypt_response.assert_called_once() self.assertEqual(response.data, self._transport.decrypt_response.return_value) def test_process_jose_request_success_unencrypted_response(self): self._transport._http_client = MagicMock() self._transport._http_client.get.return_value = APIResponse({"a": "response"}, {}, 200) response = self._transport._process_jose_request('GET', '/path', ANY) self._transport.decrypt_response.assert_not_called() self.assertIsInstance(response, APIResponse) self.assertEqual(response.data, {"a": "response"}) self.assertEqual(response.status_code, 200) def test_process_jose_request_success_empty_response(self): self._transport._http_client = MagicMock() self._transport._http_client.get.return_value = APIResponse(None, {}, 201) response = self._transport._process_jose_request('GET', '/path', 'subject') self._transport.decrypt_response.assert_not_called() self.assertIsInstance(response, APIResponse) self.assertIsNone(response.data) self.assertEqual(response.status_code, 201) @patch('launchkey.transports.jose_auth.json') def test_process_jose_request_data_json_success(self, json_patch): json_patch.return_value = "{\"json\": true}" response = self._transport._process_jose_request('POST', '/path', 'subject', 'data') json_patch.loads.assert_called_with(self._transport.decrypt_response.return_value) self.assertEqual(response.data, json_patch.loads.return_value) def test_process_jose_request_error_response(self): self._transport._http_client = MagicMock() self._transport._http_client.put.return_value = APIErrorResponse(ANY, ANY, ANY) with self.assertRaises(LaunchKeyAPIException): self._transport._process_jose_request('PUT', '/path', 'subject')
class TestJOSETransportRESTCalls(unittest.TestCase): def setUp(self): self._transport = JOSETransport() self._transport.verify_jwt_response = MagicMock() self._transport._build_jwt_signature = MagicMock() self._transport.content_hash_function = MagicMock() self._transport.decrypt_response = MagicMock() self._transport._encrypt_request = MagicMock() @patch('requests.get') def test_get(self, requests_patch): requests_patch.return_value = MagicMock() self.assertIsInstance(self._transport.get('/path'), APIResponse) @patch('requests.get') def test_get_with_subject(self, requests_patch): requests_patch.return_value = MagicMock() self.assertIsInstance(self._transport.get('/path', ANY), APIResponse) @patch('requests.post') def test_post(self, requests_patch): requests_patch.return_value = MagicMock() self.assertIsInstance(self._transport.post('/path', ANY), APIResponse) @patch('requests.put') def test_put(self, requests_patch): requests_patch.return_value = MagicMock() self.assertIsInstance(self._transport.put('/path', ANY), APIResponse) @patch('requests.delete') def test_delete(self, requests_patch): requests_patch.return_value = MagicMock() self.assertIsInstance(self._transport.delete('/path', ANY), APIResponse) @patch('requests.patch') def test_patch(self, requests_patch): requests_patch.return_value = MagicMock() self.assertIsInstance(self._transport.patch('/path', ANY), APIResponse)
def test_supported_content_hash_algorithm_failure(self): with self.assertRaises(InvalidAlgorithm): JOSETransport(content_hash_algorithm="Invalid")
def test_supported_content_hash_algorithm_success(self, alg): transport = JOSETransport(content_hash_algorithm=alg) self.assertEqual(transport.content_hash_algorithm, alg)
def test_supported_jwe_encryptions_failure(self): with self.assertRaises(InvalidAlgorithm): JOSETransport(jwe_claims_encryption="Invalid")
def test_supported_jwe_encryptions_success(self, enc): transport = JOSETransport(jwe_claims_encryption=enc) self.assertEqual(transport.jwe_claims_encryption, enc)
def test_supported_jwe_algorithms_success(self, alg): transport = JOSETransport(jwe_cek_encryption=alg) self.assertEqual(transport.jwe_cek_encryption, alg)
def test_supported_jwt_algorithms_failure(self): with self.assertRaises(InvalidAlgorithm): JOSETransport(jwt_algorithm="Invalid")
class TestJOSETransport3rdParty(unittest.TestCase): def setUp(self): self._transport = JOSETransport() self._transport.get = MagicMock(return_value=MagicMock(spec=APIResponse)) public_key = APIResponse(valid_private_key, {"X-IOV-KEY-ID": "59:12:e2:f6:3f:79:d5:1e:18:75:c5:25:ff:b3:b7:f2"}, 200) self._transport.get.return_value = public_key self._transport._server_time_difference = 0, time() def test_public_key_parse(self): self._transport.get.return_value.data = valid_public_key keys = self._transport.api_public_keys self.assertEqual(len(keys), 1) self.assertIsInstance(keys[0], RSAKey) def test_build_jwt_signature_no_key(self): with self.assertRaises(NoIssuerKey): self._transport._build_jwt_signature(MagicMock(spec=str), ANY, ANY, ANY, ANY) def test_build_jwt_signature(self): self._transport.set_issuer(ANY, uuid4(), valid_private_key) jwt = self._transport._build_jwt_signature("PUT", "/test", str(uuid4()), "test:subject", "ABCDEFG") self.assertIn('IOV-JWT', jwt) jwt = jwt.strip('IOV-JWT ') self.assertEqual(len(jwt.split(".")), 3) def test_invalid_jwt_response(self): headers = {"X-IOV-JWT": 'invalid'} with self.assertRaises(InvalidJWTResponse): self._transport.verify_jwt_response(headers, ANY, ANY, ANY) def test_jwt_response_error(self): headers = {"X-IOV-JWT": 'invalid'} with self.assertRaises(InvalidJWTResponse): self._transport.verify_jwt_response(headers, ANY, ANY, ANY) def test_empty_jwt_response(self): headers = {} with self.assertRaises(InvalidJWTResponse): self._transport.verify_jwt_response(headers, ANY, ANY, ANY) @patch.object(JWS, 'verify_compact') def test_jwt_error_raises_expected_exception(self, verify_compact_patch): verify_compact_patch.side_effect = JWKESTException with self.assertRaises(JWTValidationFailure): self._transport.verify_jwt_response({}, ANY, ANY, ANY)
def setUp(self): self._transport = JOSETransport() self._transport.get = MagicMock(return_value=MagicMock( spec=APIResponse))
def test_supported_content_hash_algorithm_success_in_get_content_hash(self): for alg in JOSE_SUPPORTED_CONTENT_HASH_ALGS: transport = JOSETransport() to_hash = str(uuid4()) result = transport._get_content_hash(to_hash, alg) self.assertNotEqual(to_hash, result)
def test_unsupported_content_hash_algorithm_success_in_get_content_hash_raises_invalid_algorithm_error(self): transport = JOSETransport() with self.assertRaises(InvalidAlgorithm): transport._get_content_hash(None, "invalid")
class TestJOSETransportJWTResponse(unittest.TestCase): def setUp(self): self._transport = JOSETransport() self._transport.get = MagicMock(return_value=MagicMock(spec=APIResponse)) public_key = APIResponse(valid_private_key, {"X-IOV-KEY-ID": "59:12:e2:f6:3f:79:d5:1e:18:75:c5:25:ff:b3:b7:f2"}, 200) self._transport.get.return_value = public_key self._transport._server_time_difference = 0, time() self.issuer = "svc" self.issuer_id = uuid4() self._transport.set_issuer(self.issuer, self.issuer_id, valid_private_key) self._body = str(uuid4()) self._content_hash = '16793293daadb5a03b7cbbb9d15a1a705b22e762a1a751bc8625dec666101ff2' self.jwt_payload = { 'aud': '%s:%s' % (self.issuer, self.issuer_id), 'iss': 'lka', 'cty': 'application/json', 'nbf': time(), 'jti': str(uuid4()), 'exp': time() + 30, 'iat': time(), 'response': { 'status': 200, 'hash': self._content_hash, 'func': 'S256', 'cache': 'expected cache-control', 'location': 'expected location'}, 'sub': '%s:%s' % (self.issuer, self.issuer_id) } self._headers = {'Location': 'expected location', 'Cache-Control': 'expected cache-control'} self._transport._get_jwt_payload = MagicMock(return_value=self.jwt_payload) self._transport.content_hash_function = MagicMock() self._transport.content_hash_function.return_value.hexdigest.return_value = self._content_hash patcher = patch('launchkey.transports.jose_auth.sha256') patched = patcher.start() patched.return_value = MagicMock() patched.return_value.hexdigest.return_value = self._content_hash self.addCleanup(patcher.stop) def _verify_jwt_response(self, headers=None, jti=None, body=False, subject=None, status_code=200): headers = self._headers if headers is None else headers jti = self.jwt_payload['jti'] if jti is None else jti body = MagicMock() if body is False else body subject = self.jwt_payload['sub'] if subject is None else subject return self._transport.verify_jwt_response(headers, jti, body, subject, status_code) def test_verify_jwt_response_success_returns_payload(self): actual = self._verify_jwt_response() self.assertEqual(actual, self.jwt_payload) def test_verify_jwt_response_invalid_audience(self): self.jwt_payload['aud'] = MagicMock() with self.assertRaises(JWTValidationFailure): self._verify_jwt_response() @patch("launchkey.transports.jose_auth.time") def test_verify_jwt_response_invalid_nbf(self, time_patch): time_patch.return_value = self.jwt_payload['nbf'] - JOSE_JWT_LEEWAY - 1 with self.assertRaises(JWTValidationFailure): self._verify_jwt_response() @patch("launchkey.transports.jose_auth.time") def test_verify_jwt_response_invalid_exp(self, time_patch): time_patch.return_value = self.jwt_payload['exp'] + JOSE_JWT_LEEWAY + 1 with self.assertRaises(JWTValidationFailure): self._verify_jwt_response() def test_verify_jwt_response_invalid_sub(self): with self.assertRaises(JWTValidationFailure): self._transport.verify_jwt_response(MagicMock(), self.jwt_payload['jti'], MagicMock(), MagicMock()) def test_verify_jwt_response_invalid_sub_401(self): self.jwt_payload['response']['status'] = 401 self.jwt_payload['aud'] = "public" with self.assertRaises(JWTValidationFailure): self._verify_jwt_response(subject="Invalid subject") def test_verify_jwt_response_invalid_iat(self): self.jwt_payload['iat'] = time() + JOSE_JWT_LEEWAY + 1 with self.assertRaises(JWTValidationFailure): self._verify_jwt_response() def test_verify_jwt_response_invalid_content_body(self): self.jwt_payload['response']['hash'] = MagicMock() with self.assertRaises(JWTValidationFailure): self._verify_jwt_response() def test_verify_jwt_response_invalid_content_jti(self): with self.assertRaises(JWTValidationFailure): self._verify_jwt_response(jti='InvalidJTI') def test_verify_no_response_raises_validation_failure(self): del self.jwt_payload['response'] with self.assertRaises(JWTValidationFailure): self._verify_jwt_response(body=None) def test_verify_no_body_but_response_body_hash_algorithm_raises_validation_failure(self): del self.jwt_payload['response']['hash'] with self.assertRaises(JWTValidationFailure): self._verify_jwt_response(body=None) def test_verify_no_body_but_response_body_hash_raises_validation_failure(self): del self.jwt_payload['response']['func'] with self.assertRaises(JWTValidationFailure): self._verify_jwt_response(body=None) def test_verify_invalid_status_code_raises_validation_failure(self): self.jwt_payload['response']['status'] = 999 with self.assertRaises(JWTValidationFailure): self._verify_jwt_response() def test_verify_any_status_with_no_status_code_still_passes(self): self.assertIsNotNone(self._verify_jwt_response(status_code=None)) def test_verify_no_status_in_jwt_response_and_no_status_code_raises_validation_failure(self): del self.jwt_payload['response']['status'] with self.assertRaises(JWTValidationFailure): self.assertIsNotNone(self._verify_jwt_response(status_code=None)) def test_verify_no_cache_control_header_and_jwt_response_cache_raises_validation_failure(self): del self._headers['Cache-Control'] with self.assertRaises(JWTValidationFailure): self._verify_jwt_response() def test_verify_cache_control_header_and_jwt_with_no_response_cache_raises_validation_failure(self): del self.jwt_payload['response']['cache'] with self.assertRaises(JWTValidationFailure): self._verify_jwt_response() def test_verify_invalid_cache_control_header_raises_validation_failure(self): self.jwt_payload['response']['cache'] = "Unexpected" with self.assertRaises(JWTValidationFailure): self._verify_jwt_response() def test_verify_no_location_header_and_jwt_response_cache_raises_validation_failure(self): del self._headers['Location'] with self.assertRaises(JWTValidationFailure): self._verify_jwt_response() def test_verify_location_header_and_jwt_with_no_response_cache_raises_validation_failure(self): del self.jwt_payload['response']['location'] with self.assertRaises(JWTValidationFailure): self._verify_jwt_response() def test_verify_invalid_location_header_raises_validation_failure(self): self.jwt_payload['response']['location'] = "Unexpected" with self.assertRaises(JWTValidationFailure): self._verify_jwt_response()
class TestJOSETransportIssuers(unittest.TestCase): def setUp(self): self._transport = JOSETransport() def test_add_issuer_key(self): self.assertEqual(len(self._transport.issuer_private_keys), 0) self._transport.add_issuer_key(valid_private_key) self.assertEqual(len(self._transport.issuer_private_keys), 1) def test_add_duplicate_issuer_key(self): self.assertEqual(len(self._transport.issuer_private_keys), 0) self._transport.add_issuer_key(valid_private_key) self.assertEqual(len(self._transport.issuer_private_keys), 1) resp = self._transport.add_issuer_key(valid_private_key) self.assertFalse(resp) self.assertEqual(len(self._transport.issuer_private_keys), 1) def test_generate_key_id(self): self._transport.add_issuer_key(valid_private_key) self.assertEqual(self._transport.issuer_private_keys[0].kid, '59:12:e2:f6:3f:79:d5:1e:18:75:c5:25:ff:b3:b7:f2') def test_set_url(self): self._transport._http_client = MagicMock() self._transport.set_url(ANY, ANY) self._transport._http_client.set_url.assert_called_once() def test_set_issuer_invalid_entity_id(self): with self.assertRaises(InvalidEntityID): self._transport.set_issuer(ANY, ANY, ANY) def test_set_issuer_invalid_entity_issuer(self): with self.assertRaises(InvalidIssuer): self._transport.set_issuer(MagicMock(spec=str), uuid4(), ANY) def test_set_issuer_invalid_private_key(self): with self.assertRaises(InvalidPrivateKey): self._transport.set_issuer(ANY, uuid4(), "InvalidKey") @patch("launchkey.transports.jose_auth.RSAKey") @patch("launchkey.transports.jose_auth.import_rsa_key") def test_issuer_list(self, rsa_key_patch, import_key_patch): rsa_key_patch.return_value = MagicMock(spec=RSAKey) import_key_patch.return_value = MagicMock() self._transport.add_issuer_key = MagicMock() for issuer in VALID_JWT_ISSUER_LIST: self._transport.set_issuer(issuer, uuid4(), ANY)
def setUp(self): self._transport = JOSETransport() patch.object(self._transport, 'loaded_issuer_private_keys', {"key_id": MagicMock()}).start() self.addCleanup(patch.stopall)
class TestCacheAndRetrieveKeyByKid(unittest.TestCase): def setUp(self): self.jti = str(uuid4()) minified_jwt_payload = {"jti": self.jti, "response": {"status": 200}} self._requests_transport = MagicMock(spec=RequestsTransport) self._requests_transport.get.return_value = APIResponse( valid_private_key, {}, 200) self._transport = JOSETransport(http_client=self._requests_transport) self._import_rsa_key_patch = patch( "launchkey.transports.jose_auth.import_rsa_key", return_value=MagicMock(spec=RsaKey)).start() self._jwt_patch = patch("launchkey.transports.jose_auth.JWT", return_value=MagicMock(spec=JWT)).start() self._jwt_patch.return_value.unpack.return_value.headers = faux_jwt_headers self._jws_patch = patch("launchkey.transports.jose_auth.JWS", return_value=MagicMock(spec=JWS)).start() self._jws_patch.return_value.verify_compact.return_value = minified_jwt_payload patch.object(JOSETransport, "_verify_jwt_payload").start() patch.object(JOSETransport, "_verify_jwt_response_headers").start() patch.object(JOSETransport, "_verify_jwt_content_hash").start() self.addCleanup(patch.stopall) def test_key_retrieved_by_id_in_jwt_header(self): self._transport.verify_jwt_response(MagicMock(), self.jti, ANY, None) self._requests_transport.get.assert_called_once_with( "/public/v3/public-key/%s" % faux_kid, data={}) def test_key_is_cached_by_id(self): self._transport.verify_jwt_response(MagicMock(), self.jti, ANY, None) self._transport.verify_jwt_response(MagicMock(), self.jti, ANY, None) self._requests_transport.get.assert_called_once() def test_key_is_retrieved_by_id_when_key_changed(self): jwt1 = MagicMock() jwt1.headers = {"alg": "RS512", "typ": "JWT", "kid": "jwt1keyid"} jwt2 = MagicMock() jwt2.headers = {"alg": "RS512", "typ": "JWT", "kid": "jwt2keyid"} self._jwt_patch.return_value.unpack.side_effect = [ jwt1, jwt1, jwt2, jwt2 ] self._transport.verify_jwt_response(MagicMock(), self.jti, ANY, None) self._transport.verify_jwt_response(MagicMock(), self.jti, ANY, None) self._requests_transport.get.assert_has_calls([ call('/public/v3/public-key/jwt1keyid', data={}), call('/public/v3/public-key/jwt2keyid', data={}) ], any_order=True) @patch("launchkey.transports.jose_auth.RSAKey") def test_key_retrieved_is_used_to_verify_payload(self, rsa_key_patch): self._requests_transport.get.return_value = MagicMock(spec=APIResponse) self._requests_transport.get.return_value.data = valid_public_key self._transport.verify_jwt_response(MagicMock(), self.jti, ANY, None) # Verify that verify_compact is called one time with key created by our jwkest key patch self._jws_patch.return_value.verify_compact.assert_called_once_with( ANY, keys=[rsa_key_patch.return_value]) # Assert that the jwkest key patch is built using the import_rsa_key patch return value and the key id # from the header rsa_key_patch.assert_called_with( key=self._import_rsa_key_patch.return_value, kid=faux_kid) # Verify that we used the correct key to retrieve the key id from the header self._requests_transport.get.return_value.headers.get.assert_called_with( "X-IOV-JWT") def test_raises_when_kid_header_is_missing(self): headers_without_kid = {"alg": "RS512", "typ": "JWT"} jwt = MagicMock() jwt.headers = headers_without_kid self._jwt_patch.return_value.unpack.side_effect = jwt with self.assertRaises(JWTValidationFailure): self._transport.verify_jwt_response(MagicMock(), self.jti, ANY, None) def test_raises_when_kid_header_is_malformed(self): headers_with_kid_of_wrong_type = { "alg": "RS512", "typ": "JWT", "kid": 1234 } jwt = MagicMock() jwt.headers = headers_with_kid_of_wrong_type self._jwt_patch.return_value.unpack.side_effect = jwt with self.assertRaises(JWTValidationFailure): self._transport.verify_jwt_response(MagicMock(), self.jti, ANY, None) def test_raises_on_api_404(self): self._requests_transport.get.return_value = APIResponse( "Not Found", {}, 404) with self.assertRaises(UnexpectedAPIResponse): self._transport.verify_jwt_response(MagicMock(), self.jti, ANY, None) def test_raises_on_malformed_response_object(self): self._requests_transport.get.return_value = APIResponse(None, {}, 200) with self.assertRaises(UnexpectedAPIResponse): self._transport.verify_jwt_response(MagicMock(), self.jti, ANY, None) @patch("launchkey.transports.jose_auth.RSAKey") def test_raises_on_rsa_key_parsing_error(self, rsa_key_patch): rsa_key_patch.side_effect = TypeError with self.assertRaises(UnexpectedAPIResponse): self._transport.verify_jwt_response(MagicMock(), self.jti, ANY, None)
def test_supported_jwt_algorithms_success(self, alg): transport = JOSETransport(jwt_algorithm=alg) self.assertEqual(transport.jwt_algorithm, alg)
class TestJOSETransportJWTRequest(unittest.TestCase): def setUp(self): self._transport = JOSETransport() self._transport.get = MagicMock(return_value=MagicMock(spec=APIResponse)) public_key = APIResponse(valid_private_key, {"X-IOV-KEY-ID": "59:12:e2:f6:3f:79:d5:1e:18:75:c5:25:ff:b3:b7:f2"}, 200) self._transport.get.return_value = public_key self._transport._server_time_difference = 0, time() self.issuer = "svc" self.issuer_id = uuid4() self._transport.set_issuer(self.issuer, self.issuer_id, valid_private_key) self._body = str(uuid4()) self._content_hash = '16793293daadb5a03b7cbbb9d15a1a705b22e762a1a751bc8625dec666101ff2' self.jwt_payload = { 'aud': '%s:%s' % (self.issuer, self.issuer_id), 'iss': 'lka', 'cty': 'application/json', 'nbf': time(), 'jti': str(uuid4()), 'exp': time() + 30, 'iat': time(), 'request': { 'meth': 'POST', 'path': '/', 'hash': self._content_hash, 'func': 'S256'}, 'sub': '%s:%s' % (self.issuer, self.issuer_id) } self._transport._get_jwt_payload = MagicMock(return_value=self.jwt_payload) self._transport.content_hash_function = MagicMock() self._transport.content_hash_function().hexdigest.return_value = self._content_hash patcher = patch('launchkey.transports.jose_auth.sha256') patched = patcher.start() patched.return_value = MagicMock() patched.return_value.hexdigest.return_value = self._content_hash self.addCleanup(patcher.stop) def _verify_jwt_request(self, compact_jwt="compact.jtw.value", subscriber=None, method='POST', path='/', body="body"): subscriber = self.jwt_payload['sub'] if subscriber is None else subscriber return self._transport.verify_jwt_request(compact_jwt, subscriber, method, path, body) def test_all_params_returns_payload(self): actual = self._verify_jwt_request() self.assertEqual(actual, self.jwt_payload) def test_none_path_returns_payload(self): actual = self._transport.verify_jwt_request(MagicMock(), self.jwt_payload['sub'], 'POST', None, MagicMock()) self.assertEqual(actual, self.jwt_payload) def test_none_path_still_requires_jwt_request_path(self): del self.jwt_payload['request']['path'] with self.assertRaises(JWTValidationFailure): self._verify_jwt_request(path=None) def test_none_method_returns_payload(self): actual = self._verify_jwt_request(method=None) self.assertEqual(actual, self.jwt_payload) def test_none_method_still_requires_jwt_request_path(self): del self.jwt_payload['request']['meth'] with self.assertRaises(JWTValidationFailure): self._verify_jwt_request(method=None) def test_invalid_audience_raises_validation_failure(self): self.jwt_payload['aud'] = MagicMock() with self.assertRaises(JWTValidationFailure): self._verify_jwt_request() def test_invalid_issuer_raises_validation_failure(self): self.jwt_payload['iss'] = MagicMock() with self.assertRaises(JWTValidationFailure): self._verify_jwt_request() @patch("launchkey.transports.jose_auth.time") def test_invalid_nbf_raises_validation_failure(self, time_patch): time_patch.return_value = self.jwt_payload['nbf'] - JOSE_JWT_LEEWAY - 1 with self.assertRaises(JWTValidationFailure): self._verify_jwt_request() @patch("launchkey.transports.jose_auth.time") def test_invalid_exp_raises_validation_failure(self, time_patch): time_patch.return_value = self.jwt_payload['exp'] + JOSE_JWT_LEEWAY + 1 with self.assertRaises(JWTValidationFailure): self._verify_jwt_request() def test_invalid_sub_raises_validation_failure(self): with self.assertRaises(JWTValidationFailure): self._verify_jwt_request(subscriber='other') def test_invalid_iat_raises_validation_failure(self): self.jwt_payload['iat'] = time() + JOSE_JWT_LEEWAY + 1 with self.assertRaises(JWTValidationFailure): self._verify_jwt_request() def test_invalid_content_body_raises_validation_failure(self): self.jwt_payload['request']['hash'] = MagicMock() with self.assertRaises(JWTValidationFailure): self._verify_jwt_request() def test_no_body_but_response_body_hash_algorithm_raises_validation_failure(self): del self.jwt_payload['request']['func'] with self.assertRaises(JWTValidationFailure): self._verify_jwt_request(body=None) def test_no_body_but_response_body_hash_raises_validation_failure(self): del self.jwt_payload['request']['hash'] with self.assertRaises(JWTValidationFailure): self._verify_jwt_request(body=None) def test_body_but_no_response_body_hash_algorithm_raises_validation_failure(self): del self.jwt_payload['request']['hash'] with self.assertRaises(JWTValidationFailure): self._verify_jwt_request() def test_body_but_no_response_body_hash_raises_validation_failure(self): del self.jwt_payload['request']['func'] with self.assertRaises(JWTValidationFailure): self._verify_jwt_request() def test_invalid_hash_func_raises_validation_failure(self): self.jwt_payload['request']['func'] = 'INVALID' with self.assertRaises(JWTValidationFailure): self._verify_jwt_request() def test_no_request_raises_validation_failure(self): del self.jwt_payload['request'] with self.assertRaises(JWTValidationFailure): self._verify_jwt_request(method='INV') def test_no_method_raises_validation_failure(self): del self.jwt_payload['request']['meth'] with self.assertRaises(JWTValidationFailure): self._verify_jwt_request(method='INV') def test_invalid_method_raises_validation_failure(self): with self.assertRaises(JWTValidationFailure): self._verify_jwt_request(method='INV') def test_no_path_raises_validation_failure(self): del self.jwt_payload['request']['path'] with self.assertRaises(JWTValidationFailure): self._verify_jwt_request(path='/invalid') def test_invalid_path_raises_validation_failure(self): with self.assertRaises(JWTValidationFailure): self._verify_jwt_request(path='/invalid') def test_invalid_jwt_raises_validation_failure(self): self._transport._get_jwt_payload.side_effect = JWKESTException with self.assertRaises(JWTValidationFailure): self._verify_jwt_request()
def setUp(self): self._transport = JOSETransport()