keycloakauthenticator 3.3.0__tar.gz → 4.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {keycloakauthenticator-3.3.0 → keycloakauthenticator-4.0.1}/PKG-INFO +16 -3
- {keycloakauthenticator-3.3.0 → keycloakauthenticator-4.0.1}/README.md +1 -1
- {keycloakauthenticator-3.3.0 → keycloakauthenticator-4.0.1}/keycloakauthenticator/_version.py +1 -1
- {keycloakauthenticator-3.3.0 → keycloakauthenticator-4.0.1}/keycloakauthenticator/auth.py +15 -18
- {keycloakauthenticator-3.3.0 → keycloakauthenticator-4.0.1}/keycloakauthenticator/tests/test_auth.py +11 -9
- {keycloakauthenticator-3.3.0 → keycloakauthenticator-4.0.1}/keycloakauthenticator.egg-info/PKG-INFO +16 -3
- {keycloakauthenticator-3.3.0 → keycloakauthenticator-4.0.1}/keycloakauthenticator.egg-info/requires.txt +1 -1
- {keycloakauthenticator-3.3.0 → keycloakauthenticator-4.0.1}/setup.py +1 -1
- {keycloakauthenticator-3.3.0 → keycloakauthenticator-4.0.1}/MANIFEST.in +0 -0
- {keycloakauthenticator-3.3.0 → keycloakauthenticator-4.0.1}/keycloakauthenticator/__init__.py +0 -0
- {keycloakauthenticator-3.3.0 → keycloakauthenticator-4.0.1}/keycloakauthenticator/metrics.py +0 -0
- {keycloakauthenticator-3.3.0 → keycloakauthenticator-4.0.1}/keycloakauthenticator/tests/__init__.py +0 -0
- {keycloakauthenticator-3.3.0 → keycloakauthenticator-4.0.1}/keycloakauthenticator.egg-info/SOURCES.txt +0 -0
- {keycloakauthenticator-3.3.0 → keycloakauthenticator-4.0.1}/keycloakauthenticator.egg-info/dependency_links.txt +0 -0
- {keycloakauthenticator-3.3.0 → keycloakauthenticator-4.0.1}/keycloakauthenticator.egg-info/not-zip-safe +0 -0
- {keycloakauthenticator-3.3.0 → keycloakauthenticator-4.0.1}/keycloakauthenticator.egg-info/top_level.txt +0 -0
- {keycloakauthenticator-3.3.0 → keycloakauthenticator-4.0.1}/pyproject.toml +0 -0
- {keycloakauthenticator-3.3.0 → keycloakauthenticator-4.0.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.4
|
2
2
|
Name: keycloakauthenticator
|
3
|
-
Version:
|
3
|
+
Version: 4.0.1
|
4
4
|
Summary: KeyCloakAuthenticator: Authenticate JupyterHub users with KeyCloak and OIDC
|
5
5
|
Home-page: https://github.com/swan-cern/jupyterhub-extensions
|
6
6
|
Author: SWAN Admins
|
@@ -15,6 +15,19 @@ Classifier: Intended Audience :: Science/Research
|
|
15
15
|
Classifier: Programming Language :: Python
|
16
16
|
Classifier: Programming Language :: Python :: 3
|
17
17
|
Description-Content-Type: text/markdown
|
18
|
+
Requires-Dist: jupyterhub
|
19
|
+
Requires-Dist: oauthenticator==16.1.0
|
20
|
+
Requires-Dist: PyJWT[crypto]>=2.0.0
|
21
|
+
Dynamic: author
|
22
|
+
Dynamic: classifier
|
23
|
+
Dynamic: description
|
24
|
+
Dynamic: description-content-type
|
25
|
+
Dynamic: home-page
|
26
|
+
Dynamic: keywords
|
27
|
+
Dynamic: license
|
28
|
+
Dynamic: platform
|
29
|
+
Dynamic: requires-dist
|
30
|
+
Dynamic: summary
|
18
31
|
|
19
32
|
# KeyCloakAuthenticator
|
20
33
|
|
@@ -47,7 +60,7 @@ In your JupyterHub config file, set the authenticator and configure it:
|
|
47
60
|
```python
|
48
61
|
# Enable the authenticator
|
49
62
|
c.JupyterHub.authenticator_class = 'keycloakauthenticator.KeyCloakAuthenticator'
|
50
|
-
c.KeyCloakAuthenticator.
|
63
|
+
c.KeyCloakAuthenticator.username_claim = 'preferred_username'
|
51
64
|
|
52
65
|
# URL to redirect to after logout is complete with auth provider.
|
53
66
|
c.KeyCloakAuthenticator.logout_redirect_url = 'https://cern.ch/swan'
|
@@ -29,7 +29,7 @@ In your JupyterHub config file, set the authenticator and configure it:
|
|
29
29
|
```python
|
30
30
|
# Enable the authenticator
|
31
31
|
c.JupyterHub.authenticator_class = 'keycloakauthenticator.KeyCloakAuthenticator'
|
32
|
-
c.KeyCloakAuthenticator.
|
32
|
+
c.KeyCloakAuthenticator.username_claim = 'preferred_username'
|
33
33
|
|
34
34
|
# URL to redirect to after logout is complete with auth provider.
|
35
35
|
c.KeyCloakAuthenticator.logout_redirect_url = 'https://cern.ch/swan'
|
@@ -153,8 +153,7 @@ class KeyCloakAuthenticator(GenericOAuthenticator):
|
|
153
153
|
# Try to load the configs until it succeeds
|
154
154
|
while True:
|
155
155
|
try:
|
156
|
-
|
157
|
-
data = await self.fetch(req, "fetching oidc config")
|
156
|
+
data = await self.httpfetch(f"{self.oidc_issuer}/.well-known/openid-configuration", label="fetching oidc config")
|
158
157
|
|
159
158
|
if not set(['authorization_endpoint', 'token_endpoint', 'userinfo_endpoint']).issubset(data.keys()):
|
160
159
|
raise Exception('Unable to retrieve OIDC necessary values')
|
@@ -166,15 +165,15 @@ class KeyCloakAuthenticator(GenericOAuthenticator):
|
|
166
165
|
end_session_url = data.get('end_session_endpoint')
|
167
166
|
if self.enable_logout and end_session_url:
|
168
167
|
if self.logout_redirect_url:
|
169
|
-
end_session_url += '?
|
168
|
+
end_session_url += '?post_logout_redirect_uri=%s' % self.logout_redirect_url
|
169
|
+
end_session_url += '&client_id=%s' % self.client_id
|
170
170
|
# Update parent class OAuthenticator.logout_redirect_url
|
171
171
|
self.logout_redirect_url = end_session_url
|
172
172
|
|
173
173
|
if self.config.check_signature :
|
174
174
|
jwks_uri = data['jwks_uri']
|
175
175
|
|
176
|
-
|
177
|
-
jwk_data = await self.fetch(req, "fetching jwks")
|
176
|
+
jwk_data = await self.httpfetch(jwks_uri, label="fetching jwks")
|
178
177
|
self.public_key = RSAAlgorithm(RSAAlgorithm.SHA256).from_jwk(jwk_data['keys'][0])
|
179
178
|
self.log.info(f"aquired public key from {jwks_uri}")
|
180
179
|
else:
|
@@ -185,7 +184,7 @@ class KeyCloakAuthenticator(GenericOAuthenticator):
|
|
185
184
|
self.log.info('KeycloakAuthenticator fully configured')
|
186
185
|
break
|
187
186
|
except:
|
188
|
-
self.log.error("Failure to retrieve the openid configuration, will try again in 1 min (auth calls will fail)")
|
187
|
+
self.log.error("Failure to retrieve the openid configuration, will try again in 1 min (auth calls will fail)", exc_info=True)
|
189
188
|
await asyncio.sleep(60)
|
190
189
|
|
191
190
|
def _validate_roles(self, user_roles):
|
@@ -216,14 +215,14 @@ class KeyCloakAuthenticator(GenericOAuthenticator):
|
|
216
215
|
client_secret = self.client_secret,
|
217
216
|
subject_token = token,
|
218
217
|
audience = service_name,
|
219
|
-
requested_token_type = 'urn:ietf:params:oauth:token-type:access_token'
|
218
|
+
requested_token_type = 'urn:ietf:params:oauth:token-type:access_token',
|
219
|
+
subject_token_type = 'urn:ietf:params:oauth:token-type:access_token'
|
220
220
|
)
|
221
221
|
data = parse.urlencode(values)
|
222
222
|
|
223
223
|
req = HTTPRequest(
|
224
224
|
self.token_url,
|
225
225
|
method="POST",
|
226
|
-
headers=self._get_headers(),
|
227
226
|
body=data,
|
228
227
|
)
|
229
228
|
|
@@ -250,7 +249,7 @@ class KeyCloakAuthenticator(GenericOAuthenticator):
|
|
250
249
|
access_tokens[service_name] = access_token
|
251
250
|
|
252
251
|
# Produce logs and metrics for this token exchange
|
253
|
-
queue_t = response.time_info['queue']
|
252
|
+
queue_t = response.time_info['queue'] if 'queue' in response.time_info else -1
|
254
253
|
request_t = response.request_time
|
255
254
|
self.log.info('Exchanged {} token, queue time: {} s, request time: {} s'.format(service_name, queue_t, request_t))
|
256
255
|
metric_exchange_tornado_queue_time.labels("exchange_token_{}".format(service_name.replace("-","_"))).observe(queue_t)
|
@@ -269,16 +268,13 @@ class KeyCloakAuthenticator(GenericOAuthenticator):
|
|
269
268
|
refresh_token = refresh_token
|
270
269
|
)
|
271
270
|
data = parse.urlencode(values)
|
272
|
-
|
273
|
-
req = HTTPRequest(
|
274
|
-
self.token_url,
|
275
|
-
method="POST",
|
276
|
-
headers=self._get_headers(),
|
277
|
-
body=data,
|
278
|
-
)
|
279
271
|
access_t = None
|
280
272
|
refresh_t = None
|
281
|
-
response = await self.
|
273
|
+
response = await self.httpfetch(self.token_url,
|
274
|
+
label="refreshing token",
|
275
|
+
method="POST",
|
276
|
+
body=data,
|
277
|
+
parse_json=False)
|
282
278
|
if response.body:
|
283
279
|
body = json.loads(response.body.decode('utf8', 'replace'))
|
284
280
|
access_t = body.get('access_token', None)
|
@@ -289,7 +285,8 @@ class KeyCloakAuthenticator(GenericOAuthenticator):
|
|
289
285
|
self.log.error("Could not obtain refresh token for swan")
|
290
286
|
|
291
287
|
metric_refresh_tornado_request_time.labels("refresh_token",response.code).observe(response.request_time)
|
292
|
-
|
288
|
+
queue_t = response.time_info['queue'] if 'queue' in response.time_info else -1
|
289
|
+
metric_refresh_tornado_queue_time.observe(queue_t)
|
293
290
|
self.log.info('Refresh token request completed in {} seconds'.format(time.time() - start))
|
294
291
|
return access_t, refresh_t
|
295
292
|
|
{keycloakauthenticator-3.3.0 → keycloakauthenticator-4.0.1}/keycloakauthenticator/tests/test_auth.py
RENAMED
@@ -50,19 +50,20 @@ async def test_refresh_user(monkeypatch):
|
|
50
50
|
monkeypatch.setattr(KeyCloakAuthenticator, "oidc_issuer", "dummy-oidc-url")
|
51
51
|
|
52
52
|
# Mock the response from the server on refresh and exchange tokens
|
53
|
-
async def _mock_fetch(self,
|
54
|
-
print("Mocking fetch for
|
53
|
+
async def _mock_fetch(self, req, label, **kargs):
|
54
|
+
print(f"Mocking fetch for {req} ({label})")
|
55
55
|
mock_response_body = json.dumps({
|
56
56
|
"access_token": _get_mock_token(private_key, "new_access_token"),
|
57
57
|
"refresh_token": _get_mock_token(private_key, "new_refresh_token"),
|
58
58
|
})
|
59
|
+
|
59
60
|
class MockResponse:
|
60
61
|
def __init__(self):
|
61
62
|
self.code = 200
|
62
63
|
self.body = mock_response_body.encode('utf-8')
|
63
64
|
self.request_time = 0
|
64
|
-
self.time_info ={
|
65
|
-
|
65
|
+
self.time_info = {
|
66
|
+
"queue": 0
|
66
67
|
}
|
67
68
|
return MockResponse()
|
68
69
|
monkeypatch.setattr(KeyCloakAuthenticator, "fetch", _mock_fetch)
|
@@ -120,22 +121,23 @@ async def test_refresh_user_with_expired_refresh_token(monkeypatch):
|
|
120
121
|
# Mock the response from the server on refresh and exchange tokens
|
121
122
|
monkeypatch.setattr(KeyCloakAuthenticator, "oidc_issuer", "dummy-oidc-url")
|
122
123
|
|
123
|
-
async def
|
124
|
-
print("Mocking
|
124
|
+
async def _mock_httpfetch(self, url, label, **kargs):
|
125
|
+
print(f"Mocking httpfetch for {url} ({label})")
|
125
126
|
mock_response_body = json.dumps({
|
126
127
|
"access_token": _get_mock_token(private_key, "new_access_token"),
|
127
128
|
"refresh_token": _get_mock_token(private_key, "new_refresh_token"),
|
128
129
|
})
|
130
|
+
|
129
131
|
class MockResponse:
|
130
132
|
def __init__(self):
|
131
133
|
self.code = 200
|
132
134
|
self.body = mock_response_body.encode('utf-8')
|
133
135
|
self.request_time = 0
|
134
|
-
self.time_info ={
|
135
|
-
|
136
|
+
self.time_info = {
|
137
|
+
"queue": 0
|
136
138
|
}
|
137
139
|
return MockResponse()
|
138
|
-
monkeypatch.setattr(KeyCloakAuthenticator, "
|
140
|
+
monkeypatch.setattr(KeyCloakAuthenticator, "httpfetch", _mock_httpfetch)
|
139
141
|
|
140
142
|
class MockUser:
|
141
143
|
"""Fake user object, with dummy authentication state from DB"""
|
{keycloakauthenticator-3.3.0 → keycloakauthenticator-4.0.1}/keycloakauthenticator.egg-info/PKG-INFO
RENAMED
@@ -1,6 +1,6 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.4
|
2
2
|
Name: keycloakauthenticator
|
3
|
-
Version:
|
3
|
+
Version: 4.0.1
|
4
4
|
Summary: KeyCloakAuthenticator: Authenticate JupyterHub users with KeyCloak and OIDC
|
5
5
|
Home-page: https://github.com/swan-cern/jupyterhub-extensions
|
6
6
|
Author: SWAN Admins
|
@@ -15,6 +15,19 @@ Classifier: Intended Audience :: Science/Research
|
|
15
15
|
Classifier: Programming Language :: Python
|
16
16
|
Classifier: Programming Language :: Python :: 3
|
17
17
|
Description-Content-Type: text/markdown
|
18
|
+
Requires-Dist: jupyterhub
|
19
|
+
Requires-Dist: oauthenticator==16.1.0
|
20
|
+
Requires-Dist: PyJWT[crypto]>=2.0.0
|
21
|
+
Dynamic: author
|
22
|
+
Dynamic: classifier
|
23
|
+
Dynamic: description
|
24
|
+
Dynamic: description-content-type
|
25
|
+
Dynamic: home-page
|
26
|
+
Dynamic: keywords
|
27
|
+
Dynamic: license
|
28
|
+
Dynamic: platform
|
29
|
+
Dynamic: requires-dist
|
30
|
+
Dynamic: summary
|
18
31
|
|
19
32
|
# KeyCloakAuthenticator
|
20
33
|
|
@@ -47,7 +60,7 @@ In your JupyterHub config file, set the authenticator and configure it:
|
|
47
60
|
```python
|
48
61
|
# Enable the authenticator
|
49
62
|
c.JupyterHub.authenticator_class = 'keycloakauthenticator.KeyCloakAuthenticator'
|
50
|
-
c.KeyCloakAuthenticator.
|
63
|
+
c.KeyCloakAuthenticator.username_claim = 'preferred_username'
|
51
64
|
|
52
65
|
# URL to redirect to after logout is complete with auth provider.
|
53
66
|
c.KeyCloakAuthenticator.logout_redirect_url = 'https://cern.ch/swan'
|
File without changes
|
{keycloakauthenticator-3.3.0 → keycloakauthenticator-4.0.1}/keycloakauthenticator/__init__.py
RENAMED
File without changes
|
{keycloakauthenticator-3.3.0 → keycloakauthenticator-4.0.1}/keycloakauthenticator/metrics.py
RENAMED
File without changes
|
{keycloakauthenticator-3.3.0 → keycloakauthenticator-4.0.1}/keycloakauthenticator/tests/__init__.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|