Partial import feature does not import Identity Provider mappers in Keycloak #12861

This commit is contained in:
Douglas Palmer 2022-07-13 10:25:22 -07:00 committed by Marek Posolda
parent 98ac3829d6
commit adeef6c2a0
5 changed files with 179 additions and 6 deletions

View file

@ -36,6 +36,7 @@ public class PartialImportRepresentation {
protected List<GroupRepresentation> groups; protected List<GroupRepresentation> groups;
protected List<ClientRepresentation> clients; protected List<ClientRepresentation> clients;
protected List<IdentityProviderRepresentation> identityProviders; protected List<IdentityProviderRepresentation> identityProviders;
protected List<IdentityProviderMapperRepresentation> identityProviderMappers;
protected RolesRepresentation roles; protected RolesRepresentation roles;
public boolean hasUsers() { public boolean hasUsers() {
@ -107,6 +108,14 @@ public class PartialImportRepresentation {
this.identityProviders = identityProviders; this.identityProviders = identityProviders;
} }
public List<IdentityProviderMapperRepresentation> getIdentityProviderMappers() {
return identityProviderMappers;
}
public void setIdentityProviderMappers(List<IdentityProviderMapperRepresentation> identityProviderMappers) {
this.identityProviderMappers = identityProviderMappers;
}
public RolesRepresentation getRoles() { public RolesRepresentation getRoles() {
return roles; return roles;
} }

View file

@ -0,0 +1,84 @@
/*
* 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.partialimport;
import org.keycloak.models.IdentityProviderMapperModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.models.utils.RepresentationToModel;
import org.keycloak.representations.idm.IdentityProviderMapperRepresentation;
import org.keycloak.representations.idm.PartialImportRepresentation;
import java.util.List;
/**
* PartialImport handler for Identitiy Provider Mappers.
*
* @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
*/
public class IdentityProviderMappersPartialImport extends AbstractPartialImport<IdentityProviderMapperRepresentation> {
@Override
public List<IdentityProviderMapperRepresentation> getRepList(PartialImportRepresentation partialImportRep) {
return partialImportRep.getIdentityProviderMappers();
}
@Override
public String getName(IdentityProviderMapperRepresentation idpMapperRep) {
return idpMapperRep.getName();
}
@Override
public String getModelId(RealmModel realm, KeycloakSession session, IdentityProviderMapperRepresentation idpMapperRep) {
return realm.getIdentityProviderMapperByName(idpMapperRep.getIdentityProviderAlias(), idpMapperRep.getName()).getId();
}
@Override
public boolean exists(RealmModel realm, KeycloakSession session, IdentityProviderMapperRepresentation idpMapperRep) {
return realm.getIdentityProviderMapperByName(idpMapperRep.getIdentityProviderAlias(), idpMapperRep.getName()) != null;
}
@Override
public String existsMessage(RealmModel realm, IdentityProviderMapperRepresentation idpMapperRep) {
return "Identity Provider Mapper'" + getName(idpMapperRep) + "' already exists.";
}
@Override
public ResourceType getResourceType() {
return ResourceType.IDP_MAPPER;
}
@Override
public void remove(RealmModel realm, KeycloakSession session, IdentityProviderMapperRepresentation idpMapperRep) {
IdentityProviderMapperModel idpMapper = RepresentationToModel.toModel(idpMapperRep);
realm.removeIdentityProviderMapper(idpMapper);
}
@Override
public void create(RealmModel realm, KeycloakSession session, IdentityProviderMapperRepresentation idpMapperRep) {
IdentityProviderMapperModel existing = realm.getIdentityProviderMapperByName(idpMapperRep.getIdentityProviderAlias(), idpMapperRep.getName());
if(existing != null) {
realm.removeIdentityProviderMapper(existing);
}
idpMapperRep.setId(KeycloakModelUtils.generateId());
IdentityProviderMapperModel idpMapper = RepresentationToModel.toModel(idpMapperRep);
realm.addIdentityProviderMapper(idpMapper);
}
}

View file

@ -54,6 +54,7 @@ public class PartialImportManager {
partialImports.add(new ClientsPartialImport()); partialImports.add(new ClientsPartialImport());
partialImports.add(new RolesPartialImport()); partialImports.add(new RolesPartialImport());
partialImports.add(new IdentityProvidersPartialImport()); partialImports.add(new IdentityProvidersPartialImport());
partialImports.add(new IdentityProviderMappersPartialImport());
partialImports.add(new GroupsPartialImport()); partialImports.add(new GroupsPartialImport());
partialImports.add(new UsersPartialImport()); partialImports.add(new UsersPartialImport());
} }

View file

@ -23,7 +23,7 @@ package org.keycloak.partialimport;
* @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc. * @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
*/ */
public enum ResourceType { public enum ResourceType {
USER, GROUP, CLIENT, IDP, REALM_ROLE, CLIENT_ROLE; USER, GROUP, CLIENT, IDP, IDP_MAPPER, REALM_ROLE, CLIENT_ROLE;
/** /**
* Used to create the admin path in events. * Used to create the admin path in events.
@ -36,6 +36,7 @@ public enum ResourceType {
case GROUP: return "groups"; case GROUP: return "groups";
case CLIENT: return "clients"; case CLIENT: return "clients";
case IDP: return "identity-provider-settings"; case IDP: return "identity-provider-settings";
case IDP_MAPPER: return "mappers";
case REALM_ROLE: return "realms"; case REALM_ROLE: return "realms";
case CLIENT_ROLE: return "clients"; case CLIENT_ROLE: return "clients";
default: return ""; default: return "";
@ -49,6 +50,7 @@ public enum ResourceType {
case GROUP: return "Group"; case GROUP: return "Group";
case CLIENT: return "Client"; case CLIENT: return "Client";
case IDP: return "Identity Provider"; case IDP: return "Identity Provider";
case IDP_MAPPER: return "Identity Provider Mapper";
case REALM_ROLE: return "Realm Role"; case REALM_ROLE: return "Realm Role";
case CLIENT_ROLE: return "Client Role"; case CLIENT_ROLE: return "Client Role";
default: return super.toString(); default: return super.toString();

View file

@ -31,6 +31,7 @@ import org.keycloak.partialimport.PartialImportResults;
import org.keycloak.representations.idm.AdminEventRepresentation; import org.keycloak.representations.idm.AdminEventRepresentation;
import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.GroupRepresentation; import org.keycloak.representations.idm.GroupRepresentation;
import org.keycloak.representations.idm.IdentityProviderMapperRepresentation;
import org.keycloak.representations.idm.IdentityProviderRepresentation; import org.keycloak.representations.idm.IdentityProviderRepresentation;
import org.keycloak.representations.idm.PartialImportRepresentation; import org.keycloak.representations.idm.PartialImportRepresentation;
import org.keycloak.representations.idm.PartialImportRepresentation.Policy; import org.keycloak.representations.idm.PartialImportRepresentation.Policy;
@ -42,7 +43,6 @@ import org.keycloak.testsuite.AbstractAuthTest;
import org.keycloak.testsuite.Assert; import org.keycloak.testsuite.Assert;
import org.keycloak.testsuite.ProfileAssume; import org.keycloak.testsuite.ProfileAssume;
import org.keycloak.testsuite.admin.ApiUtil; import org.keycloak.testsuite.admin.ApiUtil;
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
import org.keycloak.testsuite.util.AssertAdminEvents; import org.keycloak.testsuite.util.AssertAdminEvents;
import org.keycloak.testsuite.util.RealmBuilder; import org.keycloak.testsuite.util.RealmBuilder;
@ -303,7 +303,12 @@ public class PartialImportTest extends AbstractAuthTest {
} }
private void addProviders() { private void addProviders() {
addProviders(false);
}
private void addProviders(boolean withMappers) {
List<IdentityProviderRepresentation> providers = new ArrayList<>(); List<IdentityProviderRepresentation> providers = new ArrayList<>();
List<IdentityProviderMapperRepresentation> mappers = new ArrayList<>();
for (String alias : IDP_ALIASES) { for (String alias : IDP_ALIASES) {
IdentityProviderRepresentation idpRep = new IdentityProviderRepresentation(); IdentityProviderRepresentation idpRep = new IdentityProviderRepresentation();
@ -318,9 +323,31 @@ public class PartialImportTest extends AbstractAuthTest {
config.put("clientId", alias); config.put("clientId", alias);
idpRep.setConfig(config); idpRep.setConfig(config);
providers.add(idpRep); providers.add(idpRep);
if(withMappers) {
Map<String, String> mapConfig = new HashMap<>();
mapConfig.put("external.role", "IDP.TEST_ROLE");
mapConfig.put("syncMode", "FORCE");
mapConfig.put("role", "TEST_ROLE");
IdentityProviderMapperRepresentation idpMapRep = new IdentityProviderMapperRepresentation();
idpMapRep.setName(alias+"_mapper");
idpMapRep.setIdentityProviderAlias(alias);
idpMapRep.setIdentityProviderMapper("keycloak-oidc-role-to-role-idp-mapper");
idpMapRep.setConfig(mapConfig);
mappers.add(idpMapRep);
}
} }
piRep.setIdentityProviders(providers); piRep.setIdentityProviders(providers);
if (withMappers) {
piRep.setIdentityProviderMappers(mappers);
}
}
private void addProviderMappers() {
addProviders(true);
} }
private List<RoleRepresentation> makeRoles(String prefix) { private List<RoleRepresentation> makeRoles(String prefix) {
@ -517,6 +544,30 @@ public class PartialImportTest extends AbstractAuthTest {
} }
} }
@Test
public void testAddProviderMappers() {
setFail();
addProviderMappers();
PartialImportResults results = doImport();
assertEquals(IDP_ALIASES.length*2, results.getAdded());
for (PartialImportResult result : results.getResults()) {
if (ResourceType.IDP.equals(result.getResourceType())) {
String id = result.getId();
IdentityProviderResource idpRsc = testRealmResource().identityProviders().get(id);
IdentityProviderMapperRepresentation idpMap = idpRsc.getMappers().get(0);
String alias = idpMap.getIdentityProviderAlias();
assertTrue(Arrays.asList(IDP_ALIASES).contains(alias));
assertEquals(alias + "_mapper", idpMap.getName());
assertEquals("keycloak-oidc-role-to-role-idp-mapper", idpMap.getIdentityProviderMapper());
assertEquals("IDP.TEST_ROLE", idpMap.getConfig().get("external.role"));
assertEquals("FORCE", idpMap.getConfig().get("syncMode"));
assertEquals("TEST_ROLE", idpMap.getConfig().get("role"));
}
}
}
@Test @Test
public void testAddRealmRoles() { public void testAddRealmRoles() {
setFail(); setFail();
@ -581,6 +632,12 @@ public class PartialImportTest extends AbstractAuthTest {
testFail(); testFail();
} }
@Test
public void testAddProviderMappersFail() {
addProviderMappers();
testFail();
}
@Test @Test
public void testAddRealmRolesFail() { public void testAddRealmRolesFail() {
addRealmRoles(); addRealmRoles();
@ -594,12 +651,16 @@ public class PartialImportTest extends AbstractAuthTest {
} }
private void testSkip() { private void testSkip() {
testSkip(NUM_ENTITIES);
}
private void testSkip(int numberEntities) {
setSkip(); setSkip();
PartialImportResults results = doImport(); PartialImportResults results = doImport();
assertEquals(NUM_ENTITIES, results.getAdded()); assertEquals(numberEntities, results.getAdded());
results = doImport(); results = doImport();
assertEquals(NUM_ENTITIES, results.getSkipped()); assertEquals(numberEntities, results.getSkipped());
} }
@Test @Test
@ -637,6 +698,12 @@ public class PartialImportTest extends AbstractAuthTest {
testSkip(); testSkip();
} }
@Test
public void testAddProviderMappersSkip() {
addProviderMappers();
testSkip(NUM_ENTITIES*2);
}
@Test @Test
public void testAddRealmRolesSkip() { public void testAddRealmRolesSkip() {
addRealmRoles(); addRealmRoles();
@ -650,12 +717,16 @@ public class PartialImportTest extends AbstractAuthTest {
} }
private void testOverwrite() { private void testOverwrite() {
testOverwrite(NUM_ENTITIES);
}
private void testOverwrite(int numberEntities) {
setOverwrite(); setOverwrite();
PartialImportResults results = doImport(); PartialImportResults results = doImport();
assertEquals(NUM_ENTITIES, results.getAdded()); assertEquals(numberEntities, results.getAdded());
results = doImport(); results = doImport();
assertEquals(NUM_ENTITIES, results.getOverwritten()); assertEquals(numberEntities, results.getOverwritten());
} }
@Test @Test
@ -717,6 +788,12 @@ public class PartialImportTest extends AbstractAuthTest {
testOverwrite(); testOverwrite();
} }
@Test
public void testAddProviderMappersOverwrite() {
addProviderMappers();
testOverwrite(NUM_ENTITIES*2);
}
@Test @Test
public void testAddRealmRolesOverwrite() { public void testAddRealmRolesOverwrite() {
addRealmRoles(); addRealmRoles();