|
27 | 27 | import org.keycloak.OAuth2Constants; |
28 | 28 | import org.keycloak.OAuthErrorException; |
29 | 29 | import org.keycloak.TokenVerifier; |
30 | | -import org.keycloak.admin.client.resource.ClientResource; |
31 | 30 | import org.keycloak.admin.client.resource.UserResource; |
32 | 31 | import org.keycloak.common.Profile; |
33 | 32 | import org.keycloak.protocol.oidc.OIDCConfigAttributes; |
34 | 33 | import org.keycloak.representations.AccessToken; |
35 | 34 | import org.keycloak.representations.IDToken; |
36 | | -import org.keycloak.representations.idm.ClientRepresentation; |
37 | 35 | import org.keycloak.representations.idm.RealmRepresentation; |
38 | | -import org.keycloak.testsuite.AbstractKeycloakTest; |
| 36 | +import org.keycloak.services.clientpolicy.ClientPolicyEvent; |
| 37 | +import org.keycloak.services.clientpolicy.condition.ClientScopesConditionFactory; |
39 | 38 | import org.keycloak.testsuite.AssertEvents; |
40 | 39 | import org.keycloak.testsuite.admin.ApiUtil; |
41 | 40 | import org.keycloak.testsuite.arquillian.annotation.EnableFeature; |
42 | 41 | import org.keycloak.testsuite.arquillian.annotation.UncaughtServerErrorExpected; |
| 42 | +import org.keycloak.testsuite.client.policies.AbstractClientPoliciesTest; |
43 | 43 | import org.keycloak.testsuite.pages.ConsentPage; |
| 44 | +import org.keycloak.testsuite.services.clientpolicy.executor.TestRaiseExceptionExecutorFactory; |
44 | 45 | import org.keycloak.testsuite.updaters.ClientAttributeUpdater; |
| 46 | +import org.keycloak.testsuite.util.ClientPoliciesUtil; |
45 | 47 | import org.keycloak.testsuite.util.oauth.AccessTokenResponse; |
46 | 48 | import org.keycloak.testsuite.util.oauth.TokenExchangeRequest; |
47 | 49 | import org.keycloak.util.TokenUtil; |
|
56 | 58 | import static org.junit.Assert.assertNull; |
57 | 59 | import static org.keycloak.testsuite.admin.AbstractAdminTest.loadJson; |
58 | 60 | import static org.keycloak.testsuite.auth.page.AuthRealm.TEST; |
| 61 | +import static org.keycloak.testsuite.util.ClientPoliciesUtil.createClientScopesConditionConfig; |
| 62 | +import static org.keycloak.testsuite.util.ClientPoliciesUtil.createTestRaiseExeptionExecutorConfig; |
59 | 63 |
|
60 | 64 | /** |
61 | 65 | * @author <a href="mailto:[email protected]">Marek Posolda</a> |
62 | 66 | */ |
63 | 67 | @EnableFeature(value = Profile.Feature.TOKEN_EXCHANGE_STANDARD_V2, skipRestart = true) |
64 | | -public class StandardTokenExchangeV2Test extends AbstractKeycloakTest { |
| 68 | +public class StandardTokenExchangeV2Test extends AbstractClientPoliciesTest { |
65 | 69 |
|
66 | 70 | @Rule |
67 | 71 | public AssertEvents events = new AssertEvents(this); |
@@ -435,6 +439,10 @@ public void testScopeFilter() throws Exception { |
435 | 439 | assertEquals(OAuthErrorException.INVALID_REQUEST, response.getError()); |
436 | 440 | assertEquals("Requested audience not available: target-client2", response.getErrorDescription()); |
437 | 441 |
|
| 442 | + oauth.scope("optional-scope2"); |
| 443 | + response = tokenExchange(accessToken, "requester-client", "secret", List.of("target-client1"), null); |
| 444 | + assertAudiencesAndScopes(response, List.of("target-client1"), List.of("default-scope1")); |
| 445 | + |
438 | 446 | oauth.scope("optional-scope2"); |
439 | 447 | response = tokenExchange(accessToken, "requester-client", "secret", List.of("target-client2"), null); |
440 | 448 | assertAudiencesAndScopes(response, List.of("target-client2"), List.of("optional-scope2")); |
@@ -549,6 +557,40 @@ public void testOfflineAccessNotAllowed() throws Exception { |
549 | 557 | } |
550 | 558 | } |
551 | 559 |
|
| 560 | + @Test |
| 561 | + public void testClientPolicies() throws Exception { |
| 562 | + |
| 563 | + String json = (new ClientPoliciesUtil.ClientProfilesBuilder()).addProfile( |
| 564 | + (new ClientPoliciesUtil.ClientProfileBuilder()).createProfile(PROFILE_NAME, "Profilo") |
| 565 | + .addExecutor(TestRaiseExceptionExecutorFactory.PROVIDER_ID, |
| 566 | + createTestRaiseExeptionExecutorConfig(List.of(ClientPolicyEvent.TOKEN_EXCHANGE_REQUEST))) |
| 567 | + .toRepresentation() |
| 568 | + ).toString(); |
| 569 | + updateProfiles(json); |
| 570 | + |
| 571 | + // register policy with condition on client scope optional-scope2 |
| 572 | + json = (new ClientPoliciesUtil.ClientPoliciesBuilder()).addPolicy( |
| 573 | + (new ClientPoliciesUtil.ClientPolicyBuilder()).createPolicy(POLICY_NAME, "Client Scope Policy", Boolean.TRUE) |
| 574 | + .addCondition(ClientScopesConditionFactory.PROVIDER_ID, |
| 575 | + createClientScopesConditionConfig(ClientScopesConditionFactory.ANY, List.of("optional-scope2"))) |
| 576 | + .addProfile(PROFILE_NAME) |
| 577 | + .toRepresentation() |
| 578 | + ).toString(); |
| 579 | + updatePolicies(json); |
| 580 | + |
| 581 | + String accessToken = resourceOwnerLogin("john", "password", "subject-client", "secret"); |
| 582 | + |
| 583 | + AccessTokenResponse response = tokenExchange(accessToken, "requester-client", "secret", List.of("target-client1"), null); |
| 584 | + assertAudiencesAndScopes(response, List.of("target-client1"), List.of("default-scope1")); |
| 585 | + |
| 586 | + //block token exchange request if optional-scope2 is requested |
| 587 | + oauth.scope("optional-scope2"); |
| 588 | + response = tokenExchange(accessToken, "requester-client", "secret", List.of("target-client2"), null); |
| 589 | + assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), response.getStatusCode()); |
| 590 | + assertEquals(ClientPolicyEvent.TOKEN_EXCHANGE_REQUEST.toString(), response.getError()); |
| 591 | + assertEquals("Exception thrown intentionally", response.getErrorDescription()); |
| 592 | + } |
| 593 | + |
552 | 594 | private void assertAudiences(AccessToken token, List<String> expectedAudiences) { |
553 | 595 | MatcherAssert.assertThat("Incompatible audiences", token.getAudience() == null ? List.of() : List.of(token.getAudience()), containsInAnyOrder(expectedAudiences.toArray())); |
554 | 596 | MatcherAssert.assertThat("Incompatible resource access", token.getResourceAccess().keySet(), containsInAnyOrder(expectedAudiences.toArray())); |
|
0 commit comments