|
31 | 31 |
|
32 | 32 | CLIENT_NOT_FOUND_REGEX = '404: b\'{"error":"Client not found".*}\'' |
33 | 33 | CLIENT_SCOPE_NOT_FOUND_REGEX = '404: b\'{"error":"Client scope not found".*}\'' |
| 34 | +CONSENT_NOT_FOUND_REGEX = '404: b\'{"error":"Consent nor offline token not found".*}\'' |
34 | 35 | COULD_NOT_FIND_ROLE_REGEX = '404: b\'{"error":"Could not find role".*}\'' |
35 | 36 | COULD_NOT_FIND_ROLE_WITH_ID_REGEX = '404: b\'{"error":"Could not find role with id".*}\'' |
36 | 37 | HTTP_404_REGEX = '404: b\'{"error":"HTTP 404 Not Found".*}\'' |
@@ -3548,6 +3549,57 @@ def test_refresh_token(admin: KeycloakAdmin) -> None: |
3548 | 3549 | admin.connection.refresh_token() |
3549 | 3550 |
|
3550 | 3551 |
|
| 3552 | +def test_consents( |
| 3553 | + admin: KeycloakAdmin, oid_with_credentials: tuple[KeycloakOpenID, str, str] |
| 3554 | +) -> None: |
| 3555 | + """ |
| 3556 | + Test getting and revoking offline access via the consents API. |
| 3557 | +
|
| 3558 | + :param admin: Keycloak admin |
| 3559 | + :type admin: KeycloakAdmin |
| 3560 | + :param oid_with_credentials: Keycloak OpenID client with pre-configured user credentials |
| 3561 | + :type oid_with_credentials: Tuple[KeycloakOpenID, str, str] |
| 3562 | + """ |
| 3563 | + oid, username, password = oid_with_credentials |
| 3564 | + |
| 3565 | + # Use offline access as ersatz consent |
| 3566 | + offline_token = oid.token(username, password, scope="offline_access") |
| 3567 | + decoded_access_token = oid.decode_token(token=offline_token["access_token"]) |
| 3568 | + user_id = decoded_access_token["sub"] |
| 3569 | + |
| 3570 | + # Test get consents/offline access |
| 3571 | + res = admin.user_consents(user_id=user_id) |
| 3572 | + assert len(res) == 1, res |
| 3573 | + assert "additionalGrants" in res[0], res[0] |
| 3574 | + assert res[0]["additionalGrants"][0].get("key") == "Offline Token", res[0] |
| 3575 | + |
| 3576 | + # Test get consents fail |
| 3577 | + with pytest.raises(KeycloakGetError) as err: |
| 3578 | + admin.user_consents(user_id="non-existent-id") |
| 3579 | + assert err.match(USER_NOT_FOUND_REGEX) |
| 3580 | + |
| 3581 | + # Test revoke fails |
| 3582 | + with pytest.raises(KeycloakDeleteError) as err: |
| 3583 | + admin.revoke_consent(user_id="non-existent-id", client_id=oid.client_id) |
| 3584 | + assert err.match(USER_NOT_FOUND_REGEX) |
| 3585 | + |
| 3586 | + with pytest.raises(KeycloakDeleteError) as err: |
| 3587 | + admin.revoke_consent(user_id=user_id, client_id="non-existent-client") |
| 3588 | + assert err.match(CLIENT_NOT_FOUND_REGEX) |
| 3589 | + |
| 3590 | + # Test revoke offline access |
| 3591 | + res = admin.revoke_consent(user_id=user_id, client_id=oid.client_id) |
| 3592 | + assert res == {}, res |
| 3593 | + |
| 3594 | + res = admin.user_consents(user_id=user_id) |
| 3595 | + assert len(res) == 0, res |
| 3596 | + |
| 3597 | + # Test re-revoke fails |
| 3598 | + with pytest.raises(KeycloakDeleteError) as err: |
| 3599 | + admin.revoke_consent(user_id=user_id, client_id=oid.client_id) |
| 3600 | + assert err.match(CONSENT_NOT_FOUND_REGEX) |
| 3601 | + |
| 3602 | + |
3551 | 3603 | # async function start |
3552 | 3604 |
|
3553 | 3605 |
|
@@ -7199,6 +7251,58 @@ async def test_a_refresh_token(admin: KeycloakAdmin) -> None: |
7199 | 7251 | admin.connection.refresh_token() |
7200 | 7252 |
|
7201 | 7253 |
|
| 7254 | +@pytest.mark.asyncio |
| 7255 | +async def test_a_consents( |
| 7256 | + admin: KeycloakAdmin, oid_with_credentials: tuple[KeycloakOpenID, str, str] |
| 7257 | +) -> None: |
| 7258 | + """ |
| 7259 | + Test getting and revoking offline access via the consents API. |
| 7260 | +
|
| 7261 | + :param admin: Keycloak admin |
| 7262 | + :type admin: KeycloakAdmin |
| 7263 | + :param oid_with_credentials: Keycloak OpenID client with pre-configured user credentials |
| 7264 | + :type oid_with_credentials: Tuple[KeycloakOpenID, str, str] |
| 7265 | + """ |
| 7266 | + oid, username, password = oid_with_credentials |
| 7267 | + |
| 7268 | + # Use offline access as ersatz consent |
| 7269 | + offline_token = await oid.a_token(username, password, scope="offline_access") |
| 7270 | + decoded_access_token = await oid.a_decode_token(token=offline_token["access_token"]) |
| 7271 | + user_id = decoded_access_token["sub"] |
| 7272 | + |
| 7273 | + # Test get consents/offline access |
| 7274 | + res = await admin.a_user_consents(user_id=user_id) |
| 7275 | + assert len(res) == 1, res |
| 7276 | + assert "additionalGrants" in res[0], res[0] |
| 7277 | + assert res[0]["additionalGrants"][0].get("key") == "Offline Token", res[0] |
| 7278 | + |
| 7279 | + # Test get consents fail |
| 7280 | + with pytest.raises(KeycloakGetError) as err: |
| 7281 | + await admin.a_user_consents(user_id="non-existent-id") |
| 7282 | + assert err.match(USER_NOT_FOUND_REGEX) |
| 7283 | + |
| 7284 | + # Test revoke fails |
| 7285 | + with pytest.raises(KeycloakDeleteError) as err: |
| 7286 | + await admin.a_revoke_consent(user_id="non-existent-id", client_id=oid.client_id) |
| 7287 | + assert err.match(USER_NOT_FOUND_REGEX) |
| 7288 | + |
| 7289 | + with pytest.raises(KeycloakDeleteError) as err: |
| 7290 | + await admin.a_revoke_consent(user_id=user_id, client_id="non-existent-client") |
| 7291 | + assert err.match(CLIENT_NOT_FOUND_REGEX) |
| 7292 | + |
| 7293 | + # Test revoke offline access |
| 7294 | + res = await admin.a_revoke_consent(user_id=user_id, client_id=oid.client_id) |
| 7295 | + assert res == {}, res |
| 7296 | + |
| 7297 | + res = await admin.a_user_consents(user_id=user_id) |
| 7298 | + assert len(res) == 0, res |
| 7299 | + |
| 7300 | + # Test re-revoke fails |
| 7301 | + with pytest.raises(KeycloakDeleteError) as err: |
| 7302 | + await admin.a_revoke_consent(user_id=user_id, client_id=oid.client_id) |
| 7303 | + assert err.match(CONSENT_NOT_FOUND_REGEX) |
| 7304 | + |
| 7305 | + |
7202 | 7306 | def test_counter_part() -> None: |
7203 | 7307 | """Test that each function has its async counter part.""" |
7204 | 7308 | admin_methods = [func for func in dir(KeycloakAdmin) if callable(getattr(KeycloakAdmin, func))] |
|
0 commit comments