/* * Copyright 2016 Red Hat, Inc. and/or its affiliates * and other contributors as indicated by the @author tags. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.keycloak.testsuite.client; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.keycloak.admin.client.resource.RealmResource; import org.keycloak.client.registration.Auth; import org.keycloak.client.registration.ClientRegistrationException; import org.keycloak.client.registration.HttpErrorException; import org.keycloak.jose.jws.JWSInput; import org.keycloak.protocol.oidc.OIDCLoginProtocol; import org.keycloak.protocol.oidc.mappers.FullNameMapper; import org.keycloak.protocol.oidc.mappers.HardcodedRole; import org.keycloak.protocol.oidc.mappers.UserAttributeMapper; import org.keycloak.protocol.oidc.mappers.UserPropertyMapper; import org.keycloak.protocol.saml.mappers.UserAttributeStatementMapper; import org.keycloak.protocol.saml.mappers.UserPropertyAttributeStatementMapper; import org.keycloak.representations.idm.ClientInitialAccessCreatePresentation; import org.keycloak.representations.idm.ClientInitialAccessPresentation; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.ClientTemplateRepresentation; import org.keycloak.representations.idm.ComponentRepresentation; import org.keycloak.representations.idm.ComponentTypeRepresentation; import org.keycloak.representations.idm.ConfigPropertyRepresentation; import org.keycloak.representations.idm.ProtocolMapperRepresentation; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.oidc.OIDCClientRepresentation; import org.keycloak.services.clientregistration.RegistrationAccessToken; import org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy; import org.keycloak.services.clientregistration.policy.ClientRegistrationPolicyManager; import org.keycloak.services.clientregistration.policy.RegistrationAuth; import org.keycloak.services.clientregistration.policy.impl.ClientDisabledClientRegistrationPolicyFactory; import org.keycloak.services.clientregistration.policy.impl.ClientTemplatesClientRegistrationPolicyFactory; import org.keycloak.services.clientregistration.policy.impl.MaxClientsClientRegistrationPolicyFactory; import org.keycloak.services.clientregistration.policy.impl.ProtocolMappersClientRegistrationPolicyFactory; import org.keycloak.services.clientregistration.policy.impl.TrustedHostClientRegistrationPolicyFactory; import org.keycloak.testsuite.Assert; import org.keycloak.testsuite.admin.ApiUtil; import org.keycloak.util.JsonSerialization; import javax.ws.rs.core.Response; import static org.junit.Assert.assertTrue; /** * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a> */ public class ClientRegistrationPoliciesTest extends AbstractClientRegistrationTest { private static final String PRIVATE_KEY = "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y="; private static final String PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB"; @Override public void addTestRealms(List<RealmRepresentation> testRealms) { super.addTestRealms(testRealms); testRealms.get(0).setId(REALM_NAME); testRealms.get(0).setPrivateKey(PRIVATE_KEY); testRealms.get(0).setPublicKey(PUBLIC_KEY); } @After public void after() throws Exception { super.after(); // Default setup of trustedHostPolicy ComponentRepresentation trustedHostPolicy = findPolicyByProviderAndAuth(TrustedHostClientRegistrationPolicyFactory.PROVIDER_ID, getPolicyAnon()); trustedHostPolicy.getConfig().putSingle(TrustedHostClientRegistrationPolicyFactory.HOST_SENDING_REGISTRATION_REQUEST_MUST_MATCH, "true"); trustedHostPolicy.getConfig().putSingle(TrustedHostClientRegistrationPolicyFactory.CLIENT_URIS_MUST_MATCH, "true"); trustedHostPolicy.getConfig().put(TrustedHostClientRegistrationPolicyFactory.TRUSTED_HOSTS, Collections.emptyList()); realmResource().components().component(trustedHostPolicy.getId()).update(trustedHostPolicy); } private RealmResource realmResource() { return adminClient.realm(REALM_NAME); } private ClientRepresentation createRep(String clientId) { ClientRepresentation client = new ClientRepresentation(); client.setClientId(clientId); client.setSecret("test-secret"); return client; } private OIDCClientRepresentation createRepOidc() { return createRepOidc("http://localhost:8080/foo", "http://localhost:8080/foo"); } private OIDCClientRepresentation createRepOidc(String clientBaseUri, String clientRedirectUri) { OIDCClientRepresentation client = new OIDCClientRepresentation(); client.setClientName("RegistrationAccessTokenTest"); client.setClientUri(clientBaseUri); client.setRedirectUris(Collections.singletonList(clientRedirectUri)); return client; } public OIDCClientRepresentation create() throws ClientRegistrationException { OIDCClientRepresentation client = createRepOidc(); OIDCClientRepresentation response = reg.oidc().create(client); reg.auth(Auth.token(response)); return response; } private void assertOidcFail(ClientRegOp operation, OIDCClientRepresentation client, int expectedStatusCode) { assertOidcFail(operation, client, expectedStatusCode, null); } private void assertOidcFail(ClientRegOp operation, OIDCClientRepresentation client, int expectedStatusCode, String expectedErrorContains) { try { switch (operation) { case CREATE: reg.oidc().create(client); break; case UPDATE: reg.oidc().update(client); break; case DELETE: reg.oidc().delete(client); break; } Assert.fail("Not expected to successfuly run operation " + operation.toString() + " on client"); } catch (ClientRegistrationException expected) { HttpErrorException httpEx = (HttpErrorException) expected.getCause(); Assert.assertEquals(expectedStatusCode, httpEx.getStatusLine().getStatusCode()); if (expectedErrorContains != null) { assertTrue("Error response doesn't contain expected text. The error response text is: " + httpEx.getErrorResponse(), httpEx.getErrorResponse().contains(expectedErrorContains)); } } } private void assertFail(ClientRegOp operation, ClientRepresentation client, int expectedStatusCode, String expectedErrorContains) { try { switch (operation) { case CREATE: reg.create(client); break; case UPDATE: reg.update(client); break; case DELETE: reg.delete(client); break; } Assert.fail("Not expected to successfuly run operation " + operation.toString() + " on client"); } catch (ClientRegistrationException expected) { HttpErrorException httpEx = (HttpErrorException) expected.getCause(); Assert.assertEquals(expectedStatusCode, httpEx.getStatusLine().getStatusCode()); if (expectedErrorContains != null) { assertTrue("Error response doesn't contain expected text. The error response text is: " + httpEx.getErrorResponse(), httpEx.getErrorResponse().contains(expectedErrorContains)); } } } @Test public void testAnonCreateWithTrustedHost() throws Exception { // Failed to create client (untrusted host) OIDCClientRepresentation client = createRepOidc("http://root", "http://redirect"); assertOidcFail(ClientRegOp.CREATE, client, 403, "Host not trusted"); // Should still fail (bad redirect_uri) setTrustedHost("localhost"); assertOidcFail(ClientRegOp.CREATE, client, 403, "URL doesn't match"); // Should still fail (bad base_uri) client.setRedirectUris(Collections.singletonList("http://localhost:8080/foo")); assertOidcFail(ClientRegOp.CREATE, client, 403, "URL doesn't match"); // Success create client client.setClientUri("http://localhost:8080/foo"); OIDCClientRepresentation oidcClientRep = reg.oidc().create(client); // Test registration access token assertRegAccessToken(oidcClientRep.getRegistrationAccessToken(), RegistrationAuth.ANONYMOUS); } @Test public void testAnonUpdateWithTrustedHost() throws Exception { setTrustedHost("localhost"); OIDCClientRepresentation client = create(); // Fail update client client.setRedirectUris(Collections.singletonList("http://bad:8080/foo")); assertOidcFail(ClientRegOp.UPDATE, client, 403, "URL doesn't match"); // Should be fine now client.setRedirectUris(Collections.singletonList("http://localhost:8080/foo")); reg.oidc().update(client); } @Test public void testRedirectUriWithDomain() throws Exception { // Change the policy to avoid checking hosts ComponentRepresentation trustedHostPolicyRep = findPolicyByProviderAndAuth(TrustedHostClientRegistrationPolicyFactory.PROVIDER_ID, getPolicyAnon()); trustedHostPolicyRep.getConfig().putSingle(TrustedHostClientRegistrationPolicyFactory.HOST_SENDING_REGISTRATION_REQUEST_MUST_MATCH, "false"); // Configure some trusted host and domain trustedHostPolicyRep.getConfig().put(TrustedHostClientRegistrationPolicyFactory.TRUSTED_HOSTS, Arrays.asList("www.host.com", "*.example.com")); realmResource().components().component(trustedHostPolicyRep.getId()).update(trustedHostPolicyRep); // Verify client can be created with the redirectUri from trusted host and domain OIDCClientRepresentation oidcClientRep = createRepOidc("http://www.host.com", "http://www.example.com"); reg.oidc().create(oidcClientRep); // Remove domain from the config trustedHostPolicyRep.getConfig().put(TrustedHostClientRegistrationPolicyFactory.TRUSTED_HOSTS, Arrays.asList("www.host.com", "www1.example.com")); realmResource().components().component(trustedHostPolicyRep.getId()).update(trustedHostPolicyRep); // Check new client can't be created anymore oidcClientRep = createRepOidc("http://www.host.com", "http://www.example.com"); assertOidcFail(ClientRegOp.CREATE, oidcClientRep, 403, "URL doesn't match"); } @Test public void testAnonConsentRequired() throws Exception { setTrustedHost("localhost"); OIDCClientRepresentation client = create(); // Assert new client has consent required String clientId = client.getClientId(); ClientRepresentation clientRep = ApiUtil.findClientByClientId(realmResource(), clientId).toRepresentation(); Assert.assertTrue(clientRep.isConsentRequired()); // Try update with disabled consent required. Should fail clientRep.setConsentRequired(false); assertFail(ClientRegOp.UPDATE, clientRep, 403, "Not permitted to update consentRequired to false"); // Try update with enabled consent required. Should pass clientRep.setConsentRequired(true); reg.update(clientRep); } @Test public void testAnonFullScopeAllowed() throws Exception { setTrustedHost("localhost"); OIDCClientRepresentation client = create(); // Assert new client has fullScopeAllowed disabled String clientId = client.getClientId(); ClientRepresentation clientRep = ApiUtil.findClientByClientId(realmResource(), clientId).toRepresentation(); Assert.assertFalse(clientRep.isFullScopeAllowed()); // Try update with disabled consent required. Should fail clientRep.setFullScopeAllowed(true); assertFail(ClientRegOp.UPDATE, clientRep, 403, "Not permitted to enable fullScopeAllowed"); // Try update with enabled consent required. Should pass clientRep.setFullScopeAllowed(false); reg.update(clientRep); } @Test public void testClientDisabledPolicy() throws Exception { setTrustedHost("localhost"); // Assert new client is enabled OIDCClientRepresentation client = create(); String clientId = client.getClientId(); ClientRepresentation clientRep = ApiUtil.findClientByClientId(realmResource(), clientId).toRepresentation(); Assert.assertTrue(clientRep.isEnabled()); // Add client-disabled policy ComponentRepresentation rep = new ComponentRepresentation(); rep.setName("Clients disabled"); rep.setParentId(REALM_NAME); rep.setProviderId(ClientDisabledClientRegistrationPolicyFactory.PROVIDER_ID); rep.setProviderType(ClientRegistrationPolicy.class.getName()); rep.setSubType(getPolicyAnon()); Response response = realmResource().components().add(rep); String policyId = ApiUtil.getCreatedId(response); response.close(); // Assert new client is disabled client = create(); clientId = client.getClientId(); clientRep = ApiUtil.findClientByClientId(realmResource(), clientId).toRepresentation(); Assert.assertFalse(clientRep.isEnabled()); // Try enable client. Should fail clientRep.setEnabled(true); assertFail(ClientRegOp.UPDATE, clientRep, 403, "Not permitted to enable client"); // Try update disabled client. Should pass clientRep.setEnabled(false); reg.update(clientRep); // Revert realmResource().components().component(policyId).remove(); } @Test public void testMaxClientsPolicy() throws Exception { setTrustedHost("localhost"); int clientsCount = realmResource().clients().findAll().size(); int newClientsLimit = clientsCount + 1; // Allow to create one more client to current limit ComponentRepresentation maxClientsPolicyRep = findPolicyByProviderAndAuth(MaxClientsClientRegistrationPolicyFactory.PROVIDER_ID, getPolicyAnon()); maxClientsPolicyRep.getConfig().putSingle(MaxClientsClientRegistrationPolicyFactory.MAX_CLIENTS, String.valueOf(newClientsLimit)); realmResource().components().component(maxClientsPolicyRep.getId()).update(maxClientsPolicyRep); // I can register one new client OIDCClientRepresentation client = create(); // I can't register more clients assertOidcFail(ClientRegOp.CREATE, createRepOidc(), 403, "It's allowed to have max " + newClientsLimit + " clients per realm"); // Revert maxClientsPolicyRep.getConfig().putSingle(MaxClientsClientRegistrationPolicyFactory.MAX_CLIENTS, String.valueOf(10000)); realmResource().components().component(maxClientsPolicyRep.getId()).update(maxClientsPolicyRep); } @Test public void testProviders() throws Exception { List<ComponentTypeRepresentation> reps = realmResource().clientRegistrationPolicy().getProviders(); Map<String, ComponentTypeRepresentation> providersMap = reps.stream().collect(Collectors.toMap((ComponentTypeRepresentation rep) -> { return rep.getId(); }, (ComponentTypeRepresentation rep) -> { return rep; })); // test that ProtocolMappersClientRegistrationPolicy provider contains available protocol mappers ComponentTypeRepresentation protMappersRep = providersMap.get(ProtocolMappersClientRegistrationPolicyFactory.PROVIDER_ID); List<String> availableMappers = getProviderConfigProperty(protMappersRep, ProtocolMappersClientRegistrationPolicyFactory.ALLOWED_PROTOCOL_MAPPER_TYPES); List<String> someExpectedMappers = Arrays.asList(UserAttributeStatementMapper.PROVIDER_ID, UserAttributeMapper.PROVIDER_ID, UserPropertyAttributeStatementMapper.PROVIDER_ID, UserPropertyMapper.PROVIDER_ID, HardcodedRole.PROVIDER_ID); availableMappers.containsAll(someExpectedMappers); // test that clientTemplate provider doesn't contain any client templates yet ComponentTypeRepresentation clientTemplateRep = providersMap.get(ClientTemplatesClientRegistrationPolicyFactory.PROVIDER_ID); List<String> clientTemplates = getProviderConfigProperty(clientTemplateRep, ClientTemplatesClientRegistrationPolicyFactory.ALLOWED_CLIENT_TEMPLATES); Assert.assertTrue(clientTemplates.isEmpty()); // Add some clientTemplates ClientTemplateRepresentation clientTemplate = new ClientTemplateRepresentation(); clientTemplate.setName("foo"); Response response = realmResource().clientTemplates().create(clientTemplate); String fooTemplateId = ApiUtil.getCreatedId(response); response.close(); clientTemplate = new ClientTemplateRepresentation(); clientTemplate.setName("bar"); response = realmResource().clientTemplates().create(clientTemplate); String barTemplateId = ApiUtil.getCreatedId(response); response.close(); // send request again and test that clientTemplate provider contains added client templates reps = realmResource().clientRegistrationPolicy().getProviders(); clientTemplateRep = reps.stream().filter((ComponentTypeRepresentation rep1) -> { return rep1.getId().equals(ClientTemplatesClientRegistrationPolicyFactory.PROVIDER_ID); }).findFirst().get(); clientTemplates = getProviderConfigProperty(clientTemplateRep, ClientTemplatesClientRegistrationPolicyFactory.ALLOWED_CLIENT_TEMPLATES); Assert.assertNames(clientTemplates, "foo", "bar"); // Revert client templates realmResource().clientTemplates().get(fooTemplateId).remove(); realmResource().clientTemplates().get(barTemplateId).remove(); } private List<String> getProviderConfigProperty(ComponentTypeRepresentation provider, String expectedConfigPropName) { Assert.assertNotNull(provider); List<ConfigPropertyRepresentation> list = provider.getProperties(); list = list.stream().filter((ConfigPropertyRepresentation rep) -> { return rep.getName().equals(expectedConfigPropName); }).collect(Collectors.toList()); Assert.assertEquals(list.size(), 1); ConfigPropertyRepresentation allowedProtocolMappers = list.get(0); Assert.assertEquals(allowedProtocolMappers.getName(), expectedConfigPropName); return allowedProtocolMappers.getOptions(); } @Test public void testClientTemplatesPolicy() throws Exception { setTrustedHost("localhost"); // Add some clientTemplate through Admin REST ClientTemplateRepresentation clientTemplate = new ClientTemplateRepresentation(); clientTemplate.setName("foo"); Response response = realmResource().clientTemplates().create(clientTemplate); String clientTemplateId = ApiUtil.getCreatedId(response); response.close(); // I can't register new client with this template ClientRepresentation clientRep = createRep("test-app"); clientRep.setClientTemplate("foo"); assertFail(ClientRegOp.CREATE, clientRep, 403, "Not permitted to use specified clientTemplate"); // Register client without template - should success clientRep.setClientTemplate(null); ClientRepresentation registeredClient = reg.create(clientRep); reg.auth(Auth.token(registeredClient)); // Try to update client with template - should fail registeredClient.setClientTemplate("foo"); assertFail(ClientRegOp.UPDATE, registeredClient, 403, "Not permitted to use specified clientTemplate"); // Update client with the clientTemplate via Admin REST ClientRepresentation client = ApiUtil.findClientByClientId(realmResource(), "test-app").toRepresentation(); client.setClientTemplate("foo"); realmResource().clients().get(client.getId()).update(client); // Now the update via clientRegistration is permitted too as template was already set reg.update(registeredClient); // Revert client template realmResource().clients().get(client.getId()).remove(); realmResource().clientTemplates().get(clientTemplateId).remove(); } @Test public void testClientTemplatesPolicyWithPermittedTemplate() throws Exception { setTrustedHost("localhost"); // Add some clientTemplate through Admin REST ClientTemplateRepresentation clientTemplate = new ClientTemplateRepresentation(); clientTemplate.setName("foo"); Response response = realmResource().clientTemplates().create(clientTemplate); String clientTemplateId = ApiUtil.getCreatedId(response); response.close(); // I can't register new client with this template ClientRepresentation clientRep = createRep("test-app"); clientRep.setClientTemplate("foo"); assertFail(ClientRegOp.CREATE, clientRep, 403, "Not permitted to use specified clientTemplate"); // Update the policy to allow the "foo" template ComponentRepresentation clientTemplatesPolicyRep = findPolicyByProviderAndAuth(ClientTemplatesClientRegistrationPolicyFactory.PROVIDER_ID, getPolicyAnon()); clientTemplatesPolicyRep.getConfig().putSingle(ClientTemplatesClientRegistrationPolicyFactory.ALLOWED_CLIENT_TEMPLATES, "foo"); realmResource().components().component(clientTemplatesPolicyRep.getId()).update(clientTemplatesPolicyRep); // Check that I can register client now ClientRepresentation registeredClient = reg.create(clientRep); Assert.assertNotNull(registeredClient.getRegistrationAccessToken()); // Revert client template ApiUtil.findClientResourceByClientId(realmResource(), "test-app").remove(); realmResource().clientTemplates().get(clientTemplateId).remove(); } // PROTOCOL MAPPERS @Test public void testProtocolMappersCreate() throws Exception { setTrustedHost("localhost"); // Try to add client with some "hardcoded role" mapper. Should fail ClientRepresentation clientRep = createRep("test-app"); clientRep.setProtocolMappers(Collections.singletonList(createHardcodedMapperRep())); assertFail(ClientRegOp.CREATE, clientRep, 403, "ProtocolMapper type not allowed"); // Try the same authenticated. Should still fail. ClientInitialAccessPresentation token = adminClient.realm(REALM_NAME).clientInitialAccess().create(new ClientInitialAccessCreatePresentation(0, 10)); reg.auth(Auth.token(token)); assertFail(ClientRegOp.CREATE, clientRep, 403, "ProtocolMapper type not allowed"); // Update the "authenticated" policy and allow hardcoded role mapper ComponentRepresentation protocolMapperPolicyRep = findPolicyByProviderAndAuth(ProtocolMappersClientRegistrationPolicyFactory.PROVIDER_ID, getPolicyAuth()); protocolMapperPolicyRep.getConfig().add(ProtocolMappersClientRegistrationPolicyFactory.ALLOWED_PROTOCOL_MAPPER_TYPES, HardcodedRole.PROVIDER_ID); realmResource().components().component(protocolMapperPolicyRep.getId()).update(protocolMapperPolicyRep); // Check authenticated registration is permitted ClientRepresentation registeredClient = reg.create(clientRep); Assert.assertNotNull(registeredClient.getRegistrationAccessToken()); // Check "anonymous" registration still fails clientRep = createRep("test-app-2"); clientRep.setProtocolMappers(Collections.singletonList(createHardcodedMapperRep())); reg.auth(null); assertFail(ClientRegOp.CREATE, clientRep, 403, "ProtocolMapper type not allowed"); // Revert policy change ApiUtil.findClientResourceByClientId(realmResource(), "test-app").remove(); protocolMapperPolicyRep.getConfig().remove(ProtocolMappersClientRegistrationPolicyFactory.ALLOWED_PROTOCOL_MAPPER_TYPES, HardcodedRole.PROVIDER_ID); realmResource().components().component(protocolMapperPolicyRep.getId()).update(protocolMapperPolicyRep); } private ProtocolMapperRepresentation createHardcodedMapperRep() { ProtocolMapperRepresentation protocolMapper = new ProtocolMapperRepresentation(); protocolMapper.setName("Hardcoded foo role"); protocolMapper.setProtocolMapper(HardcodedRole.PROVIDER_ID); protocolMapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL); protocolMapper.setConsentRequired(false); protocolMapper.setConsentText(null); protocolMapper.getConfig().put(HardcodedRole.ROLE_CONFIG, "foo-role"); return protocolMapper; } @Test public void testProtocolMappersUpdate() throws Exception { setTrustedHost("localhost"); // Check I can add client with allowed protocolMappers ProtocolMapperRepresentation protocolMapper = new ProtocolMapperRepresentation(); protocolMapper.setName("Full name"); protocolMapper.setProtocolMapper(FullNameMapper.PROVIDER_ID); protocolMapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL); protocolMapper.setConsentRequired(true); protocolMapper.setConsentText("Full name"); ClientRepresentation clientRep = createRep("test-app"); clientRep.setProtocolMappers(Collections.singletonList(protocolMapper)); ClientRepresentation registeredClient = reg.create(clientRep); reg.auth(Auth.token(registeredClient)); // Add some disallowed protocolMapper registeredClient.getProtocolMappers().add(createHardcodedMapperRep()); // Check I can't update client because of protocolMapper assertFail(ClientRegOp.UPDATE, registeredClient, 403, "ProtocolMapper type not allowed"); // Remove "bad" protocolMapper registeredClient.getProtocolMappers().removeIf((ProtocolMapperRepresentation mapper) -> { return mapper.getProtocolMapper().equals(HardcodedRole.PROVIDER_ID); }); // Check I can update client now reg.update(registeredClient); // Revert client ApiUtil.findClientResourceByClientId(realmResource(), "test-app").remove(); } @Test public void testProtocolMappersConsentRequired() throws Exception { setTrustedHost("localhost"); // Register client and assert it has builtin protocol mappers ClientRepresentation clientRep = createRep("test-app"); ClientRepresentation registeredClient = reg.create(clientRep); long usernamePropMappersCount = registeredClient.getProtocolMappers().stream().filter((ProtocolMapperRepresentation protocolMapper) -> { return protocolMapper.getProtocolMapper().equals(UserPropertyMapper.PROVIDER_ID); }).count(); Assert.assertTrue(usernamePropMappersCount > 0); // Remove USernamePropertyMapper from the policy configuration ComponentRepresentation protocolMapperPolicyRep = findPolicyByProviderAndAuth(ProtocolMappersClientRegistrationPolicyFactory.PROVIDER_ID, getPolicyAnon()); protocolMapperPolicyRep.getConfig().getList(ProtocolMappersClientRegistrationPolicyFactory.ALLOWED_PROTOCOL_MAPPER_TYPES).remove(UserPropertyMapper.PROVIDER_ID); realmResource().components().component(protocolMapperPolicyRep.getId()).update(protocolMapperPolicyRep); // Register another client. Assert it doesn't have builtin mappers anymore clientRep = createRep("test-app-2"); registeredClient = reg.create(clientRep); usernamePropMappersCount = registeredClient.getProtocolMappers().stream().filter((ProtocolMapperRepresentation protocolMapper) -> { return protocolMapper.getProtocolMapper().equals(UserPropertyMapper.PROVIDER_ID); }).count(); Assert.assertEquals(0, usernamePropMappersCount); // Revert ApiUtil.findClientResourceByClientId(realmResource(), "test-app").remove(); protocolMapperPolicyRep.getConfig().getList(ProtocolMappersClientRegistrationPolicyFactory.ALLOWED_PROTOCOL_MAPPER_TYPES).add(UserPropertyMapper.PROVIDER_ID); realmResource().components().component(protocolMapperPolicyRep.getId()).update(protocolMapperPolicyRep); } @Test public void testProtocolMappersRemoveBuiltins() throws Exception { setTrustedHost("localhost"); // Change policy to allow hardcoded mapper ComponentRepresentation protocolMapperPolicyRep = findPolicyByProviderAndAuth(ProtocolMappersClientRegistrationPolicyFactory.PROVIDER_ID, getPolicyAnon()); protocolMapperPolicyRep.getConfig().add(ProtocolMappersClientRegistrationPolicyFactory.ALLOWED_PROTOCOL_MAPPER_TYPES, HardcodedRole.PROVIDER_ID); realmResource().components().component(protocolMapperPolicyRep.getId()).update(protocolMapperPolicyRep); // Create client with hardcoded mapper ClientRepresentation clientRep = createRep("test-app"); clientRep.setProtocolMappers(Collections.singletonList(createHardcodedMapperRep())); ClientRepresentation registeredClient = reg.create(clientRep); Assert.assertEquals(1, registeredClient.getProtocolMappers().size()); ProtocolMapperRepresentation hardcodedMapper = registeredClient.getProtocolMappers().get(0); Assert.assertTrue(hardcodedMapper.isConsentRequired()); Assert.assertEquals("Hardcoded foo role", hardcodedMapper.getConsentText()); // Revert ApiUtil.findClientResourceByClientId(realmResource(), "test-app").remove(); protocolMapperPolicyRep.getConfig().remove(ProtocolMappersClientRegistrationPolicyFactory.ALLOWED_PROTOCOL_MAPPER_TYPES, HardcodedRole.PROVIDER_ID); realmResource().components().component(protocolMapperPolicyRep.getId()).update(protocolMapperPolicyRep); } // HELPER METHODS private String getPolicyAnon() { return ClientRegistrationPolicyManager.getComponentTypeKey(RegistrationAuth.ANONYMOUS); } private String getPolicyAuth() { return ClientRegistrationPolicyManager.getComponentTypeKey(RegistrationAuth.AUTHENTICATED); } private ComponentRepresentation findPolicyByProviderAndAuth(String providerId, String authType) { // Change the policy to avoid checking hosts List<ComponentRepresentation> reps = realmResource().components().query(REALM_NAME, ClientRegistrationPolicy.class.getName()); for (ComponentRepresentation rep : reps) { if (rep.getSubType().equals(authType) && rep.getProviderId().equals(providerId)) { return rep; } } return null; } private void setTrustedHost(String hostname) { ComponentRepresentation trustedHostRep = findPolicyByProviderAndAuth(TrustedHostClientRegistrationPolicyFactory.PROVIDER_ID, getPolicyAnon()); trustedHostRep.getConfig().putSingle(TrustedHostClientRegistrationPolicyFactory.TRUSTED_HOSTS, hostname); realmResource().components().component(trustedHostRep.getId()).update(trustedHostRep); } private void assertRegAccessToken(String registrationAccessToken, RegistrationAuth expectedRegAuth) throws Exception { byte[] content = new JWSInput(registrationAccessToken).getContent(); RegistrationAccessToken regAccessToken = JsonSerialization.readValue(content, RegistrationAccessToken.class); Assert.assertEquals(regAccessToken.getRegistrationAuth(), expectedRegAuth.toString().toLowerCase()); } private enum ClientRegOp { CREATE, READ, UPDATE, DELETE } }