Move UserFederatedStorageProvider into legacy module

Closes #13627
This commit is contained in:
Alexander Schwartz 2022-08-17 10:12:09 +02:00 committed by Hynek Mlnařík
parent 962a685b7b
commit 1d2d3e5ca5
29 changed files with 227 additions and 158 deletions

View file

@ -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

View file

@ -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);

View file

@ -16,3 +16,4 @@
# #
org.keycloak.storage.UserStorageProviderSpi org.keycloak.storage.UserStorageProviderSpi
org.keycloak.storage.federated.UserFederatedStorageProviderSpi

View file

@ -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) {

View file

@ -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;
}
}

View file

@ -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>

View file

@ -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);
}
}
} }

View file

@ -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;

View file

@ -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) {

View file

@ -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);
} }

View file

@ -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

View file

@ -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
* *

View file

@ -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")

View file

@ -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() {

View file

@ -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")

View file

@ -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;

View file

@ -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);

View file

@ -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);