mirror of
https://github.com/jupyterhub/jupyterhub.git
synced 2025-10-19 07:53:00 +00:00
tests for oauth roles
This commit is contained in:
@@ -351,7 +351,7 @@ class JupyterHubRequestValidator(RequestValidator):
|
|||||||
orm.APIToken.new(
|
orm.APIToken.new(
|
||||||
client_id=client.identifier,
|
client_id=client.identifier,
|
||||||
expires_in=token['expires_in'],
|
expires_in=token['expires_in'],
|
||||||
roles=request._jupyterhub_roles,
|
roles=[rolename for rolename in request.scopes],
|
||||||
token=token['access_token'],
|
token=token['access_token'],
|
||||||
session_id=request.session_id,
|
session_id=request.session_id,
|
||||||
user=request.user,
|
user=request.user,
|
||||||
@@ -454,7 +454,6 @@ class JupyterHubRequestValidator(RequestValidator):
|
|||||||
request.user = orm_code.user
|
request.user = orm_code.user
|
||||||
request.session_id = orm_code.session_id
|
request.session_id = orm_code.session_id
|
||||||
request.scopes = [role.name for role in orm_code.roles]
|
request.scopes = [role.name for role in orm_code.roles]
|
||||||
request._jupyterhub_roles = orm_code.roles
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def validate_grant_type(
|
def validate_grant_type(
|
||||||
@@ -573,6 +572,7 @@ class JupyterHubRequestValidator(RequestValidator):
|
|||||||
app_log.debug(
|
app_log.debug(
|
||||||
f"Allowing request for role(s) for {client_id}: {','.join(requested_roles) or '[]'}"
|
f"Allowing request for role(s) for {client_id}: {','.join(requested_roles) or '[]'}"
|
||||||
)
|
)
|
||||||
|
# these will be stored on the OAuthCode object
|
||||||
request._jupyterhub_roles = [
|
request._jupyterhub_roles = [
|
||||||
client_allowed_roles[name]
|
client_allowed_roles[name]
|
||||||
for name in requested_roles
|
for name in requested_roles
|
||||||
|
@@ -9,8 +9,10 @@ from functools import partial
|
|||||||
from queue import Queue
|
from queue import Queue
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
from urllib.parse import parse_qs
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
|
import pytest
|
||||||
import requests
|
import requests
|
||||||
import requests_mock
|
import requests_mock
|
||||||
from pytest import raises
|
from pytest import raises
|
||||||
@@ -318,8 +320,48 @@ async def test_hubauth_service_token(app, mockservice_url):
|
|||||||
assert path.endswith('/hub/login')
|
assert path.endswith('/hub/login')
|
||||||
|
|
||||||
|
|
||||||
async def test_oauth_service(app, mockservice_url):
|
@pytest.mark.parametrize(
|
||||||
|
"client_allowed_roles, request_roles, expected_roles",
|
||||||
|
[
|
||||||
|
# allow empty roles
|
||||||
|
([], [], []),
|
||||||
|
# allow original 'identify' scope to map to no role
|
||||||
|
([], ["identify"], []),
|
||||||
|
# requesting roles outside client list doesn't work
|
||||||
|
([], ["admin"], None),
|
||||||
|
([], ["token"], None),
|
||||||
|
# requesting nonexistent roles fails in the same way (no server error)
|
||||||
|
([], ["nosuchrole"], None),
|
||||||
|
# requesting exactly client allow list works
|
||||||
|
(["user"], ["user"], ["user"]),
|
||||||
|
# no explicit request, defaults to all
|
||||||
|
(["token", "user"], [], ["token", "user"]),
|
||||||
|
# explicit 'identify' maps to none
|
||||||
|
(["token", "user"], ["identify"], []),
|
||||||
|
# any item outside the list isn't allowed
|
||||||
|
(["token", "user"], ["token", "server"], None),
|
||||||
|
# reuesting subset
|
||||||
|
(["admin", "user"], ["user"], ["user"]),
|
||||||
|
(["user", "token", "server"], ["token", "user"], ["token", "user"]),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_oauth_service(
|
||||||
|
app,
|
||||||
|
mockservice_url,
|
||||||
|
client_allowed_roles,
|
||||||
|
request_roles,
|
||||||
|
expected_roles,
|
||||||
|
):
|
||||||
service = mockservice_url
|
service = mockservice_url
|
||||||
|
oauth_client = (
|
||||||
|
app.db.query(orm.OAuthClient)
|
||||||
|
.filter_by(identifier=service.oauth_client_id)
|
||||||
|
.one()
|
||||||
|
)
|
||||||
|
oauth_client.allowed_roles = [
|
||||||
|
orm.Role.find(app.db, role_name) for role_name in client_allowed_roles
|
||||||
|
]
|
||||||
|
app.db.commit()
|
||||||
url = url_path_join(public_url(app, mockservice_url) + 'owhoami/?arg=x')
|
url = url_path_join(public_url(app, mockservice_url) + 'owhoami/?arg=x')
|
||||||
# first request is only going to login and get us to the oauth form page
|
# first request is only going to login and get us to the oauth form page
|
||||||
s = AsyncSession()
|
s = AsyncSession()
|
||||||
@@ -334,7 +376,18 @@ async def test_oauth_service(app, mockservice_url):
|
|||||||
assert set(r.history[0].cookies.keys()) == {'service-%s-oauth-state' % service.name}
|
assert set(r.history[0].cookies.keys()) == {'service-%s-oauth-state' % service.name}
|
||||||
|
|
||||||
# submit the oauth form to complete authorization
|
# submit the oauth form to complete authorization
|
||||||
r = await s.post(r.url, data={'scopes': ['identify']}, headers={'Referer': r.url})
|
data = {}
|
||||||
|
if request_roles:
|
||||||
|
data["scopes"] = request_roles
|
||||||
|
r = await s.post(r.url, data=data, headers={'Referer': r.url})
|
||||||
|
if expected_roles is None:
|
||||||
|
# expected failed auth, stop here
|
||||||
|
# verify expected 'invalid scope' error, not server error
|
||||||
|
dest_url, _, query = r.url.partition("?")
|
||||||
|
assert dest_url == public_url(app, mockservice_url) + "oauth_callback"
|
||||||
|
assert parse_qs(query).get("error") == ["invalid_scope"]
|
||||||
|
assert r.status_code == 400
|
||||||
|
return
|
||||||
r.raise_for_status()
|
r.raise_for_status()
|
||||||
assert r.url == url
|
assert r.url == url
|
||||||
# verify oauth cookie is set
|
# verify oauth cookie is set
|
||||||
|
Reference in New Issue
Block a user