2024-10-14 12:04:39 +00:00
|
|
|
import logging
|
2023-02-14 17:43:43 +00:00
|
|
|
from urllib.parse import parse_qs
|
|
|
|
from urllib.parse import urlsplit
|
|
|
|
|
2023-04-09 09:37:04 +00:00
|
|
|
from canaille.app import models
|
2023-02-14 17:43:43 +00:00
|
|
|
|
|
|
|
from . import client_credentials
|
2022-12-10 10:24:53 +00:00
|
|
|
|
|
|
|
|
2020-09-17 10:01:21 +00:00
|
|
|
def test_no_logged_no_access(testclient):
|
|
|
|
testclient.get("/consent", status=403)
|
|
|
|
|
|
|
|
|
2024-10-14 12:04:39 +00:00
|
|
|
def test_revokation(testclient, client, consent, logged_user, token, backend, caplog):
|
2020-10-30 18:19:34 +00:00
|
|
|
res = testclient.get("/consent", status=200)
|
2023-03-16 15:25:14 +00:00
|
|
|
res.mustcontain(client.client_name)
|
|
|
|
res.mustcontain("Revoke access")
|
|
|
|
res.mustcontain(no="Restore access")
|
2023-02-14 17:43:43 +00:00
|
|
|
assert not consent.revoked
|
2020-09-17 10:01:21 +00:00
|
|
|
assert not token.revoked
|
|
|
|
|
2023-11-15 17:20:13 +00:00
|
|
|
res = testclient.get(f"/consent/revoke/{consent.consent_id}", status=302)
|
2023-01-28 18:02:00 +00:00
|
|
|
assert ("success", "The access has been revoked") in res.flashes
|
2024-10-14 12:04:39 +00:00
|
|
|
assert (
|
|
|
|
"canaille",
|
2024-10-21 09:17:55 +00:00
|
|
|
logging.SECURITY,
|
|
|
|
f"Consent revoked for {logged_user.user_name} in client {client.client_name} from unknown IP",
|
2024-10-14 12:04:39 +00:00
|
|
|
) in caplog.record_tuples
|
2020-10-30 22:41:02 +00:00
|
|
|
res = res.follow(status=200)
|
2023-03-16 15:25:14 +00:00
|
|
|
res.mustcontain(no="Revoke access")
|
|
|
|
res.mustcontain("Restore access")
|
2020-09-17 10:01:21 +00:00
|
|
|
|
2024-04-14 20:51:58 +00:00
|
|
|
backend.reload(consent)
|
2023-02-14 17:43:43 +00:00
|
|
|
assert consent.revoked
|
2024-04-14 20:51:58 +00:00
|
|
|
backend.reload(token)
|
2020-09-17 10:01:21 +00:00
|
|
|
assert token.revoked
|
2022-12-04 12:57:56 +00:00
|
|
|
|
|
|
|
|
2024-04-14 20:51:58 +00:00
|
|
|
def test_revokation_already_revoked(testclient, client, consent, logged_user, backend):
|
2023-02-14 17:43:43 +00:00
|
|
|
consent.revoke()
|
|
|
|
|
|
|
|
assert consent.revoked
|
|
|
|
|
2023-11-15 17:20:13 +00:00
|
|
|
res = testclient.get(f"/consent/revoke/{consent.consent_id}", status=302)
|
2023-02-14 17:43:43 +00:00
|
|
|
assert ("error", "The access is already revoked") in res.flashes
|
|
|
|
res = res.follow(status=200)
|
|
|
|
|
2024-04-14 20:51:58 +00:00
|
|
|
backend.reload(consent)
|
2023-02-14 17:43:43 +00:00
|
|
|
assert consent.revoked
|
|
|
|
|
2022-12-10 10:24:53 +00:00
|
|
|
|
2024-04-14 20:51:58 +00:00
|
|
|
def test_restoration(testclient, client, consent, logged_user, token, backend):
|
2023-02-14 17:43:43 +00:00
|
|
|
consent.revoke()
|
|
|
|
|
|
|
|
assert consent.revoked
|
2024-04-14 20:51:58 +00:00
|
|
|
backend.reload(token)
|
2022-12-10 10:24:53 +00:00
|
|
|
assert token.revoked
|
|
|
|
|
2023-11-15 17:20:13 +00:00
|
|
|
res = testclient.get(f"/consent/restore/{consent.consent_id}", status=302)
|
2023-02-14 17:43:43 +00:00
|
|
|
assert ("success", "The access has been restored") in res.flashes
|
2022-12-10 10:24:53 +00:00
|
|
|
res = res.follow(status=200)
|
|
|
|
|
2024-04-14 20:51:58 +00:00
|
|
|
backend.reload(consent)
|
2023-02-14 17:43:43 +00:00
|
|
|
assert not consent.revoked
|
2024-04-14 20:51:58 +00:00
|
|
|
backend.reload(token)
|
2022-12-10 10:24:53 +00:00
|
|
|
assert token.revoked
|
|
|
|
|
|
|
|
|
2023-02-14 17:43:43 +00:00
|
|
|
def test_restoration_already_restored(testclient, client, consent, logged_user, token):
|
|
|
|
assert not consent.revoked
|
|
|
|
|
2023-11-15 17:20:13 +00:00
|
|
|
res = testclient.get(f"/consent/restore/{consent.consent_id}", status=302)
|
2023-02-14 17:43:43 +00:00
|
|
|
assert ("error", "The access is not revoked") in res.flashes
|
|
|
|
res = res.follow(status=200)
|
|
|
|
|
|
|
|
|
|
|
|
def test_invalid_consent_revokation(testclient, client, logged_user):
|
2023-05-25 11:37:58 +00:00
|
|
|
res = testclient.get("/consent/revoke/invalid", status=302)
|
2023-01-28 18:02:00 +00:00
|
|
|
assert ("success", "The access has been revoked") not in res.flashes
|
2023-02-14 17:43:43 +00:00
|
|
|
assert ("error", "Could not revoke this access") in res.flashes
|
2022-12-04 12:57:56 +00:00
|
|
|
|
|
|
|
|
2023-02-14 17:43:43 +00:00
|
|
|
def test_someone_else_consent_revokation(testclient, client, consent, logged_moderator):
|
2023-11-15 17:20:13 +00:00
|
|
|
res = testclient.get(f"/consent/revoke/{consent.consent_id}", status=302)
|
2023-01-28 18:02:00 +00:00
|
|
|
assert ("success", "The access has been revoked") not in res.flashes
|
2023-02-14 17:43:43 +00:00
|
|
|
assert ("error", "Could not revoke this access") in res.flashes
|
|
|
|
|
|
|
|
|
|
|
|
def test_invalid_consent_restoration(testclient, client, logged_user):
|
2023-05-25 11:37:58 +00:00
|
|
|
res = testclient.get("/consent/restore/invalid", status=302)
|
2023-02-14 17:43:43 +00:00
|
|
|
assert ("success", "The access has been restored") not in res.flashes
|
|
|
|
assert ("error", "Could not restore this access") in res.flashes
|
|
|
|
|
|
|
|
|
|
|
|
def test_someone_else_consent_restoration(
|
|
|
|
testclient, client, consent, logged_moderator
|
|
|
|
):
|
2023-11-15 17:20:13 +00:00
|
|
|
res = testclient.get(f"/consent/restore/{consent.consent_id}", status=302)
|
2023-02-14 17:43:43 +00:00
|
|
|
assert ("success", "The access has been restore") not in res.flashes
|
|
|
|
assert ("error", "Could not restore this access") in res.flashes
|
|
|
|
|
|
|
|
|
|
|
|
def test_oidc_authorization_after_revokation(
|
2024-04-10 13:44:11 +00:00
|
|
|
testclient, logged_user, client, keypair, consent, backend
|
2023-02-14 17:43:43 +00:00
|
|
|
):
|
|
|
|
consent.revoke()
|
|
|
|
|
|
|
|
assert consent.revoked
|
|
|
|
|
|
|
|
res = testclient.get(
|
|
|
|
"/oauth/authorize",
|
|
|
|
params=dict(
|
|
|
|
response_type="code",
|
|
|
|
client_id=client.client_id,
|
|
|
|
scope="openid profile",
|
|
|
|
nonce="somenonce",
|
|
|
|
),
|
|
|
|
status=200,
|
|
|
|
)
|
|
|
|
|
|
|
|
res = res.form.submit(name="answer", value="accept", status=302)
|
|
|
|
|
2024-04-10 13:44:11 +00:00
|
|
|
consents = backend.query(models.Consent, client=client, subject=logged_user)
|
2024-04-14 20:51:58 +00:00
|
|
|
backend.reload(consent)
|
2023-03-08 22:53:53 +00:00
|
|
|
assert consents[0] == consent
|
2023-02-14 17:43:43 +00:00
|
|
|
assert not consent.revoked
|
|
|
|
|
|
|
|
params = parse_qs(urlsplit(res.location).query)
|
|
|
|
code = params["code"][0]
|
|
|
|
res = testclient.post(
|
|
|
|
"/oauth/token",
|
|
|
|
params=dict(
|
|
|
|
grant_type="authorization_code",
|
|
|
|
code=code,
|
|
|
|
scope="openid profile",
|
|
|
|
redirect_uri=client.redirect_uris[0],
|
|
|
|
),
|
|
|
|
headers={"Authorization": f"Basic {client_credentials(client)}"},
|
|
|
|
status=200,
|
|
|
|
)
|
|
|
|
|
|
|
|
access_token = res.json["access_token"]
|
2024-04-14 15:30:59 +00:00
|
|
|
token = backend.get(models.Token, access_token=access_token)
|
2023-03-08 22:53:53 +00:00
|
|
|
assert token.client == client
|
|
|
|
assert token.subject == logged_user
|
2023-02-14 20:55:46 +00:00
|
|
|
|
|
|
|
|
2024-04-14 18:31:43 +00:00
|
|
|
def test_preconsented_client_appears_in_consent_list(
|
|
|
|
testclient, client, logged_user, backend
|
|
|
|
):
|
2023-02-14 20:55:46 +00:00
|
|
|
assert not client.preconsent
|
2023-03-15 16:38:32 +00:00
|
|
|
res = testclient.get("/consent/pre-consents")
|
2023-03-16 15:25:14 +00:00
|
|
|
res.mustcontain(no=client.client_name)
|
2023-02-14 20:55:46 +00:00
|
|
|
|
|
|
|
client.preconsent = True
|
2024-04-14 18:31:43 +00:00
|
|
|
backend.save(client)
|
2023-02-14 20:55:46 +00:00
|
|
|
|
2023-03-15 16:38:32 +00:00
|
|
|
res = testclient.get("/consent/pre-consents")
|
2023-03-16 15:25:14 +00:00
|
|
|
res.mustcontain(client.client_name)
|
2023-02-14 20:55:46 +00:00
|
|
|
|
|
|
|
|
2024-04-14 15:30:59 +00:00
|
|
|
def test_revoke_preconsented_client(testclient, client, logged_user, token, backend):
|
2023-02-14 20:55:46 +00:00
|
|
|
client.preconsent = True
|
2024-04-14 18:31:43 +00:00
|
|
|
backend.save(client)
|
2024-04-14 15:30:59 +00:00
|
|
|
assert not backend.get(models.Consent)
|
2023-02-14 20:55:46 +00:00
|
|
|
assert not token.revoked
|
|
|
|
|
|
|
|
res = testclient.get(f"/consent/revoke-preconsent/{client.client_id}", status=302)
|
|
|
|
assert ("success", "The access has been revoked") in res.flashes
|
|
|
|
|
2024-04-14 15:30:59 +00:00
|
|
|
consent = backend.get(models.Consent)
|
2023-03-08 22:53:53 +00:00
|
|
|
assert consent.client == client
|
|
|
|
assert consent.subject == logged_user
|
2023-02-14 20:55:46 +00:00
|
|
|
assert consent.scope == ["openid", "email", "profile", "groups", "address", "phone"]
|
|
|
|
assert not consent.issue_date
|
2024-04-14 20:51:58 +00:00
|
|
|
backend.reload(token)
|
2023-02-14 20:55:46 +00:00
|
|
|
assert token.revoked
|
|
|
|
|
2023-11-15 17:20:13 +00:00
|
|
|
res = testclient.get(f"/consent/restore/{consent.consent_id}", status=302)
|
2023-02-14 20:55:46 +00:00
|
|
|
assert ("success", "The access has been restored") in res.flashes
|
|
|
|
|
2024-04-14 20:51:58 +00:00
|
|
|
backend.reload(consent)
|
2023-02-14 20:55:46 +00:00
|
|
|
assert not consent.revoked
|
|
|
|
assert consent.issue_date
|
2024-04-14 20:51:58 +00:00
|
|
|
backend.reload(token)
|
2023-02-14 20:55:46 +00:00
|
|
|
assert token.revoked
|
|
|
|
|
2023-11-15 17:20:13 +00:00
|
|
|
res = testclient.get(f"/consent/revoke/{consent.consent_id}", status=302)
|
2023-02-14 20:55:46 +00:00
|
|
|
assert ("success", "The access has been revoked") in res.flashes
|
2024-04-14 20:51:58 +00:00
|
|
|
backend.reload(consent)
|
2023-02-14 20:55:46 +00:00
|
|
|
assert consent.revoked
|
|
|
|
assert consent.issue_date
|
|
|
|
|
|
|
|
|
|
|
|
def test_revoke_invalid_preconsented_client(testclient, logged_user):
|
|
|
|
res = testclient.get("/consent/revoke-preconsent/invalid", status=302)
|
|
|
|
assert ("error", "Could not revoke this access") in res.flashes
|
|
|
|
|
|
|
|
|
|
|
|
def test_revoke_preconsented_client_with_manual_consent(
|
2024-04-14 18:31:43 +00:00
|
|
|
testclient, logged_user, client, consent, backend
|
2023-02-14 20:55:46 +00:00
|
|
|
):
|
|
|
|
client.preconsent = True
|
2024-04-14 18:31:43 +00:00
|
|
|
backend.save(client)
|
2023-02-14 20:55:46 +00:00
|
|
|
res = testclient.get(f"/consent/revoke-preconsent/{client.client_id}", status=302)
|
|
|
|
res = res.follow()
|
|
|
|
assert ("success", "The access has been revoked") in res.flashes
|
|
|
|
|
|
|
|
|
|
|
|
def test_revoke_preconsented_client_with_manual_revokation(
|
2024-04-14 18:31:43 +00:00
|
|
|
testclient, logged_user, client, consent, backend
|
2023-02-14 20:55:46 +00:00
|
|
|
):
|
|
|
|
client.preconsent = True
|
2024-04-14 18:31:43 +00:00
|
|
|
backend.save(client)
|
2023-02-14 20:55:46 +00:00
|
|
|
consent.revoke()
|
2024-04-14 18:31:43 +00:00
|
|
|
backend.save(consent)
|
2023-02-14 20:55:46 +00:00
|
|
|
|
|
|
|
res = testclient.get(f"/consent/revoke-preconsent/{client.client_id}", status=302)
|
|
|
|
res = res.follow()
|
|
|
|
assert ("error", "The access is already revoked") in res.flashes
|