Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(743)

Unified Diff: third_party/google-endpoints/google/api/auth/suppliers.py

Issue 2666783008: Add google-endpoints to third_party/. (Closed)
Patch Set: Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: third_party/google-endpoints/google/api/auth/suppliers.py
diff --git a/third_party/google-endpoints/google/api/auth/suppliers.py b/third_party/google-endpoints/google/api/auth/suppliers.py
new file mode 100644
index 0000000000000000000000000000000000000000..284f3f75d7e09e407e640b497db4ad82e3453c75
--- /dev/null
+++ b/third_party/google-endpoints/google/api/auth/suppliers.py
@@ -0,0 +1,200 @@
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Defines several suppliers that are used by the authenticator."""
+
+import datetime
+from dogpile import cache
+from jwkest import jwk
+import requests
+import ssl
+
+
+_HTTP_PROTOCOL_PREFIX = "http://"
+_HTTPS_PROTOCOL_PREFIX = "https://"
+
+_OPEN_ID_CONFIG_PATH = ".well-known/openid-configuration"
+
+
+class KeyUriSupplier(object): # pylint: disable=too-few-public-methods
+ """A supplier that provides the `jwks_uri` for an issuer."""
+
+ def __init__(self, issuer_uri_configs):
+ """Construct an instance of KeyUriSupplier.
+
+ Args:
+ issuer_uri_configs: a dictionary mapping from an issuer to its jwks_uri
+ configuration.
+ """
+ self._issuer_uri_configs = issuer_uri_configs
+
+ def supply(self, issuer):
+ """Supplies the `jwks_uri` for the given issuer.
+
+ Args:
+ issuer: the issuer.
+
+ Returns:
+ The `jwks_uri` that is either statically configured or retrieved via
+ OpenId discovery. None is returned when the issuer is unknown or the
+ OpenId discovery fails.
+ """
+ issuer_uri_config = self._issuer_uri_configs.get(issuer)
+
+ if not issuer_uri_config:
+ # The issuer is unknown.
+ return
+
+ jwks_uri = issuer_uri_config.jwks_uri
+ if jwks_uri:
+ # When jwks_uri is set, return it directly.
+ return jwks_uri
+
+ # When jwksUri is empty, we try to retrieve it through the OpenID
+ # discovery.
+ open_id_valid = issuer_uri_config.open_id_valid
+ if open_id_valid:
+ discovered_jwks_uri = _discover_jwks_uri(issuer)
+ self._issuer_uri_configs[issuer] = IssuerUriConfig(False,
+ discovered_jwks_uri)
+ return discovered_jwks_uri
+
+
+class JwksSupplier(object): # pylint: disable=too-few-public-methods
+ """A supplier that returns the Json Web Token Set of an issuer."""
+
+ def __init__(self, key_uri_supplier):
+ """Constructs an instance of JwksSupplier.
+
+ Args:
+ key_uri_supplier: a KeyUriSupplier instance that returns the `jwks_uri`
+ based on the given issuer.
+ """
+ self._key_uri_supplier = key_uri_supplier
+ self._jwks_cache = cache.make_region().configure(
+ "dogpile.cache.memory", expiration_time=datetime.timedelta(minutes=5))
+
+ def supply(self, issuer):
+ """Supplies the `Json Web Key Set` for the given issuer.
+
+ Args:
+ issuer: the issuer.
+
+ Returns:
+ The successfully retrieved Json Web Key Set. None is returned if the
+ issuer is unknown or the retrieval process fails.
+
+ Raises:
+ UnauthenticatedException: When this method cannot supply JWKS for the
+ given issuer (e.g. unknown issuer, HTTP request error).
+ """
+ def _retrieve_jwks():
+ """Retrieve the JWKS from the given jwks_uri when cache misses."""
+ jwks_uri = self._key_uri_supplier.supply(issuer)
+
+ if not jwks_uri:
+ raise UnauthenticatedException("Cannot find the `jwks_uri` for issuer "
+ "%s: either the issuer is unknown or "
+ "the OpenID discovery failed" % issuer)
+
+ try:
+ response = requests.get(jwks_uri)
+ json_response = response.json()
+ except Exception as exception:
+ message = "Cannot retrieve valid verification keys from the `jwks_uri`"
+ raise UnauthenticatedException(message, exception)
+
+ if "keys" in json_response:
+ # De-serialize the JSON as a JWKS object.
+ jwks_keys = jwk.KEYS()
+ jwks_keys.load_jwks(response.text)
+ return jwks_keys._keys
+ else:
+ # The JSON is a dictionary mapping from key id to X.509 certificates.
+ # Thus we extract the public key from the X.509 certificates and
+ # construct a JWKS object.
+ return _extract_x509_certificates(json_response)
+
+ return self._jwks_cache.get_or_create(issuer, _retrieve_jwks)
+
+
+def _extract_x509_certificates(x509_certificates):
+ keys = []
+ for kid, certificate in x509_certificates.iteritems():
+ try:
+ if certificate.startswith(jwk.PREFIX):
+ # The certificate is PEM-encoded
+ der = ssl.PEM_cert_to_DER_cert(certificate)
+ key = jwk.der2rsa(der)
+ else:
+ key = jwk.import_rsa_key(certificate)
+ except Exception as exception:
+ raise UnauthenticatedException("Cannot load X.509 certificate",
+ exception)
+ rsa_key = jwk.RSAKey().load_key(key)
+ rsa_key.kid = kid
+ keys.append(rsa_key)
+ return keys
+
+
+def _discover_jwks_uri(issuer):
+ open_id_url = _construct_open_id_url(issuer)
+ try:
+ response = requests.get(open_id_url)
+ return response.json().get("jwks_uri")
+ except Exception as error:
+ raise UnauthenticatedException("Cannot discover the jwks uri", error)
+
+
+def _construct_open_id_url(issuer):
+ url = issuer
+ if (not url.startswith(_HTTP_PROTOCOL_PREFIX) and
+ not url.startswith(_HTTPS_PROTOCOL_PREFIX)):
+ url = _HTTPS_PROTOCOL_PREFIX + url
+ if not url.endswith("/"):
+ url += "/"
+ url += _OPEN_ID_CONFIG_PATH
+ return url
+
+
+class IssuerUriConfig(object):
+ """The jwks_uri configuration for an issuer.
+
+ TODO (yangguan): this class should be removed after we figure out how to
+ fetch the external configs.
+ """
+
+ def __init__(self, open_id_valid, jwks_uri):
+ """Create an instance of IsserUriConfig.
+
+ Args:
+ open_id_valid: indicates whether the corresponding issuer is valid for
+ OpenId discovery.
+ jwks_uri: is the saved jwks_uri. Its value can be None if the OpenId
+ discovery process has not begun or has already failed.
+ """
+ self._open_id_valid = open_id_valid
+ self._jwks_uri = jwks_uri
+
+ @property
+ def open_id_valid(self):
+ return self._open_id_valid
+
+ @property
+ def jwks_uri(self):
+ return self._jwks_uri
+
+
+class UnauthenticatedException(Exception):
+ pass
« no previous file with comments | « third_party/google-endpoints/google/api/auth/caches.py ('k') | third_party/google-endpoints/google/api/auth/tokens.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698