Ensure organization id is preserved on export/import
- Also fixes issues with description, enabled, and custom attributes missing when re-importing the orgs. Closes #33207 Signed-off-by: Stefan Guilhen <sguilhen@redhat.com>
This commit is contained in:
parent
c054a086cf
commit
6424708695
14 changed files with 136 additions and 133 deletions
|
@ -57,9 +57,9 @@ public class InfinispanOrganizationProvider implements OrganizationProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public OrganizationModel create(String name, String alias) {
|
public OrganizationModel create(String id, String name, String alias) {
|
||||||
registerCountInvalidation();
|
registerCountInvalidation();
|
||||||
return orgDelegate.create(name, alias);
|
return orgDelegate.create(id, name, alias);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -55,6 +55,7 @@ import org.keycloak.models.jpa.entities.GroupEntity;
|
||||||
import org.keycloak.models.jpa.entities.OrganizationEntity;
|
import org.keycloak.models.jpa.entities.OrganizationEntity;
|
||||||
import org.keycloak.models.jpa.entities.UserEntity;
|
import org.keycloak.models.jpa.entities.UserEntity;
|
||||||
import org.keycloak.models.jpa.entities.UserGroupMembershipEntity;
|
import org.keycloak.models.jpa.entities.UserGroupMembershipEntity;
|
||||||
|
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
import org.keycloak.organization.OrganizationProvider;
|
import org.keycloak.organization.OrganizationProvider;
|
||||||
import org.keycloak.representations.idm.MembershipType;
|
import org.keycloak.representations.idm.MembershipType;
|
||||||
import org.keycloak.organization.utils.Organizations;
|
import org.keycloak.organization.utils.Organizations;
|
||||||
|
@ -76,7 +77,7 @@ public class JpaOrganizationProvider implements OrganizationProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public OrganizationModel create(String name, String alias) {
|
public OrganizationModel create(String id, String name, String alias) {
|
||||||
if (StringUtil.isBlank(name)) {
|
if (StringUtil.isBlank(name)) {
|
||||||
throw new ModelValidationException("Name can not be null");
|
throw new ModelValidationException("Name can not be null");
|
||||||
}
|
}
|
||||||
|
@ -98,8 +99,10 @@ public class JpaOrganizationProvider implements OrganizationProvider {
|
||||||
throw new ModelDuplicateException("A organization with the same alias already exists");
|
throw new ModelDuplicateException("A organization with the same alias already exists");
|
||||||
}
|
}
|
||||||
|
|
||||||
RealmModel realm = getRealm();
|
OrganizationEntity entity = new OrganizationEntity();
|
||||||
OrganizationAdapter adapter = new OrganizationAdapter(session, realm, this);
|
entity.setId(id != null ? id : KeycloakModelUtils.generateId());
|
||||||
|
entity.setRealmId(getRealm().getId());
|
||||||
|
OrganizationAdapter adapter = new OrganizationAdapter(session, getRealm(), entity, this);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
session.getContext().setOrganization(adapter);
|
session.getContext().setOrganization(adapter);
|
||||||
|
|
|
@ -53,15 +53,6 @@ public final class OrganizationAdapter implements OrganizationModel, JpaModel<Or
|
||||||
private GroupModel group;
|
private GroupModel group;
|
||||||
private Map<String, List<String>> attributes;
|
private Map<String, List<String>> attributes;
|
||||||
|
|
||||||
public OrganizationAdapter(KeycloakSession session, RealmModel realm, OrganizationProvider provider) {
|
|
||||||
this.session = session;
|
|
||||||
entity = new OrganizationEntity();
|
|
||||||
entity.setId(KeycloakModelUtils.generateId());
|
|
||||||
entity.setRealmId(realm.getId());
|
|
||||||
this.realm = realm;
|
|
||||||
this.provider = provider;
|
|
||||||
}
|
|
||||||
|
|
||||||
public OrganizationAdapter(KeycloakSession session, RealmModel realm, OrganizationEntity entity, OrganizationProvider provider) {
|
public OrganizationAdapter(KeycloakSession session, RealmModel realm, OrganizationEntity entity, OrganizationProvider provider) {
|
||||||
this.session = session;
|
this.session = session;
|
||||||
this.realm = realm;
|
this.realm = realm;
|
||||||
|
|
|
@ -130,7 +130,7 @@ public class ExportUtils {
|
||||||
List<RoleRepresentation> currentAppRoleReps = exportRoles(currentAppRoles);
|
List<RoleRepresentation> currentAppRoleReps = exportRoles(currentAppRoles);
|
||||||
clientRolesReps.put(client.getClientId(), currentAppRoleReps);
|
clientRolesReps.put(client.getClientId(), currentAppRoleReps);
|
||||||
}
|
}
|
||||||
if (clientRolesReps.size() > 0) {
|
if (!clientRolesReps.isEmpty()) {
|
||||||
rolesRep.setClient(clientRolesReps);
|
rolesRep.setClient(clientRolesReps);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -156,11 +156,7 @@ public class ExportUtils {
|
||||||
} else {
|
} else {
|
||||||
ClientModel app = (ClientModel) scope.getContainer();
|
ClientModel app = (ClientModel) scope.getContainer();
|
||||||
String appName = app.getClientId();
|
String appName = app.getClientId();
|
||||||
List<ScopeMappingRepresentation> currentAppScopes = clientScopeReps.get(appName);
|
List<ScopeMappingRepresentation> currentAppScopes = clientScopeReps.computeIfAbsent(appName, k -> new ArrayList<>());
|
||||||
if (currentAppScopes == null) {
|
|
||||||
currentAppScopes = new ArrayList<>();
|
|
||||||
clientScopeReps.put(appName, currentAppScopes);
|
|
||||||
}
|
|
||||||
|
|
||||||
ScopeMappingRepresentation currentClientScope = null;
|
ScopeMappingRepresentation currentClientScope = null;
|
||||||
for (ScopeMappingRepresentation scopeMapping : currentAppScopes) {
|
for (ScopeMappingRepresentation scopeMapping : currentAppScopes) {
|
||||||
|
@ -193,11 +189,7 @@ public class ExportUtils {
|
||||||
} else {
|
} else {
|
||||||
ClientModel app = (ClientModel)scope.getContainer();
|
ClientModel app = (ClientModel)scope.getContainer();
|
||||||
String appName = app.getClientId();
|
String appName = app.getClientId();
|
||||||
List<ScopeMappingRepresentation> currentAppScopes = clientScopeReps.get(appName);
|
List<ScopeMappingRepresentation> currentAppScopes = clientScopeReps.computeIfAbsent(appName, k -> new ArrayList<>());
|
||||||
if (currentAppScopes == null) {
|
|
||||||
currentAppScopes = new ArrayList<>();
|
|
||||||
clientScopeReps.put(appName, currentAppScopes);
|
|
||||||
}
|
|
||||||
|
|
||||||
ScopeMappingRepresentation currentClientTemplateScope = null;
|
ScopeMappingRepresentation currentClientTemplateScope = null;
|
||||||
for (ScopeMappingRepresentation scopeMapping : currentAppScopes) {
|
for (ScopeMappingRepresentation scopeMapping : currentAppScopes) {
|
||||||
|
@ -216,7 +208,7 @@ public class ExportUtils {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (clientScopeReps.size() > 0) {
|
if (!clientScopeReps.isEmpty()) {
|
||||||
rep.setClientScopeMappings(clientScopeReps);
|
rep.setClientScopeMappings(clientScopeReps);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -226,7 +218,7 @@ public class ExportUtils {
|
||||||
.map(user -> exportUser(session, realm, user, options, internal))
|
.map(user -> exportUser(session, realm, user, options, internal))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
if (users.size() > 0) {
|
if (!users.isEmpty()) {
|
||||||
rep.setUsers(users);
|
rep.setUsers(users);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -234,7 +226,7 @@ public class ExportUtils {
|
||||||
if (userFederatedStorageProvider != null) {
|
if (userFederatedStorageProvider != null) {
|
||||||
List<UserRepresentation> federatedUsers = userFederatedStorage(session).getStoredUsersStream(realm, 0, -1)
|
List<UserRepresentation> federatedUsers = userFederatedStorage(session).getStoredUsersStream(realm, 0, -1)
|
||||||
.map(user -> exportFederatedUser(session, realm, user, options)).collect(Collectors.toList());
|
.map(user -> exportFederatedUser(session, realm, user, options)).collect(Collectors.toList());
|
||||||
if (federatedUsers.size() > 0) {
|
if (!federatedUsers.isEmpty()) {
|
||||||
rep.setFederatedUsers(federatedUsers);
|
rep.setFederatedUsers(federatedUsers);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -251,7 +243,7 @@ public class ExportUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (users.size() > 0) {
|
if (!users.isEmpty()) {
|
||||||
rep.setUsers(users);
|
rep.setUsers(users);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -265,32 +257,19 @@ public class ExportUtils {
|
||||||
|
|
||||||
if (Profile.isFeatureEnabled(Feature.ORGANIZATION) && !options.isPartial()) {
|
if (Profile.isFeatureEnabled(Feature.ORGANIZATION) && !options.isPartial()) {
|
||||||
OrganizationProvider orgProvider = session.getProvider(OrganizationProvider.class);
|
OrganizationProvider orgProvider = session.getProvider(OrganizationProvider.class);
|
||||||
orgProvider.getAllStream().map(m -> {
|
orgProvider.getAllStream().map(model -> {
|
||||||
OrganizationRepresentation org = new OrganizationRepresentation();
|
OrganizationRepresentation org = ModelToRepresentation.toRepresentation(model);
|
||||||
|
|
||||||
org.setName(m.getName());
|
orgProvider.getMembersStream(model, null, null, null, null)
|
||||||
org.setAlias(m.getAlias());
|
|
||||||
org.setEnabled(m.isEnabled());
|
|
||||||
org.setDescription(m.getDescription());
|
|
||||||
m.getDomains().map(d -> {
|
|
||||||
OrganizationDomainRepresentation domain = new OrganizationDomainRepresentation();
|
|
||||||
|
|
||||||
domain.setName(d.getName());
|
|
||||||
domain.setVerified(d.isVerified());
|
|
||||||
|
|
||||||
return domain;
|
|
||||||
}).forEach(org::addDomain);
|
|
||||||
|
|
||||||
orgProvider.getMembersStream(m, null, null, null, null)
|
|
||||||
.forEach(user -> {
|
.forEach(user -> {
|
||||||
MemberRepresentation member = new MemberRepresentation();
|
MemberRepresentation member = new MemberRepresentation();
|
||||||
member.setUsername(user.getUsername());
|
member.setUsername(user.getUsername());
|
||||||
member.setMembershipType(orgProvider.isManagedMember(m, user) ? MembershipType.MANAGED : MembershipType.UNMANAGED);
|
member.setMembershipType(orgProvider.isManagedMember(model, user) ? MembershipType.MANAGED : MembershipType.UNMANAGED);
|
||||||
|
|
||||||
org.addMember(member);
|
org.addMember(member);
|
||||||
});
|
});
|
||||||
|
|
||||||
orgProvider.getIdentityProviders(m)
|
orgProvider.getIdentityProviders(model)
|
||||||
.map(b -> {
|
.map(b -> {
|
||||||
IdentityProviderRepresentation broker = new IdentityProviderRepresentation();
|
IdentityProviderRepresentation broker = new IdentityProviderRepresentation();
|
||||||
broker.setAlias(b.getAlias());
|
broker.setAlias(b.getAlias());
|
||||||
|
|
|
@ -48,7 +48,6 @@ import org.keycloak.models.LDAPConstants;
|
||||||
import org.keycloak.models.ModelException;
|
import org.keycloak.models.ModelException;
|
||||||
import org.keycloak.models.OAuth2DeviceConfig;
|
import org.keycloak.models.OAuth2DeviceConfig;
|
||||||
import org.keycloak.models.OTPPolicy;
|
import org.keycloak.models.OTPPolicy;
|
||||||
import org.keycloak.models.OrganizationDomainModel;
|
|
||||||
import org.keycloak.models.OrganizationModel;
|
import org.keycloak.models.OrganizationModel;
|
||||||
import org.keycloak.models.ParConfig;
|
import org.keycloak.models.ParConfig;
|
||||||
import org.keycloak.models.PasswordPolicy;
|
import org.keycloak.models.PasswordPolicy;
|
||||||
|
@ -1590,21 +1589,20 @@ public class DefaultExportImportManager implements ExportImportManager {
|
||||||
OrganizationProvider provider = session.getProvider(OrganizationProvider.class);
|
OrganizationProvider provider = session.getProvider(OrganizationProvider.class);
|
||||||
|
|
||||||
for (OrganizationRepresentation orgRep : Optional.ofNullable(rep.getOrganizations()).orElse(Collections.emptyList())) {
|
for (OrganizationRepresentation orgRep : Optional.ofNullable(rep.getOrganizations()).orElse(Collections.emptyList())) {
|
||||||
OrganizationModel org = provider.create(orgRep.getName(), orgRep.getAlias());
|
OrganizationModel orgModel = provider.create(orgRep.getId(), orgRep.getName(), orgRep.getAlias());
|
||||||
|
RepresentationToModel.toModel(orgRep, orgModel);
|
||||||
org.setDomains(orgRep.getDomains().stream().map(r -> new OrganizationDomainModel(r.getName(), r.isVerified())).collect(Collectors.toSet()));
|
|
||||||
|
|
||||||
for (IdentityProviderRepresentation identityProvider : Optional.ofNullable(orgRep.getIdentityProviders()).orElse(Collections.emptyList())) {
|
for (IdentityProviderRepresentation identityProvider : Optional.ofNullable(orgRep.getIdentityProviders()).orElse(Collections.emptyList())) {
|
||||||
IdentityProviderModel idp = session.identityProviders().getByAlias(identityProvider.getAlias());
|
IdentityProviderModel idp = session.identityProviders().getByAlias(identityProvider.getAlias());
|
||||||
provider.addIdentityProvider(org, idp);
|
provider.addIdentityProvider(orgModel, idp);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (MemberRepresentation member : Optional.ofNullable(orgRep.getMembers()).orElse(Collections.emptyList())) {
|
for (MemberRepresentation member : Optional.ofNullable(orgRep.getMembers()).orElse(Collections.emptyList())) {
|
||||||
UserModel m = session.users().getUserByUsername(newRealm, member.getUsername());
|
UserModel m = session.users().getUserByUsername(newRealm, member.getUsername());
|
||||||
if (MembershipType.MANAGED.equals(member.getMembershipType())) {
|
if (MembershipType.MANAGED.equals(member.getMembershipType())) {
|
||||||
provider.addManagedMember(org, m);
|
provider.addManagedMember(orgModel, m);
|
||||||
} else {
|
} else {
|
||||||
provider.addMember(org, m);
|
provider.addMember(orgModel, m);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1291,4 +1291,35 @@ public class ModelToRepresentation {
|
||||||
rep.setConfig(model.getConfig());
|
rep.setConfig(model.getConfig());
|
||||||
return rep;
|
return rep;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static OrganizationRepresentation toRepresentation(OrganizationModel model) {
|
||||||
|
OrganizationRepresentation rep = toBriefRepresentation(model);
|
||||||
|
if (rep == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
rep.setAttributes(model.getAttributes());
|
||||||
|
return rep;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static OrganizationRepresentation toBriefRepresentation(OrganizationModel model) {
|
||||||
|
if (model == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
OrganizationRepresentation rep = new OrganizationRepresentation();
|
||||||
|
rep.setId(model.getId());
|
||||||
|
rep.setName(model.getName());
|
||||||
|
rep.setAlias(model.getAlias());
|
||||||
|
rep.setEnabled(model.isEnabled());
|
||||||
|
rep.setDescription(model.getDescription());
|
||||||
|
model.getDomains().filter(Objects::nonNull).map(ModelToRepresentation::toRepresentation)
|
||||||
|
.forEach(rep::addDomain);
|
||||||
|
return rep;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static OrganizationDomainRepresentation toRepresentation(OrganizationDomainModel model) {
|
||||||
|
OrganizationDomainRepresentation representation = new OrganizationDomainRepresentation();
|
||||||
|
representation.setName(model.getName());
|
||||||
|
representation.setVerified(model.isVerified());
|
||||||
|
return representation;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,6 +108,8 @@ import org.keycloak.representations.idm.FederatedIdentityRepresentation;
|
||||||
import org.keycloak.representations.idm.GroupRepresentation;
|
import org.keycloak.representations.idm.GroupRepresentation;
|
||||||
import org.keycloak.representations.idm.IdentityProviderMapperRepresentation;
|
import org.keycloak.representations.idm.IdentityProviderMapperRepresentation;
|
||||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||||
|
import org.keycloak.representations.idm.OrganizationDomainRepresentation;
|
||||||
|
import org.keycloak.representations.idm.OrganizationRepresentation;
|
||||||
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
|
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
|
||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
import org.keycloak.representations.idm.RoleRepresentation;
|
import org.keycloak.representations.idm.RoleRepresentation;
|
||||||
|
@ -127,6 +129,7 @@ import org.keycloak.storage.DatastoreProvider;
|
||||||
import org.keycloak.util.JsonSerialization;
|
import org.keycloak.util.JsonSerialization;
|
||||||
import org.keycloak.utils.StringUtil;
|
import org.keycloak.utils.StringUtil;
|
||||||
|
|
||||||
|
import static java.util.Optional.ofNullable;
|
||||||
import static org.keycloak.protocol.saml.util.ArtifactBindingUtils.computeArtifactBindingIdentifierString;
|
import static org.keycloak.protocol.saml.util.ArtifactBindingUtils.computeArtifactBindingIdentifierString;
|
||||||
|
|
||||||
public class RepresentationToModel {
|
public class RepresentationToModel {
|
||||||
|
@ -1674,4 +1677,27 @@ public class RepresentationToModel {
|
||||||
representation.setOrganizationId(orgId);
|
representation.setOrganizationId(orgId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static OrganizationModel toModel(OrganizationRepresentation rep, OrganizationModel model) {
|
||||||
|
if (rep == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
model.setName(rep.getName());
|
||||||
|
model.setAlias(rep.getAlias());
|
||||||
|
model.setEnabled(rep.isEnabled());
|
||||||
|
model.setDescription(rep.getDescription());
|
||||||
|
model.setAttributes(rep.getAttributes());
|
||||||
|
model.setDomains(ofNullable(rep.getDomains()).orElse(Set.of()).stream()
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.filter(domain -> StringUtil.isNotBlank(domain.getName()))
|
||||||
|
.map(RepresentationToModel::toModel)
|
||||||
|
.collect(Collectors.toSet()));
|
||||||
|
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static OrganizationDomainModel toModel(OrganizationDomainRepresentation domainRepresentation) {
|
||||||
|
return new OrganizationDomainModel(domainRepresentation.getName(), domainRepresentation.isVerified());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,14 +31,26 @@ import org.keycloak.provider.Provider;
|
||||||
public interface OrganizationProvider extends Provider {
|
public interface OrganizationProvider extends Provider {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new organization with given {@code name} to the realm.
|
* Creates a new organization with given {@code name} and {@code alias} to the realm.
|
||||||
* The internal ID of the organization will be created automatically.
|
* The internal ID of the organization will be created automatically.
|
||||||
* @param name String name of the organization.
|
* @param name the name of the organization.
|
||||||
* @param alias the alias of the organization. If not set, defaults to the value set to {@code name}. Once set, the alias is immutable.
|
* @param alias the alias of the organization. If not set, defaults to the value set to {@code name}. Once set, the alias is immutable.
|
||||||
* @throws ModelDuplicateException If there is already an organization with the given name or alias
|
* @throws ModelDuplicateException If there is already an organization with the given name or alias
|
||||||
* @return Model of the created organization.
|
* @return Model of the created organization.
|
||||||
*/
|
*/
|
||||||
OrganizationModel create(String name, String alias);
|
default OrganizationModel create(String name, String alias) {
|
||||||
|
return create(null, name, alias);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new organization with given {@code id}, {@code name}, and {@code alias} to the realm
|
||||||
|
* @param id the id of the organization.
|
||||||
|
* @param name the name of the organization.
|
||||||
|
* @param alias the alias of the organization. If not set, defaults to the value set to {@code name}. Once set, the alias is immutable.
|
||||||
|
* @throws ModelDuplicateException If there is already an organization with the given name or alias
|
||||||
|
* @return Model of the created organization.
|
||||||
|
*/
|
||||||
|
OrganizationModel create(String id, String name, String alias);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a {@link OrganizationModel} by its {@code id};
|
* Returns a {@link OrganizationModel} by its {@code id};
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
|
|
||||||
package org.keycloak.organization.admin.resource;
|
package org.keycloak.organization.admin.resource;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import jakarta.ws.rs.Consumes;
|
import jakarta.ws.rs.Consumes;
|
||||||
|
@ -49,11 +48,9 @@ import org.keycloak.models.UserModel;
|
||||||
|
|
||||||
import org.keycloak.models.utils.ModelToRepresentation;
|
import org.keycloak.models.utils.ModelToRepresentation;
|
||||||
import org.keycloak.organization.OrganizationProvider;
|
import org.keycloak.organization.OrganizationProvider;
|
||||||
import org.keycloak.organization.utils.Organizations;
|
|
||||||
import org.keycloak.representations.idm.MemberRepresentation;
|
import org.keycloak.representations.idm.MemberRepresentation;
|
||||||
import org.keycloak.representations.idm.MembershipType;
|
import org.keycloak.representations.idm.MembershipType;
|
||||||
import org.keycloak.representations.idm.OrganizationRepresentation;
|
import org.keycloak.representations.idm.OrganizationRepresentation;
|
||||||
import org.keycloak.representations.idm.UserRepresentation;
|
|
||||||
import org.keycloak.services.ErrorResponse;
|
import org.keycloak.services.ErrorResponse;
|
||||||
import org.keycloak.services.resources.KeycloakOpenAPI;
|
import org.keycloak.services.resources.KeycloakOpenAPI;
|
||||||
import org.keycloak.services.resources.admin.AdminEventBuilder;
|
import org.keycloak.services.resources.admin.AdminEventBuilder;
|
||||||
|
@ -193,7 +190,7 @@ public class OrganizationMemberResource {
|
||||||
|
|
||||||
UserModel member = getUser(id);
|
UserModel member = getUser(id);
|
||||||
|
|
||||||
return provider.getByMember(member).map(Organizations::toRepresentation);
|
return provider.getByMember(member).map(ModelToRepresentation::toRepresentation);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Path("count")
|
@Path("count")
|
||||||
|
|
|
@ -33,8 +33,9 @@ import org.jboss.resteasy.reactive.NoCache;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.ModelValidationException;
|
import org.keycloak.models.ModelValidationException;
|
||||||
import org.keycloak.models.OrganizationModel;
|
import org.keycloak.models.OrganizationModel;
|
||||||
|
import org.keycloak.models.utils.ModelToRepresentation;
|
||||||
|
import org.keycloak.models.utils.RepresentationToModel;
|
||||||
import org.keycloak.organization.OrganizationProvider;
|
import org.keycloak.organization.OrganizationProvider;
|
||||||
import org.keycloak.organization.utils.Organizations;
|
|
||||||
import org.keycloak.representations.idm.OrganizationRepresentation;
|
import org.keycloak.representations.idm.OrganizationRepresentation;
|
||||||
import org.keycloak.services.ErrorResponse;
|
import org.keycloak.services.ErrorResponse;
|
||||||
import org.keycloak.services.resources.KeycloakOpenAPI;
|
import org.keycloak.services.resources.KeycloakOpenAPI;
|
||||||
|
@ -67,7 +68,7 @@ public class OrganizationResource {
|
||||||
@Tag(name = KeycloakOpenAPI.Admin.Tags.ORGANIZATIONS)
|
@Tag(name = KeycloakOpenAPI.Admin.Tags.ORGANIZATIONS)
|
||||||
@Operation(summary = "Returns the organization representation")
|
@Operation(summary = "Returns the organization representation")
|
||||||
public OrganizationRepresentation get() {
|
public OrganizationRepresentation get() {
|
||||||
return Organizations.toRepresentation(organization);
|
return ModelToRepresentation.toRepresentation(organization);
|
||||||
}
|
}
|
||||||
|
|
||||||
@DELETE
|
@DELETE
|
||||||
|
@ -84,7 +85,7 @@ public class OrganizationResource {
|
||||||
@Operation(summary = "Updates the organization")
|
@Operation(summary = "Updates the organization")
|
||||||
public Response update(OrganizationRepresentation organizationRep) {
|
public Response update(OrganizationRepresentation organizationRep) {
|
||||||
try {
|
try {
|
||||||
Organizations.toModel(organizationRep, organization);
|
RepresentationToModel.toModel(organizationRep, organization);
|
||||||
return Response.noContent().build();
|
return Response.noContent().build();
|
||||||
} catch (ModelValidationException mve) {
|
} catch (ModelValidationException mve) {
|
||||||
throw ErrorResponse.error(mve.getMessage(), Response.Status.BAD_REQUEST);
|
throw ErrorResponse.error(mve.getMessage(), Response.Status.BAD_REQUEST);
|
||||||
|
|
|
@ -42,6 +42,8 @@ import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.ModelDuplicateException;
|
import org.keycloak.models.ModelDuplicateException;
|
||||||
import org.keycloak.models.ModelValidationException;
|
import org.keycloak.models.ModelValidationException;
|
||||||
import org.keycloak.models.OrganizationModel;
|
import org.keycloak.models.OrganizationModel;
|
||||||
|
import org.keycloak.models.utils.ModelToRepresentation;
|
||||||
|
import org.keycloak.models.utils.RepresentationToModel;
|
||||||
import org.keycloak.organization.OrganizationProvider;
|
import org.keycloak.organization.OrganizationProvider;
|
||||||
import org.keycloak.organization.utils.Organizations;
|
import org.keycloak.organization.utils.Organizations;
|
||||||
import org.keycloak.representations.idm.OrganizationRepresentation;
|
import org.keycloak.representations.idm.OrganizationRepresentation;
|
||||||
|
@ -96,9 +98,7 @@ public class OrganizationsResource {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
OrganizationModel model = provider.create(organization.getName(), organization.getAlias());
|
OrganizationModel model = provider.create(organization.getName(), organization.getAlias());
|
||||||
|
RepresentationToModel.toModel(organization, model);
|
||||||
Organizations.toModel(organization, model);
|
|
||||||
|
|
||||||
return Response.created(session.getContext().getUri().getAbsolutePathBuilder().path(model.getId()).build()).build();
|
return Response.created(session.getContext().getUri().getAbsolutePathBuilder().path(model.getId()).build()).build();
|
||||||
} catch (ModelValidationException mve) {
|
} catch (ModelValidationException mve) {
|
||||||
throw ErrorResponse.error(mve.getMessage(), Response.Status.BAD_REQUEST);
|
throw ErrorResponse.error(mve.getMessage(), Response.Status.BAD_REQUEST);
|
||||||
|
@ -137,9 +137,9 @@ public class OrganizationsResource {
|
||||||
// check if are searching orgs by attribute.
|
// check if are searching orgs by attribute.
|
||||||
if (StringUtil.isNotBlank(searchQuery)) {
|
if (StringUtil.isNotBlank(searchQuery)) {
|
||||||
Map<String, String> attributes = SearchQueryUtils.getFields(searchQuery);
|
Map<String, String> attributes = SearchQueryUtils.getFields(searchQuery);
|
||||||
return provider.getAllStream(attributes, first, max).map(Organizations::toBriefRepresentation);
|
return provider.getAllStream(attributes, first, max).map(ModelToRepresentation::toBriefRepresentation);
|
||||||
} else {
|
} else {
|
||||||
return provider.getAllStream(search, exact, first, max).map(Organizations::toBriefRepresentation);
|
return provider.getAllStream(search, exact, first, max).map(ModelToRepresentation::toBriefRepresentation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -151,66 +151,6 @@ public class Organizations {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static OrganizationRepresentation toRepresentation(OrganizationModel model) {
|
|
||||||
OrganizationRepresentation rep = toBriefRepresentation(model);
|
|
||||||
|
|
||||||
if (rep == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
rep.setAttributes(model.getAttributes());
|
|
||||||
|
|
||||||
return rep;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static OrganizationRepresentation toBriefRepresentation(OrganizationModel model) {
|
|
||||||
if (model == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
OrganizationRepresentation rep = new OrganizationRepresentation();
|
|
||||||
|
|
||||||
rep.setId(model.getId());
|
|
||||||
rep.setName(model.getName());
|
|
||||||
rep.setAlias(model.getAlias());
|
|
||||||
rep.setEnabled(model.isEnabled());
|
|
||||||
rep.setDescription(model.getDescription());
|
|
||||||
model.getDomains().filter(Objects::nonNull).map(Organizations::toRepresentation)
|
|
||||||
.forEach(rep::addDomain);
|
|
||||||
|
|
||||||
return rep;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static OrganizationDomainRepresentation toRepresentation(OrganizationDomainModel model) {
|
|
||||||
OrganizationDomainRepresentation representation = new OrganizationDomainRepresentation();
|
|
||||||
representation.setName(model.getName());
|
|
||||||
representation.setVerified(model.isVerified());
|
|
||||||
return representation;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static OrganizationModel toModel(OrganizationRepresentation rep, OrganizationModel model) {
|
|
||||||
if (rep == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
model.setName(rep.getName());
|
|
||||||
model.setAlias(rep.getAlias());
|
|
||||||
model.setEnabled(rep.isEnabled());
|
|
||||||
model.setDescription(rep.getDescription());
|
|
||||||
model.setAttributes(rep.getAttributes());
|
|
||||||
model.setDomains(ofNullable(rep.getDomains()).orElse(Set.of()).stream()
|
|
||||||
.filter(Objects::nonNull)
|
|
||||||
.filter(domain -> StringUtil.isNotBlank(domain.getName()))
|
|
||||||
.map(Organizations::toModel)
|
|
||||||
.collect(Collectors.toSet()));
|
|
||||||
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static OrganizationDomainModel toModel(OrganizationDomainRepresentation domainRepresentation) {
|
|
||||||
return new OrganizationDomainModel(domainRepresentation.getName(), domainRepresentation.isVerified());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static InviteOrgActionToken parseInvitationToken(HttpRequest request) throws VerificationException {
|
public static InviteOrgActionToken parseInvitationToken(HttpRequest request) throws VerificationException {
|
||||||
MultivaluedMap<String, String> queryParameters = request.getUri().getQueryParameters();
|
MultivaluedMap<String, String> queryParameters = request.getUri().getQueryParameters();
|
||||||
String tokenFromQuery = queryParameters.getFirst(Constants.TOKEN);
|
String tokenFromQuery = queryParameters.getFirst(Constants.TOKEN);
|
||||||
|
|
|
@ -152,6 +152,7 @@ public abstract class AbstractOrganizationTest extends AbstractAdminTest {
|
||||||
OrganizationRepresentation org = new OrganizationRepresentation();
|
OrganizationRepresentation org = new OrganizationRepresentation();
|
||||||
org.setName(name);
|
org.setName(name);
|
||||||
org.setAlias(name);
|
org.setAlias(name);
|
||||||
|
org.setDescription(name + " is a test organization!");
|
||||||
|
|
||||||
for (String orgDomain : orgDomains) {
|
for (String orgDomain : orgDomains) {
|
||||||
OrganizationDomainRepresentation domainRep = new OrganizationDomainRepresentation();
|
OrganizationDomainRepresentation domainRep = new OrganizationDomainRepresentation();
|
||||||
|
|
|
@ -17,8 +17,12 @@
|
||||||
|
|
||||||
package org.keycloak.testsuite.organization.exportimport;
|
package org.keycloak.testsuite.organization.exportimport;
|
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.hasItem;
|
||||||
import static org.hamcrest.CoreMatchers.is;
|
import static org.hamcrest.CoreMatchers.is;
|
||||||
|
import static org.hamcrest.CoreMatchers.notNullValue;
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||||
|
import static org.hamcrest.Matchers.hasSize;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
@ -42,6 +46,7 @@ import org.keycloak.exportimport.singlefile.SingleFileExportProviderFactory;
|
||||||
import org.keycloak.exportimport.singlefile.SingleFileImportProviderFactory;
|
import org.keycloak.exportimport.singlefile.SingleFileImportProviderFactory;
|
||||||
import org.keycloak.models.OrganizationModel;
|
import org.keycloak.models.OrganizationModel;
|
||||||
import org.keycloak.models.utils.DefaultAuthenticationFlows;
|
import org.keycloak.models.utils.DefaultAuthenticationFlows;
|
||||||
|
import org.keycloak.organization.OrganizationProvider;
|
||||||
import org.keycloak.representations.idm.AuthenticationExecutionInfoRepresentation;
|
import org.keycloak.representations.idm.AuthenticationExecutionInfoRepresentation;
|
||||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||||
import org.keycloak.representations.idm.OrganizationRepresentation;
|
import org.keycloak.representations.idm.OrganizationRepresentation;
|
||||||
|
@ -58,7 +63,7 @@ public class OrganizationExportTest extends AbstractOrganizationTest {
|
||||||
@Test
|
@Test
|
||||||
public void testExport() {
|
public void testExport() {
|
||||||
RealmResource providerRealm = realmsResouce().realm(bc.providerRealmName());
|
RealmResource providerRealm = realmsResouce().realm(bc.providerRealmName());
|
||||||
List<String> expectedOrganizations = new ArrayList<>();
|
List<OrganizationRepresentation> expectedOrganizations = new ArrayList<>();
|
||||||
Map<String, List<String>> expectedManagedMembers = new HashMap<>();
|
Map<String, List<String>> expectedManagedMembers = new HashMap<>();
|
||||||
Map<String, List<String>> expectedUnmanagedMembers = new HashMap<>();
|
Map<String, List<String>> expectedUnmanagedMembers = new HashMap<>();
|
||||||
|
|
||||||
|
@ -70,7 +75,7 @@ public class OrganizationExportTest extends AbstractOrganizationTest {
|
||||||
OrganizationRepresentation orgRep = createOrganization(testRealm(), getCleanup(), "org-" + i, broker, domain);
|
OrganizationRepresentation orgRep = createOrganization(testRealm(), getCleanup(), "org-" + i, broker, domain);
|
||||||
OrganizationResource organization = testRealm().organizations().get(orgRep.getId());
|
OrganizationResource organization = testRealm().organizations().get(orgRep.getId());
|
||||||
|
|
||||||
expectedOrganizations.add(orgRep.getName());
|
expectedOrganizations.add(orgRep);
|
||||||
|
|
||||||
for (int j = 0; j < 3; j++) {
|
for (int j = 0; j < 3; j++) {
|
||||||
UserRepresentation member = addMember(organization, "realmuser-" + j + "@" + domain);
|
UserRepresentation member = addMember(organization, "realmuser-" + j + "@" + domain);
|
||||||
|
@ -109,8 +114,27 @@ public class OrganizationExportTest extends AbstractOrganizationTest {
|
||||||
|
|
||||||
List<OrganizationRepresentation> organizations = testRealm().organizations().getAll();
|
List<OrganizationRepresentation> organizations = testRealm().organizations().getAll();
|
||||||
assertEquals(expectedOrganizations.size(), organizations.size());
|
assertEquals(expectedOrganizations.size(), organizations.size());
|
||||||
assertThat(organizations.stream().map(OrganizationRepresentation::getName).toList(), Matchers.containsInAnyOrder(expectedOrganizations.toArray()));
|
// id, name, alias, and description should have all been preserved.
|
||||||
assertThat(organizations.stream().map(OrganizationRepresentation::getAlias).toList(), Matchers.containsInAnyOrder(expectedOrganizations.toArray()));
|
assertThat(organizations.stream().map(OrganizationRepresentation::getId).toList(),
|
||||||
|
Matchers.containsInAnyOrder(expectedOrganizations.stream().map(OrganizationRepresentation::getId).toArray()));
|
||||||
|
assertThat(organizations.stream().map(OrganizationRepresentation::getName).toList(),
|
||||||
|
Matchers.containsInAnyOrder(expectedOrganizations.stream().map(OrganizationRepresentation::getName).toArray()));
|
||||||
|
assertThat(organizations.stream().map(OrganizationRepresentation::getAlias).toList(),
|
||||||
|
Matchers.containsInAnyOrder(expectedOrganizations.stream().map(OrganizationRepresentation::getAlias).toArray()));
|
||||||
|
assertThat(organizations.stream().map(OrganizationRepresentation::getDescription).toList(),
|
||||||
|
Matchers.containsInAnyOrder(expectedOrganizations.stream().map(OrganizationRepresentation::getDescription).toArray()));
|
||||||
|
|
||||||
|
// the endpoint search method returns brief representations of orgs - to get full rep we need to fetch by id.
|
||||||
|
for (OrganizationRepresentation organization : organizations) {
|
||||||
|
OrganizationRepresentation fullRep = testRealm().organizations().get(organization.getId()).toRepresentation();
|
||||||
|
// attributes should have been imported.
|
||||||
|
assertThat(fullRep.getAttributes(), notNullValue());
|
||||||
|
assertThat(fullRep.getAttributes().keySet(), hasSize(1));
|
||||||
|
assertThat(fullRep.getAttributes().keySet(), hasItem("key"));
|
||||||
|
List<String> attrValues = fullRep.getAttributes().get("key");
|
||||||
|
assertThat(attrValues, notNullValue());
|
||||||
|
assertThat(attrValues, containsInAnyOrder("value1", "value2"));
|
||||||
|
}
|
||||||
|
|
||||||
for (OrganizationRepresentation orgRep : organizations) {
|
for (OrganizationRepresentation orgRep : organizations) {
|
||||||
OrganizationResource organization = testRealm().organizations().get(orgRep.getId());
|
OrganizationResource organization = testRealm().organizations().get(orgRep.getId());
|
||||||
|
|
Loading…
Reference in a new issue