Beispiel #1
0
    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
Beispiel #3
0
    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)
Beispiel #4
0
 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)
Beispiel #6
0
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()