RHSSO-402 need a way to dump configuration (including ldap provider config) to a file

This commit is contained in:
Marko Strukelj 2017-04-26 14:27:38 +02:00
parent 16aaa3bf01
commit 7d0ca42c6c
13 changed files with 1642 additions and 101 deletions

View file

@ -163,8 +163,13 @@ public interface RealmResource {
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response partialImport(PartialImportRepresentation rep);
Response partialImport(PartialImportRepresentation rep);
@Path("partial-export")
@POST
@Produces(MediaType.APPLICATION_JSON)
RealmRepresentation partialExport(@QueryParam("exportGroupsAndRoles") Boolean exportGroupsAndRoles,
@QueryParam("exportClients") Boolean exportClients);
@Path("authentication")
@Consumes(MediaType.APPLICATION_JSON)
AuthenticationManagementResource flows();

View file

@ -54,7 +54,7 @@ public class ComponentUtil {
return getComponentFactory(session, component.getProviderType(), component.getProviderId());
}
private static Map<String, ProviderConfigProperty> getComponentConfigProperties(KeycloakSession session, String providerType, String providerId) {
public static Map<String, ProviderConfigProperty> getComponentConfigProperties(KeycloakSession session, String providerType, String providerId) {
try {
ComponentFactory componentFactory = getComponentFactory(session, providerType, providerId);
List<ProviderConfigProperty> l = componentFactory.getConfigProperties();

View file

@ -17,15 +17,18 @@
package org.keycloak.models.utils;
import org.keycloak.common.util.MultivaluedHashMap;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.ComponentExportRepresentation;
import org.keycloak.representations.idm.ComponentRepresentation;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.representations.idm.IdentityProviderRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@ -70,4 +73,86 @@ public class StripSecretsUtils {
return rep;
}
public static RealmRepresentation stripForExport(KeycloakSession session, RealmRepresentation rep) {
strip(rep);
List<ClientRepresentation> clients = rep.getClients();
if (clients != null) {
for (ClientRepresentation c : clients) {
strip(c);
}
}
List<IdentityProviderRepresentation> providers = rep.getIdentityProviders();
if (providers != null) {
for (IdentityProviderRepresentation r : providers) {
strip(r);
}
}
MultivaluedHashMap<String, ComponentExportRepresentation> components = rep.getComponents();
if (components != null) {
for (Map.Entry<String, List<ComponentExportRepresentation>> ent : components.entrySet()) {
for (ComponentExportRepresentation c : ent.getValue()) {
strip(session, ent.getKey(), c);
}
}
}
List<UserRepresentation> users = rep.getUsers();
if (users != null) {
for (UserRepresentation u: users) {
strip(u);
}
}
users = rep.getFederatedUsers();
if (users != null) {
for (UserRepresentation u: users) {
strip(u);
}
}
return rep;
}
public static UserRepresentation strip(UserRepresentation user) {
user.setCredentials(null);
return user;
}
public static ClientRepresentation strip(ClientRepresentation rep) {
if (rep.getSecret() != null) {
rep.setSecret(ComponentRepresentation.SECRET_VALUE);
}
return rep;
}
public static ComponentExportRepresentation strip(KeycloakSession session, String providerType, ComponentExportRepresentation rep) {
Map<String, ProviderConfigProperty> configProperties = ComponentUtil.getComponentConfigProperties(session, providerType, rep.getProviderId());
if (rep.getConfig() == null) {
return rep;
}
Iterator<Map.Entry<String, List<String>>> itr = rep.getConfig().entrySet().iterator();
while (itr.hasNext()) {
Map.Entry<String, List<String>> next = itr.next();
ProviderConfigProperty configProperty = configProperties.get(next.getKey());
if (configProperty != null) {
if (configProperty.isSecret()) {
next.setValue(Collections.singletonList(ComponentRepresentation.SECRET_VALUE));
}
} else {
itr.remove();
}
}
MultivaluedHashMap<String, ComponentExportRepresentation> sub = rep.getSubComponents();
for (Map.Entry<String, List<ComponentExportRepresentation>> ent: sub.entrySet()) {
for (ComponentExportRepresentation c: ent.getValue()) {
strip(session, ent.getKey(), c);
}
}
return rep;
}
}

View file

@ -0,0 +1,44 @@
package org.keycloak.exportimport.util;
/**
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
*/
public class ExportOptions {
private boolean usersIncluded = true;
private boolean clientsIncluded = true;
private boolean groupsAndRolesIncluded = true;
public ExportOptions() {
}
public ExportOptions(boolean users, boolean clients, boolean groupsAndRoles) {
usersIncluded = users;
clientsIncluded = clients;
groupsAndRolesIncluded = groupsAndRoles;
}
public boolean isUsersIncluded() {
return usersIncluded;
}
public boolean isClientsIncluded() {
return clientsIncluded;
}
public boolean isGroupsAndRolesIncluded() {
return groupsAndRolesIncluded;
}
public void setUsersIncluded(boolean value) {
usersIncluded = value;
}
public void setClientsIncluded(boolean value) {
clientsIncluded = value;
}
public void setGroupsAndRolesIncluded(boolean value) {
groupsAndRolesIncluded = value;
}
}

View file

@ -23,6 +23,7 @@ import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
@ -84,7 +85,17 @@ import com.fasterxml.jackson.databind.SerializationFeature;
public class ExportUtils {
public static RealmRepresentation exportRealm(KeycloakSession session, RealmModel realm, boolean includeUsers) {
RealmRepresentation rep = ModelToRepresentation.toRepresentation(realm, true);
ExportOptions opts = new ExportOptions(false, true, true);
if (includeUsers) {
opts.setUsersIncluded(true);
}
return exportRealm(session, realm, opts);
}
public static RealmRepresentation exportRealm(KeycloakSession session, RealmModel realm, ExportOptions options) {
RealmRepresentation rep = ModelToRepresentation.toRepresentation(realm, false);
ModelToRepresentation.exportAuthenticationFlows(realm, rep);
ModelToRepresentation.exportRequiredActions(realm, rep);
// Project/product version
rep.setKeycloakVersion(Version.VERSION);
@ -99,73 +110,87 @@ public class ExportUtils {
rep.setClientTemplates(templateReps);
// Clients
List<ClientModel> clients = realm.getClients();
List<ClientRepresentation> clientReps = new ArrayList<>();
for (ClientModel app : clients) {
ClientRepresentation clientRep = exportClient(session, app);
clientReps.add(clientRep);
}
rep.setClients(clientReps);
List<ClientModel> clients = Collections.emptyList();
// Roles
List<RoleRepresentation> realmRoleReps = null;
Map<String, List<RoleRepresentation>> clientRolesReps = new HashMap<>();
Set<RoleModel> realmRoles = realm.getRoles();
if (realmRoles != null && realmRoles.size() > 0) {
realmRoleReps = exportRoles(realmRoles);
}
for (ClientModel client : clients) {
Set<RoleModel> currentAppRoles = client.getRoles();
List<RoleRepresentation> currentAppRoleReps = exportRoles(currentAppRoles);
clientRolesReps.put(client.getClientId(), currentAppRoleReps);
if (options.isClientsIncluded()) {
clients = realm.getClients();
List<ClientRepresentation> clientReps = new ArrayList<>();
for (ClientModel app : clients) {
ClientRepresentation clientRep = exportClient(session, app);
clientReps.add(clientRep);
}
rep.setClients(clientReps);
}
RolesRepresentation rolesRep = new RolesRepresentation();
if (realmRoleReps != null) {
rolesRep.setRealm(realmRoleReps);
// Groups and Roles
if (options.isGroupsAndRolesIncluded()) {
ModelToRepresentation.exportGroups(realm, rep);
List<RoleRepresentation> realmRoleReps = null;
Map<String, List<RoleRepresentation>> clientRolesReps = new HashMap<>();
Set<RoleModel> realmRoles = realm.getRoles();
if (realmRoles != null && realmRoles.size() > 0) {
realmRoleReps = exportRoles(realmRoles);
}
RolesRepresentation rolesRep = new RolesRepresentation();
if (realmRoleReps != null) {
rolesRep.setRealm(realmRoleReps);
}
if (options.isClientsIncluded()) {
for (ClientModel client : clients) {
Set<RoleModel> currentAppRoles = client.getRoles();
List<RoleRepresentation> currentAppRoleReps = exportRoles(currentAppRoles);
clientRolesReps.put(client.getClientId(), currentAppRoleReps);
}
if (clientRolesReps.size() > 0) {
rolesRep.setClient(clientRolesReps);
}
}
rep.setRoles(rolesRep);
}
if (clientRolesReps.size() > 0) {
rolesRep.setClient(clientRolesReps);
}
rep.setRoles(rolesRep);
// Scopes
List<ClientModel> allClients = new ArrayList<>(clients);
Map<String, List<ScopeMappingRepresentation>> clientScopeReps = new HashMap<>();
// Scopes of clients
for (ClientModel client : allClients) {
Set<RoleModel> clientScopes = client.getScopeMappings();
ScopeMappingRepresentation scopeMappingRep = null;
for (RoleModel scope : clientScopes) {
if (scope.getContainer() instanceof RealmModel) {
if (scopeMappingRep == null) {
scopeMappingRep = rep.clientScopeMapping(client.getClientId());
}
scopeMappingRep.role(scope.getName());
} else {
ClientModel app = (ClientModel)scope.getContainer();
String appName = app.getClientId();
List<ScopeMappingRepresentation> currentAppScopes = clientScopeReps.get(appName);
if (currentAppScopes == null) {
currentAppScopes = new ArrayList<>();
clientScopeReps.put(appName, currentAppScopes);
}
if (options.isClientsIncluded()) {
List<ClientModel> allClients = new ArrayList<>(clients);
ScopeMappingRepresentation currentClientScope = null;
for (ScopeMappingRepresentation scopeMapping : currentAppScopes) {
if (client.getClientId().equals(scopeMapping.getClient())) {
currentClientScope = scopeMapping;
break;
// Scopes of clients
for (ClientModel client : allClients) {
Set<RoleModel> clientScopes = client.getScopeMappings();
ScopeMappingRepresentation scopeMappingRep = null;
for (RoleModel scope : clientScopes) {
if (scope.getContainer() instanceof RealmModel) {
if (scopeMappingRep == null) {
scopeMappingRep = rep.clientScopeMapping(client.getClientId());
}
scopeMappingRep.role(scope.getName());
} else {
ClientModel app = (ClientModel) scope.getContainer();
String appName = app.getClientId();
List<ScopeMappingRepresentation> currentAppScopes = clientScopeReps.get(appName);
if (currentAppScopes == null) {
currentAppScopes = new ArrayList<>();
clientScopeReps.put(appName, currentAppScopes);
}
ScopeMappingRepresentation currentClientScope = null;
for (ScopeMappingRepresentation scopeMapping : currentAppScopes) {
if (client.getClientId().equals(scopeMapping.getClient())) {
currentClientScope = scopeMapping;
break;
}
}
if (currentClientScope == null) {
currentClientScope = new ScopeMappingRepresentation();
currentClientScope.setClient(client.getClientId());
currentAppScopes.add(currentClientScope);
}
currentClientScope.role(scope.getName());
}
if (currentClientScope == null) {
currentClientScope = new ScopeMappingRepresentation();
currentClientScope.setClient(client.getClientId());
currentAppScopes.add(currentClientScope);
}
currentClientScope.role(scope.getName());
}
}
}
@ -211,11 +236,11 @@ public class ExportUtils {
}
// Finally users if needed
if (includeUsers) {
if (options.isUsersIncluded()) {
List<UserModel> allUsers = session.users().getUsers(realm, true);
List<UserRepresentation> users = new LinkedList<>();
for (UserModel user : allUsers) {
UserRepresentation userRep = exportUser(session, realm, user);
UserRepresentation userRep = exportUser(session, realm, user, options);
users.add(userRep);
}
@ -225,7 +250,7 @@ public class ExportUtils {
List<UserRepresentation> federatedUsers = new LinkedList<>();
for (String userId : session.userFederatedStorage().getStoredUsers(realm, 0, -1)) {
UserRepresentation userRep = exportFederatedUser(session, realm, userId);
UserRepresentation userRep = exportFederatedUser(session, realm, userId, options);
federatedUsers.add(userRep);
}
if (federatedUsers.size() > 0) {
@ -462,7 +487,7 @@ public class ExportUtils {
* @param user
* @return fully exported user representation
*/
public static UserRepresentation exportUser(KeycloakSession session, RealmModel realm, UserModel user) {
public static UserRepresentation exportUser(KeycloakSession session, RealmModel realm, UserModel user, ExportOptions options) {
UserRepresentation userRep = ModelToRepresentation.toRepresentation(session, realm, user);
// Social links
@ -533,12 +558,13 @@ public class ExportUtils {
}
}
List<String> groups = new LinkedList<>();
for (GroupModel group : user.getGroups()) {
groups.add(ModelToRepresentation.buildGroupPath(group));
if (options.isGroupsAndRolesIncluded()) {
List<String> groups = new LinkedList<>();
for (GroupModel group : user.getGroups()) {
groups.add(ModelToRepresentation.buildGroupPath(group));
}
userRep.setGroups(groups);
}
userRep.setGroups(groups);
return userRep;
}
@ -569,6 +595,10 @@ public class ExportUtils {
// Streaming API
public static void exportUsersToStream(KeycloakSession session, RealmModel realm, List<UserModel> usersToExport, ObjectMapper mapper, OutputStream os) throws IOException {
exportUsersToStream(session, realm, usersToExport, mapper, os, new ExportOptions());
}
public static void exportUsersToStream(KeycloakSession session, RealmModel realm, List<UserModel> usersToExport, ObjectMapper mapper, OutputStream os, ExportOptions options) throws IOException {
JsonFactory factory = mapper.getFactory();
JsonGenerator generator = factory.createGenerator(os, JsonEncoding.UTF8);
try {
@ -582,7 +612,7 @@ public class ExportUtils {
generator.writeStartArray();
for (UserModel user : usersToExport) {
UserRepresentation userRep = ExportUtils.exportUser(session, realm, user);
UserRepresentation userRep = ExportUtils.exportUser(session, realm, user, options);
generator.writeObject(userRep);
}
@ -594,6 +624,10 @@ public class ExportUtils {
}
public static void exportFederatedUsersToStream(KeycloakSession session, RealmModel realm, List<String> usersToExport, ObjectMapper mapper, OutputStream os) throws IOException {
exportFederatedUsersToStream(session, realm, usersToExport, mapper, os, new ExportOptions());
}
public static void exportFederatedUsersToStream(KeycloakSession session, RealmModel realm, List<String> usersToExport, ObjectMapper mapper, OutputStream os, ExportOptions options) throws IOException {
JsonFactory factory = mapper.getFactory();
JsonGenerator generator = factory.createGenerator(os, JsonEncoding.UTF8);
try {
@ -607,7 +641,7 @@ public class ExportUtils {
generator.writeStartArray();
for (String userId : usersToExport) {
UserRepresentation userRep = ExportUtils.exportFederatedUser(session, realm, userId);
UserRepresentation userRep = ExportUtils.exportFederatedUser(session, realm, userId, options);
generator.writeObject(userRep);
}
@ -624,7 +658,7 @@ public class ExportUtils {
* @param id
* @return fully exported user representation
*/
public static UserRepresentation exportFederatedUser(KeycloakSession session, RealmModel realm, String id) {
public static UserRepresentation exportFederatedUser(KeycloakSession session, RealmModel realm, String id, ExportOptions options) {
UserRepresentation userRep = new UserRepresentation();
userRep.setId(id);
MultivaluedHashMap<String, String> attributes = session.userFederatedStorage().getAttributes(realm, id);
@ -654,30 +688,32 @@ public class ExportUtils {
}
// Role mappings
Set<RoleModel> roles = session.userFederatedStorage().getRoleMappings(realm, id);
List<String> realmRoleNames = new ArrayList<>();
Map<String, List<String>> clientRoleNames = new HashMap<>();
for (RoleModel role : roles) {
if (role.getContainer() instanceof RealmModel) {
realmRoleNames.add(role.getName());
} else {
ClientModel client = (ClientModel)role.getContainer();
String clientId = client.getClientId();
List<String> currentClientRoles = clientRoleNames.get(clientId);
if (currentClientRoles == null) {
currentClientRoles = new ArrayList<>();
clientRoleNames.put(clientId, currentClientRoles);
if (options.isGroupsAndRolesIncluded()) {
Set<RoleModel> roles = session.userFederatedStorage().getRoleMappings(realm, id);
List<String> realmRoleNames = new ArrayList<>();
Map<String, List<String>> clientRoleNames = new HashMap<>();
for (RoleModel role : roles) {
if (role.getContainer() instanceof RealmModel) {
realmRoleNames.add(role.getName());
} else {
ClientModel client = (ClientModel) role.getContainer();
String clientId = client.getClientId();
List<String> currentClientRoles = clientRoleNames.get(clientId);
if (currentClientRoles == null) {
currentClientRoles = new ArrayList<>();
clientRoleNames.put(clientId, currentClientRoles);
}
currentClientRoles.add(role.getName());
}
currentClientRoles.add(role.getName());
}
}
if (realmRoleNames.size() > 0) {
userRep.setRealmRoles(realmRoleNames);
}
if (clientRoleNames.size() > 0) {
userRep.setClientRoles(clientRoleNames);
if (realmRoleNames.size() > 0) {
userRep.setRealmRoles(realmRoleNames);
}
if (clientRoleNames.size() > 0) {
userRep.setClientRoles(clientRoleNames);
}
}
// Credentials
@ -700,13 +736,13 @@ public class ExportUtils {
userRep.setClientConsents(consentReps);
}
List<String> groups = new LinkedList<>();
for (GroupModel group : session.userFederatedStorage().getGroups(realm, id)) {
groups.add(ModelToRepresentation.buildGroupPath(group));
if (options.isGroupsAndRolesIncluded()) {
List<String> groups = new LinkedList<>();
for (GroupModel group : session.userFederatedStorage().getGroups(realm, id)) {
groups.add(ModelToRepresentation.buildGroupPath(group));
}
userRep.setGroups(groups);
}
userRep.setGroups(groups);
return userRep;
}

View file

@ -36,6 +36,8 @@ import org.keycloak.events.admin.OperationType;
import org.keycloak.events.admin.ResourceType;
import org.keycloak.exportimport.ClientDescriptionConverter;
import org.keycloak.exportimport.ClientDescriptionConverterFactory;
import org.keycloak.exportimport.util.ExportOptions;
import org.keycloak.exportimport.util.ExportUtils;
import org.keycloak.keys.PublicKeyStorageProvider;
import org.keycloak.models.ClientModel;
import org.keycloak.models.Constants;
@ -58,7 +60,6 @@ import org.keycloak.representations.adapters.action.GlobalRequestResult;
import org.keycloak.representations.idm.AdminEventRepresentation;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.ComponentRepresentation;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.representations.idm.EventRepresentation;
import org.keycloak.representations.idm.GroupRepresentation;
import org.keycloak.representations.idm.PartialImportRepresentation;
@ -99,6 +100,8 @@ import java.util.List;
import java.util.Map;
import java.util.regex.PatternSyntaxException;
import static org.keycloak.models.utils.StripSecretsUtils.stripForExport;
/**
* Base resource class for the admin REST api of one realm
*
@ -856,6 +859,27 @@ public class RealmAdminResource {
return partialImport.saveResources();
}
/**
* Partial export of existing realm into a JSON file.
*
* @param exportGroupsAndRoles
* @param exportClients
* @return
*/
@Path("partial-export")
@POST
@Produces(MediaType.APPLICATION_JSON)
public RealmRepresentation partialExport(@QueryParam("exportGroupsAndRoles") Boolean exportGroupsAndRoles,
@QueryParam("exportClients") Boolean exportClients) {
boolean groupsAndRolesExported = exportGroupsAndRoles != null && exportGroupsAndRoles;
boolean clientsExported = exportClients != null && exportClients;
ExportOptions options = new ExportOptions(false, clientsExported, groupsAndRolesExported);
RealmRepresentation rep = ExportUtils.exportRealm(session, realm, options);
return stripForExport(session, rep);
}
/**
* Clear realm cache
*

View file

@ -0,0 +1,272 @@
package org.keycloak.testsuite.admin.partialexport;
import org.junit.Test;
import org.keycloak.common.util.MultivaluedHashMap;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.ComponentExportRepresentation;
import org.keycloak.representations.idm.ComponentRepresentation;
import org.keycloak.representations.idm.GroupRepresentation;
import org.keycloak.representations.idm.IdentityProviderRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.RoleRepresentation;
import org.keycloak.representations.idm.ScopeMappingRepresentation;
import org.keycloak.testsuite.Assert;
import org.keycloak.testsuite.admin.AbstractAdminTest;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
*/
public class PartialExportTest extends AbstractAdminTest {
private static final String EXPORT_TEST_REALM = "partial-export-test";
@Override
public void addTestRealms(List<RealmRepresentation> testRealms) {
super.addTestRealms(testRealms);
RealmRepresentation realmRepresentation = loadJson(getClass().getResourceAsStream("/export/partialexport-testrealm.json"), RealmRepresentation.class);
testRealms.add(realmRepresentation);
}
@Test
public void testExport() {
// exportGroupsAndRoles == false, exportClients == false
RealmRepresentation rep = adminClient.realm(EXPORT_TEST_REALM).partialExport(false, false);
Assert.assertNull("Default groups are empty", rep.getDefaultGroups());
Assert.assertNull("Groups are empty", rep.getGroups());
Assert.assertNotNull("Default roles not empty", rep.getDefaultRoles());
checkDefaultRoles(rep.getDefaultRoles());
Assert.assertNull("Realm and client roles are empty", rep.getRoles());
Assert.assertNull("Clients are empty", rep.getClients());
Assert.assertNull("Scope mappings empty", rep.getScopeMappings());
Assert.assertNull("Client scope mappings empty", rep.getClientScopeMappings());
// exportGroupsAndRoles == true, exportClients == false
rep = adminClient.realm(EXPORT_TEST_REALM).partialExport(true, false);
Assert.assertNull("Default groups are empty", rep.getDefaultGroups());
Assert.assertNotNull("Groups not empty", rep.getGroups());
checkGroups(rep.getGroups());
Assert.assertNotNull("Default roles not empty", rep.getDefaultRoles());
checkDefaultRoles(rep.getDefaultRoles());
Assert.assertNotNull("Realm and client roles not empty", rep.getRoles());
Assert.assertNotNull("Realm roles not empty", rep.getRoles().getRealm());
checkRealmRoles(rep.getRoles().getRealm());
Assert.assertNull("Client roles are empty", rep.getRoles().getClient());
Assert.assertNull("Clients are empty", rep.getClients());
Assert.assertNull("Scope mappings empty", rep.getScopeMappings());
Assert.assertNull("Client scope mappings empty", rep.getClientScopeMappings());
// exportGroupsAndRoles == false, exportClients == true
rep = adminClient.realm(EXPORT_TEST_REALM).partialExport(false, true);
Assert.assertNull("Default groups are empty", rep.getDefaultGroups());
Assert.assertNull("Groups are empty", rep.getGroups());
Assert.assertNotNull("Default roles not empty", rep.getDefaultRoles());
checkDefaultRoles(rep.getDefaultRoles());
Assert.assertNull("Realm and client roles are empty", rep.getRoles());
Assert.assertNotNull("Clients not empty", rep.getClients());
checkClients(rep.getClients());
checkScopeMappings(rep.getScopeMappings());
checkClientScopeMappings(rep.getClientScopeMappings());
// exportGroupsAndRoles == true, exportClients == true
rep = adminClient.realm(EXPORT_TEST_REALM).partialExport(true, true);
Assert.assertNull("Default groups are empty", rep.getDefaultGroups());
Assert.assertNotNull("Groups not empty", rep.getGroups());
checkGroups(rep.getGroups());
Assert.assertNotNull("Default roles not empty", rep.getDefaultRoles());
checkDefaultRoles(rep.getDefaultRoles());
Assert.assertNotNull("Realm and client roles not empty", rep.getRoles());
Assert.assertNotNull("Realm roles not empty", rep.getRoles().getRealm());
checkRealmRoles(rep.getRoles().getRealm());
Assert.assertNotNull("Client roles not empty", rep.getRoles().getClient());
checkClientRoles(rep.getRoles().getClient());
Assert.assertNotNull("Clients not empty", rep.getClients());
checkClients(rep.getClients());
checkScopeMappings(rep.getScopeMappings());
checkClientScopeMappings(rep.getClientScopeMappings());
// check that secrets are masked
checkSecretsAreMasked(rep);
}
private void checkSecretsAreMasked(RealmRepresentation rep) {
// Client secret
for (ClientRepresentation client: rep.getClients()) {
Assert.assertEquals("Client secret masked", ComponentRepresentation.SECRET_VALUE, client.getSecret());
}
// IdentityProvider clientSecret
for (IdentityProviderRepresentation idp: rep.getIdentityProviders()) {
Assert.assertEquals("IdentityProvider clientSecret masked", ComponentRepresentation.SECRET_VALUE, idp.getConfig().get("clientSecret"));
}
// smtpServer password
Assert.assertEquals("SMTP password masked", ComponentRepresentation.SECRET_VALUE, rep.getSmtpServer().get("password"));
// components rsa KeyProvider privateKey
MultivaluedHashMap<String, ComponentExportRepresentation> components = rep.getComponents();
List<ComponentExportRepresentation> keys = components.get("org.keycloak.keys.KeyProvider");
Assert.assertNotNull("Keys not null", keys);
Assert.assertTrue("At least one key returned", keys.size() > 0);
boolean found = false;
for (ComponentExportRepresentation component: keys) {
if ("rsa".equals(component.getProviderId())) {
Assert.assertEquals("RSA KeyProvider privateKey masked", ComponentRepresentation.SECRET_VALUE, component.getConfig().getFirst("privateKey"));
found = true;
}
}
Assert.assertTrue("Found rsa private key", found);
// components ldap UserStorageProvider bindCredential
List<ComponentExportRepresentation> userStorage = components.get("org.keycloak.storage.UserStorageProvider");
Assert.assertNotNull("UserStorageProvider not null", userStorage);
Assert.assertTrue("At least one UserStorageProvider returned", userStorage.size() > 0);
found = false;
for (ComponentExportRepresentation component: userStorage) {
if ("ldap".equals(component.getProviderId())) {
Assert.assertEquals("LDAP provider bindCredential masked", ComponentRepresentation.SECRET_VALUE, component.getConfig().getFirst("bindCredential"));
found = true;
}
}
Assert.assertTrue("Found ldap bindCredential", found);
}
private void checkClientScopeMappings(Map<String, List<ScopeMappingRepresentation>> mappings) {
Map<String, Set<String>> map = extractScopeMappings(mappings.get("test-app"));
Set<String> set = map.get("test-app-scope");
Assert.assertTrue("Client test-app / test-app-scope contains customer-admin-composite-role", set.contains("customer-admin-composite-role"));
set = map.get("third-party");
Assert.assertTrue("Client test-app / third-party contains customer-user", set.contains("customer-user"));
map = extractScopeMappings(mappings.get("test-app-scope"));
set = map.get("test-app-scope");
Assert.assertTrue("Client test-app-scope / test-app-scope contains test-app-allowed-by-scope", set.contains("test-app-allowed-by-scope"));
}
private void checkScopeMappings(List<ScopeMappingRepresentation> scopeMappings) {
Map<String, Set<String>> map = extractScopeMappings(scopeMappings);
Set<String> set = map.get("test-app");
Assert.assertTrue("Client test-app contains user", set.contains("user"));
set = map.get("test-app-scope");
Assert.assertTrue("Client test-app contains user", set.contains("user"));
Assert.assertTrue("Client test-app contains admin", set.contains("admin"));
set = map.get("third-party");
Assert.assertTrue("Client test-app contains third-party", set.contains("user"));
}
private Map<String, Set<String>> extractScopeMappings(List<ScopeMappingRepresentation> scopeMappings) {
Map<String, Set<String>> map = new HashMap<>();
for (ScopeMappingRepresentation r: scopeMappings) {
map.put(r.getClient(), r.getRoles());
}
return map;
}
private void checkClientRoles(Map<String, List<RoleRepresentation>> clientRoles) {
Map<String, RoleRepresentation> roles = collectRoles(clientRoles.get("test-app"));
Assert.assertTrue("Client role customer-admin for test-app", roles.containsKey("customer-admin"));
Assert.assertTrue("Client role sample-client-role for test-app", roles.containsKey("sample-client-role"));
Assert.assertTrue("Client role customer-user for test-app", roles.containsKey("customer-user"));
Assert.assertTrue("Client role customer-admin-composite-role for test-app", roles.containsKey("customer-admin-composite-role"));
RoleRepresentation.Composites cmp = roles.get("customer-admin-composite-role").getComposites();
Assert.assertTrue("customer-admin-composite-role / realm / customer-user-premium", cmp.getRealm().contains("customer-user-premium"));
Assert.assertTrue("customer-admin-composite-role / client['test-app'] / customer-admin", cmp.getClient().get("test-app").contains("customer-admin"));
roles = collectRoles(clientRoles.get("test-app-scope"));
Assert.assertTrue("Client role test-app-disallowed-by-scope for test-app-scope", roles.containsKey("test-app-disallowed-by-scope"));
Assert.assertTrue("Client role test-app-allowed-by-scope for test-app-scope", roles.containsKey("test-app-allowed-by-scope"));
}
private Map<String, RoleRepresentation> collectRoles(List<RoleRepresentation> roles) {
HashMap<String, RoleRepresentation> map = new HashMap<>();
if (roles == null) {
return map;
}
for (RoleRepresentation r: roles) {
map.put(r.getName(), r);
}
return map;
}
private void checkClients(List<ClientRepresentation> clients) {
HashSet<String> set = new HashSet<>();
for (ClientRepresentation c: clients) {
set.add(c.getClientId());
}
Assert.assertTrue("Client test-app", set.contains("test-app"));
Assert.assertTrue("Client test-app-scope", set.contains("test-app-scope"));
Assert.assertTrue("Client third-party", set.contains("third-party"));
}
private void checkRealmRoles(List<RoleRepresentation> realmRoles) {
Set<String> set = new HashSet<>();
for (RoleRepresentation r: realmRoles) {
set.add(r.getName());
}
Assert.assertTrue("Role sample-realm-role", set.contains("sample-realm-role"));
Assert.assertTrue("Role realm-composite-role", set.contains("realm-composite-role"));
Assert.assertTrue("Role customer-user-premium", set.contains("customer-user-premium"));
Assert.assertTrue("Role admin", set.contains("admin"));
Assert.assertTrue("Role user", set.contains("user"));
}
private void checkGroups(List<GroupRepresentation> groups) {
HashSet<String> set = new HashSet<>();
for (GroupRepresentation g: groups) {
compileGroups(set, g);
}
Assert.assertTrue("Group /roleRichGroup", set.contains("/roleRichGroup"));
Assert.assertTrue("Group /roleRichGroup/level2group", set.contains("/roleRichGroup/level2group"));
Assert.assertTrue("Group /topGroup", set.contains("/topGroup"));
Assert.assertTrue("Group /topGroup/level2group", set.contains("/topGroup/level2group"));
}
private void compileGroups(Set<String> found, GroupRepresentation g) {
found.add(g.getPath());
if (g.getSubGroups() != null) {
for (GroupRepresentation s: g.getSubGroups()) {
compileGroups(found, s);
}
}
}
private void checkDefaultRoles(List<String> defaultRoles) {
HashSet<String> roles = new HashSet<>(defaultRoles);
Assert.assertTrue("Default role 'uma_authorization'", roles.contains("uma_authorization"));
Assert.assertTrue("Default role 'offline_access'", roles.contains("offline_access"));
Assert.assertTrue("Default role 'user'", roles.contains("user"));
}
}

View file

@ -0,0 +1,989 @@
{
"id": "partial-export-test",
"realm": "partial-export-test",
"roles": {
"realm": [
{
"name": "sample-realm-role",
"description": "Sample realm role",
"scopeParamRequired": false,
"composite": false,
"clientRole": false,
"containerId": "test"
},
{
"name": "realm-composite-role",
"description": "Realm composite role containing client role",
"scopeParamRequired": false,
"composite": true,
"composites": {
"realm": [
"sample-realm-role"
],
"client": {
"test-app": [
"sample-client-role"
],
"account": [
"view-profile"
]
}
},
"clientRole": false,
"containerId": "test"
},
{
"name": "customer-user-premium",
"description": "Have User Premium privileges",
"scopeParamRequired": false,
"composite": false,
"clientRole": false,
"containerId": "test"
},
{
"name": "admin",
"description": "Have Administrator privileges",
"scopeParamRequired": false,
"composite": false,
"clientRole": false,
"containerId": "test"
},
{
"name": "user",
"description": "Have User privileges",
"scopeParamRequired": false,
"composite": false,
"clientRole": false,
"containerId": "test"
}
],
"client": {
"test-app": [
{
"name": "customer-admin",
"description": "Have Customer Admin privileges",
"scopeParamRequired": false,
"composite": false,
"clientRole": true,
"containerId": "c1a37c9e-6ba4-4d77-988d-ab11462d5668"
},
{
"name": "sample-client-role",
"description": "Sample client role",
"scopeParamRequired": false,
"composite": false,
"clientRole": true,
"containerId": "c1a37c9e-6ba4-4d77-988d-ab11462d5668"
},
{
"name": "customer-admin-composite-role",
"description": "Have Customer Admin privileges via composite role",
"scopeParamRequired": false,
"composite": true,
"composites": {
"realm": [
"customer-user-premium"
],
"client": {
"test-app": [
"customer-admin"
]
}
},
"clientRole": true,
"containerId": "c1a37c9e-6ba4-4d77-988d-ab11462d5668"
},
{
"name": "customer-user",
"description": "Have Customer User privileges",
"scopeParamRequired": false,
"composite": false,
"clientRole": true,
"containerId": "c1a37c9e-6ba4-4d77-988d-ab11462d5668"
}
],
"test-app-scope": [
{
"name": "test-app-disallowed-by-scope",
"description": "Role disallowed by scope in test-app-scope",
"scopeParamRequired": false,
"composite": false,
"clientRole": true,
"containerId": "f3ff0b0d-e922-4874-a34c-cdfa1b3305fe"
},
{
"name": "test-app-allowed-by-scope",
"description": "Role allowed by scope in test-app-scope",
"scopeParamRequired": false,
"composite": false,
"clientRole": true,
"containerId": "f3ff0b0d-e922-4874-a34c-cdfa1b3305fe"
}
]
}
},
"groups": [
{
"name": "roleRichGroup",
"path": "/roleRichGroup",
"attributes": {
"topAttribute": [
"true"
]
},
"realmRoles": [
"realm-composite-role",
"user"
],
"clientRoles": {
"account": [
"manage-account"
]
},
"subGroups": [
{
"name": "level2group",
"path": "/roleRichGroup/level2group",
"attributes": {
"level2Attribute": [
"true"
]
},
"realmRoles": [
"admin"
],
"clientRoles": {
"test-app": [
"customer-admin-composite-role",
"customer-user"
]
},
"subGroups": []
}
]
},
{
"name": "topGroup",
"path": "/topGroup",
"attributes": {
"topAttribute": [
"true"
]
},
"realmRoles": [
"user"
],
"clientRoles": {},
"subGroups": [
{
"name": "level2group",
"path": "/topGroup/level2group",
"attributes": {
"level2Attribute": [
"true"
]
},
"realmRoles": [
"admin"
],
"clientRoles": {
"test-app": [
"customer-user"
]
},
"subGroups": []
}
]
}
],
"defaultRoles": [
"user",
"offline_access",
"uma_authorization"
],
"smtpServer": {
"from": "auto@keycloak.org",
"host": "localhost",
"port": "3025",
"user": "user",
"password": "secret"
},
"scopeMappings": [
{
"client": "test-app",
"roles": [
"user"
]
},
{
"client": "test-app-scope",
"roles": [
"admin",
"user"
]
},
{
"client": "third-party",
"roles": [
"user"
]
}
],
"clientScopeMappings": {
"realm-management": [
{
"client": "admin-cli",
"roles": [
"realm-admin"
]
},
{
"client": "security-admin-console",
"roles": [
"realm-admin"
]
}
],
"test-app": [
{
"client": "test-app-scope",
"roles": [
"customer-admin-composite-role"
]
},
{
"client": "third-party",
"roles": [
"customer-user"
]
}
],
"test-app-scope": [
{
"client": "test-app-scope",
"roles": [
"test-app-allowed-by-scope"
]
}
]
},
"clients": [
{
"clientId": "test-app",
"adminUrl": "http://localhost:8180/auth/realms/master/app/admin",
"baseUrl": "http://localhost:8180/auth/realms/master/app/auth",
"surrogateAuthRequired": false,
"enabled": true,
"clientAuthenticatorType": "client-secret",
"secret": "password",
"redirectUris": [
"http://localhost:8180/auth/realms/master/app/auth/*"
],
"webOrigins": [
"http://localhost:8180"
],
"notBefore": 0,
"bearerOnly": false,
"consentRequired": false,
"standardFlowEnabled": true,
"implicitFlowEnabled": false,
"directAccessGrantsEnabled": true,
"serviceAccountsEnabled": false,
"publicClient": false,
"frontchannelLogout": false,
"attributes": {},
"fullScopeAllowed": true,
"nodeReRegistrationTimeout": -1,
"protocolMappers": [
{
"name": "role list",
"protocol": "saml",
"protocolMapper": "saml-role-list-mapper",
"consentRequired": false,
"config": {
"single": "false",
"attribute.nameformat": "Basic",
"attribute.name": "Role"
}
},
{
"name": "full name",
"protocol": "openid-connect",
"protocolMapper": "oidc-full-name-mapper",
"consentRequired": true,
"consentText": "${fullName}",
"config": {
"id.token.claim": "true",
"access.token.claim": "true"
}
},
{
"name": "email",
"protocol": "openid-connect",
"protocolMapper": "oidc-usermodel-property-mapper",
"consentRequired": true,
"consentText": "${email}",
"config": {
"userinfo.token.claim": "true",
"user.attribute": "email",
"id.token.claim": "true",
"access.token.claim": "true",
"claim.name": "email",
"jsonType.label": "String"
}
},
{
"name": "username",
"protocol": "openid-connect",
"protocolMapper": "oidc-usermodel-property-mapper",
"consentRequired": true,
"consentText": "${username}",
"config": {
"userinfo.token.claim": "true",
"user.attribute": "username",
"id.token.claim": "true",
"access.token.claim": "true",
"claim.name": "preferred_username",
"jsonType.label": "String"
}
},
{
"name": "given name",
"protocol": "openid-connect",
"protocolMapper": "oidc-usermodel-property-mapper",
"consentRequired": true,
"consentText": "${givenName}",
"config": {
"userinfo.token.claim": "true",
"user.attribute": "firstName",
"id.token.claim": "true",
"access.token.claim": "true",
"claim.name": "given_name",
"jsonType.label": "String"
}
},
{
"name": "family name",
"protocol": "openid-connect",
"protocolMapper": "oidc-usermodel-property-mapper",
"consentRequired": true,
"consentText": "${familyName}",
"config": {
"userinfo.token.claim": "true",
"user.attribute": "lastName",
"id.token.claim": "true",
"access.token.claim": "true",
"claim.name": "family_name",
"jsonType.label": "String"
}
}
],
"useTemplateConfig": false,
"useTemplateScope": false,
"useTemplateMappers": false
},
{
"clientId": "test-app-scope",
"surrogateAuthRequired": false,
"enabled": true,
"clientAuthenticatorType": "client-secret",
"secret": "password",
"redirectUris": [
"http://localhost:8180/auth/realms/master/app/*"
],
"webOrigins": [
"http://localhost:8180"
],
"notBefore": 0,
"bearerOnly": false,
"consentRequired": false,
"standardFlowEnabled": true,
"implicitFlowEnabled": false,
"directAccessGrantsEnabled": false,
"serviceAccountsEnabled": false,
"publicClient": false,
"frontchannelLogout": false,
"attributes": {},
"fullScopeAllowed": false,
"nodeReRegistrationTimeout": -1,
"protocolMappers": [
{
"name": "email",
"protocol": "openid-connect",
"protocolMapper": "oidc-usermodel-property-mapper",
"consentRequired": true,
"consentText": "${email}",
"config": {
"userinfo.token.claim": "true",
"user.attribute": "email",
"id.token.claim": "true",
"access.token.claim": "true",
"claim.name": "email",
"jsonType.label": "String"
}
},
{
"name": "full name",
"protocol": "openid-connect",
"protocolMapper": "oidc-full-name-mapper",
"consentRequired": true,
"consentText": "${fullName}",
"config": {
"id.token.claim": "true",
"access.token.claim": "true"
}
},
{
"name": "username",
"protocol": "openid-connect",
"protocolMapper": "oidc-usermodel-property-mapper",
"consentRequired": true,
"consentText": "${username}",
"config": {
"userinfo.token.claim": "true",
"user.attribute": "username",
"id.token.claim": "true",
"access.token.claim": "true",
"claim.name": "preferred_username",
"jsonType.label": "String"
}
},
{
"name": "family name",
"protocol": "openid-connect",
"protocolMapper": "oidc-usermodel-property-mapper",
"consentRequired": true,
"consentText": "${familyName}",
"config": {
"userinfo.token.claim": "true",
"user.attribute": "lastName",
"id.token.claim": "true",
"access.token.claim": "true",
"claim.name": "family_name",
"jsonType.label": "String"
}
},
{
"name": "role list",
"protocol": "saml",
"protocolMapper": "saml-role-list-mapper",
"consentRequired": false,
"config": {
"single": "false",
"attribute.nameformat": "Basic",
"attribute.name": "Role"
}
},
{
"name": "given name",
"protocol": "openid-connect",
"protocolMapper": "oidc-usermodel-property-mapper",
"consentRequired": true,
"consentText": "${givenName}",
"config": {
"userinfo.token.claim": "true",
"user.attribute": "firstName",
"id.token.claim": "true",
"access.token.claim": "true",
"claim.name": "given_name",
"jsonType.label": "String"
}
}
],
"useTemplateConfig": false,
"useTemplateScope": false,
"useTemplateMappers": false
},
{
"clientId": "third-party",
"surrogateAuthRequired": false,
"enabled": true,
"clientAuthenticatorType": "client-secret",
"secret": "password",
"redirectUris": [
"http://localhost:8180/auth/realms/master/app/*"
],
"webOrigins": [
"http://localhost:8180"
],
"notBefore": 0,
"bearerOnly": false,
"consentRequired": true,
"standardFlowEnabled": true,
"implicitFlowEnabled": false,
"directAccessGrantsEnabled": false,
"serviceAccountsEnabled": false,
"publicClient": false,
"frontchannelLogout": false,
"attributes": {},
"fullScopeAllowed": false,
"nodeReRegistrationTimeout": -1,
"protocolMappers": [
{
"name": "family name",
"protocol": "openid-connect",
"protocolMapper": "oidc-usermodel-property-mapper",
"consentRequired": true,
"consentText": "${familyName}",
"config": {
"userinfo.token.claim": "true",
"user.attribute": "lastName",
"id.token.claim": "true",
"access.token.claim": "true",
"claim.name": "family_name",
"jsonType.label": "String"
}
},
{
"name": "email",
"protocol": "openid-connect",
"protocolMapper": "oidc-usermodel-property-mapper",
"consentRequired": true,
"consentText": "${email}",
"config": {
"userinfo.token.claim": "true",
"user.attribute": "email",
"id.token.claim": "true",
"access.token.claim": "true",
"claim.name": "email",
"jsonType.label": "String"
}
},
{
"name": "username",
"protocol": "openid-connect",
"protocolMapper": "oidc-usermodel-property-mapper",
"consentRequired": true,
"consentText": "${username}",
"config": {
"userinfo.token.claim": "true",
"user.attribute": "username",
"id.token.claim": "true",
"access.token.claim": "true",
"claim.name": "preferred_username",
"jsonType.label": "String"
}
},
{
"name": "given name",
"protocol": "openid-connect",
"protocolMapper": "oidc-usermodel-property-mapper",
"consentRequired": true,
"consentText": "${givenName}",
"config": {
"userinfo.token.claim": "true",
"user.attribute": "firstName",
"id.token.claim": "true",
"access.token.claim": "true",
"claim.name": "given_name",
"jsonType.label": "String"
}
},
{
"name": "role list",
"protocol": "saml",
"protocolMapper": "saml-role-list-mapper",
"consentRequired": false,
"config": {
"single": "false",
"attribute.nameformat": "Basic",
"attribute.name": "Role"
}
},
{
"name": "full name",
"protocol": "openid-connect",
"protocolMapper": "oidc-full-name-mapper",
"consentRequired": true,
"consentText": "${fullName}",
"config": {
"id.token.claim": "true",
"access.token.claim": "true"
}
}
],
"useTemplateConfig": false,
"useTemplateScope": false,
"useTemplateMappers": false
}],
"components": {
"org.keycloak.keys.KeyProvider": [
{
"name": "rsa",
"providerId": "rsa",
"subComponents": {},
"config": {
"privateKey": [
"MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y="
],
"certificate": [
"MIIBkTCB+wIGAVufbLMuMA0GCSqGSIb3DQEBCwUAMA8xDTALBgNVBAMMBHRlc3QwHhcNMTcwNDI0MTAwNDEyWhcNMjcwNDI0MTAwNTUyWjAPMQ0wCwYDVQQDDAR0ZXN0MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAKKj6Ygftq7iSfvi8G6IoJ4RbknpA0+g+s1fYgmpdHdBEfAfbODmWrNR8GLWQDU0ccnHT0oQDc66ShfluMZ0KAVcfxNJUFP2OYdrGNRJNZbGT9WMcD8LUF8mlACa8uKVfhMU4LssOdEBnW2RpM4xEe1DYPRC+AWoFODb0wsYDwll"
],
"priority": [
"100"
]
}
}
],
"org.keycloak.storage.UserStorageProvider": [
{
"name": "ldap-apacheds",
"providerId": "ldap",
"subComponents": {
"org.keycloak.storage.ldap.mappers.LDAPStorageMapper": [
{
"name": "username",
"providerId": "user-attribute-ldap-mapper",
"subComponents": {
},
"config": {
"ldap.attribute": [
"uid"
],
"is.mandatory.in.ldap": [
"true"
],
"read.only": [
"false"
],
"always.read.value.from.ldap": [
"false"
],
"user.model.attribute": [
"username"
]
}
},
{
"name": "first name",
"providerId": "user-attribute-ldap-mapper",
"subComponents": {
},
"config": {
"ldap.attribute": [
"cn"
],
"is.mandatory.in.ldap": [
"true"
],
"read.only": [
"false"
],
"always.read.value.from.ldap": [
"false"
],
"user.model.attribute": [
"firstName"
]
}
},
{
"name": "last name",
"providerId": "user-attribute-ldap-mapper",
"subComponents": {
},
"config": {
"ldap.attribute": [
"sn"
],
"is.mandatory.in.ldap": [
"true"
],
"read.only": [
"false"
],
"always.read.value.from.ldap": [
"false"
],
"user.model.attribute": [
"lastName"
]
}
},
{
"name": "email",
"providerId": "user-attribute-ldap-mapper",
"subComponents": {
},
"config": {
"ldap.attribute": [
"mail"
],
"is.mandatory.in.ldap": [
"false"
],
"read.only": [
"false"
],
"always.read.value.from.ldap": [
"false"
],
"user.model.attribute": [
"email"
]
}
},
{
"name": "creation date",
"providerId": "user-attribute-ldap-mapper",
"subComponents": {
},
"config": {
"ldap.attribute": [
"createTimestamp"
],
"is.mandatory.in.ldap": [
"false"
],
"read.only": [
"true"
],
"always.read.value.from.ldap": [
"false"
],
"user.model.attribute": [
"createTimestamp"
]
}
},
{
"name": "modify date",
"providerId": "user-attribute-ldap-mapper",
"subComponents": {
},
"config": {
"ldap.attribute": [
"modifyTimestamp"
],
"is.mandatory.in.ldap": [
"false"
],
"read.only": [
"true"
],
"always.read.value.from.ldap": [
"false"
],
"user.model.attribute": [
"modifyTimestamp"
]
}
},
{
"name": "postal code",
"providerId": "user-attribute-ldap-mapper",
"subComponents": {
},
"config": {
"ldap.attribute": [
"postalCode"
],
"is.mandatory.in.ldap": [
"false"
],
"read.only": [
"false"
],
"always.read.value.from.ldap": [
"false"
],
"user.model.attribute": [
"postal_code"
]
}
},
{
"name": "street",
"providerId": "user-attribute-ldap-mapper",
"subComponents": {
},
"config": {
"ldap.attribute": [
"street"
],
"is.mandatory.in.ldap": [
"false"
],
"read.only": [
"false"
],
"always.read.value.from.ldap": [
"false"
],
"user.model.attribute": [
"street"
]
}
},
{
"name": "picture",
"providerId": "user-attribute-ldap-mapper",
"subComponents": {
},
"config": {
"ldap.attribute": [
"jpegPhoto"
],
"is.mandatory.in.ldap": [
"false"
],
"is.binary.attribute": [
"true"
],
"read.only": [
"false"
],
"always.read.value.from.ldap": [
"true"
],
"user.model.attribute": [
"picture"
]
}
},
{
"name": "realm roles",
"providerId": "role-ldap-mapper",
"subComponents": {
},
"config": {
"mode": [
"LDAP_ONLY"
],
"roles.dn": [
"ou=RealmRoles,dc=keycloak,dc=org"
],
"membership.ldap.attribute": [
"member"
],
"role.name.ldap.attribute": [
"cn"
],
"use.realm.roles.mapping": [
"true"
],
"role.object.classes": [
"groupOfNames"
]
}
},
{
"name": "finance roles",
"providerId": "role-ldap-mapper",
"subComponents": {
},
"config": {
"mode": [
"LDAP_ONLY"
],
"roles.dn": [
"ou=FinanceRoles,dc=keycloak,dc=org"
],
"membership.ldap.attribute": [
"member"
],
"role.name.ldap.attribute": [
"cn"
],
"use.realm.roles.mapping": [
"false"
],
"role.object.classes": [
"groupOfNames"
],
"client.id": [
"finance"
]
}
}
]
},
"config": {
"fullSyncPeriod": [
"-1"
],
"pagination": [
"true"
],
"debug": [
"false"
],
"searchScope": [
"1"
],
"connectionPooling": [
"true"
],
"usersDn": [
"ou=People,dc=keycloak,dc=org"
],
"priority": [
"1"
],
"userObjectClasses": [
"inetOrgPerson, organizationalPerson"
],
"changedSyncPeriod": [
"-1"
],
"usernameLDAPAttribute": [
"uid"
],
"bindDn": [
"uid=admin,ou=system"
],
"bindCredential": [
"secret"
],
"rdnLDAPAttribute": [
"uid"
],
"lastSync": [
"0"
],
"vendor": [
"other"
],
"editMode": [
"WRITABLE"
],
"uuidLDAPAttribute": [
"entryUUID"
],
"connectionUrl": [
"ldap://localhost:10389"
],
"syncRegistrations": [
"true"
],
"authType": [
"simple"
]
}
}
]
},
"identityProviders" : [
{
"providerId" : "google",
"alias" : "google1",
"enabled": true,
"config": {
"clientId": "googleId",
"clientSecret": "googleSecret"
}
}
]
}

View file

@ -671,6 +671,11 @@ skip=Skip
overwrite=Overwrite
if-resource-exists.tooltip=Specify what should be done if you try to import a resource that already exists.
partial-export=Partial Export
partial-export.tooltip=Partial export allows you to export realm configuration, and other associated resources into a json file.
export-groups-and-roles=Export groups and roles
export-clients=Export clients
action=Action
role-selector=Role Selector
realm-roles.tooltip=Realm roles that can be selected.

View file

@ -550,6 +550,15 @@ module.config([ '$routeProvider', function($routeProvider) {
},
controller : 'RealmImportCtrl'
})
.when('/realms/:realm/partial-export', {
templateUrl : resourceUrl + '/partials/partial-export.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
}
},
controller : 'RealmExportCtrl'
})
.when('/create/user/:realm', {
templateUrl : resourceUrl + '/partials/user-detail.html',
resolve : {

View file

@ -2709,3 +2709,40 @@ module.controller('RealmImportCtrl', function($scope, realm, $route,
}
});
module.controller('RealmExportCtrl', function($scope, realm, $http,
$httpParamSerializer, Notifications, Dialog) {
$scope.realm = realm;
$scope.exportGroupsAndRoles = false;
$scope.exportClients = false;
$scope.export = function() {
if ($scope.exportGroupsAndRoles || $scope.exportClients) {
Dialog.confirm('Export', 'This operation may make server unresponsive for a while.\n\nAre you sure you want to proceed?', download);
} else {
download();
}
}
function download() {
var exportUrl = authUrl + '/admin/realms/' + realm.realm + '/partial-export';
var params = {};
if ($scope.exportGroupsAndRoles) {
params['exportGroupsAndRoles'] = true;
}
if ($scope.exportClients) {
params['exportClients'] = true;
}
if (Object.keys(params).length > 0) {
exportUrl += '?' + $httpParamSerializer(params);
}
$http.post(exportUrl)
.success(function(data, status, headers) {
var download = angular.fromJson(data);
download = angular.toJson(download, true);
saveAs(new Blob([download], { type: 'application/json' }), 'realm-export.json');
}).error(function() {
Notifications.error("Sorry, something went wrong.");
});
}
});

View file

@ -0,0 +1,34 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<h1>
<span>{{:: 'partial-export' | translate}}</span>
<kc-tooltip>{{:: 'partial-export.tooltip' | translate}}</kc-tooltip>
</h1>
<form class="form-horizontal" name="partialExportForm" novalidate>
<fieldset class="border-top">
<div class="form-group">
<label class="col-md-2 control-label" for="exportGroupsAndRoles">{{:: 'export-groups-and-roles' | translate}}</label>
<div class="col-sm-6">
<input ng-model="exportGroupsAndRoles" name="exportGroupsAndRoles" id="exportGroupsAndRoles" onoffswitch on-text="{{:: 'onText'| translate}}" off-text="{{:: 'offText'| translate}}"/>
</div>
</div>
<div class="form-group">
<label class="col-md-2 control-label" for="exportClients">{{:: 'export-clients' | translate}}</label>
<div class="col-sm-6">
<input ng-model="exportClients" name="exportClients" id="exportClients" onoffswitch on-text="{{:: 'onText'| translate}}" off-text="{{:: 'offText'| translate}}"/>
</div>
</div>
<div class="col-sm-12">
<button class="btn btn-primary" data-ng-click="export()" type="submit">{{:: 'export' | translate}}</button>
</div>
</fieldset>
</form>
</div>
<kc-menu></kc-menu>

View file

@ -56,6 +56,7 @@
|| path[2] == 'events-settings'
|| path[2] == 'admin-events') && 'active'"><a href="#/realms/{{realm.realm}}/events"><i class="fa fa-calendar"></i> {{:: 'events' | translate}}</a></li>
<li data-ng-show="access.manageRealm" ng-class="(path[2] =='partial-import') && 'active'"><a href="#/realms/{{realm.realm}}/partial-import"><span class="pficon pficon-import"></span> {{:: 'import' | translate}}</a></li>
<li data-ng-show="access.manageRealm" ng-class="(path[2] =='partial-export') && 'active'"><a href="#/realms/{{realm.realm}}/partial-export"><span class="pficon pficon-export"></span> {{:: 'export' | translate}}</a></li>
</ul>
</div>
</div>