forked from Github-Mirrors/canaille
219 lines
6.3 KiB
Python
219 lines
6.3 KiB
Python
"""Tests the behavior of Canaille depending on the OIDC 'prompt' parameter.
|
|
|
|
https://openid.net/specs/openid-connect-core-1_0.html#AuthorizationEndpoint
|
|
"""
|
|
|
|
import datetime
|
|
import uuid
|
|
from urllib.parse import parse_qs
|
|
from urllib.parse import urlsplit
|
|
|
|
from flask import url_for
|
|
|
|
from canaille.app import models
|
|
from canaille.core.endpoints.account import RegistrationPayload
|
|
|
|
|
|
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")
|
|
|
|
|
|
def test_prompt_create_logged(testclient, logged_user, client):
|
|
"""If prompt=create and user is already logged in, then go straight to the
|
|
consent page."""
|
|
testclient.app.config["ENABLE_REGISTRATION"] = True
|
|
|
|
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="create",
|
|
),
|
|
status=302,
|
|
)
|
|
assert res.location.startswith(client.redirect_uris[0])
|
|
|
|
consent.delete()
|
|
|
|
|
|
def test_prompt_create_registration_disabled(testclient, trusted_client, smtpd):
|
|
"""If prompt=create but Canaille registration is disabled, an error
|
|
response should be returned.
|
|
|
|
If the OpenID Provider receives a prompt value that it does not
|
|
support (not declared in the prompt_values_supported metadata field)
|
|
the OP SHOULD respond with an HTTP 400 (Bad Request) status code and
|
|
an error value of invalid_request. It is RECOMMENDED that the OP
|
|
return an error_description value identifying the invalid parameter
|
|
value.
|
|
"""
|
|
res = testclient.get(
|
|
"/oauth/authorize",
|
|
params=dict(
|
|
response_type="code",
|
|
client_id=trusted_client.client_id,
|
|
scope="openid profile",
|
|
nonce="somenonce",
|
|
prompt="create",
|
|
),
|
|
status=400,
|
|
)
|
|
assert res.json == {
|
|
"error": "invalid_request",
|
|
"error_description": "prompt 'create' value is not supported",
|
|
}
|
|
|
|
|
|
def test_prompt_create_not_logged(testclient, trusted_client, smtpd):
|
|
"""If prompt=create and user is not logged in, then display the
|
|
registration form.
|
|
|
|
Check that the user is correctly redirected to the client page after
|
|
the registration process.
|
|
"""
|
|
testclient.app.config["ENABLE_REGISTRATION"] = True
|
|
|
|
res = testclient.get(
|
|
"/oauth/authorize",
|
|
params=dict(
|
|
response_type="code",
|
|
client_id=trusted_client.client_id,
|
|
scope="openid profile",
|
|
nonce="somenonce",
|
|
prompt="create",
|
|
),
|
|
)
|
|
|
|
# Display the registration form
|
|
res = res.follow()
|
|
res.form["email"] = "foo@bar.com"
|
|
res = res.form.submit()
|
|
|
|
# Checks the registration mail is sent
|
|
assert len(smtpd.messages) == 1
|
|
|
|
# Simulate a click on the validation link in the mail
|
|
payload = RegistrationPayload(
|
|
creation_date_isoformat=datetime.datetime.now(
|
|
datetime.timezone.utc
|
|
).isoformat(),
|
|
user_name="",
|
|
user_name_editable=True,
|
|
email="foo@bar.com",
|
|
groups=[],
|
|
)
|
|
registration_url = url_for(
|
|
"core.account.registration",
|
|
data=payload.b64(),
|
|
hash=payload.build_hash(),
|
|
_external=True,
|
|
)
|
|
|
|
# Fill the user creation form
|
|
res = testclient.get(registration_url)
|
|
res.form["user_name"] = "newuser"
|
|
res.form["password1"] = "password"
|
|
res.form["password2"] = "password"
|
|
res.form["family_name"] = "newuser"
|
|
res = res.form.submit()
|
|
|
|
assert res.flashes == [
|
|
("success", "Your account has been created successfully."),
|
|
]
|
|
|
|
# Return to the client
|
|
res = res.follow()
|
|
assert res.location.startswith(trusted_client.redirect_uris[0])
|