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

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

Issue 2666783008: Add google-endpoints to third_party/. (Closed)
Patch Set: Created 3 years, 10 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 unified diff | Download patch
OLDNEW
(Empty)
1 # Copyright 2016 Google Inc. All Rights Reserved.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 """Defines several suppliers that are used by the authenticator."""
16
17 import datetime
18 from dogpile import cache
19 from jwkest import jwk
20 import requests
21 import ssl
22
23
24 _HTTP_PROTOCOL_PREFIX = "http://"
25 _HTTPS_PROTOCOL_PREFIX = "https://"
26
27 _OPEN_ID_CONFIG_PATH = ".well-known/openid-configuration"
28
29
30 class KeyUriSupplier(object): # pylint: disable=too-few-public-methods
31 """A supplier that provides the `jwks_uri` for an issuer."""
32
33 def __init__(self, issuer_uri_configs):
34 """Construct an instance of KeyUriSupplier.
35
36 Args:
37 issuer_uri_configs: a dictionary mapping from an issuer to its jwks_uri
38 configuration.
39 """
40 self._issuer_uri_configs = issuer_uri_configs
41
42 def supply(self, issuer):
43 """Supplies the `jwks_uri` for the given issuer.
44
45 Args:
46 issuer: the issuer.
47
48 Returns:
49 The `jwks_uri` that is either statically configured or retrieved via
50 OpenId discovery. None is returned when the issuer is unknown or the
51 OpenId discovery fails.
52 """
53 issuer_uri_config = self._issuer_uri_configs.get(issuer)
54
55 if not issuer_uri_config:
56 # The issuer is unknown.
57 return
58
59 jwks_uri = issuer_uri_config.jwks_uri
60 if jwks_uri:
61 # When jwks_uri is set, return it directly.
62 return jwks_uri
63
64 # When jwksUri is empty, we try to retrieve it through the OpenID
65 # discovery.
66 open_id_valid = issuer_uri_config.open_id_valid
67 if open_id_valid:
68 discovered_jwks_uri = _discover_jwks_uri(issuer)
69 self._issuer_uri_configs[issuer] = IssuerUriConfig(False,
70 discovered_jwks_uri)
71 return discovered_jwks_uri
72
73
74 class JwksSupplier(object): # pylint: disable=too-few-public-methods
75 """A supplier that returns the Json Web Token Set of an issuer."""
76
77 def __init__(self, key_uri_supplier):
78 """Constructs an instance of JwksSupplier.
79
80 Args:
81 key_uri_supplier: a KeyUriSupplier instance that returns the `jwks_uri`
82 based on the given issuer.
83 """
84 self._key_uri_supplier = key_uri_supplier
85 self._jwks_cache = cache.make_region().configure(
86 "dogpile.cache.memory", expiration_time=datetime.timedelta(minutes=5))
87
88 def supply(self, issuer):
89 """Supplies the `Json Web Key Set` for the given issuer.
90
91 Args:
92 issuer: the issuer.
93
94 Returns:
95 The successfully retrieved Json Web Key Set. None is returned if the
96 issuer is unknown or the retrieval process fails.
97
98 Raises:
99 UnauthenticatedException: When this method cannot supply JWKS for the
100 given issuer (e.g. unknown issuer, HTTP request error).
101 """
102 def _retrieve_jwks():
103 """Retrieve the JWKS from the given jwks_uri when cache misses."""
104 jwks_uri = self._key_uri_supplier.supply(issuer)
105
106 if not jwks_uri:
107 raise UnauthenticatedException("Cannot find the `jwks_uri` for issuer "
108 "%s: either the issuer is unknown or "
109 "the OpenID discovery failed" % issuer)
110
111 try:
112 response = requests.get(jwks_uri)
113 json_response = response.json()
114 except Exception as exception:
115 message = "Cannot retrieve valid verification keys from the `jwks_uri`"
116 raise UnauthenticatedException(message, exception)
117
118 if "keys" in json_response:
119 # De-serialize the JSON as a JWKS object.
120 jwks_keys = jwk.KEYS()
121 jwks_keys.load_jwks(response.text)
122 return jwks_keys._keys
123 else:
124 # The JSON is a dictionary mapping from key id to X.509 certificates.
125 # Thus we extract the public key from the X.509 certificates and
126 # construct a JWKS object.
127 return _extract_x509_certificates(json_response)
128
129 return self._jwks_cache.get_or_create(issuer, _retrieve_jwks)
130
131
132 def _extract_x509_certificates(x509_certificates):
133 keys = []
134 for kid, certificate in x509_certificates.iteritems():
135 try:
136 if certificate.startswith(jwk.PREFIX):
137 # The certificate is PEM-encoded
138 der = ssl.PEM_cert_to_DER_cert(certificate)
139 key = jwk.der2rsa(der)
140 else:
141 key = jwk.import_rsa_key(certificate)
142 except Exception as exception:
143 raise UnauthenticatedException("Cannot load X.509 certificate",
144 exception)
145 rsa_key = jwk.RSAKey().load_key(key)
146 rsa_key.kid = kid
147 keys.append(rsa_key)
148 return keys
149
150
151 def _discover_jwks_uri(issuer):
152 open_id_url = _construct_open_id_url(issuer)
153 try:
154 response = requests.get(open_id_url)
155 return response.json().get("jwks_uri")
156 except Exception as error:
157 raise UnauthenticatedException("Cannot discover the jwks uri", error)
158
159
160 def _construct_open_id_url(issuer):
161 url = issuer
162 if (not url.startswith(_HTTP_PROTOCOL_PREFIX) and
163 not url.startswith(_HTTPS_PROTOCOL_PREFIX)):
164 url = _HTTPS_PROTOCOL_PREFIX + url
165 if not url.endswith("/"):
166 url += "/"
167 url += _OPEN_ID_CONFIG_PATH
168 return url
169
170
171 class IssuerUriConfig(object):
172 """The jwks_uri configuration for an issuer.
173
174 TODO (yangguan): this class should be removed after we figure out how to
175 fetch the external configs.
176 """
177
178 def __init__(self, open_id_valid, jwks_uri):
179 """Create an instance of IsserUriConfig.
180
181 Args:
182 open_id_valid: indicates whether the corresponding issuer is valid for
183 OpenId discovery.
184 jwks_uri: is the saved jwks_uri. Its value can be None if the OpenId
185 discovery process has not begun or has already failed.
186 """
187 self._open_id_valid = open_id_valid
188 self._jwks_uri = jwks_uri
189
190 @property
191 def open_id_valid(self):
192 return self._open_id_valid
193
194 @property
195 def jwks_uri(self):
196 return self._jwks_uri
197
198
199 class UnauthenticatedException(Exception):
200 pass
OLDNEW
« 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