forked from Github-Mirrors/canaille
tests: extracted the prompt tests in a dedicated file
This commit is contained in:
parent
f7162e4a5a
commit
9ff0411e9e
4 changed files with 111 additions and 75 deletions
|
@ -55,9 +55,8 @@ def authorize():
|
||||||
abort(400, "Invalid client.")
|
abort(400, "Invalid client.")
|
||||||
|
|
||||||
user = current_user()
|
user = current_user()
|
||||||
scopes = client.get_allowed_scope(request.args.get("scope", "").split(" ")).split(
|
requested_scopes = request.args.get("scope", "").split(" ")
|
||||||
" "
|
allowed_scopes = client.get_allowed_scope(requested_scopes).split(" ")
|
||||||
)
|
|
||||||
|
|
||||||
# LOGIN
|
# LOGIN
|
||||||
|
|
||||||
|
@ -69,7 +68,9 @@ def authorize():
|
||||||
return redirect(url_for("core.auth.login"))
|
return redirect(url_for("core.auth.login"))
|
||||||
|
|
||||||
if not user.can_use_oidc:
|
if not user.can_use_oidc:
|
||||||
abort(403, "User does not have the permission to achieve OIDC authentication.")
|
abort(
|
||||||
|
403, "The user does not have the permission to achieve OIDC authentication."
|
||||||
|
)
|
||||||
|
|
||||||
# CONSENT
|
# CONSENT
|
||||||
|
|
||||||
|
@ -82,7 +83,9 @@ def authorize():
|
||||||
if request.method == "GET":
|
if request.method == "GET":
|
||||||
if (
|
if (
|
||||||
(client.preconsent and (not consent or not consent.revoked))
|
(client.preconsent and (not consent or not consent.revoked))
|
||||||
or (consent and all(scope in set(consent.scope) for scope in scopes))
|
or (
|
||||||
|
consent and all(scope in set(consent.scope) for scope in allowed_scopes)
|
||||||
|
)
|
||||||
and not consent.revoked
|
and not consent.revoked
|
||||||
):
|
):
|
||||||
return authorization.create_authorization_response(grant_user=user)
|
return authorization.create_authorization_response(grant_user=user)
|
||||||
|
@ -125,14 +128,14 @@ def authorize():
|
||||||
if consent.revoked:
|
if consent.revoked:
|
||||||
consent.restore()
|
consent.restore()
|
||||||
consent.scope = client.get_allowed_scope(
|
consent.scope = client.get_allowed_scope(
|
||||||
list(set(scopes + consents[0].scope))
|
list(set(allowed_scopes + consents[0].scope))
|
||||||
).split(" ")
|
).split(" ")
|
||||||
else:
|
else:
|
||||||
consent = models.Consent(
|
consent = models.Consent(
|
||||||
consent_id=str(uuid.uuid4()),
|
consent_id=str(uuid.uuid4()),
|
||||||
client=client,
|
client=client,
|
||||||
subject=user,
|
subject=user,
|
||||||
scope=scopes,
|
scope=allowed_scopes,
|
||||||
issue_date=datetime.datetime.now(datetime.timezone.utc),
|
issue_date=datetime.datetime.now(datetime.timezone.utc),
|
||||||
)
|
)
|
||||||
consent.save()
|
consent.save()
|
||||||
|
|
|
@ -76,6 +76,7 @@ def openid_configuration():
|
||||||
],
|
],
|
||||||
"subject_types_supported": ["pairwise", "public"],
|
"subject_types_supported": ["pairwise", "public"],
|
||||||
"id_token_signing_alg_values_supported": ["RS256", "ES256", "HS256"],
|
"id_token_signing_alg_values_supported": ["RS256", "ES256", "HS256"],
|
||||||
|
"prompt_values_supported": ["none"],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import uuid
|
|
||||||
from urllib.parse import parse_qs
|
from urllib.parse import parse_qs
|
||||||
from urllib.parse import urlsplit
|
from urllib.parse import urlsplit
|
||||||
|
|
||||||
|
@ -610,73 +609,6 @@ def test_authorization_code_flow_but_user_cannot_use_oidc(
|
||||||
res = res.follow(status=403)
|
res = res.follow(status=403)
|
||||||
|
|
||||||
|
|
||||||
def test_prompt_none(testclient, logged_user, client):
|
|
||||||
consent = models.Consent(
|
|
||||||
consent_id=str(uuid.uuid4()),
|
|
||||||
client=client,
|
|
||||||
subject=logged_user,
|
|
||||||
scope=["openid", "profile"],
|
|
||||||
)
|
|
||||||
consent.save()
|
|
||||||
|
|
||||||
res = testclient.get(
|
|
||||||
"/oauth/authorize",
|
|
||||||
params=dict(
|
|
||||||
response_type="code",
|
|
||||||
client_id=client.client_id,
|
|
||||||
scope="openid profile",
|
|
||||||
nonce="somenonce",
|
|
||||||
prompt="none",
|
|
||||||
),
|
|
||||||
status=302,
|
|
||||||
)
|
|
||||||
assert res.location.startswith(client.redirect_uris[0])
|
|
||||||
params = parse_qs(urlsplit(res.location).query)
|
|
||||||
assert "code" in params
|
|
||||||
|
|
||||||
consent.delete()
|
|
||||||
|
|
||||||
|
|
||||||
def test_prompt_not_logged(testclient, user, client):
|
|
||||||
consent = models.Consent(
|
|
||||||
consent_id=str(uuid.uuid4()),
|
|
||||||
client=client,
|
|
||||||
subject=user,
|
|
||||||
scope=["openid", "profile"],
|
|
||||||
)
|
|
||||||
consent.save()
|
|
||||||
|
|
||||||
res = testclient.get(
|
|
||||||
"/oauth/authorize",
|
|
||||||
params=dict(
|
|
||||||
response_type="code",
|
|
||||||
client_id=client.client_id,
|
|
||||||
scope="openid profile",
|
|
||||||
nonce="somenonce",
|
|
||||||
prompt="none",
|
|
||||||
),
|
|
||||||
status=200,
|
|
||||||
)
|
|
||||||
assert "login_required" == res.json.get("error")
|
|
||||||
|
|
||||||
consent.delete()
|
|
||||||
|
|
||||||
|
|
||||||
def test_prompt_no_consent(testclient, logged_user, client):
|
|
||||||
res = testclient.get(
|
|
||||||
"/oauth/authorize",
|
|
||||||
params=dict(
|
|
||||||
response_type="code",
|
|
||||||
client_id=client.client_id,
|
|
||||||
scope="openid profile",
|
|
||||||
nonce="somenonce",
|
|
||||||
prompt="none",
|
|
||||||
),
|
|
||||||
status=200,
|
|
||||||
)
|
|
||||||
assert "consent_required" == res.json.get("error")
|
|
||||||
|
|
||||||
|
|
||||||
def test_nonce_required_in_oidc_requests(testclient, logged_user, client):
|
def test_nonce_required_in_oidc_requests(testclient, logged_user, client):
|
||||||
res = testclient.get(
|
res = testclient.get(
|
||||||
"/oauth/authorize",
|
"/oauth/authorize",
|
||||||
|
|
100
tests/oidc/test_authorization_prompt.py
Normal file
100
tests/oidc/test_authorization_prompt.py
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
"""
|
||||||
|
Tests the behavior of Canaille depending on the OIDC 'prompt' parameter.
|
||||||
|
https://openid.net/specs/openid-connect-core-1_0.html#AuthorizationEndpoint
|
||||||
|
"""
|
||||||
|
import uuid
|
||||||
|
from urllib.parse import parse_qs
|
||||||
|
from urllib.parse import urlsplit
|
||||||
|
|
||||||
|
from canaille.app import models
|
||||||
|
|
||||||
|
|
||||||
|
def test_prompt_none(testclient, logged_user, client):
|
||||||
|
"""
|
||||||
|
Nominal case with prompt=none
|
||||||
|
"""
|
||||||
|
consent = models.Consent(
|
||||||
|
consent_id=str(uuid.uuid4()),
|
||||||
|
client=client,
|
||||||
|
subject=logged_user,
|
||||||
|
scope=["openid", "profile"],
|
||||||
|
)
|
||||||
|
consent.save()
|
||||||
|
|
||||||
|
res = testclient.get(
|
||||||
|
"/oauth/authorize",
|
||||||
|
params=dict(
|
||||||
|
response_type="code",
|
||||||
|
client_id=client.client_id,
|
||||||
|
scope="openid profile",
|
||||||
|
nonce="somenonce",
|
||||||
|
prompt="none",
|
||||||
|
),
|
||||||
|
status=302,
|
||||||
|
)
|
||||||
|
assert res.location.startswith(client.redirect_uris[0])
|
||||||
|
params = parse_qs(urlsplit(res.location).query)
|
||||||
|
assert "code" in params
|
||||||
|
|
||||||
|
consent.delete()
|
||||||
|
|
||||||
|
|
||||||
|
def test_prompt_not_logged(testclient, user, client):
|
||||||
|
"""
|
||||||
|
prompt=none should return a login_required error when no
|
||||||
|
user is logged in.
|
||||||
|
|
||||||
|
login_required
|
||||||
|
The Authorization Server requires End-User authentication.
|
||||||
|
This error MAY be returned when the prompt parameter value in the
|
||||||
|
Authentication Request is none, but the Authentication Request
|
||||||
|
cannot be completed without displaying a user interface for End-User
|
||||||
|
authentication.
|
||||||
|
"""
|
||||||
|
consent = models.Consent(
|
||||||
|
consent_id=str(uuid.uuid4()),
|
||||||
|
client=client,
|
||||||
|
subject=user,
|
||||||
|
scope=["openid", "profile"],
|
||||||
|
)
|
||||||
|
consent.save()
|
||||||
|
|
||||||
|
res = testclient.get(
|
||||||
|
"/oauth/authorize",
|
||||||
|
params=dict(
|
||||||
|
response_type="code",
|
||||||
|
client_id=client.client_id,
|
||||||
|
scope="openid profile",
|
||||||
|
nonce="somenonce",
|
||||||
|
prompt="none",
|
||||||
|
),
|
||||||
|
status=200,
|
||||||
|
)
|
||||||
|
assert "login_required" == res.json.get("error")
|
||||||
|
|
||||||
|
consent.delete()
|
||||||
|
|
||||||
|
|
||||||
|
def test_prompt_no_consent(testclient, logged_user, client):
|
||||||
|
"""
|
||||||
|
prompt=none should return a consent_required error when user
|
||||||
|
are logged in but have not granted their consent.
|
||||||
|
|
||||||
|
consent_required
|
||||||
|
The Authorization Server requires End-User consent. This error MAY be
|
||||||
|
returned when the prompt parameter value in the Authentication Request
|
||||||
|
is none, but the Authentication Request cannot be completed without
|
||||||
|
displaying a user interface for End-User consent.
|
||||||
|
"""
|
||||||
|
res = testclient.get(
|
||||||
|
"/oauth/authorize",
|
||||||
|
params=dict(
|
||||||
|
response_type="code",
|
||||||
|
client_id=client.client_id,
|
||||||
|
scope="openid profile",
|
||||||
|
nonce="somenonce",
|
||||||
|
prompt="none",
|
||||||
|
),
|
||||||
|
status=200,
|
||||||
|
)
|
||||||
|
assert "consent_required" == res.json.get("error")
|
Loading…
Reference in a new issue