parent
962a685b7b
commit
1d2d3e5ca5
29 changed files with 227 additions and 158 deletions
|
@ -22,18 +22,11 @@ import com.fasterxml.jackson.core.JsonFactory;
|
||||||
import com.fasterxml.jackson.core.JsonGenerator;
|
import com.fasterxml.jackson.core.JsonGenerator;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||||
import org.keycloak.authorization.AuthorizationProvider;
|
|
||||||
import org.keycloak.authorization.AuthorizationProviderFactory;
|
|
||||||
import org.keycloak.authorization.model.Policy;
|
|
||||||
import org.keycloak.authorization.model.Resource;
|
|
||||||
import org.keycloak.authorization.model.ResourceServer;
|
|
||||||
import org.keycloak.authorization.model.Scope;
|
|
||||||
import org.keycloak.authorization.store.PolicyStore;
|
|
||||||
import org.keycloak.authorization.store.StoreFactory;
|
|
||||||
import org.keycloak.common.Profile;
|
import org.keycloak.common.Profile;
|
||||||
import org.keycloak.common.Version;
|
import org.keycloak.common.Version;
|
||||||
import org.keycloak.common.util.MultivaluedHashMap;
|
import org.keycloak.common.util.MultivaluedHashMap;
|
||||||
import org.keycloak.credential.CredentialModel;
|
import org.keycloak.credential.CredentialModel;
|
||||||
|
import org.keycloak.exportimport.ExportOptions;
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
import org.keycloak.models.ClientScopeModel;
|
import org.keycloak.models.ClientScopeModel;
|
||||||
import org.keycloak.models.FederatedIdentityModel;
|
import org.keycloak.models.FederatedIdentityModel;
|
||||||
|
@ -53,18 +46,11 @@ import org.keycloak.representations.idm.RolesRepresentation;
|
||||||
import org.keycloak.representations.idm.ScopeMappingRepresentation;
|
import org.keycloak.representations.idm.ScopeMappingRepresentation;
|
||||||
import org.keycloak.representations.idm.UserConsentRepresentation;
|
import org.keycloak.representations.idm.UserConsentRepresentation;
|
||||||
import org.keycloak.representations.idm.UserRepresentation;
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
|
|
||||||
import org.keycloak.representations.idm.authorization.ResourceOwnerRepresentation;
|
|
||||||
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
|
||||||
import org.keycloak.representations.idm.authorization.ResourceServerRepresentation;
|
|
||||||
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
|
|
||||||
import org.keycloak.storage.federated.UserFederatedStorageProvider;
|
import org.keycloak.storage.federated.UserFederatedStorageProvider;
|
||||||
import org.keycloak.util.JsonSerialization;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
@ -293,118 +279,15 @@ public class ExportUtils {
|
||||||
ClientRepresentation clientRep = ModelToRepresentation.toRepresentation(client, session);
|
ClientRepresentation clientRep = ModelToRepresentation.toRepresentation(client, session);
|
||||||
clientRep.setSecret(client.getSecret());
|
clientRep.setSecret(client.getSecret());
|
||||||
if (Profile.isFeatureEnabled(Profile.Feature.AUTHORIZATION)) {
|
if (Profile.isFeatureEnabled(Profile.Feature.AUTHORIZATION)) {
|
||||||
clientRep.setAuthorizationSettings(exportAuthorizationSettings(session, client));
|
clientRep.setAuthorizationSettings(ModelToRepresentation.toResourceServerRepresentation(session, client));
|
||||||
}
|
}
|
||||||
return clientRep;
|
return clientRep;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ResourceServerRepresentation exportAuthorizationSettings(KeycloakSession session, ClientModel client) {
|
|
||||||
AuthorizationProviderFactory providerFactory = (AuthorizationProviderFactory) session.getKeycloakSessionFactory().getProviderFactory(AuthorizationProvider.class);
|
|
||||||
AuthorizationProvider authorization = providerFactory.create(session, client.getRealm());
|
|
||||||
StoreFactory storeFactory = authorization.getStoreFactory();
|
|
||||||
ResourceServer settingsModel = authorization.getStoreFactory().getResourceServerStore().findByClient(client);
|
|
||||||
|
|
||||||
if (settingsModel == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
ResourceServerRepresentation representation = toRepresentation(settingsModel, client);
|
|
||||||
|
|
||||||
representation.setId(null);
|
|
||||||
representation.setName(null);
|
|
||||||
representation.setClientId(null);
|
|
||||||
|
|
||||||
List<ResourceRepresentation> resources = storeFactory.getResourceStore().findByResourceServer(settingsModel)
|
|
||||||
.stream().map(resource -> {
|
|
||||||
ResourceRepresentation rep = toRepresentation(resource, settingsModel, authorization);
|
|
||||||
|
|
||||||
if (rep.getOwner().getId().equals(settingsModel.getClientId())) {
|
|
||||||
rep.setOwner((ResourceOwnerRepresentation) null);
|
|
||||||
} else {
|
|
||||||
rep.getOwner().setId(null);
|
|
||||||
}
|
|
||||||
rep.getScopes().forEach(scopeRepresentation -> {
|
|
||||||
scopeRepresentation.setId(null);
|
|
||||||
scopeRepresentation.setIconUri(null);
|
|
||||||
});
|
|
||||||
|
|
||||||
return rep;
|
|
||||||
}).collect(Collectors.toList());
|
|
||||||
|
|
||||||
representation.setResources(resources);
|
|
||||||
|
|
||||||
List<PolicyRepresentation> policies = new ArrayList<>();
|
|
||||||
PolicyStore policyStore = storeFactory.getPolicyStore();
|
|
||||||
|
|
||||||
policies.addAll(policyStore.findByResourceServer(settingsModel)
|
|
||||||
.stream().filter(policy -> !policy.getType().equals("resource") && !policy.getType().equals("scope") && policy.getOwner() == null)
|
|
||||||
.map(policy -> createPolicyRepresentation(authorization, policy)).collect(Collectors.toList()));
|
|
||||||
policies.addAll(policyStore.findByResourceServer(settingsModel)
|
|
||||||
.stream().filter(policy -> (policy.getType().equals("resource") || policy.getType().equals("scope") && policy.getOwner() == null))
|
|
||||||
.map(policy -> createPolicyRepresentation(authorization, policy)).collect(Collectors.toList()));
|
|
||||||
|
|
||||||
representation.setPolicies(policies);
|
|
||||||
|
|
||||||
List<ScopeRepresentation> scopes = storeFactory.getScopeStore().findByResourceServer(settingsModel).stream().map(scope -> {
|
|
||||||
ScopeRepresentation rep = toRepresentation(scope);
|
|
||||||
|
|
||||||
rep.setPolicies(null);
|
|
||||||
rep.setResources(null);
|
|
||||||
|
|
||||||
return rep;
|
|
||||||
}).collect(Collectors.toList());
|
|
||||||
|
|
||||||
representation.setScopes(scopes);
|
|
||||||
|
|
||||||
return representation;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static PolicyRepresentation createPolicyRepresentation(AuthorizationProvider authorizationProvider, Policy policy) {
|
|
||||||
try {
|
|
||||||
PolicyRepresentation rep = toRepresentation(policy, authorizationProvider, true, true);
|
|
||||||
|
|
||||||
Map<String, String> config = new HashMap<>(rep.getConfig());
|
|
||||||
|
|
||||||
rep.setConfig(config);
|
|
||||||
|
|
||||||
Set<Scope> scopes = policy.getScopes();
|
|
||||||
|
|
||||||
if (!scopes.isEmpty()) {
|
|
||||||
List<String> scopeNames = scopes.stream().map(Scope::getName).collect(Collectors.toList());
|
|
||||||
config.put("scopes", JsonSerialization.writeValueAsString(scopeNames));
|
|
||||||
}
|
|
||||||
|
|
||||||
Set<Resource> policyResources = policy.getResources();
|
|
||||||
|
|
||||||
if (!policyResources.isEmpty()) {
|
|
||||||
List<String> resourceNames = policyResources.stream().map(Resource::getName).collect(Collectors.toList());
|
|
||||||
config.put("resources", JsonSerialization.writeValueAsString(resourceNames));
|
|
||||||
}
|
|
||||||
|
|
||||||
Set<Policy> associatedPolicies = policy.getAssociatedPolicies();
|
|
||||||
|
|
||||||
if (!associatedPolicies.isEmpty()) {
|
|
||||||
config.put("applyPolicies", JsonSerialization.writeValueAsString(associatedPolicies.stream().map(associated -> associated.getName()).collect(Collectors.toList())));
|
|
||||||
}
|
|
||||||
|
|
||||||
return rep;
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException("Error while exporting policy [" + policy.getName() + "].", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<RoleRepresentation> exportRoles(Stream<RoleModel> roles) {
|
public static List<RoleRepresentation> exportRoles(Stream<RoleModel> roles) {
|
||||||
return roles.map(ExportUtils::exportRole).collect(Collectors.toList());
|
return roles.map(ExportUtils::exportRole).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<String> getRoleNames(Collection<RoleModel> roles) {
|
|
||||||
List<String> roleNames = new ArrayList<>();
|
|
||||||
for (RoleModel role : roles) {
|
|
||||||
roleNames.add(role.getName());
|
|
||||||
}
|
|
||||||
return roleNames;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Full export of role including composite roles
|
* Full export of role including composite roles
|
||||||
* @param role
|
* @param role
|
|
@ -21,6 +21,9 @@ import org.jboss.logging.Logger;
|
||||||
import org.keycloak.common.enums.SslRequired;
|
import org.keycloak.common.enums.SslRequired;
|
||||||
import org.keycloak.common.util.MultivaluedHashMap;
|
import org.keycloak.common.util.MultivaluedHashMap;
|
||||||
import org.keycloak.component.ComponentModel;
|
import org.keycloak.component.ComponentModel;
|
||||||
|
import org.keycloak.exportimport.ExportAdapter;
|
||||||
|
import org.keycloak.exportimport.ExportOptions;
|
||||||
|
import org.keycloak.exportimport.util.ExportUtils;
|
||||||
import org.keycloak.keys.KeyProvider;
|
import org.keycloak.keys.KeyProvider;
|
||||||
import org.keycloak.migration.MigrationProvider;
|
import org.keycloak.migration.MigrationProvider;
|
||||||
import org.keycloak.migration.migrators.MigrateTo8_0_0;
|
import org.keycloak.migration.migrators.MigrateTo8_0_0;
|
||||||
|
@ -89,8 +92,10 @@ import org.keycloak.storage.UserStorageProviderModel;
|
||||||
import org.keycloak.storage.UserStorageUtil;
|
import org.keycloak.storage.UserStorageUtil;
|
||||||
import org.keycloak.storage.federated.UserFederatedStorageProvider;
|
import org.keycloak.storage.federated.UserFederatedStorageProvider;
|
||||||
import org.keycloak.userprofile.UserProfileProvider;
|
import org.keycloak.userprofile.UserProfileProvider;
|
||||||
|
import org.keycloak.util.JsonSerialization;
|
||||||
import org.keycloak.validation.ValidationUtil;
|
import org.keycloak.validation.ValidationUtil;
|
||||||
|
|
||||||
|
import javax.ws.rs.core.MediaType;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
@ -108,6 +113,7 @@ import static org.keycloak.models.utils.RepresentationToModel.createGroups;
|
||||||
import static org.keycloak.models.utils.RepresentationToModel.createRoleMappings;
|
import static org.keycloak.models.utils.RepresentationToModel.createRoleMappings;
|
||||||
import static org.keycloak.models.utils.RepresentationToModel.importGroup;
|
import static org.keycloak.models.utils.RepresentationToModel.importGroup;
|
||||||
import static org.keycloak.models.utils.RepresentationToModel.importRoles;
|
import static org.keycloak.models.utils.RepresentationToModel.importRoles;
|
||||||
|
import static org.keycloak.models.utils.StripSecretsUtils.stripForExport;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This wraps the functionality about export/import for legacy storage. This will be handled differently for the new map storage,
|
* This wraps the functionality about export/import for legacy storage. This will be handled differently for the new map storage,
|
||||||
|
@ -123,6 +129,16 @@ public class LegacyExportImportManager implements ExportImportManager {
|
||||||
this.session = session;
|
this.session = session;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void exportRealm(RealmModel realm, ExportOptions options, ExportAdapter callback) {
|
||||||
|
callback.setType(MediaType.APPLICATION_JSON);
|
||||||
|
callback.writeToOutputStream(outputStream -> {
|
||||||
|
RealmRepresentation rep = ExportUtils.exportRealm(session, realm, options, false);
|
||||||
|
stripForExport(session, rep);
|
||||||
|
JsonSerialization.writeValueToStream(outputStream, rep);
|
||||||
|
outputStream.close();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void importRealm(RealmRepresentation rep, RealmModel newRealm, boolean skipUserDependent) {
|
public void importRealm(RealmRepresentation rep, RealmModel newRealm, boolean skipUserDependent) {
|
||||||
convertDeprecatedSocialProviders(rep);
|
convertDeprecatedSocialProviders(rep);
|
||||||
|
|
|
@ -16,3 +16,4 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
org.keycloak.storage.UserStorageProviderSpi
|
org.keycloak.storage.UserStorageProviderSpi
|
||||||
|
org.keycloak.storage.federated.UserFederatedStorageProviderSpi
|
||||||
|
|
|
@ -21,6 +21,9 @@ import org.jboss.logging.Logger;
|
||||||
import org.keycloak.common.enums.SslRequired;
|
import org.keycloak.common.enums.SslRequired;
|
||||||
import org.keycloak.common.util.MultivaluedHashMap;
|
import org.keycloak.common.util.MultivaluedHashMap;
|
||||||
import org.keycloak.component.ComponentModel;
|
import org.keycloak.component.ComponentModel;
|
||||||
|
import org.keycloak.models.ModelException;
|
||||||
|
import org.keycloak.exportimport.ExportAdapter;
|
||||||
|
import org.keycloak.exportimport.ExportOptions;
|
||||||
import org.keycloak.keys.KeyProvider;
|
import org.keycloak.keys.KeyProvider;
|
||||||
import org.keycloak.migration.MigrationProvider;
|
import org.keycloak.migration.MigrationProvider;
|
||||||
import org.keycloak.migration.migrators.MigrationUtils;
|
import org.keycloak.migration.migrators.MigrationUtils;
|
||||||
|
@ -413,6 +416,10 @@ public class MapExportImportManager implements ExportImportManager {
|
||||||
return role;
|
return role;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void exportRealm(RealmModel realm, ExportOptions options, ExportAdapter callback) {
|
||||||
|
throw new ModelException("exporting for map storage is currently not supported");
|
||||||
|
}
|
||||||
|
|
||||||
private static void convertDeprecatedDefaultRoles(RealmRepresentation rep, RealmModel newRealm) {
|
private static void convertDeprecatedDefaultRoles(RealmRepresentation rep, RealmModel newRealm) {
|
||||||
if (rep.getDefaultRole() == null) {
|
if (rep.getDefaultRole() == null) {
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2022 Red Hat, Inc. and/or its affiliates
|
||||||
|
* and other contributors as indicated by the @author tags.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.keycloak.exportimport;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This adapter allows the exporter to act independent of APIs used to serve the exported data to the caller.
|
||||||
|
*
|
||||||
|
* @author Alexander Schwartz
|
||||||
|
*/
|
||||||
|
public interface ExportAdapter {
|
||||||
|
/**
|
||||||
|
* Set the mime type of the output.
|
||||||
|
*
|
||||||
|
* @param mediaType Mime Type
|
||||||
|
*/
|
||||||
|
void setType(String mediaType);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write to the output stream. Once writing is complete, close it.
|
||||||
|
*
|
||||||
|
* @param consumer A consumer to that accepts the output stream.
|
||||||
|
*/
|
||||||
|
void writeToOutputStream(ConsumerOfOutputStream consumer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom consumer that is allowed to throw an {@link IOException} as writing to an output stream might do this.
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
interface ConsumerOfOutputStream {
|
||||||
|
void accept(java.io.OutputStream t) throws IOException;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,21 @@
|
||||||
package org.keycloak.exportimport.util;
|
/*
|
||||||
|
* Copyright 2022 Red Hat, Inc. and/or its affiliates
|
||||||
|
* and other contributors as indicated by the @author tags.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.keycloak.exportimport;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
|
@ -19,12 +19,15 @@ package org.keycloak.models.utils;
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
import org.keycloak.authorization.AuthorizationProvider;
|
import org.keycloak.authorization.AuthorizationProvider;
|
||||||
|
import org.keycloak.authorization.AuthorizationProviderFactory;
|
||||||
import org.keycloak.authorization.model.PermissionTicket;
|
import org.keycloak.authorization.model.PermissionTicket;
|
||||||
import org.keycloak.authorization.model.Policy;
|
import org.keycloak.authorization.model.Policy;
|
||||||
import org.keycloak.authorization.model.Resource;
|
import org.keycloak.authorization.model.Resource;
|
||||||
import org.keycloak.authorization.model.ResourceServer;
|
import org.keycloak.authorization.model.ResourceServer;
|
||||||
import org.keycloak.authorization.model.Scope;
|
import org.keycloak.authorization.model.Scope;
|
||||||
import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
|
import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
|
||||||
|
import org.keycloak.authorization.store.PolicyStore;
|
||||||
|
import org.keycloak.authorization.store.StoreFactory;
|
||||||
import org.keycloak.common.Profile;
|
import org.keycloak.common.Profile;
|
||||||
import org.keycloak.common.util.MultivaluedHashMap;
|
import org.keycloak.common.util.MultivaluedHashMap;
|
||||||
import org.keycloak.common.util.Time;
|
import org.keycloak.common.util.Time;
|
||||||
|
@ -1073,4 +1076,100 @@ public class ModelToRepresentation {
|
||||||
|
|
||||||
return representation;
|
return representation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ResourceServerRepresentation toResourceServerRepresentation(KeycloakSession session, ClientModel client) {
|
||||||
|
AuthorizationProviderFactory providerFactory = (AuthorizationProviderFactory) session.getKeycloakSessionFactory().getProviderFactory(AuthorizationProvider.class);
|
||||||
|
AuthorizationProvider authorization = providerFactory.create(session, client.getRealm());
|
||||||
|
StoreFactory storeFactory = authorization.getStoreFactory();
|
||||||
|
ResourceServer settingsModel = authorization.getStoreFactory().getResourceServerStore().findByClient(client);
|
||||||
|
|
||||||
|
if (settingsModel == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResourceServerRepresentation representation = toRepresentation(settingsModel, client);
|
||||||
|
|
||||||
|
representation.setId(null);
|
||||||
|
representation.setName(null);
|
||||||
|
representation.setClientId(null);
|
||||||
|
|
||||||
|
List<ResourceRepresentation> resources = storeFactory.getResourceStore().findByResourceServer(settingsModel)
|
||||||
|
.stream().map(resource -> {
|
||||||
|
ResourceRepresentation rep = toRepresentation(resource, settingsModel, authorization);
|
||||||
|
|
||||||
|
if (rep.getOwner().getId().equals(settingsModel.getClientId())) {
|
||||||
|
rep.setOwner((ResourceOwnerRepresentation) null);
|
||||||
|
} else {
|
||||||
|
rep.getOwner().setId(null);
|
||||||
|
}
|
||||||
|
rep.getScopes().forEach(scopeRepresentation -> {
|
||||||
|
scopeRepresentation.setId(null);
|
||||||
|
scopeRepresentation.setIconUri(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
return rep;
|
||||||
|
}).collect(Collectors.toList());
|
||||||
|
|
||||||
|
representation.setResources(resources);
|
||||||
|
|
||||||
|
List<PolicyRepresentation> policies = new ArrayList<>();
|
||||||
|
PolicyStore policyStore = storeFactory.getPolicyStore();
|
||||||
|
|
||||||
|
policies.addAll(policyStore.findByResourceServer(settingsModel)
|
||||||
|
.stream().filter(policy -> !policy.getType().equals("resource") && !policy.getType().equals("scope") && policy.getOwner() == null)
|
||||||
|
.map(policy -> toRepresentation(authorization, policy)).collect(Collectors.toList()));
|
||||||
|
policies.addAll(policyStore.findByResourceServer(settingsModel)
|
||||||
|
.stream().filter(policy -> (policy.getType().equals("resource") || policy.getType().equals("scope") && policy.getOwner() == null))
|
||||||
|
.map(policy -> toRepresentation(authorization, policy)).collect(Collectors.toList()));
|
||||||
|
|
||||||
|
representation.setPolicies(policies);
|
||||||
|
|
||||||
|
List<ScopeRepresentation> scopes = storeFactory.getScopeStore().findByResourceServer(settingsModel).stream().map(scope -> {
|
||||||
|
ScopeRepresentation rep = toRepresentation(scope);
|
||||||
|
|
||||||
|
rep.setPolicies(null);
|
||||||
|
rep.setResources(null);
|
||||||
|
|
||||||
|
return rep;
|
||||||
|
}).collect(Collectors.toList());
|
||||||
|
|
||||||
|
representation.setScopes(scopes);
|
||||||
|
|
||||||
|
return representation;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static PolicyRepresentation toRepresentation(AuthorizationProvider authorizationProvider, Policy policy) {
|
||||||
|
try {
|
||||||
|
PolicyRepresentation rep = toRepresentation(policy, authorizationProvider, true, true);
|
||||||
|
|
||||||
|
Map<String, String> config = new HashMap<>(rep.getConfig());
|
||||||
|
|
||||||
|
rep.setConfig(config);
|
||||||
|
|
||||||
|
Set<Scope> scopes = policy.getScopes();
|
||||||
|
|
||||||
|
if (!scopes.isEmpty()) {
|
||||||
|
List<String> scopeNames = scopes.stream().map(Scope::getName).collect(Collectors.toList());
|
||||||
|
config.put("scopes", JsonSerialization.writeValueAsString(scopeNames));
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<Resource> policyResources = policy.getResources();
|
||||||
|
|
||||||
|
if (!policyResources.isEmpty()) {
|
||||||
|
List<String> resourceNames = policyResources.stream().map(Resource::getName).collect(Collectors.toList());
|
||||||
|
config.put("resources", JsonSerialization.writeValueAsString(resourceNames));
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<Policy> associatedPolicies = policy.getAssociatedPolicies();
|
||||||
|
|
||||||
|
if (!associatedPolicies.isEmpty()) {
|
||||||
|
config.put("applyPolicies", JsonSerialization.writeValueAsString(associatedPolicies.stream().map(associated -> associated.getName()).collect(Collectors.toList())));
|
||||||
|
}
|
||||||
|
|
||||||
|
return rep;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("Error while exporting policy [" + policy.getName() + "].", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -112,7 +112,6 @@ import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
||||||
import org.keycloak.representations.idm.authorization.ResourceServerRepresentation;
|
import org.keycloak.representations.idm.authorization.ResourceServerRepresentation;
|
||||||
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
|
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
|
||||||
import org.keycloak.storage.DatastoreProvider;
|
import org.keycloak.storage.DatastoreProvider;
|
||||||
import org.keycloak.storage.federated.UserFederatedStorageProvider;
|
|
||||||
import org.keycloak.util.JsonSerialization;
|
import org.keycloak.util.JsonSerialization;
|
||||||
|
|
||||||
import static org.keycloak.protocol.saml.util.ArtifactBindingUtils.computeArtifactBindingIdentifierString;
|
import static org.keycloak.protocol.saml.util.ArtifactBindingUtils.computeArtifactBindingIdentifierString;
|
||||||
|
|
|
@ -89,7 +89,7 @@ public class StripSecretsUtils {
|
||||||
return rep;
|
return rep;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static RealmRepresentation stripForExport(KeycloakSession session, RealmRepresentation rep) {
|
public static void stripForExport(KeycloakSession session, RealmRepresentation rep) {
|
||||||
strip(rep);
|
strip(rep);
|
||||||
|
|
||||||
List<ClientRepresentation> clients = rep.getClients();
|
List<ClientRepresentation> clients = rep.getClients();
|
||||||
|
@ -127,8 +127,6 @@ public class StripSecretsUtils {
|
||||||
strip(u);
|
strip(u);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return rep;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static UserRepresentation strip(UserRepresentation user) {
|
public static UserRepresentation strip(UserRepresentation user) {
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
|
|
||||||
package org.keycloak.storage;
|
package org.keycloak.storage;
|
||||||
|
|
||||||
|
import org.keycloak.exportimport.ExportAdapter;
|
||||||
|
import org.keycloak.exportimport.ExportOptions;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
|
@ -33,4 +35,6 @@ public interface ExportImportManager {
|
||||||
void updateRealm(RealmRepresentation rep, RealmModel realm);
|
void updateRealm(RealmRepresentation rep, RealmModel realm);
|
||||||
|
|
||||||
UserModel createUser(RealmModel realm, UserRepresentation userRep);
|
UserModel createUser(RealmModel realm, UserRepresentation userRep);
|
||||||
|
|
||||||
|
void exportRealm(RealmModel realm, ExportOptions options, ExportAdapter callback);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
|
|
||||||
org.keycloak.component.ComponentFactorySpi
|
org.keycloak.component.ComponentFactorySpi
|
||||||
org.keycloak.provider.ExceptionConverterSpi
|
org.keycloak.provider.ExceptionConverterSpi
|
||||||
org.keycloak.storage.federated.UserFederatedStorageProviderSpi
|
|
||||||
org.keycloak.models.ClientSpi
|
org.keycloak.models.ClientSpi
|
||||||
org.keycloak.models.ClientScopeSpi
|
org.keycloak.models.ClientScopeSpi
|
||||||
org.keycloak.models.GroupSpi
|
org.keycloak.models.GroupSpi
|
||||||
|
|
|
@ -22,7 +22,6 @@ import org.keycloak.provider.InvalidationHandler.InvalidableObjectType;
|
||||||
import org.keycloak.provider.Provider;
|
import org.keycloak.provider.Provider;
|
||||||
import org.keycloak.services.clientpolicy.ClientPolicyManager;
|
import org.keycloak.services.clientpolicy.ClientPolicyManager;
|
||||||
import org.keycloak.sessions.AuthenticationSessionProvider;
|
import org.keycloak.sessions.AuthenticationSessionProvider;
|
||||||
import org.keycloak.storage.federated.UserFederatedStorageProvider;
|
|
||||||
import org.keycloak.vault.VaultTranscriber;
|
import org.keycloak.vault.VaultTranscriber;
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -302,15 +301,6 @@ public interface KeycloakSession {
|
||||||
@Deprecated
|
@Deprecated
|
||||||
RoleProvider roleLocalStorage();
|
RoleProvider roleLocalStorage();
|
||||||
|
|
||||||
/**
|
|
||||||
* Hybrid storage for UserStorageProviders that can't store a specific piece of keycloak data in their external storage.
|
|
||||||
* No cache in front.
|
|
||||||
*
|
|
||||||
* @deprecated Access to the legacy store is no longer possible via this method. Adjust your code according to the Keycloak 19 Upgrading Guide.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
UserFederatedStorageProvider userFederatedStorage();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Key manager
|
* Key manager
|
||||||
*
|
*
|
||||||
|
|
|
@ -36,7 +36,6 @@ import org.keycloak.authorization.AuthorizationProvider;
|
||||||
import org.keycloak.authorization.model.ResourceServer;
|
import org.keycloak.authorization.model.ResourceServer;
|
||||||
import org.keycloak.events.admin.OperationType;
|
import org.keycloak.events.admin.OperationType;
|
||||||
import org.keycloak.events.admin.ResourceType;
|
import org.keycloak.events.admin.ResourceType;
|
||||||
import org.keycloak.exportimport.util.ExportUtils;
|
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
|
@ -124,7 +123,7 @@ public class ResourceServerService {
|
||||||
@Produces("application/json")
|
@Produces("application/json")
|
||||||
public Response exportSettings() {
|
public Response exportSettings() {
|
||||||
this.auth.realm().requireManageAuthorization();
|
this.auth.realm().requireManageAuthorization();
|
||||||
return Response.ok(ExportUtils.exportAuthorizationSettings(session, client)).build();
|
return Response.ok(ModelToRepresentation.toResourceServerRepresentation(session, client)).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Path("/import")
|
@Path("/import")
|
||||||
|
|
|
@ -48,7 +48,6 @@ import org.keycloak.services.clientpolicy.ClientPolicyManager;
|
||||||
import org.keycloak.models.LegacySessionSupportProvider;
|
import org.keycloak.models.LegacySessionSupportProvider;
|
||||||
import org.keycloak.sessions.AuthenticationSessionProvider;
|
import org.keycloak.sessions.AuthenticationSessionProvider;
|
||||||
import org.keycloak.storage.DatastoreProvider;
|
import org.keycloak.storage.DatastoreProvider;
|
||||||
import org.keycloak.storage.federated.UserFederatedStorageProvider;
|
|
||||||
import org.keycloak.vault.DefaultVaultTranscriber;
|
import org.keycloak.vault.DefaultVaultTranscriber;
|
||||||
import org.keycloak.vault.VaultProvider;
|
import org.keycloak.vault.VaultProvider;
|
||||||
import org.keycloak.vault.VaultTranscriber;
|
import org.keycloak.vault.VaultTranscriber;
|
||||||
|
@ -83,7 +82,6 @@ public class DefaultKeycloakSession implements KeycloakSession {
|
||||||
private UserSessionProvider sessionProvider;
|
private UserSessionProvider sessionProvider;
|
||||||
private UserLoginFailureProvider userLoginFailureProvider;
|
private UserLoginFailureProvider userLoginFailureProvider;
|
||||||
private AuthenticationSessionProvider authenticationSessionProvider;
|
private AuthenticationSessionProvider authenticationSessionProvider;
|
||||||
private UserFederatedStorageProvider userFederatedStorageProvider;
|
|
||||||
private final KeycloakContext context;
|
private final KeycloakContext context;
|
||||||
private KeyManager keyManager;
|
private KeyManager keyManager;
|
||||||
private ThemeManager themeManager;
|
private ThemeManager themeManager;
|
||||||
|
@ -164,15 +162,6 @@ public class DefaultKeycloakSession implements KeycloakSession {
|
||||||
return factory;
|
return factory;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
@Deprecated
|
|
||||||
public UserFederatedStorageProvider userFederatedStorage() {
|
|
||||||
if (userFederatedStorageProvider == null) {
|
|
||||||
userFederatedStorageProvider = getProvider(UserFederatedStorageProvider.class);
|
|
||||||
}
|
|
||||||
return userFederatedStorageProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public UserProvider userLocalStorage() {
|
public UserProvider userLocalStorage() {
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
package org.keycloak.services.resources.admin;
|
package org.keycloak.services.resources.admin;
|
||||||
|
|
||||||
import static org.keycloak.utils.LockObjectsForModification.lockUserSessionsForModification;
|
import static org.keycloak.utils.LockObjectsForModification.lockUserSessionsForModification;
|
||||||
import static org.keycloak.models.utils.StripSecretsUtils.stripForExport;
|
|
||||||
import static org.keycloak.util.JsonSerialization.readValue;
|
import static org.keycloak.util.JsonSerialization.readValue;
|
||||||
|
|
||||||
import java.security.cert.X509Certificate;
|
import java.security.cert.X509Certificate;
|
||||||
|
@ -48,6 +47,7 @@ import javax.ws.rs.core.HttpHeaders;
|
||||||
import javax.ws.rs.core.MediaType;
|
import javax.ws.rs.core.MediaType;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import javax.ws.rs.core.Response.Status;
|
import javax.ws.rs.core.Response.Status;
|
||||||
|
import javax.ws.rs.core.StreamingOutput;
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.type.TypeReference;
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
|
|
||||||
|
@ -71,8 +71,8 @@ import org.keycloak.events.admin.OperationType;
|
||||||
import org.keycloak.events.admin.ResourceType;
|
import org.keycloak.events.admin.ResourceType;
|
||||||
import org.keycloak.exportimport.ClientDescriptionConverter;
|
import org.keycloak.exportimport.ClientDescriptionConverter;
|
||||||
import org.keycloak.exportimport.ClientDescriptionConverterFactory;
|
import org.keycloak.exportimport.ClientDescriptionConverterFactory;
|
||||||
import org.keycloak.exportimport.util.ExportOptions;
|
import org.keycloak.exportimport.ExportAdapter;
|
||||||
import org.keycloak.exportimport.util.ExportUtils;
|
import org.keycloak.exportimport.ExportOptions;
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
import org.keycloak.models.ClientScopeModel;
|
import org.keycloak.models.ClientScopeModel;
|
||||||
import org.keycloak.models.Constants;
|
import org.keycloak.models.Constants;
|
||||||
|
@ -110,6 +110,8 @@ import org.keycloak.services.resources.admin.ext.AdminRealmResourceProvider;
|
||||||
import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
|
import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
|
||||||
import org.keycloak.services.resources.admin.permissions.AdminPermissionManagement;
|
import org.keycloak.services.resources.admin.permissions.AdminPermissionManagement;
|
||||||
import org.keycloak.services.resources.admin.permissions.AdminPermissions;
|
import org.keycloak.services.resources.admin.permissions.AdminPermissions;
|
||||||
|
import org.keycloak.storage.DatastoreProvider;
|
||||||
|
import org.keycloak.storage.ExportImportManager;
|
||||||
import org.keycloak.storage.LegacyStoreSyncEvent;
|
import org.keycloak.storage.LegacyStoreSyncEvent;
|
||||||
import org.keycloak.utils.ProfileHelper;
|
import org.keycloak.utils.ProfileHelper;
|
||||||
import org.keycloak.utils.ReservedCharValidator;
|
import org.keycloak.utils.ReservedCharValidator;
|
||||||
|
@ -1055,8 +1057,7 @@ public class RealmAdminResource {
|
||||||
*/
|
*/
|
||||||
@Path("partial-export")
|
@Path("partial-export")
|
||||||
@POST
|
@POST
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
public Response partialExport(@QueryParam("exportGroupsAndRoles") Boolean exportGroupsAndRoles,
|
||||||
public RealmRepresentation partialExport(@QueryParam("exportGroupsAndRoles") Boolean exportGroupsAndRoles,
|
|
||||||
@QueryParam("exportClients") Boolean exportClients) {
|
@QueryParam("exportClients") Boolean exportClients) {
|
||||||
auth.realm().requireViewRealm();
|
auth.realm().requireViewRealm();
|
||||||
|
|
||||||
|
@ -1074,8 +1075,22 @@ public class RealmAdminResource {
|
||||||
// this means that if clients is true but groups/roles is false the service account is exported without roles
|
// this means that if clients is true but groups/roles is false the service account is exported without roles
|
||||||
// the other option is just include service accounts if clientsExported && groupsAndRolesExported
|
// the other option is just include service accounts if clientsExported && groupsAndRolesExported
|
||||||
ExportOptions options = new ExportOptions(false, clientsExported, groupsAndRolesExported, clientsExported);
|
ExportOptions options = new ExportOptions(false, clientsExported, groupsAndRolesExported, clientsExported);
|
||||||
RealmRepresentation rep = ExportUtils.exportRealm(session, realm, options, false);
|
|
||||||
return stripForExport(session, rep);
|
ExportImportManager exportProvider = session.getProvider(DatastoreProvider.class).getExportImportManager();
|
||||||
|
|
||||||
|
Response.ResponseBuilder response = Response.ok();
|
||||||
|
|
||||||
|
exportProvider.exportRealm(realm, options, new ExportAdapter() {
|
||||||
|
@Override
|
||||||
|
public void setType(String mediaType) {
|
||||||
|
response.type(mediaType);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void writeToOutputStream(ConsumerOfOutputStream consumer) {
|
||||||
|
response.entity((StreamingOutput) consumer::accept);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return response.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Path("keys")
|
@Path("keys")
|
||||||
|
|
|
@ -16,10 +16,8 @@
|
||||||
*/
|
*/
|
||||||
package org.keycloak.validation;
|
package org.keycloak.validation;
|
||||||
|
|
||||||
import org.keycloak.authentication.AuthenticatorUtil;
|
|
||||||
import org.keycloak.authentication.authenticators.util.LoAUtil;
|
import org.keycloak.authentication.authenticators.util.LoAUtil;
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
import org.keycloak.models.ClientScopeModel;
|
|
||||||
import org.keycloak.protocol.ProtocolMapperConfigException;
|
import org.keycloak.protocol.ProtocolMapperConfigException;
|
||||||
import org.keycloak.protocol.oidc.OIDCAdvancedConfigWrapper;
|
import org.keycloak.protocol.oidc.OIDCAdvancedConfigWrapper;
|
||||||
import org.keycloak.protocol.oidc.OIDCConfigAttributes;
|
import org.keycloak.protocol.oidc.OIDCConfigAttributes;
|
||||||
|
|
|
@ -1768,6 +1768,9 @@ public class PermissionsTest extends AbstractKeycloakTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void partialExport() {
|
public void partialExport() {
|
||||||
|
// re-enable as part of https://github.com/keycloak/keycloak/issues/14291
|
||||||
|
ProfileAssume.assumeFeatureDisabled(Profile.Feature.MAP_STORAGE);
|
||||||
|
|
||||||
invoke(new Invocation() {
|
invoke(new Invocation() {
|
||||||
public void invoke(RealmResource realm) {
|
public void invoke(RealmResource realm) {
|
||||||
realm.partialExport(false, false);
|
realm.partialExport(false, false);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package org.keycloak.testsuite.admin.partialexport;
|
package org.keycloak.testsuite.admin.partialexport;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.keycloak.common.Profile;
|
||||||
import org.keycloak.common.util.MultivaluedHashMap;
|
import org.keycloak.common.util.MultivaluedHashMap;
|
||||||
import org.keycloak.representations.idm.ClientRepresentation;
|
import org.keycloak.representations.idm.ClientRepresentation;
|
||||||
import org.keycloak.representations.idm.ComponentExportRepresentation;
|
import org.keycloak.representations.idm.ComponentExportRepresentation;
|
||||||
|
@ -12,6 +12,7 @@ import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
import org.keycloak.representations.idm.RoleRepresentation;
|
import org.keycloak.representations.idm.RoleRepresentation;
|
||||||
import org.keycloak.representations.idm.ScopeMappingRepresentation;
|
import org.keycloak.representations.idm.ScopeMappingRepresentation;
|
||||||
import org.keycloak.testsuite.Assert;
|
import org.keycloak.testsuite.Assert;
|
||||||
|
import org.keycloak.testsuite.ProfileAssume;
|
||||||
import org.keycloak.testsuite.admin.AbstractAdminTest;
|
import org.keycloak.testsuite.admin.AbstractAdminTest;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
@ -41,6 +42,8 @@ public class PartialExportTest extends AbstractAdminTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testExport() {
|
public void testExport() {
|
||||||
|
// re-enable as part of https://github.com/keycloak/keycloak/issues/14291
|
||||||
|
ProfileAssume.assumeFeatureDisabled(Profile.Feature.MAP_STORAGE);
|
||||||
|
|
||||||
// exportGroupsAndRoles == false, exportClients == false
|
// exportGroupsAndRoles == false, exportClients == false
|
||||||
RealmRepresentation rep = adminClient.realm(EXPORT_TEST_REALM).partialExport(false, false);
|
RealmRepresentation rep = adminClient.realm(EXPORT_TEST_REALM).partialExport(false, false);
|
||||||
|
|
Loading…
Reference in a new issue