mirror of
https://github.com/keycloak/keycloak.git
synced 2026-01-06 06:49:53 -06:00
Move IdentityProviderTest.java to the new testsuite
Part of: #34494 Signed-off-by: Lukas Hanusovsky <lhanusov@redhat.com>
This commit is contained in:
committed by
Stian Thorgersen
parent
21d033dc3a
commit
660a4aa48a
@@ -215,6 +215,11 @@ public class RealmConfigBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
public RealmConfigBuilder sslRequired(String sslRequired) {
|
||||
rep.setSslRequired(sslRequired);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Best practice is to use other convenience methods when configuring a realm, but while the framework is under
|
||||
* active development there may not be a way to perform all updates required. In these cases this method allows
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
package org.keycloak.tests.admin.identityprovider;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.keycloak.events.admin.OperationType;
|
||||
import org.keycloak.events.admin.ResourceType;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.models.utils.StripSecretsUtils;
|
||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||
import org.keycloak.testframework.annotations.InjectAdminEvents;
|
||||
import org.keycloak.testframework.annotations.InjectRealm;
|
||||
import org.keycloak.testframework.events.AdminEventAssertion;
|
||||
import org.keycloak.testframework.events.AdminEvents;
|
||||
import org.keycloak.testframework.realm.ManagedRealm;
|
||||
import org.keycloak.testframework.remote.runonserver.InjectRunOnServer;
|
||||
import org.keycloak.testframework.remote.runonserver.RunOnServerClient;
|
||||
import org.keycloak.tests.utils.admin.AdminEventPaths;
|
||||
import org.keycloak.tests.utils.admin.ApiUtil;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class AbstractIdentityProviderTest {
|
||||
|
||||
@InjectRealm
|
||||
ManagedRealm managedRealm;
|
||||
|
||||
@InjectAdminEvents
|
||||
AdminEvents adminEvents;
|
||||
|
||||
@InjectRunOnServer
|
||||
RunOnServerClient runOnServer;
|
||||
|
||||
protected String create(IdentityProviderRepresentation idpRep) {
|
||||
String idpId = ApiUtil.getCreatedId(managedRealm.admin().identityProviders().create(idpRep));
|
||||
Assertions.assertNotNull(idpId);
|
||||
|
||||
String secret = idpRep.getConfig() != null ? idpRep.getConfig().get("clientSecret") : null;
|
||||
idpRep = StripSecretsUtils.stripSecrets(null, idpRep);
|
||||
// if legacy hide on login page attribute was used, the attr will be removed when converted to model
|
||||
idpRep.setHideOnLogin(Boolean.parseBoolean(idpRep.getConfig().remove(IdentityProviderModel.LEGACY_HIDE_ON_LOGIN_ATTR)));
|
||||
|
||||
AdminEventAssertion.assertEvent(adminEvents.poll(), OperationType.CREATE, AdminEventPaths.identityProviderPath(idpRep.getAlias()), idpRep, ResourceType.IDENTITY_PROVIDER);
|
||||
|
||||
if (secret != null) {
|
||||
idpRep.getConfig().put("clientSecret", secret);
|
||||
}
|
||||
|
||||
return idpId;
|
||||
}
|
||||
|
||||
protected IdentityProviderRepresentation createRep(String alias, String providerId) {
|
||||
return createRep(alias, providerId,true, null);
|
||||
}
|
||||
|
||||
protected IdentityProviderRepresentation createRep(String alias, String providerId,boolean enabled, Map<String, String> config) {
|
||||
return createRep(alias, alias, providerId, enabled, config);
|
||||
}
|
||||
|
||||
protected IdentityProviderRepresentation createRep(String alias, String displayName, String providerId, boolean enabled, Map<String, String> config) {
|
||||
IdentityProviderRepresentation idp = new IdentityProviderRepresentation();
|
||||
|
||||
idp.setAlias(alias);
|
||||
idp.setDisplayName(displayName);
|
||||
idp.setProviderId(providerId);
|
||||
idp.setEnabled(enabled);
|
||||
if (config != null) {
|
||||
idp.setConfig(config);
|
||||
}
|
||||
return idp;
|
||||
}
|
||||
|
||||
protected void assertProviderInfo(Map<String, String> info, String id, String name) {
|
||||
System.out.println(info);
|
||||
Assertions.assertEquals(id, info.get("id"), "id");
|
||||
Assertions.assertEquals(name, info.get("name"), "name");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,273 @@
|
||||
package org.keycloak.tests.admin.identityprovider;
|
||||
|
||||
import jakarta.ws.rs.NotFoundException;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.keycloak.admin.client.resource.IdentityProviderResource;
|
||||
import org.keycloak.events.admin.OperationType;
|
||||
import org.keycloak.events.admin.ResourceType;
|
||||
import org.keycloak.models.IdentityProviderMapperModel;
|
||||
import org.keycloak.models.IdentityProviderMapperSyncMode;
|
||||
import org.keycloak.representations.idm.IdentityProviderMapperRepresentation;
|
||||
import org.keycloak.representations.idm.IdentityProviderMapperTypeRepresentation;
|
||||
import org.keycloak.testframework.annotations.InjectRealm;
|
||||
import org.keycloak.testframework.annotations.KeycloakIntegrationTest;
|
||||
import org.keycloak.testframework.events.AdminEventAssertion;
|
||||
import org.keycloak.testframework.injection.LifeCycle;
|
||||
import org.keycloak.testframework.realm.ManagedRealm;
|
||||
import org.keycloak.testframework.server.KeycloakServerConfig;
|
||||
import org.keycloak.testframework.server.KeycloakServerConfigBuilder;
|
||||
import org.keycloak.tests.utils.admin.AdminEventPaths;
|
||||
import org.keycloak.tests.utils.admin.ApiUtil;
|
||||
import org.keycloak.testsuite.broker.oidc.OverwrittenMappersTestIdentityProviderFactory;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.empty;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.hasSize;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
@KeycloakIntegrationTest(config = IdentityProviderMapperTest.IdentityProviderMapperServerConf.class)
|
||||
public class IdentityProviderMapperTest extends AbstractIdentityProviderTest {
|
||||
|
||||
@InjectRealm(lifecycle = LifeCycle.METHOD)
|
||||
ManagedRealm managedRealm;
|
||||
|
||||
@Test
|
||||
public void testMapperTypes() {
|
||||
|
||||
IdentityProviderResource provider;
|
||||
Map<String, IdentityProviderMapperTypeRepresentation> mapperTypes;
|
||||
|
||||
create(createRep("google", "google"));
|
||||
provider = managedRealm.admin().identityProviders().get("google");
|
||||
mapperTypes = provider.getMapperTypes();
|
||||
assertMapperTypes(mapperTypes, "google-user-attribute-mapper", "oidc-username-idp-mapper");
|
||||
|
||||
create(createRep("facebook", "facebook"));
|
||||
provider = managedRealm.admin().identityProviders().get("facebook");
|
||||
mapperTypes = provider.getMapperTypes();
|
||||
assertMapperTypes(mapperTypes, "facebook-user-attribute-mapper", "oidc-username-idp-mapper");
|
||||
|
||||
create(createRep("github", "github"));
|
||||
provider = managedRealm.admin().identityProviders().get("github");
|
||||
mapperTypes = provider.getMapperTypes();
|
||||
assertMapperTypes(mapperTypes, "github-user-attribute-mapper", "oidc-username-idp-mapper");
|
||||
|
||||
create(createRep("twitter", "twitter"));
|
||||
provider = managedRealm.admin().identityProviders().get("twitter");
|
||||
mapperTypes = provider.getMapperTypes();
|
||||
assertMapperTypes(mapperTypes, "oidc-username-idp-mapper");
|
||||
|
||||
/*
|
||||
// disabled to prevent 429 rate limiting on GitHub actions for LinkedIn's
|
||||
// https://www.linkedin.com/oauth/.well-known/openid-configuration discovery URL
|
||||
create(createRep("linkedin-openid-connect", "linkedin-openid-connect"));
|
||||
provider = managedRealm.admin().identityProviders().get("linkedin-openid-connect");
|
||||
mapperTypes = provider.getMapperTypes();
|
||||
assertMapperTypes(mapperTypes, "linkedin-user-attribute-mapper", "oidc-username-idp-mapper");
|
||||
*/
|
||||
|
||||
create(createRep("microsoft", "microsoft"));
|
||||
provider = managedRealm.admin().identityProviders().get("microsoft");
|
||||
mapperTypes = provider.getMapperTypes();
|
||||
assertMapperTypes(mapperTypes, "microsoft-user-attribute-mapper", "oidc-username-idp-mapper");
|
||||
|
||||
create(createRep("stackoverflow", "stackoverflow"));
|
||||
provider = managedRealm.admin().identityProviders().get("stackoverflow");
|
||||
mapperTypes = provider.getMapperTypes();
|
||||
assertMapperTypes(mapperTypes, "stackoverflow-user-attribute-mapper", "oidc-username-idp-mapper");
|
||||
|
||||
create(createRep("keycloak-oidc", "keycloak-oidc"));
|
||||
provider = managedRealm.admin().identityProviders().get("keycloak-oidc");
|
||||
mapperTypes = provider.getMapperTypes();
|
||||
assertMapperTypes(mapperTypes, "keycloak-oidc-role-to-role-idp-mapper", "oidc-user-attribute-idp-mapper", "oidc-role-idp-mapper", "oidc-username-idp-mapper", "oidc-advanced-group-idp-mapper", "oidc-advanced-role-idp-mapper", "oidc-user-session-note-idp-mapper");
|
||||
|
||||
create(createRep("oidc", "oidc"));
|
||||
provider = managedRealm.admin().identityProviders().get("oidc");
|
||||
mapperTypes = provider.getMapperTypes();
|
||||
assertMapperTypes(mapperTypes, "oidc-user-attribute-idp-mapper", "oidc-role-idp-mapper", "oidc-username-idp-mapper", "oidc-advanced-group-idp-mapper", "oidc-advanced-role-idp-mapper", "oidc-user-session-note-idp-mapper");
|
||||
|
||||
create(createRep("saml", "saml"));
|
||||
provider = managedRealm.admin().identityProviders().get("saml");
|
||||
mapperTypes = provider.getMapperTypes();
|
||||
assertMapperTypes(mapperTypes, "saml-user-attribute-idp-mapper", "saml-role-idp-mapper", "saml-username-idp-mapper", "saml-advanced-role-idp-mapper", "saml-advanced-group-idp-mapper", "saml-xpath-attribute-idp-mapper");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mapperTypesCanBeOverwritten() {
|
||||
String kcOidcProviderId = "keycloak-oidc";
|
||||
create(createRep(kcOidcProviderId, kcOidcProviderId));
|
||||
|
||||
String testProviderId = OverwrittenMappersTestIdentityProviderFactory.PROVIDER_ID;
|
||||
create(createRep(testProviderId, testProviderId));
|
||||
|
||||
/*
|
||||
* in the test provider, we have overwritten the mapper types to be the same as supported by "keycloak-oidc", so
|
||||
* the "keycloak-oidc" mappers are the expected mappers for the test provider
|
||||
*/
|
||||
IdentityProviderResource kcOidcProvider = managedRealm.admin().identityProviders().get(kcOidcProviderId);
|
||||
Set<String> expectedMapperTypes = kcOidcProvider.getMapperTypes().keySet();
|
||||
|
||||
IdentityProviderResource testProvider = managedRealm.admin().identityProviders().get(testProviderId);
|
||||
Set<String> actualMapperTypes = testProvider.getMapperTypes().keySet();
|
||||
|
||||
assertThat(actualMapperTypes, equalTo(expectedMapperTypes));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMappers() {
|
||||
create(createRep("google", "google"));
|
||||
|
||||
IdentityProviderResource provider = managedRealm.admin().identityProviders().get("google");
|
||||
|
||||
IdentityProviderMapperRepresentation mapper = new IdentityProviderMapperRepresentation();
|
||||
mapper.setIdentityProviderAlias("google");
|
||||
mapper.setName("my_mapper");
|
||||
mapper.setIdentityProviderMapper("oidc-hardcoded-role-idp-mapper");
|
||||
Map<String, String> config = new HashMap<>();
|
||||
config.put("role", "offline_access");
|
||||
config.put(IdentityProviderMapperModel.SYNC_MODE, IdentityProviderMapperSyncMode.INHERIT.toString());
|
||||
mapper.setConfig(config);
|
||||
|
||||
// createRep and add mapper
|
||||
Response response = provider.addMapper(mapper);
|
||||
String id = ApiUtil.getCreatedId(response);
|
||||
Assertions.assertNotNull(id);
|
||||
response.close();
|
||||
AdminEventAssertion.assertEvent(adminEvents.poll(), OperationType.CREATE, AdminEventPaths.identityProviderMapperPath("google", id), mapper, ResourceType.IDENTITY_PROVIDER_MAPPER);
|
||||
|
||||
// list mappers
|
||||
List<IdentityProviderMapperRepresentation> mappers = provider.getMappers();
|
||||
Assertions.assertEquals(1, mappers.size(), "mappers count");
|
||||
Assertions.assertEquals(id, mappers.get(0).getId(), "newly created mapper id");
|
||||
|
||||
// get mapper
|
||||
mapper = provider.getMapperById(id);
|
||||
Assertions.assertEquals("INHERIT", mappers.get(0).getConfig().get(IdentityProviderMapperModel.SYNC_MODE));
|
||||
Assertions.assertNotNull(mapper, "mapperById not null");
|
||||
Assertions.assertEquals(id, mapper.getId(), "mapper id");
|
||||
Assertions.assertNotNull(mapper.getConfig(), "mapper.config exists");
|
||||
Assertions.assertEquals("offline_access", mapper.getConfig().get("role"), "config retained");
|
||||
|
||||
// add duplicate mapper
|
||||
Response error = provider.addMapper(mapper);
|
||||
Assertions.assertEquals(400, error.getStatus(), "mapper unique name");
|
||||
error.close();
|
||||
|
||||
// update mapper
|
||||
mapper.getConfig().put("role", "master-realm.manage-realm");
|
||||
provider.update(id, mapper);
|
||||
AdminEventAssertion.assertEvent(adminEvents.poll(), OperationType.UPDATE, AdminEventPaths.identityProviderMapperPath("google", id), mapper, ResourceType.IDENTITY_PROVIDER_MAPPER);
|
||||
|
||||
mapper = provider.getMapperById(id);
|
||||
Assertions.assertNotNull(mapper, "mapperById not null");
|
||||
Assertions.assertEquals("master-realm.manage-realm", mapper.getConfig().get("role"), "config changed");
|
||||
|
||||
// delete mapper
|
||||
provider.delete(id);
|
||||
AdminEventAssertion.assertEvent(adminEvents.poll(), OperationType.DELETE, AdminEventPaths.identityProviderMapperPath("google", id), ResourceType.IDENTITY_PROVIDER_MAPPER);
|
||||
try {
|
||||
provider.getMapperById(id);
|
||||
Assertions.fail("Should fail with NotFoundException");
|
||||
} catch (NotFoundException e) {
|
||||
// Expected
|
||||
}
|
||||
}
|
||||
|
||||
// KEYCLOAK-4962
|
||||
@Test
|
||||
public void testUpdateProtocolMappers() {
|
||||
create(createRep("google2", "google"));
|
||||
|
||||
IdentityProviderResource provider = managedRealm.admin().identityProviders().get("google2");
|
||||
|
||||
IdentityProviderMapperRepresentation mapper = new IdentityProviderMapperRepresentation();
|
||||
mapper.setIdentityProviderAlias("google2");
|
||||
mapper.setName("my_mapper");
|
||||
mapper.setIdentityProviderMapper("oidc-hardcoded-role-idp-mapper");
|
||||
Map<String, String> config = new HashMap<>();
|
||||
config.put(IdentityProviderMapperModel.SYNC_MODE, IdentityProviderMapperSyncMode.INHERIT.toString());
|
||||
config.put("role", "");
|
||||
mapper.setConfig(config);
|
||||
|
||||
Response response = provider.addMapper(mapper);
|
||||
String mapperId = ApiUtil.getCreatedId(response);
|
||||
|
||||
|
||||
List<IdentityProviderMapperRepresentation> mappers = provider.getMappers();
|
||||
assertEquals(1, mappers.size());
|
||||
assertEquals(1, mappers.get(0).getConfig().size());
|
||||
|
||||
mapper = provider.getMapperById(mapperId);
|
||||
mapper.getConfig().put("role", "offline_access");
|
||||
|
||||
provider.update(mapperId, mapper);
|
||||
|
||||
mappers = provider.getMappers();
|
||||
assertEquals("INHERIT", mappers.get(0).getConfig().get(IdentityProviderMapperModel.SYNC_MODE));
|
||||
assertEquals(1, mappers.size());
|
||||
assertEquals(2, mappers.get(0).getConfig().size());
|
||||
assertEquals("offline_access", mappers.get(0).getConfig().get("role"));
|
||||
}
|
||||
|
||||
// KEYCLOAK-7872
|
||||
@Test
|
||||
public void testDeleteProtocolMappersAfterDeleteIdentityProvider() {
|
||||
create(createRep("google3", "google"));
|
||||
|
||||
IdentityProviderResource provider = managedRealm.admin().identityProviders().get("google3");
|
||||
|
||||
IdentityProviderMapperRepresentation mapper = new IdentityProviderMapperRepresentation();
|
||||
mapper.setIdentityProviderAlias("google3");
|
||||
mapper.setName("my_mapper");
|
||||
mapper.setIdentityProviderMapper("oidc-hardcoded-role-idp-mapper");
|
||||
Map<String, String> config = new HashMap<>();
|
||||
config.put(IdentityProviderMapperModel.SYNC_MODE, IdentityProviderMapperSyncMode.INHERIT.toString());
|
||||
config.put("role", "offline_access");
|
||||
mapper.setConfig(config);
|
||||
|
||||
provider.addMapper(mapper);
|
||||
|
||||
List<IdentityProviderMapperRepresentation> mappers = provider.getMappers();
|
||||
assertThat(mappers, hasSize(1));
|
||||
|
||||
adminEvents.clear();
|
||||
|
||||
provider.remove();
|
||||
AdminEventAssertion.assertEvent(adminEvents.poll(), OperationType.DELETE, AdminEventPaths.identityProviderPath("google3"), ResourceType.IDENTITY_PROVIDER);
|
||||
|
||||
create(createRep("google3", "google"));
|
||||
|
||||
IdentityProviderResource newProvider = managedRealm.admin().identityProviders().get("google3");
|
||||
|
||||
assertThat(newProvider.getMappers(), empty());
|
||||
}
|
||||
|
||||
private void assertMapperTypes(Map<String, IdentityProviderMapperTypeRepresentation> mapperTypes, String ... mapperIds) {
|
||||
Set<String> expected = new HashSet<>();
|
||||
expected.add("hardcoded-user-session-attribute-idp-mapper");
|
||||
expected.add("oidc-hardcoded-role-idp-mapper");
|
||||
expected.add("oidc-hardcoded-group-idp-mapper");
|
||||
expected.add("hardcoded-attribute-idp-mapper");
|
||||
expected.addAll(Arrays.asList(mapperIds));
|
||||
|
||||
Assertions.assertEquals(expected, mapperTypes.keySet(), "mapperTypes");
|
||||
}
|
||||
|
||||
public static class IdentityProviderMapperServerConf implements KeycloakServerConfig {
|
||||
|
||||
@Override
|
||||
public KeycloakServerConfigBuilder configure(KeycloakServerConfigBuilder builder) {
|
||||
return builder.dependency("org.keycloak.tests", "keycloak-tests-custom-providers");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,452 @@
|
||||
/*
|
||||
* 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.tests.admin.identityprovider;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.hasEntry;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.keycloak.admin.client.resource.IdentityProviderResource;
|
||||
import org.keycloak.common.enums.SslRequired;
|
||||
import org.keycloak.events.admin.OperationType;
|
||||
import org.keycloak.events.admin.ResourceType;
|
||||
import org.keycloak.models.IdentityProviderMapperModel;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||
import org.keycloak.representations.idm.AdminEventRepresentation;
|
||||
import org.keycloak.representations.idm.ComponentRepresentation;
|
||||
import org.keycloak.representations.idm.ErrorRepresentation;
|
||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||
import org.keycloak.testframework.annotations.KeycloakIntegrationTest;
|
||||
import org.keycloak.testframework.events.AdminEventAssertion;
|
||||
import org.keycloak.tests.utils.admin.AdminEventPaths;
|
||||
|
||||
import jakarta.ws.rs.ClientErrorException;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import org.keycloak.testsuite.util.broker.OIDCIdentityProviderConfigRep;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
@KeycloakIntegrationTest
|
||||
public class IdentityProviderOidcTest extends AbstractIdentityProviderTest {
|
||||
|
||||
@Test
|
||||
public void testCreateWithReservedCharacterForAlias() {
|
||||
IdentityProviderRepresentation newIdentityProvider = createRep("ne$&w-identity-provider", "oidc");
|
||||
|
||||
newIdentityProvider.getConfig().put("clientId", "clientId");
|
||||
newIdentityProvider.getConfig().put("clientSecret", "some secret value");
|
||||
|
||||
Response response = managedRealm.admin().identityProviders().create(newIdentityProvider);
|
||||
Assertions.assertEquals(400, response.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreate() {
|
||||
IdentityProviderRepresentation newIdentityProvider = createRep("new-identity-provider", "oidc");
|
||||
|
||||
newIdentityProvider.getConfig().put(IdentityProviderModel.SYNC_MODE, "IMPORT");
|
||||
newIdentityProvider.getConfig().put("clientId", "clientId");
|
||||
newIdentityProvider.getConfig().put("clientSecret", "some secret value");
|
||||
|
||||
String id = create(newIdentityProvider);
|
||||
|
||||
IdentityProviderResource identityProviderResource = managedRealm.admin().identityProviders().get("new-identity-provider");
|
||||
|
||||
assertNotNull(identityProviderResource);
|
||||
|
||||
IdentityProviderRepresentation representation = identityProviderResource.toRepresentation();
|
||||
|
||||
assertNotNull(representation);
|
||||
|
||||
assertNotNull(representation.getInternalId());
|
||||
assertEquals("new-identity-provider", representation.getAlias());
|
||||
assertEquals("oidc", representation.getProviderId());
|
||||
assertEquals("IMPORT", representation.getConfig().get(IdentityProviderMapperModel.SYNC_MODE));
|
||||
assertEquals("clientId", representation.getConfig().get("clientId"));
|
||||
assertEquals(ComponentRepresentation.SECRET_VALUE, representation.getConfig().get("clientSecret"));
|
||||
assertTrue(representation.isEnabled());
|
||||
assertFalse(representation.isStoreToken());
|
||||
assertFalse(representation.isTrustEmail());
|
||||
assertNull(representation.getFirstBrokerLoginFlowAlias());
|
||||
|
||||
assertEquals("some secret value", runOnServer.fetch(s -> s.identityProviders().getByAlias("new-identity-provider").getConfig().get("clientSecret"), String.class));
|
||||
|
||||
IdentityProviderRepresentation rep = managedRealm.admin().identityProviders().findAll().stream().filter(i -> i.getAlias().equals("new-identity-provider")).findFirst().get();
|
||||
assertEquals(ComponentRepresentation.SECRET_VALUE, rep.getConfig().get("clientSecret"));
|
||||
|
||||
managedRealm.cleanup().add(r -> r.identityProviders().get(id).remove());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void failCreateInvalidUrl() throws Exception {
|
||||
managedRealm.updateWithCleanup(r -> r.sslRequired(SslRequired.ALL.name()));
|
||||
|
||||
IdentityProviderRepresentation newIdentityProvider = createRep("new-identity-provider", "oidc");
|
||||
|
||||
newIdentityProvider.getConfig().put("clientId", "clientId");
|
||||
newIdentityProvider.getConfig().put("clientSecret", "some secret value");
|
||||
|
||||
OIDCIdentityProviderConfigRep oidcConfig = new OIDCIdentityProviderConfigRep(newIdentityProvider);
|
||||
|
||||
oidcConfig.setAuthorizationUrl("invalid://test");
|
||||
|
||||
try (Response response = this.managedRealm.admin().identityProviders().create(newIdentityProvider)) {
|
||||
assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), response.getStatus());
|
||||
ErrorRepresentation error = response.readEntity(ErrorRepresentation.class);
|
||||
assertEquals("The url [authorization_url] is malformed", error.getErrorMessage());
|
||||
}
|
||||
|
||||
oidcConfig.setAuthorizationUrl(null);
|
||||
oidcConfig.setTokenUrl("http://test");
|
||||
|
||||
try (Response response = this.managedRealm.admin().identityProviders().create(newIdentityProvider)) {
|
||||
assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), response.getStatus());
|
||||
ErrorRepresentation error = response.readEntity(ErrorRepresentation.class);
|
||||
assertEquals("The url [token_url] requires secure connections", error.getErrorMessage());
|
||||
}
|
||||
|
||||
oidcConfig.setAuthorizationUrl(null);
|
||||
oidcConfig.setTokenUrl(null);
|
||||
oidcConfig.setJwksUrl("http://test");
|
||||
|
||||
try (Response response = this.managedRealm.admin().identityProviders().create(newIdentityProvider)) {
|
||||
assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), response.getStatus());
|
||||
ErrorRepresentation error = response.readEntity(ErrorRepresentation.class);
|
||||
assertEquals("The url [jwks_url] requires secure connections", error.getErrorMessage());
|
||||
}
|
||||
|
||||
oidcConfig.setAuthorizationUrl(null);
|
||||
oidcConfig.setTokenUrl(null);
|
||||
oidcConfig.setJwksUrl(null);
|
||||
oidcConfig.setLogoutUrl("http://test");
|
||||
|
||||
try (Response response = this.managedRealm.admin().identityProviders().create(newIdentityProvider)) {
|
||||
assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), response.getStatus());
|
||||
ErrorRepresentation error = response.readEntity(ErrorRepresentation.class);
|
||||
assertEquals("The url [logout_url] requires secure connections", error.getErrorMessage());
|
||||
}
|
||||
|
||||
oidcConfig.setAuthorizationUrl(null);
|
||||
oidcConfig.setTokenUrl(null);
|
||||
oidcConfig.setJwksUrl(null);
|
||||
oidcConfig.setLogoutUrl(null);
|
||||
oidcConfig.setUserInfoUrl("http://test");
|
||||
|
||||
try (Response response = this.managedRealm.admin().identityProviders().create(newIdentityProvider)) {
|
||||
assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), response.getStatus());
|
||||
ErrorRepresentation error = response.readEntity(ErrorRepresentation.class);
|
||||
assertEquals("The url [userinfo_url] requires secure connections", error.getErrorMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldFailWhenAliasHasSpaceDuringCreation() {
|
||||
IdentityProviderRepresentation newIdentityProvider = createRep("New Identity Provider", "oidc");
|
||||
|
||||
newIdentityProvider.getConfig().put(IdentityProviderModel.SYNC_MODE, "IMPORT");
|
||||
newIdentityProvider.getConfig().put("clientId", "clientId");
|
||||
newIdentityProvider.getConfig().put("clientSecret", "some secret value");
|
||||
newIdentityProvider.getConfig().put("clientAuthMethod",OIDCLoginProtocol.CLIENT_SECRET_BASIC);
|
||||
|
||||
try (Response response = this.managedRealm.admin().identityProviders().create(newIdentityProvider)) {
|
||||
assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), response.getStatus());
|
||||
String error = response.readEntity(String.class);
|
||||
assertTrue(error.contains("Empty Space not allowed."));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateWithBasicAuth() {
|
||||
IdentityProviderRepresentation newIdentityProvider = createRep("new-identity-provider", "oidc");
|
||||
|
||||
newIdentityProvider.getConfig().put(IdentityProviderModel.SYNC_MODE, "IMPORT");
|
||||
newIdentityProvider.getConfig().put("clientId", "clientId");
|
||||
newIdentityProvider.getConfig().put("clientSecret", "some secret value");
|
||||
newIdentityProvider.getConfig().put("clientAuthMethod",OIDCLoginProtocol.CLIENT_SECRET_BASIC);
|
||||
|
||||
String id = create(newIdentityProvider);
|
||||
|
||||
IdentityProviderResource identityProviderResource = managedRealm.admin().identityProviders().get("new-identity-provider");
|
||||
|
||||
assertNotNull(identityProviderResource);
|
||||
|
||||
IdentityProviderRepresentation representation = identityProviderResource.toRepresentation();
|
||||
|
||||
assertNotNull(representation);
|
||||
|
||||
assertNotNull(representation.getInternalId());
|
||||
assertEquals("new-identity-provider", representation.getAlias());
|
||||
assertEquals("oidc", representation.getProviderId());
|
||||
assertEquals("IMPORT", representation.getConfig().get(IdentityProviderMapperModel.SYNC_MODE));
|
||||
assertEquals("clientId", representation.getConfig().get("clientId"));
|
||||
assertEquals(ComponentRepresentation.SECRET_VALUE, representation.getConfig().get("clientSecret"));
|
||||
assertEquals(OIDCLoginProtocol.CLIENT_SECRET_BASIC, representation.getConfig().get("clientAuthMethod"));
|
||||
|
||||
assertTrue(representation.isEnabled());
|
||||
assertFalse(representation.isStoreToken());
|
||||
assertFalse(representation.isTrustEmail());
|
||||
|
||||
assertEquals("some secret value", runOnServer.fetch(s -> s.identityProviders().getByAlias("new-identity-provider").getConfig().get("clientSecret"), String.class));
|
||||
|
||||
IdentityProviderRepresentation rep = managedRealm.admin().identityProviders().findAll().stream().filter(i -> i.getAlias().equals("new-identity-provider")).findFirst().get();
|
||||
assertEquals(ComponentRepresentation.SECRET_VALUE, rep.getConfig().get("clientSecret"));
|
||||
|
||||
managedRealm.cleanup().add(r -> r.identityProviders().get(id).remove());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateWithJWT() {
|
||||
IdentityProviderRepresentation newIdentityProvider = createRep("new-identity-provider", "oidc");
|
||||
|
||||
newIdentityProvider.getConfig().put(IdentityProviderModel.SYNC_MODE, "IMPORT");
|
||||
newIdentityProvider.getConfig().put("clientId", "clientId");
|
||||
newIdentityProvider.getConfig().put("clientAuthMethod", OIDCLoginProtocol.PRIVATE_KEY_JWT);
|
||||
|
||||
String id = create(newIdentityProvider);
|
||||
|
||||
IdentityProviderResource identityProviderResource = managedRealm.admin().identityProviders().get("new-identity-provider");
|
||||
|
||||
assertNotNull(identityProviderResource);
|
||||
|
||||
IdentityProviderRepresentation representation = identityProviderResource.toRepresentation();
|
||||
|
||||
assertNotNull(representation);
|
||||
|
||||
assertNotNull(representation.getInternalId());
|
||||
assertEquals("new-identity-provider", representation.getAlias());
|
||||
assertEquals("oidc", representation.getProviderId());
|
||||
assertEquals("IMPORT", representation.getConfig().get(IdentityProviderMapperModel.SYNC_MODE));
|
||||
assertEquals("clientId", representation.getConfig().get("clientId"));
|
||||
assertNull(representation.getConfig().get("clientSecret"));
|
||||
assertEquals(OIDCLoginProtocol.PRIVATE_KEY_JWT, representation.getConfig().get("clientAuthMethod"));
|
||||
assertNull(representation.getConfig().get("jwtX509HeadersEnabled"));
|
||||
assertTrue(representation.isEnabled());
|
||||
assertFalse(representation.isStoreToken());
|
||||
assertFalse(representation.isTrustEmail());
|
||||
|
||||
managedRealm.cleanup().add(r -> r.identityProviders().get(id).remove());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateWithJWTAndX509Headers() {
|
||||
IdentityProviderRepresentation newIdentityProvider = createRep("new-identity-provider", "oidc");
|
||||
|
||||
newIdentityProvider.getConfig().put(IdentityProviderModel.SYNC_MODE, "IMPORT");
|
||||
newIdentityProvider.getConfig().put("clientId", "clientId");
|
||||
newIdentityProvider.getConfig().put("clientAuthMethod", OIDCLoginProtocol.PRIVATE_KEY_JWT);
|
||||
newIdentityProvider.getConfig().put("jwtX509HeadersEnabled", "true");
|
||||
|
||||
String id = create(newIdentityProvider);
|
||||
|
||||
IdentityProviderResource identityProviderResource = managedRealm.admin().identityProviders().get("new-identity-provider");
|
||||
|
||||
assertNotNull(identityProviderResource);
|
||||
|
||||
IdentityProviderRepresentation representation = identityProviderResource.toRepresentation();
|
||||
|
||||
assertNotNull(representation);
|
||||
|
||||
assertNotNull(representation.getInternalId());
|
||||
assertEquals("new-identity-provider", representation.getAlias());
|
||||
assertEquals("oidc", representation.getProviderId());
|
||||
assertEquals("IMPORT", representation.getConfig().get(IdentityProviderMapperModel.SYNC_MODE));
|
||||
assertEquals("clientId", representation.getConfig().get("clientId"));
|
||||
assertNull(representation.getConfig().get("clientSecret"));
|
||||
assertEquals(OIDCLoginProtocol.PRIVATE_KEY_JWT, representation.getConfig().get("clientAuthMethod"));
|
||||
assertEquals("true", representation.getConfig().get("jwtX509HeadersEnabled"));
|
||||
assertTrue(representation.isEnabled());
|
||||
assertFalse(representation.isStoreToken());
|
||||
assertFalse(representation.isTrustEmail());
|
||||
|
||||
managedRealm.cleanup().add(r -> r.identityProviders().get(id).remove());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdate() {
|
||||
IdentityProviderRepresentation newIdentityProvider = createRep("update-identity-provider", "oidc");
|
||||
|
||||
newIdentityProvider.getConfig().put(IdentityProviderModel.SYNC_MODE, "IMPORT");
|
||||
newIdentityProvider.getConfig().put("clientId", "clientId");
|
||||
newIdentityProvider.getConfig().put("clientSecret", "some secret value");
|
||||
|
||||
create(newIdentityProvider);
|
||||
|
||||
IdentityProviderResource identityProviderResource = managedRealm.admin().identityProviders().get("update-identity-provider");
|
||||
|
||||
assertNotNull(identityProviderResource);
|
||||
|
||||
IdentityProviderRepresentation representation = identityProviderResource.toRepresentation();
|
||||
|
||||
assertNotNull(representation);
|
||||
|
||||
assertEquals("update-identity-provider", representation.getAlias());
|
||||
|
||||
representation.setAlias("changed-alias");
|
||||
representation.setEnabled(false);
|
||||
representation.setStoreToken(true);
|
||||
representation.getConfig().put("clientId", "changedClientId");
|
||||
|
||||
identityProviderResource.update(representation);
|
||||
AdminEventRepresentation event = adminEvents.poll();
|
||||
AdminEventAssertion.assertEvent(event, OperationType.UPDATE, AdminEventPaths.identityProviderPath("update-identity-provider"), representation, ResourceType.IDENTITY_PROVIDER);
|
||||
assertFalse(event.getRepresentation().contains("some secret value"));
|
||||
assertTrue(event.getRepresentation().contains(ComponentRepresentation.SECRET_VALUE));
|
||||
|
||||
identityProviderResource = managedRealm.admin().identityProviders().get(representation.getInternalId());
|
||||
|
||||
assertNotNull(identityProviderResource);
|
||||
|
||||
representation = identityProviderResource.toRepresentation();
|
||||
|
||||
assertFalse(representation.isEnabled());
|
||||
assertTrue(representation.isStoreToken());
|
||||
assertEquals("changedClientId", representation.getConfig().get("clientId"));
|
||||
|
||||
assertEquals("some secret value", runOnServer.fetch(s -> s.identityProviders().getByAlias("changed-alias").getConfig().get("clientSecret"), String.class));
|
||||
|
||||
representation.getConfig().put("clientSecret", "${vault.key}");
|
||||
identityProviderResource.update(representation);
|
||||
event = adminEvents.poll();
|
||||
AdminEventAssertion.assertEvent(event, OperationType.UPDATE, AdminEventPaths.identityProviderPath(representation.getInternalId()), representation, ResourceType.IDENTITY_PROVIDER);
|
||||
assertThat(event.getRepresentation(), containsString("${vault.key}"));
|
||||
assertThat(event.getRepresentation(), not(containsString(ComponentRepresentation.SECRET_VALUE)));
|
||||
|
||||
assertThat(identityProviderResource.toRepresentation().getConfig(), hasEntry("clientSecret", "${vault.key}"));
|
||||
assertEquals("${vault.key}", runOnServer.fetch(s -> s.identityProviders().getByAlias("changed-alias").getConfig().get("clientSecret"), String.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void failUpdateInvalidUrl() throws Exception {
|
||||
managedRealm.updateWithCleanup(r -> r.sslRequired(SslRequired.ALL.name()));
|
||||
adminEvents.poll(); // realm update
|
||||
IdentityProviderRepresentation representation = createRep(UUID.randomUUID().toString(), "oidc");
|
||||
|
||||
representation.getConfig().put("clientId", "clientId");
|
||||
representation.getConfig().put("clientSecret", "some secret value");
|
||||
|
||||
String id = create(representation);
|
||||
|
||||
IdentityProviderResource resource = this.managedRealm.admin().identityProviders().get(representation.getAlias());
|
||||
representation = resource.toRepresentation();
|
||||
|
||||
OIDCIdentityProviderConfigRep oidcConfig = new OIDCIdentityProviderConfigRep(representation);
|
||||
|
||||
oidcConfig.setAuthorizationUrl("invalid://test");
|
||||
try {
|
||||
resource.update(representation);
|
||||
fail("Invalid URL");
|
||||
} catch (Exception e) {
|
||||
assertTrue(e instanceof ClientErrorException);
|
||||
Response response = ClientErrorException.class.cast(e).getResponse();
|
||||
assertEquals( Response.Status.BAD_REQUEST.getStatusCode(), response.getStatus());
|
||||
ErrorRepresentation error = ((ClientErrorException) e).getResponse().readEntity(ErrorRepresentation.class);
|
||||
assertEquals("The url [authorization_url] is malformed", error.getErrorMessage());
|
||||
}
|
||||
|
||||
oidcConfig.setAuthorizationUrl(null);
|
||||
oidcConfig.setTokenUrl("http://test");
|
||||
|
||||
try {
|
||||
resource.update(representation);
|
||||
fail("Invalid URL");
|
||||
} catch (Exception e) {
|
||||
assertTrue(e instanceof ClientErrorException);
|
||||
Response response = ClientErrorException.class.cast(e).getResponse();
|
||||
assertEquals( Response.Status.BAD_REQUEST.getStatusCode(), response.getStatus());
|
||||
ErrorRepresentation error = ((ClientErrorException) e).getResponse().readEntity(ErrorRepresentation.class);
|
||||
assertEquals("The url [token_url] requires secure connections", error.getErrorMessage());
|
||||
}
|
||||
|
||||
oidcConfig.setAuthorizationUrl(null);
|
||||
oidcConfig.setTokenUrl(null);
|
||||
oidcConfig.setJwksUrl("http://test");
|
||||
try {
|
||||
resource.update(representation);
|
||||
fail("Invalid URL");
|
||||
} catch (Exception e) {
|
||||
assertTrue(e instanceof ClientErrorException);
|
||||
Response response = ClientErrorException.class.cast(e).getResponse();
|
||||
assertEquals( Response.Status.BAD_REQUEST.getStatusCode(), response.getStatus());
|
||||
ErrorRepresentation error = ((ClientErrorException) e).getResponse().readEntity(ErrorRepresentation.class);
|
||||
assertEquals("The url [jwks_url] requires secure connections", error.getErrorMessage());
|
||||
}
|
||||
|
||||
oidcConfig.setAuthorizationUrl(null);
|
||||
oidcConfig.setTokenUrl(null);
|
||||
oidcConfig.setJwksUrl(null);
|
||||
oidcConfig.setLogoutUrl("http://test");
|
||||
try {
|
||||
resource.update(representation);
|
||||
fail("Invalid URL");
|
||||
} catch (Exception e) {
|
||||
assertTrue(e instanceof ClientErrorException);
|
||||
Response response = ClientErrorException.class.cast(e).getResponse();
|
||||
assertEquals( Response.Status.BAD_REQUEST.getStatusCode(), response.getStatus());
|
||||
ErrorRepresentation error = ((ClientErrorException) e).getResponse().readEntity(ErrorRepresentation.class);
|
||||
assertEquals("The url [logout_url] requires secure connections", error.getErrorMessage());
|
||||
}
|
||||
|
||||
oidcConfig.setAuthorizationUrl(null);
|
||||
oidcConfig.setTokenUrl(null);
|
||||
oidcConfig.setJwksUrl(null);
|
||||
oidcConfig.setLogoutUrl(null);
|
||||
oidcConfig.setUserInfoUrl("http://localhost");
|
||||
|
||||
try {
|
||||
resource.update(representation);
|
||||
fail("Invalid URL");
|
||||
} catch (Exception e) {
|
||||
assertTrue(e instanceof ClientErrorException);
|
||||
Response response = ClientErrorException.class.cast(e).getResponse();
|
||||
assertEquals( Response.Status.BAD_REQUEST.getStatusCode(), response.getStatus());
|
||||
ErrorRepresentation error = ((ClientErrorException) e).getResponse().readEntity(ErrorRepresentation.class);
|
||||
assertEquals("The url [userinfo_url] requires secure connections", error.getErrorMessage());
|
||||
}
|
||||
|
||||
managedRealm.updateWithCleanup(r -> r.sslRequired(SslRequired.EXTERNAL.name()));
|
||||
resource.update(representation);
|
||||
|
||||
managedRealm.cleanup().add(r -> r.identityProviders().get(id).remove());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoExport() {
|
||||
String id = create(createRep("keycloak-oidc", "keycloak-oidc"));
|
||||
|
||||
Response response = managedRealm.admin().identityProviders().get("keycloak-oidc").export("json");
|
||||
Assertions.assertEquals(204, response.getStatus(), "status");
|
||||
String body = response.readEntity(String.class);
|
||||
Assertions.assertNull(body, "body");
|
||||
response.close();
|
||||
|
||||
managedRealm.cleanup().add(r -> r.identityProviders().get(id).remove());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,539 @@
|
||||
package org.keycloak.tests.admin.identityprovider;
|
||||
|
||||
import jakarta.ws.rs.BadRequestException;
|
||||
import jakarta.ws.rs.NotFoundException;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataOutput;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.keycloak.admin.client.resource.IdentityProviderResource;
|
||||
import org.keycloak.broker.saml.SAMLIdentityProviderConfig;
|
||||
import org.keycloak.dom.saml.v2.metadata.EndpointType;
|
||||
import org.keycloak.dom.saml.v2.metadata.EntityDescriptorType;
|
||||
import org.keycloak.dom.saml.v2.metadata.IndexedEndpointType;
|
||||
import org.keycloak.dom.saml.v2.metadata.KeyDescriptorType;
|
||||
import org.keycloak.dom.saml.v2.metadata.KeyTypes;
|
||||
import org.keycloak.dom.saml.v2.metadata.SPSSODescriptorType;
|
||||
import org.keycloak.events.admin.OperationType;
|
||||
import org.keycloak.events.admin.ResourceType;
|
||||
import org.keycloak.models.Constants;
|
||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.rotation.HardcodedKeyLocator;
|
||||
import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
|
||||
import org.keycloak.saml.common.exceptions.ConfigurationException;
|
||||
import org.keycloak.saml.common.exceptions.ParsingException;
|
||||
import org.keycloak.saml.common.exceptions.ProcessingException;
|
||||
import org.keycloak.saml.common.util.DocumentUtil;
|
||||
import org.keycloak.saml.common.util.XmlKeyInfoKeyNameTransformer;
|
||||
import org.keycloak.saml.processing.api.saml.v2.sig.SAML2Signature;
|
||||
import org.keycloak.saml.processing.core.parsers.saml.SAMLParser;
|
||||
import org.keycloak.saml.processing.core.util.XMLSignatureUtil;
|
||||
import org.keycloak.testframework.annotations.InjectKeycloakUrls;
|
||||
import org.keycloak.testframework.annotations.KeycloakIntegrationTest;
|
||||
import org.keycloak.testframework.events.AdminEventAssertion;
|
||||
import org.keycloak.testframework.server.KeycloakUrls;
|
||||
import org.keycloak.tests.utils.Assert;
|
||||
import org.keycloak.tests.utils.KeyUtils;
|
||||
import org.keycloak.tests.utils.admin.AdminEventPaths;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import javax.xml.crypto.dsig.XMLSignature;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.security.PublicKey;
|
||||
import java.security.cert.CertificateEncodingException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Base64;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.hasEntry;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.keycloak.saml.common.constants.JBossSAMLURIConstants.XMLDSIG_NSURI;
|
||||
|
||||
@KeycloakIntegrationTest
|
||||
public class IdentityProviderSamlTest extends AbstractIdentityProviderTest {
|
||||
|
||||
@InjectKeycloakUrls
|
||||
KeycloakUrls keycloakUrls;
|
||||
|
||||
// Certificate imported from
|
||||
private static final String SIGNING_CERT_1 = "MIICmzCCAYMCBgFUYnC0OjANBgkqhkiG9w0BAQsFADARMQ8wDQY"
|
||||
+ "DVQQDDAZtYXN0ZXIwHhcNMTYwNDI5MTQzMjEzWhcNMjYwNDI5MTQzMzUzWjARMQ8wDQYDVQQDDAZtYXN0ZXI"
|
||||
+ "wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCN25AW1poMEZRbuMAHG58AThZmCwMV6/Gcui4mjGa"
|
||||
+ "cRFyudgqzLjQ2rxpoW41JAtLjbjeAhuWvirUcFVcOeS3gM/ZC27qCpYighAcylZz6MYocnEe1+e8rPPk4JlI"
|
||||
+ "D6Wv62dgu+pL/vYsQpRhvD3Y2c/ytgr5D32xF+KnzDehUy5BSyzypvu12Wq9mS5vK5tzkN37EjkhpY2ZxaXP"
|
||||
+ "ubjDIITCAL4Q8M/m5IlacBaUZbzI4AQrHnMP1O1IH2dHSWuMiBe+xSDTco72PmuYPJKTV4wQdeBUIkYbfLc4"
|
||||
+ "RxVmXEvgkQgyW86EoMPxlWJpj7+mTIR+l+2thZPr/VgwTs82rAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAA/"
|
||||
+ "Ip/Hi8RoVu5ouaFFlc5whT7ltuK8slfLGW4tM4vJXhInYwsqIRQKBNDYW/64xle3eII4u1yAH1OYRRwEs7Em"
|
||||
+ "1pr4QuFuTY1at+aE0sE46XDlyESI0txJjWxYoT133vM0We2pj1b2nxgU30rwjKA3whnKEfTEYT/n3JBSqNgg"
|
||||
+ "y6l8ZGw/oPSgvPaR4+xeB1tfQFC4VrLoYKoqH6hAL530nKxL+qV8AIfL64NDEE8ankIAEDAAFe8x3CPUfXR/"
|
||||
+ "p4KOANKkpz8ieQaHDb1eITkAwUwjESj6UF9D1aePlhWls/HX0gujFXtWfWfrJ8CU/ogwlH8y1jgRuLjFQYZk6llc=";
|
||||
|
||||
private static final String SIGNING_CERT_2 = "MIIBnDCCAQUCBgFYKXKsPTANBgkqhkiG9w0BAQsFADAUMRIwEAY"
|
||||
+ "DVQQDDAlzYW1sLWRlbW8wHhcNMTYxMTAzMDkwNzEwWhcNMjYxMTAzMDkwODUwWjAUMRIwEAYDVQQDDAlzYW1"
|
||||
+ "sLWRlbW8wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAKtWsK5O0CtuBpnMvWG+HTG0vmZzujQ2o9WdheQ"
|
||||
+ "u+BzCILcGMsbDW0YQaglpcO5JpGWWhubnckGGPHfdQ2/7nP9QwbiTK0FbGF41UqcvoaCqU1psxoV88s8IXyQ"
|
||||
+ "CAqeyLv00yj6foqdJjxh5SZ5z+na+M7Y2OxIBVxYRAxWEnfUvAgMBAAEwDQYJKoZIhvcNAQELBQADgYEAhet"
|
||||
+ "vOU8TyqfZF5jpv0IcrviLl/DoFrbjByeHR+pu/vClcAOjL/u7oQELuuTfNsBI4tpexUj5G8q/YbEz0gk7idf"
|
||||
+ "LXrAUVcsR73oTngrhRfwUSmPrjjK0kjcRb6HL9V/+wh3R/6mEd59U08ExT8N38rhmn0CI3ehMdebReprP7U8=";
|
||||
|
||||
@Test
|
||||
public void testRemove() {
|
||||
IdentityProviderRepresentation newIdentityProvider = createRep("remove-identity-provider", "saml");
|
||||
|
||||
create(newIdentityProvider);
|
||||
|
||||
IdentityProviderResource identityProviderResource = managedRealm.admin().identityProviders().get("remove-identity-provider");
|
||||
|
||||
assertNotNull(identityProviderResource);
|
||||
|
||||
IdentityProviderRepresentation representation = identityProviderResource.toRepresentation();
|
||||
|
||||
assertNotNull(representation);
|
||||
|
||||
identityProviderResource.remove();
|
||||
AdminEventAssertion.assertEvent(adminEvents.poll(), OperationType.DELETE, AdminEventPaths.identityProviderPath("remove-identity-provider"), ResourceType.IDENTITY_PROVIDER);
|
||||
|
||||
try {
|
||||
managedRealm.admin().identityProviders().get("remove-identity-provider").toRepresentation();
|
||||
Assertions.fail("Not expected to found");
|
||||
} catch (NotFoundException nfe) {
|
||||
// Expected
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotAvailableFromRealRepresentation() {
|
||||
IdentityProviderRepresentation newIdentityProvider = createRep("remove-identity-provider", "saml");
|
||||
|
||||
String id = create(newIdentityProvider);
|
||||
|
||||
RealmRepresentation rep = this.managedRealm.admin().toRepresentation();
|
||||
assertNull(rep.getIdentityProviders());
|
||||
assertNull(rep.getIdentityProviderMappers());
|
||||
|
||||
managedRealm.cleanup().add(r -> r.identityProviders().get(id).remove());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void importShouldFailDueAliasWithSpace() {
|
||||
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("providerId", "saml");
|
||||
data.put("alias", "Alias With Space");
|
||||
data.put("fromUrl", "http://");
|
||||
|
||||
assertThrows(BadRequestException.class, () -> {
|
||||
managedRealm.admin().identityProviders().importFrom(data);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSamlImportAndExport() throws URISyntaxException, IOException, ParsingException {
|
||||
testSamlImport("saml-idp-metadata.xml", true);
|
||||
|
||||
// Perform export, and make sure some of the values are like they're supposed to be
|
||||
Response response = managedRealm.admin().identityProviders().get("saml").export("xml");
|
||||
Assertions.assertEquals(200, response.getStatus());
|
||||
String body = response.readEntity(String.class);
|
||||
response.close();
|
||||
|
||||
assertSamlExport(body, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSamlImportWithBom() throws URISyntaxException, IOException, ParsingException {
|
||||
testSamlImport("saml-idp-metadata_utf8_bom.xml", true);
|
||||
|
||||
// Perform export, and make sure some of the values are like they're supposed to be
|
||||
Response response = managedRealm.admin().identityProviders().get("saml").export("xml");
|
||||
Assertions.assertEquals(200, response.getStatus());
|
||||
String body = response.readEntity(String.class);
|
||||
response.close();
|
||||
|
||||
assertSamlExport(body, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSamlImportAndExportDifferentBindings() throws URISyntaxException, IOException, ParsingException {
|
||||
testSamlImport("saml-idp-metadata-different-bindings.xml", false);
|
||||
|
||||
// Perform export, and make sure some of the values are like they're supposed to be
|
||||
try (Response response = managedRealm.admin().identityProviders().get("saml").export("xml")) {
|
||||
Assertions.assertEquals(200, response.getStatus());
|
||||
String body = response.readEntity(String.class);
|
||||
assertSamlExport(body, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSamlImportWithAnyEncryptionMethod() throws URISyntaxException, IOException, ParsingException {
|
||||
testSamlImport("saml-idp-metadata-encryption-methods.xml", true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSamlImportAndExportDisabled() throws URISyntaxException, IOException, ParsingException {
|
||||
|
||||
// Use import-config to convert IDPSSODescriptor file into key value pairs
|
||||
// to use when creating a SAML Identity Provider
|
||||
MultipartFormDataOutput form = new MultipartFormDataOutput();
|
||||
form.addFormData("providerId", "saml", MediaType.TEXT_PLAIN_TYPE);
|
||||
|
||||
URL idpMeta = IdentityProviderSamlTest.class.getResource("saml-idp-metadata-disabled.xml");
|
||||
byte[] content = Files.readAllBytes(Paths.get(idpMeta.toURI()));
|
||||
String body = new String(content, Charset.forName("utf-8"));
|
||||
form.addFormData("file", body, MediaType.APPLICATION_XML_TYPE, "saml-idp-metadata-disabled.xml");
|
||||
|
||||
Map<String, String> result = managedRealm.admin().identityProviders().importFrom(form);
|
||||
assertSamlImport(result, SIGNING_CERT_1, false, true);
|
||||
|
||||
// Create new SAML identity provider using configuration retrieved from import-config
|
||||
String id = create(createRep("saml", "saml", false, result));
|
||||
|
||||
IdentityProviderResource provider = managedRealm.admin().identityProviders().get("saml");
|
||||
IdentityProviderRepresentation rep = provider.toRepresentation();
|
||||
assertCreatedSamlIdp(rep, false, true);
|
||||
|
||||
managedRealm.cleanup().add(r -> r.identityProviders().get(id).remove());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSamlImportAndExportMultipleSigningKeys() throws URISyntaxException, IOException, ParsingException {
|
||||
|
||||
// Use import-config to convert IDPSSODescriptor file into key value pairs
|
||||
// to use when creating a SAML Identity Provider
|
||||
MultipartFormDataOutput form = new MultipartFormDataOutput();
|
||||
form.addFormData("providerId", "saml", MediaType.TEXT_PLAIN_TYPE);
|
||||
|
||||
URL idpMeta = IdentityProviderSamlTest.class.getResource("saml-idp-metadata-two-signing-certs.xml");
|
||||
byte [] content = Files.readAllBytes(Paths.get(idpMeta.toURI()));
|
||||
String body = new String(content, Charset.forName("utf-8"));
|
||||
form.addFormData("file", body, MediaType.APPLICATION_XML_TYPE, "saml-idp-metadata-two-signing-certs");
|
||||
|
||||
Map<String, String> result = managedRealm.admin().identityProviders().importFrom(form);
|
||||
assertSamlImport(result, SIGNING_CERT_1 + "," + SIGNING_CERT_2, true, true);
|
||||
|
||||
// Create new SAML identity provider using configuration retrieved from import-config
|
||||
String id = create(createRep("saml", "saml",true, result));
|
||||
|
||||
IdentityProviderResource provider = managedRealm.admin().identityProviders().get("saml");
|
||||
IdentityProviderRepresentation rep = provider.toRepresentation();
|
||||
assertCreatedSamlIdp(rep, true, true);
|
||||
|
||||
// Now list the providers - we should see the one just created
|
||||
List<IdentityProviderRepresentation> providers = managedRealm.admin().identityProviders().findAll();
|
||||
Assertions.assertNotNull(providers, "identityProviders not null");
|
||||
Assertions.assertEquals(1, providers.size(), "identityProviders instance count");
|
||||
assertEqual(rep, providers.get(0));
|
||||
|
||||
// Perform export, and make sure some of the values are like they're supposed to be
|
||||
Response response = managedRealm.admin().identityProviders().get("saml").export("xml");
|
||||
Assertions.assertEquals(200, response.getStatus());
|
||||
body = response.readEntity(String.class);
|
||||
response.close();
|
||||
|
||||
assertSamlExport(body, true);
|
||||
|
||||
managedRealm.cleanup().add(r -> r.identityProviders().get(id).remove());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSamlExportSignatureOff() throws URISyntaxException, IOException, ConfigurationException, ParsingException, ProcessingException {
|
||||
// Use import-config to convert IDPSSODescriptor file into key value pairs
|
||||
// to use when creating a SAML Identity Provider
|
||||
MultipartFormDataOutput form = new MultipartFormDataOutput();
|
||||
form.addFormData("providerId", "saml", MediaType.TEXT_PLAIN_TYPE);
|
||||
|
||||
URL idpMeta = IdentityProviderSamlTest.class.getResource("saml-idp-metadata.xml");
|
||||
byte [] content = Files.readAllBytes(Paths.get(idpMeta.toURI()));
|
||||
String body = new String(content, Charset.forName("utf-8"));
|
||||
form.addFormData("file", body, MediaType.APPLICATION_XML_TYPE, "saml-idp-metadata.xml");
|
||||
|
||||
Map<String, String> result = managedRealm.admin().identityProviders().importFrom(form);
|
||||
|
||||
// Explicitly disable SP Metadata Signature
|
||||
result.put(SAMLIdentityProviderConfig.SIGN_SP_METADATA, "false");
|
||||
|
||||
// Create new SAML identity provider using configuration retrieved from import-config
|
||||
IdentityProviderRepresentation idpRep = createRep("saml", "saml", true, result);
|
||||
String id = create(idpRep);
|
||||
|
||||
// Perform export, and make sure some of the values are like they're supposed to be
|
||||
Response response = managedRealm.admin().identityProviders().get("saml").export("xml");
|
||||
Assertions.assertEquals(200, response.getStatus());
|
||||
body = response.readEntity(String.class);
|
||||
response.close();
|
||||
|
||||
Document document = DocumentUtil.getDocument(body);
|
||||
Element signatureElement = DocumentUtil.getDirectChildElement(document.getDocumentElement(), XMLDSIG_NSURI.get(), "Signature");
|
||||
Assertions.assertNull(signatureElement);
|
||||
|
||||
managedRealm.cleanup().add(r -> r.identityProviders().get(id).remove());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSamlExportSignatureOn() throws URISyntaxException, IOException, ConfigurationException, ParsingException, ProcessingException, CertificateEncodingException {
|
||||
// Use import-config to convert IDPSSODescriptor file into key value pairs
|
||||
// to use when creating a SAML Identity Provider
|
||||
MultipartFormDataOutput form = new MultipartFormDataOutput();
|
||||
form.addFormData("providerId", "saml", MediaType.TEXT_PLAIN_TYPE);
|
||||
|
||||
URL idpMeta = IdentityProviderSamlTest.class.getResource("saml-idp-metadata.xml");
|
||||
byte [] content = Files.readAllBytes(Paths.get(idpMeta.toURI()));
|
||||
String body = new String(content, Charset.forName("utf-8"));
|
||||
form.addFormData("file", body, MediaType.APPLICATION_XML_TYPE, "saml-idp-metadata.xml");
|
||||
|
||||
Map<String, String> result = managedRealm.admin().identityProviders().importFrom(form);
|
||||
|
||||
// Explicitly enable SP Metadata Signature
|
||||
result.put(SAMLIdentityProviderConfig.SIGN_SP_METADATA, "true");
|
||||
result.put(SAMLIdentityProviderConfig.XML_SIG_KEY_INFO_KEY_NAME_TRANSFORMER, XmlKeyInfoKeyNameTransformer.CERT_SUBJECT.name());
|
||||
|
||||
// Create new SAML identity provider using configuration retrieved from import-config
|
||||
IdentityProviderRepresentation idpRep = createRep("saml", "saml", true, result);
|
||||
String id = create(idpRep);
|
||||
|
||||
// Perform export, and make sure some of the values are like they're supposed to be
|
||||
Response response = managedRealm.admin().identityProviders().get("saml").export("xml");
|
||||
Assertions.assertEquals(200, response.getStatus());
|
||||
body = response.readEntity(String.class);
|
||||
response.close();
|
||||
|
||||
Document document = DocumentUtil.getDocument(body);
|
||||
|
||||
Element signatureElement = DocumentUtil.getDirectChildElement(document.getDocumentElement(), XMLDSIG_NSURI.get(), "Signature");
|
||||
assertThat("Signature not null", signatureElement, notNullValue());
|
||||
|
||||
Element keyInfoElement = DocumentUtil.getDirectChildElement(signatureElement, XMLDSIG_NSURI.get(), "KeyInfo");
|
||||
assertThat("KeyInfo not null", keyInfoElement, notNullValue());
|
||||
|
||||
Element x509DataElement = DocumentUtil.getDirectChildElement(keyInfoElement, XMLDSIG_NSURI.get(), "X509Data");
|
||||
assertThat("X509Data not null", x509DataElement, notNullValue());
|
||||
|
||||
Element x509CertificateElement = DocumentUtil.getDirectChildElement(x509DataElement, XMLDSIG_NSURI.get(), "X509Certificate");
|
||||
assertThat("X509Certificate not null", x509CertificateElement, notNullValue());
|
||||
|
||||
Element keyNameElement = DocumentUtil.getDirectChildElement(keyInfoElement, XMLDSIG_NSURI.get(), "KeyName");
|
||||
assertThat("KeyName not null", keyNameElement, notNullValue());
|
||||
|
||||
String activeSigCert = KeyUtils.findActiveSigningKey(managedRealm.admin(), Constants.DEFAULT_SIGNATURE_ALGORITHM).getCertificate();
|
||||
assertThat("activeSigCert not null", activeSigCert, notNullValue());
|
||||
|
||||
X509Certificate activeX509SigCert = XMLSignatureUtil.getX509CertificateFromKeyInfoString(activeSigCert);
|
||||
assertThat("KeyName matches subject DN",
|
||||
keyNameElement.getTextContent().trim(), equalTo(activeX509SigCert.getSubjectX500Principal().getName()));
|
||||
|
||||
assertThat("Signing cert matches active realm cert",
|
||||
x509CertificateElement.getTextContent().trim(), equalTo(Base64.getEncoder().encodeToString(activeX509SigCert.getEncoded())));
|
||||
|
||||
PublicKey activePublicSigKey = activeX509SigCert.getPublicKey();
|
||||
assertThat("Metadata signature is valid",
|
||||
new SAML2Signature().validate(document, new HardcodedKeyLocator(activePublicSigKey)), is(true));
|
||||
|
||||
managedRealm.cleanup().add(r -> r.identityProviders().get(id).remove());
|
||||
}
|
||||
|
||||
private void testSamlImport(String fileName, boolean postBindingResponse) throws URISyntaxException, IOException, ParsingException {
|
||||
// Use import-config to convert IDPSSODescriptor file into key value pairs
|
||||
// to use when creating a SAML Identity Provider
|
||||
MultipartFormDataOutput form = new MultipartFormDataOutput();
|
||||
form.addFormData("providerId", "saml", MediaType.TEXT_PLAIN_TYPE);
|
||||
|
||||
URL idpMeta = IdentityProviderSamlTest.class.getResource(fileName);
|
||||
byte [] content = Files.readAllBytes(Paths.get(idpMeta.toURI()));
|
||||
String body = new String(content, Charset.forName("utf-8"));
|
||||
form.addFormData("file", body, MediaType.APPLICATION_XML_TYPE, fileName);
|
||||
|
||||
Map<String, String> result = managedRealm.admin().identityProviders().importFrom(form);
|
||||
assertSamlImport(result, SIGNING_CERT_1, true, postBindingResponse);
|
||||
|
||||
// Create new SAML identity provider using configuration retrieved from import-config
|
||||
String id = create(createRep("saml", "saml",true, result));
|
||||
|
||||
IdentityProviderResource provider = managedRealm.admin().identityProviders().get("saml");
|
||||
IdentityProviderRepresentation rep = provider.toRepresentation();
|
||||
assertCreatedSamlIdp(rep, true, postBindingResponse);
|
||||
|
||||
// Now list the providers - we should see the one just created
|
||||
List<IdentityProviderRepresentation> providers = managedRealm.admin().identityProviders().findAll();
|
||||
Assertions.assertNotNull(providers, "identityProviders not null");
|
||||
Assertions.assertEquals(1, providers.size(), "identityProviders instance count");
|
||||
assertEqual(rep, providers.get(0));
|
||||
|
||||
managedRealm.cleanup().add(r -> r.identityProviders().get(id).remove());
|
||||
}
|
||||
|
||||
private void assertEqual(IdentityProviderRepresentation expected, IdentityProviderRepresentation actual) {
|
||||
//System.out.println("expected: " + expected);
|
||||
//System.out.println("actual: " + actual);
|
||||
Assertions.assertNotNull(expected, "expected IdentityProviderRepresentation not null");
|
||||
Assertions.assertNotNull(actual, "actual IdentityProviderRepresentation not null");
|
||||
Assertions.assertEquals(expected.getInternalId(), actual.getInternalId(), "internalId");
|
||||
Assertions.assertEquals(expected.getAlias(), actual.getAlias(), "alias");
|
||||
Assertions.assertEquals(expected.getProviderId(), actual.getProviderId(), "providerId");
|
||||
Assertions.assertEquals(expected.isEnabled(), actual.isEnabled(), "enabled");
|
||||
Assertions.assertEquals(expected.isHideOnLogin(), actual.isHideOnLogin(), "hideOnLogin");
|
||||
Assertions.assertEquals(expected.getFirstBrokerLoginFlowAlias(), actual.getFirstBrokerLoginFlowAlias(), "firstBrokerLoginFlowAlias");
|
||||
Assertions.assertEquals(expected.getConfig(), actual.getConfig(), "config");
|
||||
}
|
||||
|
||||
private void assertCreatedSamlIdp(IdentityProviderRepresentation idp, boolean enabled, boolean postBindingResponse) {
|
||||
//System.out.println("idp: " + idp);
|
||||
Assertions.assertNotNull(idp, "IdentityProviderRepresentation not null");
|
||||
Assertions.assertNotNull(idp.getInternalId(), "internalId");
|
||||
Assertions.assertEquals("saml", idp.getAlias(), "alias");
|
||||
Assertions.assertEquals("saml", idp.getProviderId(), "providerId");
|
||||
Assertions.assertEquals(enabled, idp.isEnabled(), "enabled");
|
||||
Assertions.assertTrue(idp.isHideOnLogin(), "hideOnLogin");
|
||||
Assertions.assertNull(idp.getFirstBrokerLoginFlowAlias(), "firstBrokerLoginFlowAlias");
|
||||
assertSamlConfig(idp.getConfig(), postBindingResponse, false);
|
||||
}
|
||||
|
||||
private void assertSamlConfig(Map<String, String> config, boolean postBindingResponse, boolean hasHideOnLoginPage) {
|
||||
// import endpoint simply converts IDPSSODescriptor into key value pairs.
|
||||
// check that saml-idp-metadata.xml was properly converted into key value pairs
|
||||
//System.out.println(config);
|
||||
List<String> keys = new ArrayList<>(List.of("syncMode",
|
||||
"validateSignature",
|
||||
"singleLogoutServiceUrl",
|
||||
"postBindingLogout",
|
||||
"postBindingResponse",
|
||||
"artifactBindingResponse",
|
||||
"postBindingAuthnRequest",
|
||||
"singleSignOnServiceUrl",
|
||||
"artifactResolutionServiceUrl",
|
||||
"wantAuthnRequestsSigned",
|
||||
"nameIDPolicyFormat",
|
||||
"signingCertificate",
|
||||
"addExtensionsElementWithKeyInfo",
|
||||
"loginHint",
|
||||
"idpEntityId"
|
||||
));
|
||||
if (hasHideOnLoginPage) {
|
||||
keys.add("hideOnLoginPage");
|
||||
}
|
||||
assertThat(config.keySet(), containsInAnyOrder(keys.toArray()));
|
||||
assertThat(config, hasEntry("validateSignature", "true"));
|
||||
assertThat(config, hasEntry("singleLogoutServiceUrl", "http://localhost:8080/auth/realms/master/protocol/saml"));
|
||||
assertThat(config, hasEntry("artifactResolutionServiceUrl", "http://localhost:8080/auth/realms/master/protocol/saml/resolve"));
|
||||
assertThat(config, hasEntry("postBindingResponse", Boolean.toString(postBindingResponse)));
|
||||
assertThat(config, hasEntry("artifactBindingResponse", "false"));
|
||||
assertThat(config, hasEntry("postBindingAuthnRequest", Boolean.toString(postBindingResponse)));
|
||||
assertThat(config, hasEntry("singleSignOnServiceUrl", "http://localhost:8080/auth/realms/master/protocol/saml"));
|
||||
assertThat(config, hasEntry("wantAuthnRequestsSigned", "true"));
|
||||
assertThat(config, hasEntry("addExtensionsElementWithKeyInfo", "false"));
|
||||
assertThat(config, hasEntry("nameIDPolicyFormat", "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"));
|
||||
if (hasHideOnLoginPage) {
|
||||
assertThat(config, hasEntry("hideOnLoginPage", "true"));
|
||||
}
|
||||
assertThat(config, hasEntry("idpEntityId", "http://localhost:8080/auth/realms/master"));
|
||||
assertThat(config, hasEntry(is("signingCertificate"), notNullValue()));
|
||||
}
|
||||
|
||||
private void assertSamlImport(Map<String, String> config, String expectedSigningCertificates, boolean enabled, boolean postBindingResponse) {
|
||||
//firtsly check and remove enabledFromMetadata from config
|
||||
boolean enabledFromMetadata = Boolean.valueOf(config.get(SAMLIdentityProviderConfig.ENABLED_FROM_METADATA));
|
||||
config.remove(SAMLIdentityProviderConfig.ENABLED_FROM_METADATA);
|
||||
Assert.assertEquals(enabledFromMetadata,enabled);
|
||||
assertSamlConfig(config, postBindingResponse, true);
|
||||
assertThat(config, hasEntry("signingCertificate", expectedSigningCertificates));
|
||||
}
|
||||
|
||||
private void assertSamlExport(String body, boolean postBindingResponse) throws ParsingException, URISyntaxException {
|
||||
//System.out.println(body);
|
||||
|
||||
Object entBody = SAMLParser.getInstance().parse(
|
||||
new ByteArrayInputStream(body.getBytes(Charset.forName("utf-8"))));
|
||||
|
||||
Assertions.assertEquals(EntityDescriptorType.class, entBody.getClass(), "Parsed export type");
|
||||
EntityDescriptorType entity = (EntityDescriptorType) entBody;
|
||||
|
||||
Assertions.assertEquals(keycloakUrls.getBaseUrl() + "/realms/default", entity.getEntityID(), "EntityID");
|
||||
|
||||
Assertions.assertNotNull(entity.getChoiceType(), "ChoiceType not null");
|
||||
Assertions.assertEquals(1, entity.getChoiceType().size(), "ChoiceType.size");
|
||||
|
||||
List<EntityDescriptorType.EDTDescriptorChoiceType> descriptors = entity.getChoiceType().get(0).getDescriptors();
|
||||
Assertions.assertNotNull(descriptors, "Descriptors not null");
|
||||
Assertions.assertEquals(1, descriptors.size(), "Descriptors.size");
|
||||
|
||||
SPSSODescriptorType desc = descriptors.get(0).getSpDescriptor();
|
||||
Assertions.assertNotNull(desc, "SPSSODescriptor not null");
|
||||
|
||||
Assertions.assertTrue(desc.isAuthnRequestsSigned(), "AuthnRequestsSigned");
|
||||
|
||||
Set<String> expected = new HashSet<>(Arrays.asList(
|
||||
"urn:oasis:names:tc:SAML:2.0:protocol"));
|
||||
|
||||
Set<String> actual = new HashSet<>(desc.getProtocolSupportEnumeration());
|
||||
|
||||
Assertions.assertEquals(expected, actual, "ProtocolSupportEnumeration");
|
||||
|
||||
Assertions.assertNotNull(desc.getAssertionConsumerService(), "AssertionConsumerService not null");
|
||||
Assertions.assertEquals(3, desc.getAssertionConsumerService().size(), "AssertionConsumerService.size");
|
||||
|
||||
IndexedEndpointType endpoint = desc.getAssertionConsumerService().get(0);
|
||||
final URI samlUri = new URI(keycloakUrls.getBase() + "/realms/default/broker/saml/endpoint");
|
||||
|
||||
Assertions.assertEquals(samlUri, endpoint.getLocation(), "AssertionConsumerService.Location");
|
||||
Assert.assertEquals(postBindingResponse ? JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.getUri() : JBossSAMLURIConstants.SAML_HTTP_REDIRECT_BINDING.getUri(),
|
||||
endpoint.getBinding(), "AssertionConsumerService.Binding");
|
||||
Assertions.assertTrue(endpoint.isIsDefault(), "AssertionConsumerService.isDefault");
|
||||
|
||||
endpoint = desc.getAssertionConsumerService().get(1);
|
||||
|
||||
Assertions.assertEquals(samlUri, endpoint.getLocation(), "AssertionConsumerService.Location");
|
||||
Assert.assertEquals(postBindingResponse ? JBossSAMLURIConstants.SAML_HTTP_REDIRECT_BINDING.getUri() : JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.getUri(),
|
||||
endpoint.getBinding(), "AssertionConsumerService.Binding");
|
||||
|
||||
endpoint = desc.getAssertionConsumerService().get(2);
|
||||
|
||||
Assertions.assertEquals(samlUri, endpoint.getLocation(), "AssertionConsumerService.Location");
|
||||
Assertions.assertEquals(JBossSAMLURIConstants.SAML_HTTP_ARTIFACT_BINDING.getUri(), endpoint.getBinding(), "AssertionConsumerService.Binding");
|
||||
|
||||
Assertions.assertNotNull(desc.getSingleLogoutService(), "SingleLogoutService not null");
|
||||
Assertions.assertEquals(2, desc.getSingleLogoutService().size(), "SingleLogoutService.size");
|
||||
|
||||
EndpointType sloEndpoint = desc.getSingleLogoutService().get(0);
|
||||
|
||||
Assertions.assertEquals(samlUri, sloEndpoint.getLocation(), "SingleLogoutService.Location");
|
||||
Assertions.assertEquals(JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.getUri(), sloEndpoint.getBinding(), "SingleLogoutService.Binding");
|
||||
|
||||
sloEndpoint = desc.getSingleLogoutService().get(1);
|
||||
|
||||
Assertions.assertEquals(samlUri, sloEndpoint.getLocation(), "SingleLogoutService.Location");
|
||||
Assertions.assertEquals(JBossSAMLURIConstants.SAML_HTTP_REDIRECT_BINDING.getUri(), sloEndpoint.getBinding(), "SingleLogoutService.Binding");
|
||||
|
||||
Assertions.assertNotNull(desc.getKeyDescriptor(), "KeyDescriptor not null");
|
||||
Assertions.assertEquals(1, desc.getKeyDescriptor().size(), "KeyDescriptor.size");
|
||||
KeyDescriptorType keyDesc = desc.getKeyDescriptor().get(0);
|
||||
assertThat(keyDesc, notNullValue());
|
||||
assertThat(keyDesc.getUse(), equalTo(KeyTypes.SIGNING));
|
||||
NodeList cert = keyDesc.getKeyInfo().getElementsByTagNameNS(XMLSignature.XMLNS, "X509Certificate");
|
||||
assertThat("KeyDescriptor.Signing.Cert existence", cert.getLength(), is(1));
|
||||
}
|
||||
}
|
||||
1315
tests/base/src/test/java/org/keycloak/tests/admin/identityprovider/IdentityProviderTest.java
Executable file → Normal file
1315
tests/base/src/test/java/org/keycloak/tests/admin/identityprovider/IdentityProviderTest.java
Executable file → Normal file
File diff suppressed because it is too large
Load Diff
@@ -44,5 +44,9 @@
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-server-spi-private</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-services</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package org.keycloak.testsuite.broker.oidc;
|
||||
|
||||
import org.keycloak.broker.oidc.KeycloakOIDCIdentityProvider;
|
||||
import org.keycloak.broker.oidc.KeycloakOIDCIdentityProviderFactory;
|
||||
import org.keycloak.broker.oidc.OIDCIdentityProviderConfig;
|
||||
import org.keycloak.broker.provider.IdentityProviderMapper;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Daniel Fesenmeyer <daniel.fesenmeyer@bosch.com>
|
||||
*/
|
||||
public class OverwrittenMappersTestIdentityProvider extends KeycloakOIDCIdentityProvider {
|
||||
|
||||
public OverwrittenMappersTestIdentityProvider(KeycloakSession session, OIDCIdentityProviderConfig config) {
|
||||
super(session, config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMapperSupported(IdentityProviderMapper mapper) {
|
||||
List<String> compatibleIdps = Arrays.asList(mapper.getCompatibleProviders());
|
||||
|
||||
// provide the same mappers as are available for the parent provider (Keycloak-OIDC)
|
||||
return compatibleIdps.contains(IdentityProviderMapper.ANY_PROVIDER)
|
||||
|| compatibleIdps.contains(KeycloakOIDCIdentityProviderFactory.PROVIDER_ID);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2020 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.broker.oidc;
|
||||
|
||||
import org.keycloak.broker.oidc.KeycloakOIDCIdentityProvider;
|
||||
import org.keycloak.broker.oidc.OIDCIdentityProviderConfig;
|
||||
import org.keycloak.broker.oidc.OIDCIdentityProviderFactory;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
|
||||
/**
|
||||
* @author Daniel Fesenmeyer <daniel.fesenmeyer@bosch.com>
|
||||
*/
|
||||
public class OverwrittenMappersTestIdentityProviderFactory extends OIDCIdentityProviderFactory {
|
||||
|
||||
public static final String PROVIDER_ID = "overwritten-mappers-test-id-idp";
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return PROVIDER_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeycloakOIDCIdentityProvider create(KeycloakSession session, IdentityProviderModel model) {
|
||||
return new OverwrittenMappersTestIdentityProvider(session, new OIDCIdentityProviderConfig(model));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return PROVIDER_ID;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#
|
||||
# Copyright 2020 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.
|
||||
#
|
||||
|
||||
org.keycloak.testsuite.broker.oidc.OverwrittenMappersTestIdentityProviderFactory
|
||||
@@ -15,7 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.testsuite.broker;
|
||||
package org.keycloak.testsuite.util.broker;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@@ -44,6 +44,7 @@ import org.keycloak.testsuite.Assert;
|
||||
import org.keycloak.testsuite.client.resources.TestingCacheResource;
|
||||
import org.keycloak.testsuite.updaters.ClientAttributeUpdater;
|
||||
import org.keycloak.testsuite.util.AccountHelper;
|
||||
import org.keycloak.testsuite.util.broker.OIDCIdentityProviderConfigRep;
|
||||
import org.keycloak.testsuite.util.oauth.OAuthClient;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
@@ -15,6 +15,7 @@ import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
|
||||
import org.keycloak.testsuite.util.ClientBuilder;
|
||||
import org.keycloak.testsuite.util.broker.OIDCIdentityProviderConfigRep;
|
||||
import org.keycloak.testsuite.util.oauth.AccessTokenResponse;
|
||||
import org.keycloak.testsuite.util.oauth.AuthorizationEndpointResponse;
|
||||
|
||||
|
||||
@@ -75,6 +75,7 @@ import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
|
||||
import org.keycloak.testsuite.arquillian.annotation.EnableFeatures;
|
||||
import org.keycloak.testsuite.updaters.IdentityProviderAttributeUpdater;
|
||||
import org.keycloak.testsuite.util.AdminClientUtil;
|
||||
import org.keycloak.testsuite.util.broker.OIDCIdentityProviderConfigRep;
|
||||
import org.keycloak.testsuite.util.oauth.OAuthClient;
|
||||
import org.keycloak.testsuite.util.ServerURLs;
|
||||
import org.keycloak.util.BasicAuthHelper;
|
||||
|
||||
Reference in New Issue
Block a user