RHSSO-402 need a way to dump configuration (including ldap provider config) to a file
This commit is contained in:
parent
16aaa3bf01
commit
7d0ca42c6c
13 changed files with 1642 additions and 101 deletions
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
|
|
@ -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"));
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -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.
|
||||
|
|
|
@ -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 : {
|
||||
|
|
|
@ -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.");
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
Loading…
Reference in a new issue