|
15 | 15 | import atexit
|
16 | 16 | import base64
|
17 | 17 | import datetime
|
| 18 | +import json |
18 | 19 | import os
|
19 | 20 | import tempfile
|
20 | 21 |
|
21 | 22 | import google.auth
|
22 | 23 | import google.auth.transport.requests
|
| 24 | +import oauthlib.oauth2 |
23 | 25 | import urllib3
|
24 | 26 | import yaml
|
| 27 | +from requests_oauthlib import OAuth2Session |
| 28 | +from six import PY3 |
25 | 29 |
|
26 | 30 | from kubernetes.client import ApiClient, Configuration
|
27 | 31 |
|
@@ -169,14 +173,17 @@ def _load_authentication(self):
|
169 | 173 | 1. GCP auth-provider
|
170 | 174 | 2. token_data
|
171 | 175 | 3. token field (point to a token file)
|
172 |
| - 4. username/password |
| 176 | + 4. oidc auth-provider |
| 177 | + 5. username/password |
173 | 178 | """
|
174 | 179 | if not self._user:
|
175 | 180 | return
|
176 | 181 | if self._load_gcp_token():
|
177 | 182 | return
|
178 | 183 | if self._load_user_token():
|
179 | 184 | return
|
| 185 | + if self._load_oid_token(): |
| 186 | + return |
180 | 187 | self._load_user_pass_token()
|
181 | 188 |
|
182 | 189 | def _load_gcp_token(self):
|
@@ -208,6 +215,100 @@ def _refresh_gcp_token(self):
|
208 | 215 | if self._config_persister:
|
209 | 216 | self._config_persister(self._config.value)
|
210 | 217 |
|
| 218 | + def _load_oid_token(self): |
| 219 | + if 'auth-provider' not in self._user: |
| 220 | + return |
| 221 | + provider = self._user['auth-provider'] |
| 222 | + |
| 223 | + if 'name' not in provider or 'config' not in provider: |
| 224 | + return |
| 225 | + |
| 226 | + if provider['name'] != 'oidc': |
| 227 | + return |
| 228 | + |
| 229 | + parts = provider['config']['id-token'].split('.') |
| 230 | + |
| 231 | + if len(parts) != 3: # Not a valid JWT |
| 232 | + return None |
| 233 | + |
| 234 | + if PY3: |
| 235 | + jwt_attributes = json.loads( |
| 236 | + base64.b64decode(parts[1]).decode('utf-8') |
| 237 | + ) |
| 238 | + else: |
| 239 | + jwt_attributes = json.loads( |
| 240 | + base64.b64decode(parts[1] + "==") |
| 241 | + ) |
| 242 | + |
| 243 | + expire = jwt_attributes.get('exp') |
| 244 | + |
| 245 | + if ((expire is not None) and |
| 246 | + (_is_expired(datetime.datetime.fromtimestamp(expire, |
| 247 | + tz=UTC)))): |
| 248 | + self._refresh_oidc(provider) |
| 249 | + |
| 250 | + if self._config_persister: |
| 251 | + self._config_persister(self._config.value) |
| 252 | + |
| 253 | + self.token = "Bearer %s" % provider['config']['id-token'] |
| 254 | + |
| 255 | + return self.token |
| 256 | + |
| 257 | + def _refresh_oidc(self, provider): |
| 258 | + ca_cert = tempfile.NamedTemporaryFile(delete=True) |
| 259 | + |
| 260 | + if PY3: |
| 261 | + cert = base64.b64decode( |
| 262 | + provider['config']['idp-certificate-authority-data'] |
| 263 | + ).decode('utf-8') |
| 264 | + else: |
| 265 | + cert = base64.b64decode( |
| 266 | + provider['config']['idp-certificate-authority-data'] + "==" |
| 267 | + ) |
| 268 | + |
| 269 | + with open(ca_cert.name, 'w') as fh: |
| 270 | + fh.write(cert) |
| 271 | + |
| 272 | + config = Configuration() |
| 273 | + config.ssl_ca_cert = ca_cert.name |
| 274 | + |
| 275 | + client = ApiClient(configuration=config) |
| 276 | + |
| 277 | + response = client.request( |
| 278 | + method="GET", |
| 279 | + url="%s/.well-known/openid-configuration" |
| 280 | + % provider['config']['idp-issuer-url'] |
| 281 | + ) |
| 282 | + |
| 283 | + if response.status != 200: |
| 284 | + return |
| 285 | + |
| 286 | + response = json.loads(response.data) |
| 287 | + |
| 288 | + request = OAuth2Session( |
| 289 | + client_id=provider['config']['client-id'], |
| 290 | + token=provider['config']['refresh-token'], |
| 291 | + auto_refresh_kwargs={ |
| 292 | + 'client_id': provider['config']['client-id'], |
| 293 | + 'client_secret': provider['config']['client-secret'] |
| 294 | + }, |
| 295 | + auto_refresh_url=response['token_endpoint'] |
| 296 | + ) |
| 297 | + |
| 298 | + try: |
| 299 | + refresh = request.refresh_token( |
| 300 | + token_url=response['token_endpoint'], |
| 301 | + refresh_token=provider['config']['refresh-token'], |
| 302 | + auth=(provider['config']['client-id'], |
| 303 | + provider['config']['client-secret']), |
| 304 | + verify=ca_cert.name |
| 305 | + ) |
| 306 | + except oauthlib.oauth2.rfc6749.errors.InvalidClientIdError: |
| 307 | + return |
| 308 | + |
| 309 | + provider['config'].value['id-token'] = refresh['id_token'] |
| 310 | + provider['config'].value['refresh-token'] = refresh['refresh_token'] |
| 311 | + |
211 | 312 | def _load_user_token(self):
|
212 | 313 | token = FileOrData(
|
213 | 314 | self._user, 'tokenFile', 'token',
|
|
0 commit comments