def setUp(self): ec_jwk = jwk.ECKey(use=u"sig").load_key(ecc.P256) ec_jwk.kid = self._ec_kid rsa_key = jwk.RSAKey(use=u"sig").load_key(PublicKey.RSA.generate(1024)) rsa_key.kid = self._rsa_kid jwks = jwk.KEYS() jwks._keys.append(ec_jwk) jwks._keys.append(rsa_key) self._issuers_to_provider_ids = {} self._jwks_supplier = mock.MagicMock() self._authenticator = tokens.Authenticator(self._issuers_to_provider_ids, self._jwks_supplier) self._jwks = jwks self._jwks_supplier.supply.return_value = self._jwks self._method_info = mock.MagicMock() self._service_name = u"service.name.com" self._jwt_claims = { u"aud": [u"first.com", u"second.com"], u"email": u"*****@*****.**", u"exp": int(time.time()) + 10, u"iss": u"https://issuer.com", u"sub": u"subject-id"}
def convert(self): """ Convert manifest from schema 2 to schema 1 """ self.compute_layers() manifest = dict( name=self.name, tag=self.tag, architecture=self.config_layer['architecture'], schemaVersion=1, fsLayers=self.fs_layers, history=self.history ) key = jwk.ECKey().load_key(ecc.P256) key.kid = getKeyId(key) manifData = sign(manifest, key) return manifData
def test_verify_fails(self): auth_token = token_utils.generate_auth_token(self._jwt_claims, self._jwks._keys, kid=self._ec_kid) # Let the _jwks_supplier return a different key than the one we use to sign # the JWT. new_jwk = jwk.ECKey(use=u"sig").load_key(ecc.P256) new_jwks = jwk.KEYS() new_jwks._keys.append(new_jwk) self._jwks_supplier.supply.return_value = new_jwks with self.assertRaises(suppliers.UnauthenticatedException): self._authenticator.get_jwt_claims(auth_token)
def convert(self): """ Convert manifest from schema 2 to schema 1 """ if self.manifest.get("schemaVersion") == 1: log.info("Manifest is already schema 1") return _jsonDumps(self.manifest) log.info("Converting manifest to schema 1") name = "%s/%s" % (self.namespace, self.repository) self.compute_layers() manifest = dict(name=name, tag=self.tag, architecture=self.config_layer['architecture'], schemaVersion=1, fsLayers=self.fs_layers, history=self.history) key = jwk.ECKey().load_key(ecc.P256) key.kid = getKeyId(key) manifData = sign(manifest, key) return manifData
def test_auth_token_cache_capacity(self): authenticator = tokens.Authenticator({}, self._jwks_supplier, cache_capacity=2) self._jwt_claims[u"email"] = u"*****@*****.**" auth_token1 = token_utils.generate_auth_token(self._jwt_claims, self._jwks._keys) self._jwt_claims[u"email"] = u"*****@*****.**" auth_token2 = token_utils.generate_auth_token(self._jwt_claims, self._jwks._keys) # Populate the decoded result into cache. authenticator.get_jwt_claims(auth_token1) authenticator.get_jwt_claims(auth_token2) # Reset the returned JWKS so the signature verification will fail next # time. new_ec_jwk = jwk.ECKey(use=u"sig").load_key(ecc.P256) new_ec_jwk.kid = self._ec_kid new_jwks = jwk.KEYS() new_jwks._keys.append(new_ec_jwk) self._jwks_supplier.supply.return_value = new_jwks # Verify the following calls still succeed since the auth tokens are # cached. authenticator.get_jwt_claims(auth_token1) authenticator.get_jwt_claims(auth_token2) # Populate a third auth token into the cache. self._jwt_claims[u"email"] = u"*****@*****.**" auth_token3 = token_utils.generate_auth_token(self._jwt_claims, new_jwks._keys) authenticator.get_jwt_claims(auth_token3) # Make sure the first auth token is evicted from the cache since the cache # is full. with self.assertRaises(suppliers.UnauthenticatedException): authenticator.get_jwt_claims(auth_token1)
class IntegrationTest(unittest.TestCase): _CURRENT_TIME = int(time.time()) _PORT = 8080 _ISSUER = u"https://*****:*****@name.com" _X509_PATH = u"x509" _JWT_CLAIMS = { u"aud": [u"https://aud1.local.host", u"https://aud2.local.host"], u"exp": _CURRENT_TIME + 60, u"email": u"*****@*****.**", u"iss": _ISSUER, u"sub": u"subject-id" } _ec_jwk = jwk.ECKey(use=u"sig").load_key(ecc.P256) _rsa_key = jwk.RSAKey(use=u"sig").load_key(PublicKey.RSA.generate(1024)) _ec_jwk.kid = u"ec-key-id" _rsa_key.kid = u"rsa-key-id" _mock_timer = mock.MagicMock() _jwks = jwk.KEYS() _jwks._keys.append(_ec_jwk) _jwks._keys.append(_rsa_key) _AUTH_TOKEN = token_utils.generate_auth_token(_JWT_CLAIMS, _jwks._keys, alg=u"RS256", kid=_rsa_key.kid) @classmethod def setUpClass(cls): dirname = os.path.dirname(os.path.realpath(__file__)) cls._cert_file = os.path.join(dirname, u"ssl.cert") cls._key_file = os.path.join(dirname, u"ssl.key") os.environ[u"REQUESTS_CA_BUNDLE"] = cls._cert_file rest_server = cls._RestServer() rest_server.start() def setUp(self): self._provider_ids = {} self._configs = {} self._authenticator = auth.create_authenticator( self._provider_ids, self._configs) self._auth_info = mock.MagicMock() self._auth_info.is_provider_allowed.return_value = True self._auth_info.get_allowed_audiences.return_value = [ u"https://aud1.local.host" ] def test_verify_auth_token_with_jwks(self): url = get_url(IntegrationTest._JWKS_PATH) self._provider_ids[self._ISSUER] = self._PROVIDER_ID self._configs[IntegrationTest._ISSUER] = suppliers.IssuerUriConfig( False, url) user_info = self._authenticator.authenticate( IntegrationTest._AUTH_TOKEN, self._auth_info, IntegrationTest._SERVICE_NAME) self._assert_user_info_equals( tokens.UserInfo(IntegrationTest._JWT_CLAIMS), user_info) def test_authenticate_auth_token_with_bad_signature(self): new_rsa_key = jwk.RSAKey(use=u"sig").load_key( PublicKey.RSA.generate(2048)) kid = IntegrationTest._rsa_key.kid new_rsa_key.kid = kid new_jwks = jwk.KEYS() new_jwks._keys.append(new_rsa_key) auth_token = token_utils.generate_auth_token( IntegrationTest._JWT_CLAIMS, new_jwks._keys, alg=u"RS256", kid=kid) url = get_url(IntegrationTest._JWKS_PATH) self._provider_ids[self._ISSUER] = self._PROVIDER_ID self._configs[IntegrationTest._ISSUER] = suppliers.IssuerUriConfig( False, url) message = u"Signature verification failed" with self.assertRaisesRegexp(suppliers.UnauthenticatedException, message): self._authenticator.authenticate(auth_token, self._auth_info, IntegrationTest._SERVICE_NAME) def test_verify_auth_token_with_x509(self): url = get_url(IntegrationTest._X509_PATH) self._provider_ids[self._ISSUER] = self._PROVIDER_ID self._configs[IntegrationTest._ISSUER] = suppliers.IssuerUriConfig( False, url) user_info = self._authenticator.authenticate( IntegrationTest._AUTH_TOKEN, self._auth_info, IntegrationTest._SERVICE_NAME) self._assert_user_info_equals( tokens.UserInfo(IntegrationTest._JWT_CLAIMS), user_info) def test_verify_auth_token_with_invalid_x509(self): url = get_url(IntegrationTest._INVALID_X509_PATH) self._provider_ids[self._ISSUER] = self._PROVIDER_ID self._configs[IntegrationTest._ISSUER] = suppliers.IssuerUriConfig( False, url) message = u"Cannot load X.509 certificate" with self.assertRaisesRegexp(suppliers.UnauthenticatedException, message): self._authenticator.authenticate(IntegrationTest._AUTH_TOKEN, self._auth_info, IntegrationTest._SERVICE_NAME) def test_openid_discovery(self): self._provider_ids[self._ISSUER] = self._PROVIDER_ID self._configs[IntegrationTest._ISSUER] = suppliers.IssuerUriConfig( True, None) user_info = self._authenticator.authenticate( IntegrationTest._AUTH_TOKEN, self._auth_info, IntegrationTest._SERVICE_NAME) self._assert_user_info_equals( tokens.UserInfo(IntegrationTest._JWT_CLAIMS), user_info) def test_openid_discovery_failed(self): self._provider_ids[self._ISSUER] = self._PROVIDER_ID self._configs[IntegrationTest._ISSUER] = suppliers.IssuerUriConfig( False, None) message = (u"Cannot find the `jwks_uri` for issuer %s" % IntegrationTest._ISSUER) with self.assertRaisesRegexp(suppliers.UnauthenticatedException, message): self._authenticator.authenticate(IntegrationTest._AUTH_TOKEN, self._auth_info, IntegrationTest._SERVICE_NAME) def test_authenticate_with_malformed_auth_code(self): with self.assertRaisesRegexp(suppliers.UnauthenticatedException, u"Cannot decode the auth token"): self._authenticator.authenticate(u"invalid-auth-code", self._auth_info, IntegrationTest._SERVICE_NAME) def test_authenticate_with_disallowed_issuer(self): url = get_url(IntegrationTest._JWKS_PATH) self._configs[IntegrationTest._ISSUER] = suppliers.IssuerUriConfig( False, url) message = u"Unknown issuer: " + self._ISSUER with self.assertRaisesRegexp(suppliers.UnauthenticatedException, message): self._authenticator.authenticate(IntegrationTest._AUTH_TOKEN, self._auth_info, IntegrationTest._SERVICE_NAME) def test_authenticate_with_unknown_issuer(self): message = (u"Cannot find the `jwks_uri` for issuer %s: " u"either the issuer is unknown") % IntegrationTest._ISSUER with self.assertRaisesRegexp(suppliers.UnauthenticatedException, message): self._authenticator.authenticate(IntegrationTest._AUTH_TOKEN, self._auth_info, IntegrationTest._SERVICE_NAME) def test_authenticate_with_invalid_audience(self): url = get_url(IntegrationTest._JWKS_PATH) self._provider_ids[self._ISSUER] = self._PROVIDER_ID self._configs[IntegrationTest._ISSUER] = suppliers.IssuerUriConfig( False, url) self._auth_info.get_allowed_audiences.return_value = [] with self.assertRaisesRegexp(suppliers.UnauthenticatedException, u"Audiences not allowed"): self._authenticator.authenticate(IntegrationTest._AUTH_TOKEN, self._auth_info, IntegrationTest._SERVICE_NAME) @mock.patch(u"time.time", _mock_timer) def test_authenticate_with_expired_auth_token(self): url = get_url(IntegrationTest._JWKS_PATH) self._provider_ids[self._ISSUER] = self._PROVIDER_ID self._configs[IntegrationTest._ISSUER] = suppliers.IssuerUriConfig( False, url) IntegrationTest._mock_timer.return_value = 0 # Create an auth token that expires in 10 seconds. jwt_claims = copy.deepcopy(IntegrationTest._JWT_CLAIMS) jwt_claims[u"exp"] = time.time() + 10 auth_token = token_utils.generate_auth_token( jwt_claims, IntegrationTest._jwks._keys, alg=u"RS256", kid=IntegrationTest._rsa_key.kid) # Verify that the auth token can be authenticated successfully. self._authenticator.authenticate(IntegrationTest._AUTH_TOKEN, self._auth_info, IntegrationTest._SERVICE_NAME) # Advance the timer by 20 seconds and make sure the token is expired. IntegrationTest._mock_timer.return_value += 20 message = u"The auth token has already expired" with self.assertRaisesRegexp(suppliers.UnauthenticatedException, message): self._authenticator.authenticate(auth_token, self._auth_info, IntegrationTest._SERVICE_NAME) def test_invalid_openid_discovery_url(self): issuer = u"https://invalid.issuer" self._provider_ids[self._ISSUER] = self._PROVIDER_ID self._configs[issuer] = suppliers.IssuerUriConfig(True, None) jwt_claims = copy.deepcopy(IntegrationTest._JWT_CLAIMS) jwt_claims[u"iss"] = issuer auth_token = token_utils.generate_auth_token( jwt_claims, IntegrationTest._jwks._keys, alg=u"RS256", kid=IntegrationTest._rsa_key.kid) message = u"Cannot discover the jwks uri" with self.assertRaisesRegexp(suppliers.UnauthenticatedException, message): self._authenticator.authenticate(auth_token, self._auth_info, IntegrationTest._SERVICE_NAME) def test_invalid_jwks_uri(self): url = u"https://invalid.jwks.uri" self._provider_ids[self._ISSUER] = self._PROVIDER_ID self._configs[IntegrationTest._ISSUER] = suppliers.IssuerUriConfig( False, url) message = u"Cannot retrieve valid verification keys from the `jwks_uri`" with self.assertRaisesRegexp(suppliers.UnauthenticatedException, message): self._authenticator.authenticate(IntegrationTest._AUTH_TOKEN, self._auth_info, IntegrationTest._SERVICE_NAME) def _assert_user_info_equals(self, expected, actual): self.assertEqual(expected.audiences, actual.audiences) self.assertEqual(expected.email, actual.email) self.assertEqual(expected.subject_id, actual.subject_id) self.assertEqual(expected.issuer, actual.issuer) class _RestServer(object): def __init__(self): app = flask.Flask(u"integration-test-server") @app.route(u"/" + IntegrationTest._JWKS_PATH) def get_json_web_key_set(): # pylint: disable=unused-variable return IntegrationTest._jwks.dump_jwks() @app.route(u"/" + IntegrationTest._X509_PATH) def get_x509_certificates(): # pylint: disable=unused-variable cert = IntegrationTest._rsa_key.key.publickey().exportKey( u"PEM") return flask.jsonify( {IntegrationTest._rsa_key.kid: cert.decode('ascii')}) @app.route(u"/" + IntegrationTest._INVALID_X509_PATH) def get_invalid_x509_certificates(): # pylint: disable=unused-variable return flask.jsonify( {IntegrationTest._rsa_key.kid: u"invalid cert"}) @app.route(u"/.well-known/openid-configuration") def get_openid_configuration(): # pylint: disable=unused-variable return flask.jsonify( {u"jwks_uri": get_url(IntegrationTest._JWKS_PATH)}) self._application = app def start(self): def run_app(): ssl_context = (IntegrationTest._cert_file, IntegrationTest._key_file) self._application.run(port=IntegrationTest._PORT, ssl_context=ssl_context) thread = threading.Thread(target=run_app, args=()) thread.daemon = True thread.start()