Prevent members with an email other than the domain set to an organization
Closes #28644 Signed-off-by: Pedro Igor <pigor.craveiro@gmail.com>
This commit is contained in:
parent
b4cfebd8d5
commit
61b1eec504
14 changed files with 350 additions and 109 deletions
|
@ -25,7 +25,7 @@ package org.keycloak.representations.idm;
|
||||||
public class OrganizationDomainRepresentation {
|
public class OrganizationDomainRepresentation {
|
||||||
|
|
||||||
private String name;
|
private String name;
|
||||||
private Boolean verified;
|
private boolean verified;
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return this.name;
|
return this.name;
|
||||||
|
@ -35,11 +35,11 @@ public class OrganizationDomainRepresentation {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Boolean isVerified() {
|
public boolean isVerified() {
|
||||||
return this.verified;
|
return this.verified;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setVerified(Boolean verified) {
|
public void setVerified(boolean verified) {
|
||||||
this.verified = verified;
|
this.verified = verified;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,8 +28,8 @@ public class OrganizationRepresentation {
|
||||||
|
|
||||||
private String id;
|
private String id;
|
||||||
private String name;
|
private String name;
|
||||||
private Map<String, List<String>> attributes = new HashMap<>();
|
private Map<String, List<String>> attributes;
|
||||||
private Set<OrganizationDomainRepresentation> domains = new HashSet<>();
|
private Set<OrganizationDomainRepresentation> domains;
|
||||||
|
|
||||||
public String getId() {
|
public String getId() {
|
||||||
return id;
|
return id;
|
||||||
|
@ -62,15 +62,31 @@ public class OrganizationRepresentation {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<OrganizationDomainRepresentation> getDomains() {
|
public Set<OrganizationDomainRepresentation> getDomains() {
|
||||||
return this.domains;
|
return domains;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OrganizationDomainRepresentation getDomain(String name) {
|
||||||
|
if (domains == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return domains.stream()
|
||||||
|
.filter(organizationDomainRepresentation -> name.equals(organizationDomainRepresentation.getName()))
|
||||||
|
.findAny()
|
||||||
|
.orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addDomain(OrganizationDomainRepresentation domain) {
|
public void addDomain(OrganizationDomainRepresentation domain) {
|
||||||
this.domains.add(domain);
|
if (domains == null) {
|
||||||
|
domains = new HashSet<>();
|
||||||
|
}
|
||||||
|
domains.add(domain);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeDomain(OrganizationDomainRepresentation domain) {
|
public void removeDomain(OrganizationDomainRepresentation domain) {
|
||||||
this.domains.remove(domain);
|
if (domains == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
getDomains().remove(domain);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -20,6 +20,8 @@ package org.keycloak.organization.jpa;
|
||||||
import static org.keycloak.models.OrganizationModel.USER_ORGANIZATION_ATTRIBUTE;
|
import static org.keycloak.models.OrganizationModel.USER_ORGANIZATION_ATTRIBUTE;
|
||||||
import static org.keycloak.utils.StreamsUtil.closing;
|
import static org.keycloak.utils.StreamsUtil.closing;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import jakarta.persistence.EntityManager;
|
import jakarta.persistence.EntityManager;
|
||||||
|
@ -32,6 +34,8 @@ import org.keycloak.models.IdentityProviderModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.ModelDuplicateException;
|
import org.keycloak.models.ModelDuplicateException;
|
||||||
import org.keycloak.models.ModelException;
|
import org.keycloak.models.ModelException;
|
||||||
|
import org.keycloak.models.ModelValidationException;
|
||||||
|
import org.keycloak.models.OrganizationDomainModel;
|
||||||
import org.keycloak.models.OrganizationModel;
|
import org.keycloak.models.OrganizationModel;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
|
@ -40,6 +44,7 @@ import org.keycloak.models.jpa.entities.OrganizationDomainEntity;
|
||||||
import org.keycloak.models.jpa.entities.OrganizationEntity;
|
import org.keycloak.models.jpa.entities.OrganizationEntity;
|
||||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
import org.keycloak.organization.OrganizationProvider;
|
import org.keycloak.organization.OrganizationProvider;
|
||||||
|
import org.keycloak.utils.StringUtil;
|
||||||
|
|
||||||
public class JpaOrganizationProvider implements OrganizationProvider {
|
public class JpaOrganizationProvider implements OrganizationProvider {
|
||||||
|
|
||||||
|
@ -59,7 +64,11 @@ public class JpaOrganizationProvider implements OrganizationProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public OrganizationModel create(String name) {
|
public OrganizationModel create(String name, Set<String> domains) {
|
||||||
|
if (StringUtil.isBlank(name)) {
|
||||||
|
throw new ModelValidationException("Name can not be null");
|
||||||
|
}
|
||||||
|
|
||||||
GroupModel group = createOrganizationGroup(name);
|
GroupModel group = createOrganizationGroup(name);
|
||||||
OrganizationEntity entity = new OrganizationEntity();
|
OrganizationEntity entity = new OrganizationEntity();
|
||||||
|
|
||||||
|
@ -70,7 +79,11 @@ public class JpaOrganizationProvider implements OrganizationProvider {
|
||||||
|
|
||||||
em.persist(entity);
|
em.persist(entity);
|
||||||
|
|
||||||
return new OrganizationAdapter(realm, entity);
|
OrganizationAdapter adapter = new OrganizationAdapter(realm, entity, this);
|
||||||
|
|
||||||
|
adapter.setDomains(domains.stream().map(OrganizationDomainModel::new).collect(Collectors.toSet()));
|
||||||
|
|
||||||
|
return adapter;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -121,7 +134,7 @@ public class JpaOrganizationProvider implements OrganizationProvider {
|
||||||
@Override
|
@Override
|
||||||
public OrganizationModel getById(String id) {
|
public OrganizationModel getById(String id) {
|
||||||
OrganizationEntity entity = getEntity(id, false);
|
OrganizationEntity entity = getEntity(id, false);
|
||||||
return entity == null ? null : new OrganizationAdapter(realm, entity);
|
return entity == null ? null : new OrganizationAdapter(realm, entity, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -130,7 +143,7 @@ public class JpaOrganizationProvider implements OrganizationProvider {
|
||||||
query.setParameter("name", domain.toLowerCase());
|
query.setParameter("name", domain.toLowerCase());
|
||||||
try {
|
try {
|
||||||
OrganizationDomainEntity entity = query.getSingleResult();
|
OrganizationDomainEntity entity = query.getSingleResult();
|
||||||
return new OrganizationAdapter(realm, entity.getOrganization());
|
return new OrganizationAdapter(realm, entity.getOrganization(), this);
|
||||||
} catch (NoResultException nre) {
|
} catch (NoResultException nre) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -142,7 +155,7 @@ public class JpaOrganizationProvider implements OrganizationProvider {
|
||||||
|
|
||||||
query.setParameter("realmId", realm.getId());
|
query.setParameter("realmId", realm.getId());
|
||||||
|
|
||||||
return closing(query.getResultStream().map(entity -> new OrganizationAdapter(realm, entity)));
|
return closing(query.getResultStream().map(entity -> new OrganizationAdapter(realm, entity, this)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -18,19 +18,24 @@
|
||||||
package org.keycloak.organization.jpa;
|
package org.keycloak.organization.jpa;
|
||||||
|
|
||||||
import org.keycloak.models.GroupModel;
|
import org.keycloak.models.GroupModel;
|
||||||
import java.util.Collection;
|
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import org.keycloak.models.ModelValidationException;
|
||||||
import org.keycloak.models.OrganizationDomainModel;
|
import org.keycloak.models.OrganizationDomainModel;
|
||||||
import org.keycloak.models.OrganizationModel;
|
import org.keycloak.models.OrganizationModel;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.jpa.JpaModel;
|
import org.keycloak.models.jpa.JpaModel;
|
||||||
import org.keycloak.models.jpa.entities.OrganizationDomainEntity;
|
import org.keycloak.models.jpa.entities.OrganizationDomainEntity;
|
||||||
import org.keycloak.models.jpa.entities.OrganizationEntity;
|
import org.keycloak.models.jpa.entities.OrganizationEntity;
|
||||||
|
import org.keycloak.organization.OrganizationProvider;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -38,11 +43,13 @@ public final class OrganizationAdapter implements OrganizationModel, JpaModel<Or
|
||||||
|
|
||||||
private final RealmModel realm;
|
private final RealmModel realm;
|
||||||
private final OrganizationEntity entity;
|
private final OrganizationEntity entity;
|
||||||
|
private final OrganizationProvider provider;
|
||||||
private GroupModel group;
|
private GroupModel group;
|
||||||
|
|
||||||
public OrganizationAdapter(RealmModel realm, OrganizationEntity entity) {
|
public OrganizationAdapter(RealmModel realm, OrganizationEntity entity, OrganizationProvider provider) {
|
||||||
this.realm = realm;
|
this.realm = realm;
|
||||||
this.entity = entity;
|
this.entity = entity;
|
||||||
|
this.provider = provider;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -90,10 +97,16 @@ public final class OrganizationAdapter implements OrganizationModel, JpaModel<Or
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setDomains(Collection<OrganizationDomainModel> domains) {
|
public void setDomains(Set<OrganizationDomainModel> domains) {
|
||||||
|
if (domains == null || domains.isEmpty()) {
|
||||||
|
throw new ModelValidationException("You must provide at least one domain");
|
||||||
|
}
|
||||||
|
|
||||||
Map<String, OrganizationDomainModel> modelMap = domains.stream()
|
Map<String, OrganizationDomainModel> modelMap = domains.stream()
|
||||||
.collect(Collectors.toMap(model -> model.getName(), Function.identity()));
|
.peek(this::isDomainInUse)
|
||||||
for (OrganizationDomainEntity domainEntity : this.entity.getDomains()) {
|
.collect(Collectors.toMap(OrganizationDomainModel::getName, Function.identity()));
|
||||||
|
|
||||||
|
for (OrganizationDomainEntity domainEntity : new HashSet<>(this.entity.getDomains())) {
|
||||||
// update the existing domain (for now, only the verified flag can be changed).
|
// update the existing domain (for now, only the verified flag can be changed).
|
||||||
if (modelMap.containsKey(domainEntity.getName())) {
|
if (modelMap.containsKey(domainEntity.getName())) {
|
||||||
domainEntity.setVerified(modelMap.get(domainEntity.getName()).getVerified());
|
domainEntity.setVerified(modelMap.get(domainEntity.getName()).getVerified());
|
||||||
|
@ -109,7 +122,7 @@ public final class OrganizationAdapter implements OrganizationModel, JpaModel<Or
|
||||||
for (OrganizationDomainModel model : modelMap.values()) {
|
for (OrganizationDomainModel model : modelMap.values()) {
|
||||||
OrganizationDomainEntity domainEntity = new OrganizationDomainEntity();
|
OrganizationDomainEntity domainEntity = new OrganizationDomainEntity();
|
||||||
domainEntity.setName(model.getName().toLowerCase());
|
domainEntity.setName(model.getName().toLowerCase());
|
||||||
domainEntity.setVerified(model.getVerified() == null ? Boolean.FALSE : model.getVerified());
|
domainEntity.setVerified(model.getVerified());
|
||||||
domainEntity.setOrganization(this.entity);
|
domainEntity.setOrganization(this.entity);
|
||||||
this.entity.addDomain(domainEntity);
|
this.entity.addDomain(domainEntity);
|
||||||
}
|
}
|
||||||
|
@ -120,13 +133,6 @@ public final class OrganizationAdapter implements OrganizationModel, JpaModel<Or
|
||||||
return entity;
|
return entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
private GroupModel getGroup() {
|
|
||||||
if (group == null) {
|
|
||||||
group = realm.getGroupById(getGroupId());
|
|
||||||
}
|
|
||||||
return group;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return new StringBuilder()
|
return new StringBuilder()
|
||||||
|
@ -146,4 +152,18 @@ public final class OrganizationAdapter implements OrganizationModel, JpaModel<Or
|
||||||
private OrganizationDomainModel toModel(OrganizationDomainEntity entity) {
|
private OrganizationDomainModel toModel(OrganizationDomainEntity entity) {
|
||||||
return new OrganizationDomainModel(entity.getName(), entity.isVerified());
|
return new OrganizationDomainModel(entity.getName(), entity.isVerified());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void isDomainInUse(OrganizationDomainModel domainRep) {
|
||||||
|
OrganizationModel orgModel = provider.getByDomainName(domainRep.getName());
|
||||||
|
if (orgModel != null && !Objects.equals(getId(), orgModel.getId())) {
|
||||||
|
throw new ModelValidationException("Domain " + domainRep.getName() + " is already linked to another organization");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private GroupModel getGroup() {
|
||||||
|
if (group == null) {
|
||||||
|
group = realm.getGroupById(getGroupId());
|
||||||
|
}
|
||||||
|
return group;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
*/
|
*/
|
||||||
package org.keycloak.organization;
|
package org.keycloak.organization;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
import org.keycloak.models.IdentityProviderModel;
|
import org.keycloak.models.IdentityProviderModel;
|
||||||
import org.keycloak.models.ModelDuplicateException;
|
import org.keycloak.models.ModelDuplicateException;
|
||||||
|
@ -32,10 +33,11 @@ public interface OrganizationProvider extends Provider {
|
||||||
* Creates a new organization with given {@code name} to the realm.
|
* Creates a new organization with given {@code name} 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 String name of the organization.
|
||||||
|
* @param domains the domains
|
||||||
* @throws ModelDuplicateException If there is already an organization with the given name
|
* @throws ModelDuplicateException If there is already an organization with the given name
|
||||||
* @return Model of the created organization.
|
* @return Model of the created organization.
|
||||||
*/
|
*/
|
||||||
OrganizationModel create(String name);
|
OrganizationModel create(String name, Set<String> domains);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a {@link OrganizationModel} by its {@code id};
|
* Returns a {@link OrganizationModel} by its {@code id};
|
||||||
|
|
|
@ -27,9 +27,13 @@ import java.io.Serializable;
|
||||||
public class OrganizationDomainModel implements Serializable {
|
public class OrganizationDomainModel implements Serializable {
|
||||||
|
|
||||||
private String name;
|
private String name;
|
||||||
private Boolean verified;
|
private boolean verified;
|
||||||
|
|
||||||
public OrganizationDomainModel(String name, Boolean verified) {
|
public OrganizationDomainModel(String name) {
|
||||||
|
this(name, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public OrganizationDomainModel(String name, boolean verified) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.verified = verified;
|
this.verified = verified;
|
||||||
}
|
}
|
||||||
|
@ -42,11 +46,11 @@ public class OrganizationDomainModel implements Serializable {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Boolean getVerified() {
|
public boolean getVerified() {
|
||||||
return this.verified;
|
return this.verified;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setVerified(Boolean verified) {
|
public void setVerified(boolean verified) {
|
||||||
this.verified = verified;
|
this.verified = verified;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ package org.keycloak.models;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Collection;
|
import java.util.Set;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
public interface OrganizationModel {
|
public interface OrganizationModel {
|
||||||
|
@ -38,5 +38,5 @@ public interface OrganizationModel {
|
||||||
|
|
||||||
Stream<OrganizationDomainModel> getDomains();
|
Stream<OrganizationDomainModel> getDomains();
|
||||||
|
|
||||||
void setDomains(Collection<OrganizationDomainModel> domains);
|
void setDomains(Set<OrganizationDomainModel> domains);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
|
|
||||||
package org.keycloak.organization.admin.resource;
|
package org.keycloak.organization.admin.resource;
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
@ -45,7 +44,6 @@ import org.keycloak.models.OrganizationModel;
|
||||||
import org.keycloak.organization.OrganizationProvider;
|
import org.keycloak.organization.OrganizationProvider;
|
||||||
import org.keycloak.representations.idm.OrganizationDomainRepresentation;
|
import org.keycloak.representations.idm.OrganizationDomainRepresentation;
|
||||||
import org.keycloak.representations.idm.OrganizationRepresentation;
|
import org.keycloak.representations.idm.OrganizationRepresentation;
|
||||||
import org.keycloak.services.ErrorResponse;
|
|
||||||
import org.keycloak.services.resources.admin.AdminEventBuilder;
|
import org.keycloak.services.resources.admin.AdminEventBuilder;
|
||||||
import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
|
import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
|
||||||
import org.keycloak.utils.StringUtil;
|
import org.keycloak.utils.StringUtil;
|
||||||
|
@ -77,7 +75,9 @@ public class OrganizationResource {
|
||||||
throw new BadRequestException();
|
throw new BadRequestException();
|
||||||
}
|
}
|
||||||
|
|
||||||
OrganizationModel model = provider.create(organization.getName());
|
Set<String> domains = organization.getDomains().stream().map(OrganizationDomainRepresentation::getName).collect(Collectors.toSet());
|
||||||
|
OrganizationModel model = provider.create(organization.getName(), domains);
|
||||||
|
|
||||||
toModel(organization, model);
|
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();
|
||||||
|
@ -133,6 +133,7 @@ public class OrganizationResource {
|
||||||
@Path("{id}/members")
|
@Path("{id}/members")
|
||||||
public OrganizationMemberResource members(@PathParam("id") String id) {
|
public OrganizationMemberResource members(@PathParam("id") String id) {
|
||||||
OrganizationModel organization = getOrganization(id);
|
OrganizationModel organization = getOrganization(id);
|
||||||
|
session.setAttribute(OrganizationModel.class.getName(), organization);
|
||||||
return new OrganizationMemberResource(session, organization, auth, adminEvent);
|
return new OrganizationMemberResource(session, organization, auth, adminEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,15 +187,8 @@ public class OrganizationResource {
|
||||||
model.setName(rep.getName());
|
model.setName(rep.getName());
|
||||||
model.setAttributes(rep.getAttributes());
|
model.setAttributes(rep.getAttributes());
|
||||||
model.setDomains(Optional.ofNullable(rep.getDomains()).orElse(Set.of()).stream()
|
model.setDomains(Optional.ofNullable(rep.getDomains()).orElse(Set.of()).stream()
|
||||||
.filter(this::validateDomainRepresentation)
|
.map(this::toModel)
|
||||||
.peek(domainRep -> {
|
.collect(Collectors.toSet()));
|
||||||
OrganizationModel orgModel = provider.getByDomainName(domainRep.getName());
|
|
||||||
if (orgModel != null && !Objects.equals(model.getId(), orgModel.getId())) {
|
|
||||||
throw ErrorResponse.error("Domain " + domainRep.getName() + " is already linked to another organization", Response.Status.BAD_REQUEST);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.map(this::toModel)
|
|
||||||
.collect(Collectors.toSet()));
|
|
||||||
|
|
||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
|
@ -202,8 +196,4 @@ public class OrganizationResource {
|
||||||
private OrganizationDomainModel toModel(OrganizationDomainRepresentation domainRepresentation) {
|
private OrganizationDomainModel toModel(OrganizationDomainRepresentation domainRepresentation) {
|
||||||
return new OrganizationDomainModel(domainRepresentation.getName(), domainRepresentation.isVerified());
|
return new OrganizationDomainModel(domainRepresentation.getName(), domainRepresentation.isVerified());
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean validateDomainRepresentation(OrganizationDomainRepresentation rep) {
|
|
||||||
return rep != null && rep.getName() != null && !rep.getName().trim().isEmpty();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,110 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 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.organization.validator;
|
||||||
|
|
||||||
|
import static org.keycloak.validate.BuiltinValidators.emailValidator;
|
||||||
|
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import org.keycloak.Config.Scope;
|
||||||
|
import org.keycloak.common.Profile;
|
||||||
|
import org.keycloak.common.Profile.Feature;
|
||||||
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.models.OrganizationDomainModel;
|
||||||
|
import org.keycloak.models.OrganizationModel;
|
||||||
|
import org.keycloak.models.UserModel;
|
||||||
|
import org.keycloak.organization.OrganizationProvider;
|
||||||
|
import org.keycloak.provider.EnvironmentDependentProviderFactory;
|
||||||
|
import org.keycloak.userprofile.AttributeContext;
|
||||||
|
import org.keycloak.userprofile.UserProfileAttributeValidationContext;
|
||||||
|
import org.keycloak.utils.StringUtil;
|
||||||
|
import org.keycloak.validate.AbstractSimpleValidator;
|
||||||
|
import org.keycloak.validate.ValidationContext;
|
||||||
|
import org.keycloak.validate.ValidationError;
|
||||||
|
import org.keycloak.validate.ValidatorConfig;
|
||||||
|
|
||||||
|
public class OrganizationMemberValidator extends AbstractSimpleValidator implements EnvironmentDependentProviderFactory {
|
||||||
|
|
||||||
|
public static final String ID = "organization-member-validator";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doValidate(Object value, String inputHint, ValidationContext context, ValidatorConfig config) {
|
||||||
|
KeycloakSession session = context.getSession();
|
||||||
|
OrganizationModel organization = resolveOrganization(context, session);
|
||||||
|
|
||||||
|
if (organization == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
validateEmailDomain((String) value, inputHint, context, organization);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean skipValidation(Object value, ValidatorConfig config) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSupported(Scope config) {
|
||||||
|
return Profile.isFeatureEnabled(Feature.ORGANIZATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateEmailDomain(String email, String inputHint, ValidationContext context, OrganizationModel organization) {
|
||||||
|
if (UserModel.USERNAME.equals(inputHint) || UserModel.EMAIL.equals(inputHint)) {
|
||||||
|
if (StringUtil.isBlank(email)) {
|
||||||
|
context.addError(new ValidationError(ID, inputHint, "Email not set"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!emailValidator().validate(email, inputHint, context).isValid()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String domain = email.substring(email.indexOf('@') + 1);
|
||||||
|
Stream<OrganizationDomainModel> expectedDomains = organization.getDomains();
|
||||||
|
|
||||||
|
if (expectedDomains.map(OrganizationDomainModel::getName).noneMatch(domain::equals)) {
|
||||||
|
context.addError(new ValidationError(ID, inputHint, "Email domain does not match any domain from the organization"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private OrganizationModel resolveOrganization(ValidationContext context, KeycloakSession session) {
|
||||||
|
OrganizationModel organization = (OrganizationModel) session.getAttribute(OrganizationModel.class.getName());
|
||||||
|
|
||||||
|
if (organization != null) {
|
||||||
|
return organization;
|
||||||
|
}
|
||||||
|
|
||||||
|
UserProfileAttributeValidationContext upContext = (UserProfileAttributeValidationContext) context;
|
||||||
|
AttributeContext attributeContext = upContext.getAttributeContext();
|
||||||
|
UserModel user = attributeContext.getUser();
|
||||||
|
|
||||||
|
if (user != null) {
|
||||||
|
OrganizationProvider provider = session.getProvider(OrganizationProvider.class);
|
||||||
|
return provider.getByMember(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -35,6 +35,7 @@ import org.keycloak.Config;
|
||||||
import org.keycloak.Config.Scope;
|
import org.keycloak.Config.Scope;
|
||||||
import org.keycloak.authentication.requiredactions.TermsAndConditions;
|
import org.keycloak.authentication.requiredactions.TermsAndConditions;
|
||||||
import org.keycloak.common.Profile;
|
import org.keycloak.common.Profile;
|
||||||
|
import org.keycloak.common.Profile.Feature;
|
||||||
import org.keycloak.component.AmphibianProviderFactory;
|
import org.keycloak.component.AmphibianProviderFactory;
|
||||||
import org.keycloak.component.ComponentModel;
|
import org.keycloak.component.ComponentModel;
|
||||||
import org.keycloak.component.ComponentValidationException;
|
import org.keycloak.component.ComponentValidationException;
|
||||||
|
@ -44,6 +45,7 @@ import org.keycloak.models.KeycloakSessionFactory;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.RequiredActionProviderModel;
|
import org.keycloak.models.RequiredActionProviderModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
|
import org.keycloak.organization.validator.OrganizationMemberValidator;
|
||||||
import org.keycloak.provider.ProviderConfigProperty;
|
import org.keycloak.provider.ProviderConfigProperty;
|
||||||
import org.keycloak.provider.ProviderConfigurationBuilder;
|
import org.keycloak.provider.ProviderConfigurationBuilder;
|
||||||
import org.keycloak.representations.userprofile.config.UPConfig;
|
import org.keycloak.representations.userprofile.config.UPConfig;
|
||||||
|
@ -341,6 +343,16 @@ public class DeclarativeUserProfileProviderFactory implements UserProfileProvide
|
||||||
if (contextualMetadataRegistry.putIfAbsent(metadata.getContext(), metadata) != null) {
|
if (contextualMetadataRegistry.putIfAbsent(metadata.getContext(), metadata) != null) {
|
||||||
throw new IllegalStateException("Multiple profile metadata found for context " + metadata.getContext());
|
throw new IllegalStateException("Multiple profile metadata found for context " + metadata.getContext());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Profile.isFeatureEnabled(Feature.ORGANIZATION)) {
|
||||||
|
for (AttributeMetadata attribute : metadata.getAttributes()) {
|
||||||
|
String name = attribute.getName();
|
||||||
|
|
||||||
|
if (UserModel.EMAIL.equals(name) || UserModel.USERNAME.equals(name)) {
|
||||||
|
attribute.addValidators(List.of(new AttributeValidatorMetadata(OrganizationMemberValidator.ID)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private UserProfileMetadata createBrokeringProfile(AttributeValidatorMetadata readOnlyValidator) {
|
private UserProfileMetadata createBrokeringProfile(AttributeValidatorMetadata readOnlyValidator) {
|
||||||
|
|
|
@ -15,3 +15,4 @@ org.keycloak.userprofile.validator.ImmutableAttributeValidator
|
||||||
org.keycloak.userprofile.validator.UsernameProhibitedCharactersValidator
|
org.keycloak.userprofile.validator.UsernameProhibitedCharactersValidator
|
||||||
org.keycloak.userprofile.validator.PersonNameProhibitedCharactersValidator
|
org.keycloak.userprofile.validator.PersonNameProhibitedCharactersValidator
|
||||||
org.keycloak.userprofile.validator.MultiValueValidator
|
org.keycloak.userprofile.validator.MultiValueValidator
|
||||||
|
org.keycloak.organization.validator.OrganizationMemberValidator
|
|
@ -44,7 +44,7 @@ public abstract class AbstractOrganizationTest extends AbstractAdminTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected OrganizationRepresentation createOrganization(String name) {
|
protected OrganizationRepresentation createOrganization(String name) {
|
||||||
return createOrganization(name, null);
|
return createOrganization(name, name + ".org");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected OrganizationRepresentation createOrganization(String name, String orgDomain) {
|
protected OrganizationRepresentation createOrganization(String name, String orgDomain) {
|
||||||
|
@ -54,12 +54,9 @@ public abstract class AbstractOrganizationTest extends AbstractAdminTest {
|
||||||
|
|
||||||
String id;
|
String id;
|
||||||
|
|
||||||
if (orgDomain != null) {
|
OrganizationDomainRepresentation domainRep = new OrganizationDomainRepresentation();
|
||||||
OrganizationDomainRepresentation domainRep = new OrganizationDomainRepresentation();
|
domainRep.setName(orgDomain);
|
||||||
domainRep.setName(orgDomain);
|
org.addDomain(domainRep);
|
||||||
domainRep.setVerified(true);
|
|
||||||
org.addDomain(domainRep);
|
|
||||||
}
|
|
||||||
|
|
||||||
try (Response response = testRealm().organizations().create(org)) {
|
try (Response response = testRealm().organizations().create(org)) {
|
||||||
assertEquals(Status.CREATED.getStatusCode(), response.getStatus());
|
assertEquals(Status.CREATED.getStatusCode(), response.getStatus());
|
||||||
|
|
|
@ -27,6 +27,7 @@ import static org.keycloak.models.OrganizationModel.USER_ORGANIZATION_ATTRIBUTE;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import jakarta.ws.rs.BadRequestException;
|
||||||
import jakarta.ws.rs.NotFoundException;
|
import jakarta.ws.rs.NotFoundException;
|
||||||
import jakarta.ws.rs.core.Response;
|
import jakarta.ws.rs.core.Response;
|
||||||
import jakarta.ws.rs.core.Response.Status;
|
import jakarta.ws.rs.core.Response.Status;
|
||||||
|
@ -34,11 +35,12 @@ import org.junit.Test;
|
||||||
import org.keycloak.admin.client.resource.OrganizationMemberResource;
|
import org.keycloak.admin.client.resource.OrganizationMemberResource;
|
||||||
import org.keycloak.admin.client.resource.OrganizationResource;
|
import org.keycloak.admin.client.resource.OrganizationResource;
|
||||||
import org.keycloak.common.Profile.Feature;
|
import org.keycloak.common.Profile.Feature;
|
||||||
|
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
import org.keycloak.representations.idm.OrganizationRepresentation;
|
import org.keycloak.representations.idm.OrganizationRepresentation;
|
||||||
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
import org.keycloak.representations.idm.UserRepresentation;
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
import org.keycloak.representations.userprofile.config.UPConfig;
|
import org.keycloak.representations.userprofile.config.UPConfig;
|
||||||
import org.keycloak.representations.userprofile.config.UPConfig.UnmanagedAttributePolicy;
|
import org.keycloak.representations.userprofile.config.UPConfig.UnmanagedAttributePolicy;
|
||||||
import org.keycloak.testsuite.admin.ApiUtil;
|
|
||||||
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
|
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
|
||||||
|
|
||||||
@EnableFeature(Feature.ORGANIZATION)
|
@EnableFeature(Feature.ORGANIZATION)
|
||||||
|
@ -67,14 +69,13 @@ public class OrganizationMemberTest extends AbstractOrganizationTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailCreateUser() {
|
public void testFailSetUserOrganizationAttribute() {
|
||||||
UPConfig upConfig = testRealm().users().userProfile().getConfiguration();
|
UPConfig upConfig = testRealm().users().userProfile().getConfiguration();
|
||||||
upConfig.setUnmanagedAttributePolicy(UnmanagedAttributePolicy.ENABLED);
|
upConfig.setUnmanagedAttributePolicy(UnmanagedAttributePolicy.ENABLED);
|
||||||
testRealm().users().userProfile().update(upConfig);
|
testRealm().users().userProfile().update(upConfig);
|
||||||
OrganizationResource organization = testRealm().organizations().get(createOrganization().getId());
|
OrganizationResource organization = testRealm().organizations().get(createOrganization().getId());
|
||||||
UserRepresentation expected = new UserRepresentation();
|
UserRepresentation expected = new UserRepresentation();
|
||||||
|
|
||||||
expected.setEmail("u@o.org");
|
|
||||||
expected.setUsername(expected.getEmail());
|
expected.setUsername(expected.getEmail());
|
||||||
expected.singleAttribute(USER_ORGANIZATION_ATTRIBUTE, "invalid");
|
expected.singleAttribute(USER_ORGANIZATION_ATTRIBUTE, "invalid");
|
||||||
|
|
||||||
|
@ -84,6 +85,58 @@ public class OrganizationMemberTest extends AbstractOrganizationTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFailSetEmailDomainOtherThanOrganizationDomain() {
|
||||||
|
UPConfig upConfig = testRealm().users().userProfile().getConfiguration();
|
||||||
|
upConfig.setUnmanagedAttributePolicy(UnmanagedAttributePolicy.ENABLED);
|
||||||
|
testRealm().users().userProfile().update(upConfig);
|
||||||
|
OrganizationResource organization = testRealm().organizations().get(createOrganization().getId());
|
||||||
|
UserRepresentation expected = new UserRepresentation();
|
||||||
|
|
||||||
|
expected.setUsername(KeycloakModelUtils.generateId() + "@user.org");
|
||||||
|
expected.setEmail(expected.getUsername());
|
||||||
|
|
||||||
|
try (Response response = organization.members().addMember(expected)) {
|
||||||
|
assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
|
||||||
|
assertTrue(testRealm().users().search(expected.getUsername()).isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
expected.setUsername(expected.getUsername().replace("@user.org", "@" + organizationName + ".org"));
|
||||||
|
expected.setEmail(expected.getUsername());
|
||||||
|
|
||||||
|
try (Response response = organization.members().addMember(expected)) {
|
||||||
|
assertEquals(Status.CREATED.getStatusCode(), response.getStatus());
|
||||||
|
assertFalse(testRealm().users().search(expected.getUsername()).isEmpty());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFailSetEmailDomainOtherThanOrganizationDomainViaUserApi() {
|
||||||
|
RealmRepresentation representation = testRealm().toRepresentation();
|
||||||
|
representation.setEditUsernameAllowed(true);
|
||||||
|
testRealm().update(representation);
|
||||||
|
OrganizationRepresentation organization = createOrganization();
|
||||||
|
UserRepresentation member = addMember(testRealm().organizations().get(organization.getId()));
|
||||||
|
|
||||||
|
member.setUsername(KeycloakModelUtils.generateId() + "@user.org");
|
||||||
|
member.setEmail(member.getUsername());
|
||||||
|
member.setFirstName("f");
|
||||||
|
member.setLastName("l");
|
||||||
|
member.setEnabled(true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
testRealm().users().get(member.getId()).update(member);
|
||||||
|
fail("Should fail because email domain does not match any from organization");
|
||||||
|
} catch (BadRequestException expected) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
member.setUsername(member.getUsername().replace("@user.org", "@" + organizationName + ".org"));
|
||||||
|
member.setEmail(member.getUsername());
|
||||||
|
|
||||||
|
testRealm().users().get(member.getId()).update(member);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGet() {
|
public void testGet() {
|
||||||
OrganizationResource organization = testRealm().organizations().get(createOrganization().getId());
|
OrganizationResource organization = testRealm().organizations().get(createOrganization().getId());
|
||||||
|
|
|
@ -50,12 +50,6 @@ public class OrganizationTest extends AbstractOrganizationTest {
|
||||||
assertEquals(organizationName, expected.getName());
|
assertEquals(organizationName, expected.getName());
|
||||||
expected.setName("acme");
|
expected.setName("acme");
|
||||||
|
|
||||||
// add an internet domain to the organization.
|
|
||||||
OrganizationDomainRepresentation orgDomain = new OrganizationDomainRepresentation();
|
|
||||||
orgDomain.setName("neworg.org");
|
|
||||||
orgDomain.setVerified(true);
|
|
||||||
expected.addDomain(orgDomain);
|
|
||||||
|
|
||||||
OrganizationResource organization = testRealm().organizations().get(expected.getId());
|
OrganizationResource organization = testRealm().organizations().get(expected.getId());
|
||||||
|
|
||||||
try (Response response = organization.update(expected)) {
|
try (Response response = organization.update(expected)) {
|
||||||
|
@ -66,48 +60,6 @@ public class OrganizationTest extends AbstractOrganizationTest {
|
||||||
assertEquals(expected.getId(), existing.getId());
|
assertEquals(expected.getId(), existing.getId());
|
||||||
assertEquals(expected.getName(), existing.getName());
|
assertEquals(expected.getName(), existing.getName());
|
||||||
assertEquals(1, existing.getDomains().size());
|
assertEquals(1, existing.getDomains().size());
|
||||||
|
|
||||||
OrganizationDomainRepresentation existingDomain = existing.getDomains().iterator().next();
|
|
||||||
assertEquals(orgDomain.getName(), existingDomain.getName());
|
|
||||||
assertEquals(orgDomain.isVerified(), existingDomain.isVerified());
|
|
||||||
|
|
||||||
// now test updating an existing internet domain (change verified to false and check the model was updated).
|
|
||||||
orgDomain.setVerified(false);
|
|
||||||
try (Response response = organization.update(expected)) {
|
|
||||||
assertEquals(Status.NO_CONTENT.getStatusCode(), response.getStatus());
|
|
||||||
}
|
|
||||||
existing = organization.toRepresentation();
|
|
||||||
assertEquals(1, existing.getDomains().size());
|
|
||||||
existingDomain = existing.getDomains().iterator().next();
|
|
||||||
assertEquals(false, existingDomain.isVerified());
|
|
||||||
|
|
||||||
// now replace the internet domain for a different one.
|
|
||||||
orgDomain.setName("acme.com");
|
|
||||||
try (Response response = organization.update(expected)) {
|
|
||||||
assertEquals(Status.NO_CONTENT.getStatusCode(), response.getStatus());
|
|
||||||
}
|
|
||||||
existing = organization.toRepresentation();
|
|
||||||
assertEquals(1, existing.getDomains().size());
|
|
||||||
existingDomain = existing.getDomains().iterator().next();
|
|
||||||
assertEquals("acme.com", existingDomain.getName());
|
|
||||||
assertEquals(false, existingDomain.isVerified());
|
|
||||||
|
|
||||||
// create another org and attempt to set the same internet domain during update - should not be possible.
|
|
||||||
OrganizationRepresentation anotherOrg = createOrganization("another-org");
|
|
||||||
anotherOrg.addDomain(orgDomain);
|
|
||||||
|
|
||||||
organization = testRealm().organizations().get(anotherOrg.getId());
|
|
||||||
try (Response response = organization.update(anotherOrg)) {
|
|
||||||
assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
|
|
||||||
}
|
|
||||||
|
|
||||||
// finally, attempt to create a new org with an existing internet domain in the representation - should not be possible.
|
|
||||||
OrganizationRepresentation newOrg = new OrganizationRepresentation();
|
|
||||||
newOrg.setName("new-org");
|
|
||||||
newOrg.addDomain(orgDomain);
|
|
||||||
try (Response response = testRealm().organizations().create(newOrg)) {
|
|
||||||
assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -147,7 +99,7 @@ public class OrganizationTest extends AbstractOrganizationTest {
|
||||||
assertEquals(1, orgRep.getDomains().size());
|
assertEquals(1, orgRep.getDomains().size());
|
||||||
OrganizationDomainRepresentation domainRep = orgRep.getDomains().iterator().next();
|
OrganizationDomainRepresentation domainRep = orgRep.getDomains().iterator().next();
|
||||||
assertEquals("testorg2.org", domainRep.getName());
|
assertEquals("testorg2.org", domainRep.getName());
|
||||||
assertTrue(domainRep.isVerified());
|
assertFalse(domainRep.isVerified());
|
||||||
|
|
||||||
// search for an organization with an non-existent domain.
|
// search for an organization with an non-existent domain.
|
||||||
existing = testRealm().organizations().getAll("someother.org");
|
existing = testRealm().organizations().getAll("someother.org");
|
||||||
|
@ -208,4 +160,75 @@ public class OrganizationTest extends AbstractOrganizationTest {
|
||||||
updated = organization.toRepresentation();
|
updated = organization.toRepresentation();
|
||||||
assertEquals(0, updated.getAttributes().size());
|
assertEquals(0, updated.getAttributes().size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDomains() {
|
||||||
|
// test create org with default domain settings
|
||||||
|
OrganizationRepresentation expected = createOrganization();
|
||||||
|
OrganizationDomainRepresentation expectedNewOrgDomain = expected.getDomains().iterator().next();
|
||||||
|
OrganizationResource organization = testRealm().organizations().get(expected.getId());
|
||||||
|
OrganizationRepresentation existing = organization.toRepresentation();
|
||||||
|
assertEquals(1, existing.getDomains().size());
|
||||||
|
OrganizationDomainRepresentation existingNewOrgDomain = existing.getDomain("neworg.org");
|
||||||
|
assertEquals(expectedNewOrgDomain.getName(), existingNewOrgDomain.getName());
|
||||||
|
assertFalse(existingNewOrgDomain.isVerified());
|
||||||
|
|
||||||
|
// create a second domain with verified true
|
||||||
|
OrganizationDomainRepresentation expectedNewOrgBrDomain = new OrganizationDomainRepresentation();
|
||||||
|
expectedNewOrgBrDomain.setName("neworg.org.br");
|
||||||
|
expectedNewOrgBrDomain.setVerified(true);
|
||||||
|
expected.addDomain(expectedNewOrgBrDomain);
|
||||||
|
try (Response response = organization.update(expected)) {
|
||||||
|
assertEquals(Status.NO_CONTENT.getStatusCode(), response.getStatus());
|
||||||
|
}
|
||||||
|
existing = organization.toRepresentation();
|
||||||
|
assertEquals(2, existing.getDomains().size());
|
||||||
|
OrganizationDomainRepresentation existingNewOrgBrDomain = existing.getDomain("neworg.org.br");
|
||||||
|
assertEquals(expectedNewOrgBrDomain.getName(), existingNewOrgBrDomain.getName());
|
||||||
|
assertEquals(expectedNewOrgBrDomain.isVerified(), existingNewOrgBrDomain.isVerified());
|
||||||
|
|
||||||
|
// now test updating an existing internet domain (change verified to false and check the model was updated).
|
||||||
|
expectedNewOrgDomain.setVerified(true);
|
||||||
|
try (Response response = organization.update(expected)) {
|
||||||
|
assertEquals(Status.NO_CONTENT.getStatusCode(), response.getStatus());
|
||||||
|
}
|
||||||
|
existing = organization.toRepresentation();
|
||||||
|
existingNewOrgDomain = existing.getDomain("neworg.org");
|
||||||
|
assertEquals(expectedNewOrgDomain.isVerified(), existingNewOrgDomain.isVerified());
|
||||||
|
existingNewOrgBrDomain = existing.getDomain("neworg.org.br");
|
||||||
|
assertNotNull(existingNewOrgBrDomain);
|
||||||
|
assertEquals(expectedNewOrgBrDomain.isVerified(), existingNewOrgBrDomain.isVerified());
|
||||||
|
|
||||||
|
// now replace the internet domain for a different one.
|
||||||
|
expectedNewOrgBrDomain.setName("acme.com");
|
||||||
|
expectedNewOrgBrDomain.setVerified(false);
|
||||||
|
try (Response response = organization.update(expected)) {
|
||||||
|
assertEquals(Status.NO_CONTENT.getStatusCode(), response.getStatus());
|
||||||
|
}
|
||||||
|
existing = organization.toRepresentation();
|
||||||
|
assertEquals(2, existing.getDomains().size());
|
||||||
|
existingNewOrgBrDomain = existing.getDomain("acme.com");
|
||||||
|
assertNotNull(existingNewOrgBrDomain);
|
||||||
|
assertEquals(expectedNewOrgBrDomain.getName(), existingNewOrgBrDomain.getName());
|
||||||
|
assertEquals(expectedNewOrgBrDomain.isVerified(), existingNewOrgBrDomain.isVerified());
|
||||||
|
|
||||||
|
// create another org and attempt to set the same internet domain during update - should not be possible.
|
||||||
|
OrganizationRepresentation anotherOrg = createOrganization("another-org");
|
||||||
|
anotherOrg.addDomain(expectedNewOrgDomain);
|
||||||
|
organization = testRealm().organizations().get(anotherOrg.getId());
|
||||||
|
try (Response response = organization.update(anotherOrg)) {
|
||||||
|
assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to remove a domain
|
||||||
|
organization = testRealm().organizations().get(existing.getId());
|
||||||
|
existing.removeDomain(existingNewOrgDomain);
|
||||||
|
try (Response response = organization.update(existing)) {
|
||||||
|
assertEquals(Status.NO_CONTENT.getStatusCode(), response.getStatus());
|
||||||
|
}
|
||||||
|
existing = organization.toRepresentation();
|
||||||
|
assertFalse(existing.getDomains().isEmpty());
|
||||||
|
assertEquals(1, existing.getDomains().size());
|
||||||
|
assertNotNull(existing.getDomain("acme.com"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue