Make sure users created through a registration link are managed members
Closes #30743 Signed-off-by: vramik <vramik@redhat.com>
This commit is contained in:
parent
83f8622d15
commit
649b35929e
27 changed files with 337 additions and 118 deletions
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
* 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.representations.idm;
|
||||||
|
|
||||||
|
public class MemberRepresentation extends UserRepresentation {
|
||||||
|
|
||||||
|
private MembershipType membershipType;
|
||||||
|
|
||||||
|
public MemberRepresentation() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public MemberRepresentation(UserRepresentation user) {
|
||||||
|
super(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MembershipType getMembershipType() {
|
||||||
|
return membershipType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMembershipType(MembershipType membershipType) {
|
||||||
|
this.membershipType = membershipType;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* 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.representations.idm;
|
||||||
|
|
||||||
|
public enum MembershipType {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates that member can exist without group/organization.
|
||||||
|
*/
|
||||||
|
UNMANAGED,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates that member cannot exist without group/organization.
|
||||||
|
*/
|
||||||
|
MANAGED;
|
||||||
|
}
|
|
@ -34,7 +34,7 @@ public class OrganizationRepresentation {
|
||||||
private String description;
|
private String description;
|
||||||
private Map<String, List<String>> attributes;
|
private Map<String, List<String>> attributes;
|
||||||
private Set<OrganizationDomainRepresentation> domains;
|
private Set<OrganizationDomainRepresentation> domains;
|
||||||
private List<UserRepresentation> members;
|
private List<MemberRepresentation> members;
|
||||||
private List<IdentityProviderRepresentation> identityProviders;
|
private List<IdentityProviderRepresentation> identityProviders;
|
||||||
|
|
||||||
public String getId() {
|
public String getId() {
|
||||||
|
@ -119,19 +119,19 @@ public class OrganizationRepresentation {
|
||||||
getDomains().remove(domain);
|
getDomains().remove(domain);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<UserRepresentation> getMembers() {
|
public List<MemberRepresentation> getMembers() {
|
||||||
return members;
|
return members;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setMembers(List<UserRepresentation> members) {
|
public void setMembers(List<MemberRepresentation> members) {
|
||||||
this.members = members;
|
this.members = members;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addMember(UserRepresentation user) {
|
public void addMember(MemberRepresentation member) {
|
||||||
if (members == null) {
|
if (members == null) {
|
||||||
members = new ArrayList<>();
|
members = new ArrayList<>();
|
||||||
}
|
}
|
||||||
members.add(user);
|
members.add(member);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<IdentityProviderRepresentation> getIdentityProviders() {
|
public List<IdentityProviderRepresentation> getIdentityProviders() {
|
||||||
|
|
|
@ -52,6 +52,43 @@ public class UserRepresentation extends AbstractUserRepresentation{
|
||||||
protected List<String> groups;
|
protected List<String> groups;
|
||||||
private Map<String, Boolean> access;
|
private Map<String, Boolean> access;
|
||||||
|
|
||||||
|
public UserRepresentation() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserRepresentation(UserRepresentation rep) {
|
||||||
|
// AbstractUserRepresentation
|
||||||
|
this.id = rep.getId();
|
||||||
|
this.username = rep.getUsername();
|
||||||
|
this.firstName = rep.getFirstName();
|
||||||
|
this.lastName = rep.getLastName();
|
||||||
|
this.email = rep.getEmail();
|
||||||
|
this.emailVerified = rep.isEmailVerified();
|
||||||
|
this.attributes = rep.getAttributes();
|
||||||
|
this.setUserProfileMetadata(rep.getUserProfileMetadata());
|
||||||
|
|
||||||
|
this.self = rep.getSelf();
|
||||||
|
this.origin = rep.getOrigin();
|
||||||
|
this.createdTimestamp = rep.getCreatedTimestamp();
|
||||||
|
this.enabled = rep.isEnabled();
|
||||||
|
this.totp = rep.isTotp();
|
||||||
|
this.federationLink = rep.getFederationLink();
|
||||||
|
this.serviceAccountClientId = rep.getServiceAccountClientId();
|
||||||
|
this.credentials = rep.getCredentials();
|
||||||
|
this.disableableCredentialTypes = rep.getDisableableCredentialTypes();
|
||||||
|
this.requiredActions = rep.getRequiredActions();
|
||||||
|
this.federatedIdentities = rep.getFederatedIdentities();
|
||||||
|
this.realmRoles = rep.getRealmRoles();
|
||||||
|
this.clientRoles = rep.getClientRoles();
|
||||||
|
this.clientConsents = rep.getClientConsents();
|
||||||
|
this.notBefore = rep.getNotBefore();
|
||||||
|
|
||||||
|
this.applicationRoles = rep.getApplicationRoles();
|
||||||
|
this.socialLinks = rep.getSocialLinks();
|
||||||
|
|
||||||
|
this.groups = rep.getGroups();
|
||||||
|
this.access = rep.getAccess();
|
||||||
|
}
|
||||||
|
|
||||||
public String getSelf() {
|
public String getSelf() {
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,13 +22,13 @@ import javax.ws.rs.GET;
|
||||||
import javax.ws.rs.Produces;
|
import javax.ws.rs.Produces;
|
||||||
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 org.keycloak.representations.idm.UserRepresentation;
|
import org.keycloak.representations.idm.MemberRepresentation;
|
||||||
|
|
||||||
public interface OrganizationMemberResource {
|
public interface OrganizationMemberResource {
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
UserRepresentation toRepresentation();
|
MemberRepresentation toRepresentation();
|
||||||
|
|
||||||
@DELETE
|
@DELETE
|
||||||
Response delete();
|
Response delete();
|
||||||
|
|
|
@ -29,8 +29,8 @@ import javax.ws.rs.Produces;
|
||||||
import javax.ws.rs.QueryParam;
|
import javax.ws.rs.QueryParam;
|
||||||
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 org.keycloak.representations.idm.MemberRepresentation;
|
||||||
import org.keycloak.representations.idm.OrganizationRepresentation;
|
import org.keycloak.representations.idm.OrganizationRepresentation;
|
||||||
import org.keycloak.representations.idm.UserRepresentation;
|
|
||||||
|
|
||||||
public interface OrganizationMembersResource {
|
public interface OrganizationMembersResource {
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ public interface OrganizationMembersResource {
|
||||||
*/
|
*/
|
||||||
@GET
|
@GET
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
List<UserRepresentation> getAll();
|
List<MemberRepresentation> getAll();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return all organization members that match the specified filters.
|
* Return all organization members that match the specified filters.
|
||||||
|
@ -60,7 +60,7 @@ public interface OrganizationMembersResource {
|
||||||
*/
|
*/
|
||||||
@GET
|
@GET
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
List<UserRepresentation> search(
|
List<MemberRepresentation> search(
|
||||||
@QueryParam("search") String search,
|
@QueryParam("search") String search,
|
||||||
@QueryParam("exact") Boolean exact,
|
@QueryParam("exact") Boolean exact,
|
||||||
@QueryParam("first") Integer first,
|
@QueryParam("first") Integer first,
|
||||||
|
|
|
@ -21,6 +21,7 @@ import java.util.Map;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
import org.keycloak.models.IdentityProviderModel;
|
import org.keycloak.models.IdentityProviderModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.models.MembershipMetadata;
|
||||||
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;
|
||||||
|
@ -134,6 +135,11 @@ public class InfinispanOrganizationProvider implements OrganizationProvider {
|
||||||
getAllStream().forEach(this::remove);
|
getAllStream().forEach(this::remove);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean addManagedMember(OrganizationModel organization, UserModel user) {
|
||||||
|
return orgDelegate.addManagedMember(organization, user);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean addMember(OrganizationModel organization, UserModel user) {
|
public boolean addMember(OrganizationModel organization, UserModel user) {
|
||||||
return orgDelegate.addMember(organization, user);
|
return orgDelegate.addMember(organization, user);
|
||||||
|
|
|
@ -26,6 +26,7 @@ import org.keycloak.models.ClientModel;
|
||||||
import org.keycloak.models.GroupModel;
|
import org.keycloak.models.GroupModel;
|
||||||
import org.keycloak.models.GroupModel.GroupMemberJoinEvent;
|
import org.keycloak.models.GroupModel.GroupMemberJoinEvent;
|
||||||
import org.keycloak.models.GroupModel.GroupMemberLeaveEvent;
|
import org.keycloak.models.GroupModel.GroupMemberLeaveEvent;
|
||||||
|
import org.keycloak.models.MembershipMetadata;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.RoleModel;
|
import org.keycloak.models.RoleModel;
|
||||||
|
@ -55,6 +56,7 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
import org.keycloak.representations.idm.MembershipType;
|
||||||
|
|
||||||
import static org.keycloak.utils.StreamsUtil.closing;
|
import static org.keycloak.utils.StreamsUtil.closing;
|
||||||
|
|
||||||
|
@ -167,7 +169,7 @@ public class UserAdapter implements UserModel, JpaModel<UserEntity> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setAttribute(String name, List<String> values) {
|
public void setAttribute(String name, List<String> values) {
|
||||||
String valueToSet = (values != null && values.size() > 0) ? values.get(0) : null;
|
String valueToSet = (values != null && !values.isEmpty()) ? values.get(0) : null;
|
||||||
if (UserModel.FIRST_NAME.equals(name)) {
|
if (UserModel.FIRST_NAME.equals(name)) {
|
||||||
user.setFirstName(valueToSet);
|
user.setFirstName(valueToSet);
|
||||||
return;
|
return;
|
||||||
|
@ -363,7 +365,7 @@ public class UserAdapter implements UserModel, JpaModel<UserEntity> {
|
||||||
predicates.add(builder.equal(root.get("user"), getEntity()));
|
predicates.add(builder.equal(root.get("user"), getEntity()));
|
||||||
|
|
||||||
queryBuilder.select(root.get("groupId"));
|
queryBuilder.select(root.get("groupId"));
|
||||||
queryBuilder.where(predicates.toArray(new Predicate[0]));
|
queryBuilder.where(predicates.toArray(Predicate[]::new));
|
||||||
|
|
||||||
return em.createQuery(queryBuilder);
|
return em.createQuery(queryBuilder);
|
||||||
}
|
}
|
||||||
|
@ -414,20 +416,28 @@ public class UserAdapter implements UserModel, JpaModel<UserEntity> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void joinGroup(GroupModel group) {
|
public void joinGroup(GroupModel group) {
|
||||||
if (RoleUtils.isDirectMember(getGroupsStream(), group)) return;
|
joinGroup(group, null);
|
||||||
joinGroupImpl(group);
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void joinGroup(GroupModel group, MembershipMetadata metadata) {
|
||||||
|
if (RoleUtils.isDirectMember(getGroupsStream(), group)) return;
|
||||||
|
joinGroupImpl(group, metadata);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void joinGroupImpl(GroupModel group) {
|
protected void joinGroupImpl(GroupModel group) {
|
||||||
|
joinGroupImpl(group, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void joinGroupImpl(GroupModel group, MembershipMetadata metadata) {
|
||||||
UserGroupMembershipEntity entity = new UserGroupMembershipEntity();
|
UserGroupMembershipEntity entity = new UserGroupMembershipEntity();
|
||||||
entity.setUser(getEntity());
|
entity.setUser(getEntity());
|
||||||
entity.setGroupId(group.getId());
|
entity.setGroupId(group.getId());
|
||||||
|
entity.setMembershipType(metadata == null ? MembershipType.UNMANAGED : metadata.getMembershipType());
|
||||||
em.persist(entity);
|
em.persist(entity);
|
||||||
em.flush();
|
em.flush();
|
||||||
em.detach(entity);
|
em.detach(entity);
|
||||||
GroupMemberJoinEvent.fire(group, session);
|
GroupMemberJoinEvent.fire(group, session);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -437,7 +447,7 @@ public class UserAdapter implements UserModel, JpaModel<UserEntity> {
|
||||||
TypedQuery<UserGroupMembershipEntity> query = getUserGroupMappingQuery(group);
|
TypedQuery<UserGroupMembershipEntity> query = getUserGroupMappingQuery(group);
|
||||||
query.setLockMode(LockModeType.PESSIMISTIC_WRITE);
|
query.setLockMode(LockModeType.PESSIMISTIC_WRITE);
|
||||||
List<UserGroupMembershipEntity> results = query.getResultList();
|
List<UserGroupMembershipEntity> results = query.getResultList();
|
||||||
if (results.size() == 0) return;
|
if (results.isEmpty()) return;
|
||||||
for (UserGroupMembershipEntity entity : results) {
|
for (UserGroupMembershipEntity entity : results) {
|
||||||
em.remove(entity);
|
em.remove(entity);
|
||||||
}
|
}
|
||||||
|
@ -508,7 +518,7 @@ public class UserAdapter implements UserModel, JpaModel<UserEntity> {
|
||||||
TypedQuery<UserRoleMappingEntity> query = getUserRoleMappingEntityTypedQuery(role);
|
TypedQuery<UserRoleMappingEntity> query = getUserRoleMappingEntityTypedQuery(role);
|
||||||
query.setLockMode(LockModeType.PESSIMISTIC_WRITE);
|
query.setLockMode(LockModeType.PESSIMISTIC_WRITE);
|
||||||
List<UserRoleMappingEntity> results = query.getResultList();
|
List<UserRoleMappingEntity> results = query.getResultList();
|
||||||
if (results.size() == 0) return;
|
if (results.isEmpty()) return;
|
||||||
for (UserRoleMappingEntity entity : results) {
|
for (UserRoleMappingEntity entity : results) {
|
||||||
em.remove(entity);
|
em.remove(entity);
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ import jakarta.persistence.NamedQueries;
|
||||||
import jakarta.persistence.NamedQuery;
|
import jakarta.persistence.NamedQuery;
|
||||||
import jakarta.persistence.Table;
|
import jakarta.persistence.Table;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import org.keycloak.representations.idm.MembershipType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
@ -62,6 +63,9 @@ public class UserGroupMembershipEntity {
|
||||||
@Column(name = "GROUP_ID")
|
@Column(name = "GROUP_ID")
|
||||||
protected String groupId;
|
protected String groupId;
|
||||||
|
|
||||||
|
@Column(name = "MEMBERSHIP_TYPE")
|
||||||
|
private String membershipType;
|
||||||
|
|
||||||
public UserEntity getUser() {
|
public UserEntity getUser() {
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
@ -78,6 +82,14 @@ public class UserGroupMembershipEntity {
|
||||||
this.groupId = groupId;
|
this.groupId = groupId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MembershipType getMembershipType() {
|
||||||
|
return MembershipType.valueOf(membershipType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMembershipType(MembershipType membershipType) {
|
||||||
|
this.membershipType = membershipType.toString();
|
||||||
|
}
|
||||||
|
|
||||||
public static class Key implements Serializable {
|
public static class Key implements Serializable {
|
||||||
|
|
||||||
protected UserEntity user;
|
protected UserEntity user;
|
||||||
|
|
|
@ -36,24 +36,29 @@ import jakarta.persistence.criteria.CriteriaQuery;
|
||||||
import jakarta.persistence.criteria.Join;
|
import jakarta.persistence.criteria.Join;
|
||||||
import jakarta.persistence.criteria.Predicate;
|
import jakarta.persistence.criteria.Predicate;
|
||||||
import jakarta.persistence.criteria.Root;
|
import jakarta.persistence.criteria.Root;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
import org.keycloak.connections.jpa.JpaConnectionProvider;
|
import org.keycloak.connections.jpa.JpaConnectionProvider;
|
||||||
import org.keycloak.models.FederatedIdentityModel;
|
|
||||||
import org.keycloak.models.GroupModel;
|
import org.keycloak.models.GroupModel;
|
||||||
import org.keycloak.models.GroupModel.Type;
|
import org.keycloak.models.GroupModel.Type;
|
||||||
import org.keycloak.models.GroupProvider;
|
import org.keycloak.models.GroupProvider;
|
||||||
import org.keycloak.models.IdentityProviderModel;
|
import org.keycloak.models.IdentityProviderModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.models.MembershipMetadata;
|
||||||
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.ModelValidationException;
|
||||||
import org.keycloak.models.OrganizationModel;
|
import org.keycloak.models.OrganizationModel;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
|
import org.keycloak.models.UserManager;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.models.UserProvider;
|
import org.keycloak.models.UserProvider;
|
||||||
import org.keycloak.models.jpa.entities.GroupAttributeEntity;
|
import org.keycloak.models.jpa.entities.GroupAttributeEntity;
|
||||||
import org.keycloak.models.jpa.entities.GroupEntity;
|
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.UserGroupMembershipEntity;
|
||||||
import org.keycloak.organization.OrganizationProvider;
|
import org.keycloak.organization.OrganizationProvider;
|
||||||
|
import org.keycloak.representations.idm.MembershipType;
|
||||||
import org.keycloak.utils.StringUtil;
|
import org.keycloak.utils.StringUtil;
|
||||||
|
|
||||||
public class JpaOrganizationProvider implements OrganizationProvider {
|
public class JpaOrganizationProvider implements OrganizationProvider {
|
||||||
|
@ -143,8 +148,17 @@ public class JpaOrganizationProvider implements OrganizationProvider {
|
||||||
getAllStream().forEach(this::remove);
|
getAllStream().forEach(this::remove);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean addManagedMember(OrganizationModel organization, UserModel user) {
|
||||||
|
return addMember(organization, user, new MembershipMetadata(MembershipType.MANAGED));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean addMember(OrganizationModel organization, UserModel user) {
|
public boolean addMember(OrganizationModel organization, UserModel user) {
|
||||||
|
return addMember(organization, user, new MembershipMetadata(MembershipType.UNMANAGED));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean addMember(OrganizationModel organization, UserModel user, MembershipMetadata metadata) {
|
||||||
throwExceptionIfObjectIsNull(organization, "Organization");
|
throwExceptionIfObjectIsNull(organization, "Organization");
|
||||||
throwExceptionIfObjectIsNull(user, "User");
|
throwExceptionIfObjectIsNull(user, "User");
|
||||||
|
|
||||||
|
@ -171,7 +185,7 @@ public class JpaOrganizationProvider implements OrganizationProvider {
|
||||||
throw new ModelException("User [" + user.getId() + "] is a member of a different organization");
|
throw new ModelException("User [" + user.getId() + "] is a member of a different organization");
|
||||||
}
|
}
|
||||||
|
|
||||||
user.joinGroup(group);
|
user.joinGroup(group, metadata);
|
||||||
user.setSingleAttribute(ORGANIZATION_ATTRIBUTE, entity.getId());
|
user.setSingleAttribute(ORGANIZATION_ATTRIBUTE, entity.getId());
|
||||||
} finally {
|
} finally {
|
||||||
if (current == null) {
|
if (current == null) {
|
||||||
|
@ -357,20 +371,23 @@ public class JpaOrganizationProvider implements OrganizationProvider {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<IdentityProviderModel> brokers = organization.getIdentityProviders().toList();
|
UserEntity userEntity = em.find(UserEntity.class, member.getId());
|
||||||
|
if (userEntity == null) {
|
||||||
if (brokers.isEmpty()) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
RealmModel realm = getRealm();
|
GroupModel organizationGroup = getOrganizationGroup(organization);
|
||||||
List<FederatedIdentityModel> federatedIdentities = userProvider.getFederatedIdentitiesStream(realm, member)
|
try {
|
||||||
.map(federatedIdentityModel -> realm.getIdentityProviderByAlias(federatedIdentityModel.getIdentityProvider()))
|
UserGroupMembershipEntity membership = em.createNamedQuery("userMemberOf", UserGroupMembershipEntity.class)
|
||||||
.filter(brokers::contains)
|
.setParameter("user", userEntity)
|
||||||
.map(m -> userProvider.getFederatedIdentity(realm, member, m.getAlias()))
|
.setParameter("groupId", organizationGroup.getId())
|
||||||
.toList();
|
.getSingleResult();
|
||||||
|
em.detach(membership);
|
||||||
|
|
||||||
return !federatedIdentities.isEmpty();
|
return MembershipType.MANAGED.equals(membership.getMembershipType());
|
||||||
|
} catch (NoResultException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -385,7 +402,7 @@ public class JpaOrganizationProvider implements OrganizationProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isManagedMember(organization, member)) {
|
if (isManagedMember(organization, member)) {
|
||||||
userProvider.removeUser(getRealm(), member);
|
return new UserManager(session).removeUser(getRealm(), member, userProvider);
|
||||||
} else {
|
} else {
|
||||||
OrganizationModel current = (OrganizationModel) session.getAttribute(OrganizationModel.class.getName());
|
OrganizationModel current = (OrganizationModel) session.getAttribute(OrganizationModel.class.getName());
|
||||||
|
|
||||||
|
|
|
@ -45,4 +45,14 @@
|
||||||
</createIndex>
|
</createIndex>
|
||||||
</changeSet>
|
</changeSet>
|
||||||
|
|
||||||
|
<changeSet author="keycloak" id="26.0.0-org-group-membership">
|
||||||
|
<addColumn tableName="USER_GROUP_MEMBERSHIP">
|
||||||
|
<column name="MEMBERSHIP_TYPE" type="VARCHAR(255)"/>
|
||||||
|
</addColumn>
|
||||||
|
<update tableName="USER_GROUP_MEMBERSHIP">
|
||||||
|
<column name="MEMBERSHIP_TYPE" value="UNMANAGED"/>
|
||||||
|
</update>
|
||||||
|
<addNotNullConstraint tableName="USER_GROUP_MEMBERSHIP" columnName="MEMBERSHIP_TYPE" columnDataType="VARCHAR(255)"/>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
</databaseChangeLog>
|
</databaseChangeLog>
|
||||||
|
|
|
@ -68,6 +68,8 @@ import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
import org.keycloak.representations.idm.MemberRepresentation;
|
||||||
|
import org.keycloak.representations.idm.MembershipType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||||
|
@ -279,12 +281,14 @@ public class ExportUtils {
|
||||||
return domain;
|
return domain;
|
||||||
}).forEach(org::addDomain);
|
}).forEach(org::addDomain);
|
||||||
|
|
||||||
orgProvider.getMembersStream(m, null, null, -1, -1)
|
orgProvider.getMembersStream(m, null, null, null, null)
|
||||||
.map(user -> {
|
.forEach(user -> {
|
||||||
UserRepresentation member = new UserRepresentation();
|
MemberRepresentation member = new MemberRepresentation();
|
||||||
member.setUsername(user.getUsername());
|
member.setUsername(user.getUsername());
|
||||||
return member;
|
member.setMembershipType(orgProvider.isManagedMember(m, user) ? MembershipType.MANAGED : MembershipType.UNMANAGED);
|
||||||
}).forEach(org::addMember);
|
|
||||||
|
org.addMember(member);
|
||||||
|
});
|
||||||
|
|
||||||
orgProvider.getIdentityProviders(m)
|
orgProvider.getIdentityProviders(m)
|
||||||
.map(b -> {
|
.map(b -> {
|
||||||
|
|
|
@ -85,7 +85,6 @@ 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.OAuthClientRepresentation;
|
import org.keycloak.representations.idm.OAuthClientRepresentation;
|
||||||
import org.keycloak.representations.idm.OrganizationDomainRepresentation;
|
|
||||||
import org.keycloak.representations.idm.OrganizationRepresentation;
|
import org.keycloak.representations.idm.OrganizationRepresentation;
|
||||||
import org.keycloak.representations.idm.PartialImportRepresentation;
|
import org.keycloak.representations.idm.PartialImportRepresentation;
|
||||||
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
|
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
|
||||||
|
@ -134,6 +133,8 @@ 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.stripSecrets;
|
import static org.keycloak.models.utils.StripSecretsUtils.stripSecrets;
|
||||||
|
import org.keycloak.representations.idm.MemberRepresentation;
|
||||||
|
import org.keycloak.representations.idm.MembershipType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This wraps the functionality about export/import for the storage.
|
* This wraps the functionality about export/import for the storage.
|
||||||
|
@ -1598,11 +1599,15 @@ public class DefaultExportImportManager implements ExportImportManager {
|
||||||
provider.addIdentityProvider(org, idp);
|
provider.addIdentityProvider(org, idp);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (UserRepresentation 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())) {
|
||||||
|
provider.addManagedMember(org, m);
|
||||||
|
} else {
|
||||||
provider.addMember(org, m);
|
provider.addMember(org, m);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -8,34 +8,7 @@ public class BruteUser extends UserRepresentation {
|
||||||
Map<String, Object> bruteForceStatus;
|
Map<String, Object> bruteForceStatus;
|
||||||
|
|
||||||
public BruteUser(UserRepresentation user) {
|
public BruteUser(UserRepresentation user) {
|
||||||
this.id = user.getId();
|
super(user);
|
||||||
this.origin = user.getOrigin();
|
|
||||||
this.createdTimestamp = user.getCreatedTimestamp();
|
|
||||||
this.username = user.getUsername();
|
|
||||||
this.enabled = user.isEnabled();
|
|
||||||
this.totp = user.isTotp();
|
|
||||||
this.emailVerified = user.isEmailVerified();
|
|
||||||
this.firstName = user.getFirstName();
|
|
||||||
this.lastName = user.getLastName();
|
|
||||||
this.email = user.getEmail();
|
|
||||||
this.federationLink = user.getFederationLink();
|
|
||||||
this.serviceAccountClientId = user.getServiceAccountClientId();
|
|
||||||
|
|
||||||
this.attributes = user.getAttributes();
|
|
||||||
this.credentials = user.getCredentials();
|
|
||||||
this.disableableCredentialTypes = user.getDisableableCredentialTypes();
|
|
||||||
this.requiredActions = user.getRequiredActions();
|
|
||||||
this.federatedIdentities = user.getFederatedIdentities();
|
|
||||||
this.realmRoles = user.getRealmRoles();
|
|
||||||
this.clientRoles = user.getClientRoles();
|
|
||||||
this.clientConsents = user.getClientConsents();
|
|
||||||
this.notBefore = user.getNotBefore();
|
|
||||||
|
|
||||||
this.applicationRoles = user.getApplicationRoles();
|
|
||||||
this.socialLinks = user.getSocialLinks();
|
|
||||||
|
|
||||||
this.groups = user.getGroups();
|
|
||||||
this.setAccess(user.getAccess());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, Object> getBruteForceStatus() {
|
public Map<String, Object> getBruteForceStatus() {
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
/*
|
||||||
|
* 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.models;
|
||||||
|
|
||||||
|
import org.keycloak.representations.idm.MembershipType;
|
||||||
|
|
||||||
|
public class MembershipMetadata {
|
||||||
|
|
||||||
|
private final MembershipType membershipType;
|
||||||
|
|
||||||
|
public MembershipMetadata(MembershipType membershipType) {
|
||||||
|
this.membershipType = membershipType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MembershipType getMembershipType() {
|
||||||
|
return membershipType;
|
||||||
|
}
|
||||||
|
}
|
|
@ -195,6 +195,9 @@ public interface UserModel extends RoleMapperModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
void joinGroup(GroupModel group);
|
void joinGroup(GroupModel group);
|
||||||
|
default void joinGroup(GroupModel group, MembershipMetadata metadata) {
|
||||||
|
joinGroup(group);
|
||||||
|
}
|
||||||
void leaveGroup(GroupModel group);
|
void leaveGroup(GroupModel group);
|
||||||
boolean isMemberOf(GroupModel group);
|
boolean isMemberOf(GroupModel group);
|
||||||
|
|
||||||
|
|
|
@ -103,7 +103,17 @@ public interface OrganizationProvider extends Provider {
|
||||||
void removeAll();
|
void removeAll();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds the given {@link UserModel} as a member of the given {@link OrganizationModel}.
|
* Adds the given {@link UserModel} as a managed member of the given {@link OrganizationModel}.
|
||||||
|
*
|
||||||
|
* @param organization the organization
|
||||||
|
* @param user the user
|
||||||
|
* @throws ModelException if the {@link UserModel} is member of different organization
|
||||||
|
* @return {@code true} if the user was added as a member. Otherwise, returns {@code false}
|
||||||
|
*/
|
||||||
|
boolean addManagedMember(OrganizationModel organization, UserModel user);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the given {@link UserModel} as an unmanaged member of the given {@link OrganizationModel}.
|
||||||
*
|
*
|
||||||
* @param organization the organization
|
* @param organization the organization
|
||||||
* @param user the user
|
* @param user the user
|
||||||
|
|
|
@ -342,7 +342,7 @@ public class RegistrationUserCreation implements FormAction, FormActionFactory {
|
||||||
KeycloakSession session = context.getSession();
|
KeycloakSession session = context.getSession();
|
||||||
OrganizationProvider provider = session.getProvider(OrganizationProvider.class);
|
OrganizationProvider provider = session.getProvider(OrganizationProvider.class);
|
||||||
OrganizationModel orgModel = provider.getById(token.getOrgId());
|
OrganizationModel orgModel = provider.getById(token.getOrgId());
|
||||||
provider.addMember(orgModel, user);
|
provider.addManagedMember(orgModel, user);
|
||||||
context.getEvent().detail(Details.ORG_ID, orgModel.getId());
|
context.getEvent().detail(Details.ORG_ID, orgModel.getId());
|
||||||
context.getAuthenticationSession().setRedirectUri(token.getRedirectUri());
|
context.getAuthenticationSession().setRedirectUri(token.getRedirectUri());
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,6 +48,8 @@ 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.representations.idm.MemberRepresentation;
|
||||||
|
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.representations.idm.UserRepresentation;
|
||||||
import org.keycloak.services.ErrorResponse;
|
import org.keycloak.services.ErrorResponse;
|
||||||
|
@ -131,7 +133,7 @@ public class OrganizationMemberResource {
|
||||||
@NoCache
|
@NoCache
|
||||||
@Tag(name = KeycloakOpenAPI.Admin.Tags.ORGANIZATIONS)
|
@Tag(name = KeycloakOpenAPI.Admin.Tags.ORGANIZATIONS)
|
||||||
@Operation( summary = "Returns a paginated list of organization members filtered according to the specified parameters")
|
@Operation( summary = "Returns a paginated list of organization members filtered according to the specified parameters")
|
||||||
public Stream<UserRepresentation> search(
|
public Stream<MemberRepresentation> search(
|
||||||
@Parameter(description = "A String representing either a member's username, e-mail, first name, or last name.") @QueryParam("search") String search,
|
@Parameter(description = "A String representing either a member's username, e-mail, first name, or last name.") @QueryParam("search") String search,
|
||||||
@Parameter(description = "Boolean which defines whether the param 'search' must match exactly or not") @QueryParam("exact") Boolean exact,
|
@Parameter(description = "Boolean which defines whether the param 'search' must match exactly or not") @QueryParam("exact") Boolean exact,
|
||||||
@Parameter(description = "The position of the first result to be processed (pagination offset)") @QueryParam("first") @DefaultValue("0") Integer first,
|
@Parameter(description = "The position of the first result to be processed (pagination offset)") @QueryParam("first") @DefaultValue("0") Integer first,
|
||||||
|
@ -148,7 +150,7 @@ public class OrganizationMemberResource {
|
||||||
@Operation( summary = "Returns the member of the organization with the specified id", description = "Searches for a" +
|
@Operation( summary = "Returns the member of the organization with the specified id", description = "Searches for a" +
|
||||||
"user with the given id. If one is found, and is currently a member of the organization, returns it. Otherwise," +
|
"user with the given id. If one is found, and is currently a member of the organization, returns it. Otherwise," +
|
||||||
"an error response with status NOT_FOUND is returned")
|
"an error response with status NOT_FOUND is returned")
|
||||||
public UserRepresentation get(@PathParam("id") String id) {
|
public MemberRepresentation get(@PathParam("id") String id) {
|
||||||
if (StringUtil.isBlank(id)) {
|
if (StringUtil.isBlank(id)) {
|
||||||
throw ErrorResponse.error("id cannot be null", Status.BAD_REQUEST);
|
throw ErrorResponse.error("id cannot be null", Status.BAD_REQUEST);
|
||||||
}
|
}
|
||||||
|
@ -160,8 +162,8 @@ public class OrganizationMemberResource {
|
||||||
@DELETE
|
@DELETE
|
||||||
@Tag(name = KeycloakOpenAPI.Admin.Tags.ORGANIZATIONS)
|
@Tag(name = KeycloakOpenAPI.Admin.Tags.ORGANIZATIONS)
|
||||||
@Operation(summary = "Removes the user with the specified id from the organization", description = "Breaks the association " +
|
@Operation(summary = "Removes the user with the specified id from the organization", description = "Breaks the association " +
|
||||||
"between the user and organization. The user itself is not deleted. If no user is found, or if they are not " +
|
"between the user and organization. The user itself is deleted in case the membership is managed, otherwise the user is not deleted. " +
|
||||||
"a member of the organization, an error response is returned")
|
"If no user is found, or if they are not a member of the organization, an error response is returned")
|
||||||
public Response delete(@PathParam("id") String id) {
|
public Response delete(@PathParam("id") String id) {
|
||||||
if (StringUtil.isBlank(id)) {
|
if (StringUtil.isBlank(id)) {
|
||||||
throw ErrorResponse.error("id cannot be null", Status.BAD_REQUEST);
|
throw ErrorResponse.error("id cannot be null", Status.BAD_REQUEST);
|
||||||
|
@ -211,7 +213,9 @@ public class OrganizationMemberResource {
|
||||||
return member;
|
return member;
|
||||||
}
|
}
|
||||||
|
|
||||||
private UserRepresentation toRepresentation(UserModel member) {
|
private MemberRepresentation toRepresentation(UserModel member) {
|
||||||
return ModelToRepresentation.toRepresentation(session, realm, member);
|
MemberRepresentation result = new MemberRepresentation(ModelToRepresentation.toRepresentation(session, realm, member));
|
||||||
|
result.setMembershipType(provider.isManagedMember(organization, member) ? MembershipType.MANAGED : MembershipType.UNMANAGED);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,7 +58,7 @@ public class IdpAddOrganizationMemberAuthenticator extends AbstractIdpAuthentica
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
provider.addMember(organization, user);
|
provider.addManagedMember(organization, user);
|
||||||
context.success();
|
context.success();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -180,7 +180,7 @@ public class RealmAttributeUpdater extends ServerResourceUpdater<RealmAttributeU
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public RealmAttributeUpdater setOrganizationEnabled(Boolean organizationsEnabled) {
|
public RealmAttributeUpdater setOrganizationsEnabled(Boolean organizationsEnabled) {
|
||||||
rep.setOrganizationsEnabled(organizationsEnabled);
|
rep.setOrganizationsEnabled(organizationsEnabled);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ import java.util.List;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
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;
|
||||||
|
import org.hamcrest.Matchers;
|
||||||
import org.jboss.arquillian.graphene.page.Page;
|
import org.jboss.arquillian.graphene.page.Page;
|
||||||
import org.keycloak.admin.client.resource.OrganizationResource;
|
import org.keycloak.admin.client.resource.OrganizationResource;
|
||||||
import org.keycloak.models.OrganizationModel;
|
import org.keycloak.models.OrganizationModel;
|
||||||
|
@ -37,6 +38,7 @@ import org.keycloak.models.OrganizationModel.IdentityProviderRedirectMode;
|
||||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||||
import org.keycloak.admin.client.resource.RealmResource;
|
import org.keycloak.admin.client.resource.RealmResource;
|
||||||
import org.keycloak.representations.idm.GroupRepresentation;
|
import org.keycloak.representations.idm.GroupRepresentation;
|
||||||
|
import org.keycloak.representations.idm.MemberRepresentation;
|
||||||
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.representations.idm.RealmRepresentation;
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
|
@ -79,6 +81,11 @@ public abstract class AbstractOrganizationTest extends AbstractAdminTest {
|
||||||
|
|
||||||
protected BrokerConfiguration bc = brokerConfigFunction.apply(organizationName);
|
protected BrokerConfiguration bc = brokerConfigFunction.apply(organizationName);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected TestCleanup getCleanup() {
|
||||||
|
return getCleanup(TEST_REALM_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void configureTestRealm(RealmRepresentation testRealm) {
|
public void configureTestRealm(RealmRepresentation testRealm) {
|
||||||
testRealm.getClients().addAll(bc.createConsumerClients());
|
testRealm.getClients().addAll(bc.createConsumerClients());
|
||||||
|
@ -144,15 +151,15 @@ public abstract class AbstractOrganizationTest extends AbstractAdminTest {
|
||||||
return org;
|
return org;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected UserRepresentation addMember(OrganizationResource organization) {
|
protected MemberRepresentation addMember(OrganizationResource organization) {
|
||||||
return addMember(organization, memberEmail);
|
return addMember(organization, memberEmail);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected UserRepresentation addMember(OrganizationResource organization, String email) {
|
protected MemberRepresentation addMember(OrganizationResource organization, String email) {
|
||||||
return addMember(organization, email, null, null);
|
return addMember(organization, email, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected UserRepresentation addMember(OrganizationResource organization, String email, String firstName, String lastName) {
|
protected MemberRepresentation addMember(OrganizationResource organization, String email, String firstName, String lastName) {
|
||||||
UserRepresentation expected = new UserRepresentation();
|
UserRepresentation expected = new UserRepresentation();
|
||||||
|
|
||||||
expected.setEmail(email);
|
expected.setEmail(email);
|
||||||
|
@ -172,7 +179,7 @@ public abstract class AbstractOrganizationTest extends AbstractAdminTest {
|
||||||
|
|
||||||
try (Response response = organization.members().addMember(userId)) {
|
try (Response response = organization.members().addMember(userId)) {
|
||||||
assertEquals(Status.CREATED.getStatusCode(), response.getStatus());
|
assertEquals(Status.CREATED.getStatusCode(), response.getStatus());
|
||||||
UserRepresentation actual = organization.members().member(userId).toRepresentation();
|
MemberRepresentation actual = organization.members().member(userId).toRepresentation();
|
||||||
|
|
||||||
assertNotNull(expected);
|
assertNotNull(expected);
|
||||||
assertEquals(userId, actual.getId());
|
assertEquals(userId, actual.getId());
|
||||||
|
@ -210,6 +217,12 @@ public abstract class AbstractOrganizationTest extends AbstractAdminTest {
|
||||||
appPage.assertCurrent();
|
appPage.assertCurrent();
|
||||||
assertThat(appPage.getRequestType(), is(AppPage.RequestType.AUTH_RESPONSE));
|
assertThat(appPage.getRequestType(), is(AppPage.RequestType.AUTH_RESPONSE));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<UserRepresentation> users = realmsResouce().realm(bc.consumerRealmName()).users().search(username, Boolean.TRUE);
|
||||||
|
if (!users.isEmpty()) {
|
||||||
|
assertThat(users, Matchers.hasSize(1));
|
||||||
|
getCleanup(bc.consumerRealmName()).addUserId(users.get(0).getId());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void assertIsMember(String userEmail, OrganizationResource organization) {
|
protected void assertIsMember(String userEmail, OrganizationResource organization) {
|
||||||
|
@ -276,11 +289,11 @@ public abstract class AbstractOrganizationTest extends AbstractAdminTest {
|
||||||
|
|
||||||
// user automatically redirected to the organization identity provider
|
// user automatically redirected to the organization identity provider
|
||||||
if (autoIDPRedirect) {
|
if (autoIDPRedirect) {
|
||||||
Assert.assertTrue("Driver should be on the provider realm page right now",
|
assertThat("Driver should be on the provider realm page right now",
|
||||||
driver.getCurrentUrl().contains("/auth/realms/" + bc.providerRealmName() + "/"));
|
driver.getCurrentUrl(), Matchers.containsString("/auth/realms/" + bc.providerRealmName() + "/"));
|
||||||
} else {
|
} else {
|
||||||
Assert.assertTrue("Driver should be on the consumer realm page right now",
|
assertThat("Driver should be on the consumer realm page right now",
|
||||||
driver.getCurrentUrl().contains("/auth/realms/" + bc.consumerRealmName() + "/"));
|
driver.getCurrentUrl(), Matchers.containsString("/auth/realms/" + bc.consumerRealmName() + "/"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,7 @@ import jakarta.mail.internet.MimeMessage;
|
||||||
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;
|
||||||
import org.hamcrest.Matchers;
|
import org.hamcrest.Matchers;
|
||||||
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
import org.jboss.arquillian.graphene.page.Page;
|
import org.jboss.arquillian.graphene.page.Page;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
@ -42,6 +43,8 @@ import org.keycloak.common.Profile.Feature;
|
||||||
import org.keycloak.common.util.UriUtils;
|
import org.keycloak.common.util.UriUtils;
|
||||||
import org.keycloak.cookie.CookieType;
|
import org.keycloak.cookie.CookieType;
|
||||||
import org.keycloak.representations.idm.ErrorRepresentation;
|
import org.keycloak.representations.idm.ErrorRepresentation;
|
||||||
|
import org.keycloak.representations.idm.MemberRepresentation;
|
||||||
|
import org.keycloak.representations.idm.MembershipType;
|
||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
import org.keycloak.representations.idm.UserRepresentation;
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
import org.keycloak.testsuite.Assert;
|
import org.keycloak.testsuite.Assert;
|
||||||
|
@ -113,7 +116,9 @@ public class OrganizationInvitationLinkTest extends AbstractOrganizationTest {
|
||||||
List<UserRepresentation> users = testRealm().users().searchByEmail(email, true);
|
List<UserRepresentation> users = testRealm().users().searchByEmail(email, true);
|
||||||
assertThat(users, Matchers.not(empty()));
|
assertThat(users, Matchers.not(empty()));
|
||||||
// user is a member
|
// user is a member
|
||||||
Assert.assertNotNull(organization.members().member(users.get(0).getId()).toRepresentation());
|
MemberRepresentation member = organization.members().member(users.get(0).getId()).toRepresentation();
|
||||||
|
Assert.assertNotNull(member);
|
||||||
|
assertThat(member.getMembershipType(), equalTo(MembershipType.MANAGED));
|
||||||
getCleanup().addCleanup(() -> testRealm().users().get(users.get(0).getId()).remove());
|
getCleanup().addCleanup(() -> testRealm().users().get(users.get(0).getId()).remove());
|
||||||
|
|
||||||
// authenticated to the account console
|
// authenticated to the account console
|
||||||
|
|
|
@ -406,7 +406,7 @@ public class OrganizationTest extends AbstractOrganizationTest {
|
||||||
OrganizationRepresentation existing = createOrganization("acme", "acme.org", "acme.net");
|
OrganizationRepresentation existing = createOrganization("acme", "acme.org", "acme.net");
|
||||||
// disable the organization provider and try to access REST endpoints
|
// disable the organization provider and try to access REST endpoints
|
||||||
try (RealmAttributeUpdater rau = new RealmAttributeUpdater(testRealm())
|
try (RealmAttributeUpdater rau = new RealmAttributeUpdater(testRealm())
|
||||||
.setOrganizationEnabled(Boolean.FALSE)
|
.setOrganizationsEnabled(Boolean.FALSE)
|
||||||
.update()) {
|
.update()) {
|
||||||
OrganizationRepresentation org = createRepresentation("some", "some.com");
|
OrganizationRepresentation org = createRepresentation("some", "some.com");
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,8 @@ import java.util.List;
|
||||||
|
|
||||||
import jakarta.ws.rs.BadRequestException;
|
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.Status;
|
||||||
import org.hamcrest.Matchers;
|
import org.hamcrest.Matchers;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.keycloak.admin.client.resource.OrganizationIdentityProviderResource;
|
import org.keycloak.admin.client.resource.OrganizationIdentityProviderResource;
|
||||||
|
@ -45,6 +47,7 @@ import org.keycloak.representations.idm.OrganizationDomainRepresentation;
|
||||||
import org.keycloak.representations.idm.OrganizationRepresentation;
|
import org.keycloak.representations.idm.OrganizationRepresentation;
|
||||||
import org.keycloak.representations.idm.UserRepresentation;
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
import org.keycloak.testsuite.Assert;
|
import org.keycloak.testsuite.Assert;
|
||||||
|
import org.keycloak.testsuite.admin.ApiUtil;
|
||||||
import org.keycloak.testsuite.organization.admin.AbstractOrganizationTest;
|
import org.keycloak.testsuite.organization.admin.AbstractOrganizationTest;
|
||||||
import org.keycloak.testsuite.util.UserBuilder;
|
import org.keycloak.testsuite.util.UserBuilder;
|
||||||
|
|
||||||
|
@ -212,14 +215,7 @@ public abstract class AbstractBrokerSelfRegistrationTest extends AbstractOrganiz
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLinkExistingAccount() {
|
public void testLinkExistingAccount() {
|
||||||
// create a realm user in the consumer realm
|
createUserInConsumerRealm();
|
||||||
realmsResouce().realm(bc.consumerRealmName()).users()
|
|
||||||
.create(UserBuilder.create()
|
|
||||||
.username(bc.getUserLogin())
|
|
||||||
.email(bc.getUserEmail())
|
|
||||||
.password(bc.getUserPassword())
|
|
||||||
.enabled(true).build()
|
|
||||||
).close();
|
|
||||||
|
|
||||||
OrganizationResource organization = testRealm().organizations().get(createOrganization().getId());
|
OrganizationResource organization = testRealm().organizations().get(createOrganization().getId());
|
||||||
OrganizationIdentityProviderResource broker = organization.identityProviders().get(bc.getIDPAlias());
|
OrganizationIdentityProviderResource broker = organization.identityProviders().get(bc.getIDPAlias());
|
||||||
|
@ -242,14 +238,7 @@ public abstract class AbstractBrokerSelfRegistrationTest extends AbstractOrganiz
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testExistingUserUsingOrgDomain() {
|
public void testExistingUserUsingOrgDomain() {
|
||||||
// create a realm user in the consumer realm
|
createUserInConsumerRealm();
|
||||||
realmsResouce().realm(bc.consumerRealmName()).users()
|
|
||||||
.create(UserBuilder.create()
|
|
||||||
.username(bc.getUserLogin())
|
|
||||||
.email(bc.getUserEmail())
|
|
||||||
.password(bc.getUserPassword())
|
|
||||||
.enabled(true).build()
|
|
||||||
).close();
|
|
||||||
|
|
||||||
OrganizationResource organization = testRealm().organizations().get(createOrganization().getId());
|
OrganizationResource organization = testRealm().organizations().get(createOrganization().getId());
|
||||||
OrganizationIdentityProviderResource broker = organization.identityProviders().get(bc.getIDPAlias());
|
OrganizationIdentityProviderResource broker = organization.identityProviders().get(bc.getIDPAlias());
|
||||||
|
@ -425,6 +414,7 @@ public abstract class AbstractBrokerSelfRegistrationTest extends AbstractOrganiz
|
||||||
List<FederatedIdentityRepresentation> federatedIdentities = testRealm().users().get(user.getId()).getFederatedIdentity();
|
List<FederatedIdentityRepresentation> federatedIdentities = testRealm().users().get(user.getId()).getFederatedIdentity();
|
||||||
assertEquals(1, federatedIdentities.size());
|
assertEquals(1, federatedIdentities.size());
|
||||||
assertEquals(bc.getIDPAlias(), federatedIdentities.get(0).getIdentityProvider());
|
assertEquals(bc.getIDPAlias(), federatedIdentities.get(0).getIdentityProvider());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -689,4 +679,18 @@ public abstract class AbstractBrokerSelfRegistrationTest extends AbstractOrganiz
|
||||||
} catch (NotFoundException ignore) {
|
} catch (NotFoundException ignore) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void createUserInConsumerRealm() {
|
||||||
|
// create a realm user in the consumer realm
|
||||||
|
try (Response response = realmsResouce().realm(bc.consumerRealmName()).users()
|
||||||
|
.create(UserBuilder.create()
|
||||||
|
.username(bc.getUserLogin())
|
||||||
|
.email(bc.getUserEmail())
|
||||||
|
.password(bc.getUserPassword())
|
||||||
|
.enabled(true).build())) {
|
||||||
|
assertEquals(Status.CREATED.getStatusCode(), response.getStatus());
|
||||||
|
String id = ApiUtil.getCreatedId(response);
|
||||||
|
getCleanup(bc.consumerRealmName()).addUserId(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,7 +86,7 @@ public class OrganizationMemberAuthenticationTest extends AbstractOrganizationTe
|
||||||
|
|
||||||
// disable the organization provider
|
// disable the organization provider
|
||||||
try (RealmAttributeUpdater rau = new RealmAttributeUpdater(testRealm())
|
try (RealmAttributeUpdater rau = new RealmAttributeUpdater(testRealm())
|
||||||
.setOrganizationEnabled(Boolean.FALSE)
|
.setOrganizationsEnabled(Boolean.FALSE)
|
||||||
.update()) {
|
.update()) {
|
||||||
|
|
||||||
// access the page again, now it should be present username and password fields
|
// access the page again, now it should be present username and password fields
|
||||||
|
|
|
@ -53,6 +53,7 @@ import org.keycloak.models.UserModel;
|
||||||
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.representations.idm.ErrorRepresentation;
|
import org.keycloak.representations.idm.ErrorRepresentation;
|
||||||
|
import org.keycloak.representations.idm.MemberRepresentation;
|
||||||
import org.keycloak.representations.idm.OrganizationRepresentation;
|
import org.keycloak.representations.idm.OrganizationRepresentation;
|
||||||
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;
|
||||||
|
@ -68,7 +69,7 @@ public class OrganizationMemberTest extends AbstractOrganizationTest {
|
||||||
@Test
|
@Test
|
||||||
public void testUpdate() {
|
public void testUpdate() {
|
||||||
OrganizationResource organization = testRealm().organizations().get(createOrganization().getId());
|
OrganizationResource organization = testRealm().organizations().get(createOrganization().getId());
|
||||||
UserRepresentation expected = addMember(organization);
|
UserRepresentation expected = getUserRepFromMemberRep(addMember(organization));
|
||||||
|
|
||||||
expected.setFirstName("f");
|
expected.setFirstName("f");
|
||||||
expected.setLastName("l");
|
expected.setLastName("l");
|
||||||
|
@ -90,7 +91,7 @@ public class OrganizationMemberTest extends AbstractOrganizationTest {
|
||||||
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 = addMember(organization);
|
UserRepresentation expected = getUserRepFromMemberRep(addMember(organization));
|
||||||
|
|
||||||
expected.singleAttribute(ORGANIZATION_ATTRIBUTE, "invalid");
|
expected.singleAttribute(ORGANIZATION_ATTRIBUTE, "invalid");
|
||||||
|
|
||||||
|
@ -161,7 +162,7 @@ public class OrganizationMemberTest extends AbstractOrganizationTest {
|
||||||
expected.add(addMember(organization, "member-" + i + "@neworg.org"));
|
expected.add(addMember(organization, "member-" + i + "@neworg.org"));
|
||||||
}
|
}
|
||||||
|
|
||||||
List<UserRepresentation> existing = organization.members().getAll();
|
List<MemberRepresentation> existing = organization.members().getAll();
|
||||||
assertFalse(existing.isEmpty());
|
assertFalse(existing.isEmpty());
|
||||||
assertEquals(expected.size(), existing.size());
|
assertEquals(expected.size(), existing.size());
|
||||||
for (UserRepresentation expectedRep : expected) {
|
for (UserRepresentation expectedRep : expected) {
|
||||||
|
@ -200,7 +201,7 @@ public class OrganizationMemberTest extends AbstractOrganizationTest {
|
||||||
assertThat(existingOrg.isEnabled(), is(false));
|
assertThat(existingOrg.isEnabled(), is(false));
|
||||||
|
|
||||||
// now fetch all users from the org - unmanaged users should still be enabled, but managed ones should not.
|
// now fetch all users from the org - unmanaged users should still be enabled, but managed ones should not.
|
||||||
List<UserRepresentation> existing = organization.members().getAll();
|
List<MemberRepresentation> existing = organization.members().getAll();
|
||||||
assertThat(existing, not(empty()));
|
assertThat(existing, not(empty()));
|
||||||
assertThat(existing, hasSize(6));
|
assertThat(existing, hasSize(6));
|
||||||
for (UserRepresentation user : existing) {
|
for (UserRepresentation user : existing) {
|
||||||
|
@ -213,10 +214,10 @@ public class OrganizationMemberTest extends AbstractOrganizationTest {
|
||||||
|
|
||||||
// fetching users from the users endpoint should have the same result.
|
// fetching users from the users endpoint should have the same result.
|
||||||
UserRepresentation disabledUser = null;
|
UserRepresentation disabledUser = null;
|
||||||
existing = testRealm().users().search("*neworg*",0, 10);
|
List<UserRepresentation> existingUsers = testRealm().users().search("*neworg*",0, 10);
|
||||||
assertThat(existing, not(empty()));
|
assertThat(existingUsers, not(empty()));
|
||||||
assertThat(existing, hasSize(6));
|
assertThat(existingUsers, hasSize(6));
|
||||||
for (UserRepresentation user : existing) {
|
for (UserRepresentation user : existingUsers) {
|
||||||
if (user.getEmail().equals(bc.getUserEmail())) {
|
if (user.getEmail().equals(bc.getUserEmail())) {
|
||||||
assertThat(user.isEnabled(), is(false));
|
assertThat(user.isEnabled(), is(false));
|
||||||
disabledUser = user;
|
disabledUser = user;
|
||||||
|
@ -254,7 +255,7 @@ public class OrganizationMemberTest extends AbstractOrganizationTest {
|
||||||
|
|
||||||
// disable the organization provider
|
// disable the organization provider
|
||||||
try (RealmAttributeUpdater rau = new RealmAttributeUpdater(testRealm())
|
try (RealmAttributeUpdater rau = new RealmAttributeUpdater(testRealm())
|
||||||
.setOrganizationEnabled(Boolean.FALSE)
|
.setOrganizationsEnabled(Boolean.FALSE)
|
||||||
.update()) {
|
.update()) {
|
||||||
|
|
||||||
// now fetch all members from the realm - unmanaged users should still be enabled, but managed ones should not.
|
// now fetch all members from the realm - unmanaged users should still be enabled, but managed ones should not.
|
||||||
|
@ -305,7 +306,7 @@ public class OrganizationMemberTest extends AbstractOrganizationTest {
|
||||||
@Test
|
@Test
|
||||||
public void testUpdateEmailUnmanagedMember() {
|
public void testUpdateEmailUnmanagedMember() {
|
||||||
OrganizationResource organization = testRealm().organizations().get(createOrganization().getId());
|
OrganizationResource organization = testRealm().organizations().get(createOrganization().getId());
|
||||||
UserRepresentation expected = addMember(organization);
|
UserRepresentation expected = getUserRepFromMemberRep(addMember(organization));
|
||||||
expected.setEmail("some@unknown.org");
|
expected.setEmail("some@unknown.org");
|
||||||
UserResource userResource = testRealm().users().get(expected.getId());
|
UserResource userResource = testRealm().users().get(expected.getId());
|
||||||
userResource.update(expected);
|
userResource.update(expected);
|
||||||
|
@ -315,10 +316,14 @@ public class OrganizationMemberTest extends AbstractOrganizationTest {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private UserRepresentation getUserRepFromMemberRep(MemberRepresentation member) {
|
||||||
|
return new UserRepresentation(member);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDeleteMembersOnOrganizationRemoval() {
|
public void testDeleteMembersOnOrganizationRemoval() {
|
||||||
OrganizationResource organization = testRealm().organizations().get(createOrganization().getId());
|
OrganizationResource organization = testRealm().organizations().get(createOrganization().getId());
|
||||||
List<UserRepresentation> expected = new ArrayList<>();
|
List<MemberRepresentation> expected = new ArrayList<>();
|
||||||
|
|
||||||
for (int i = 0; i < 5; i++) {
|
for (int i = 0; i < 5; i++) {
|
||||||
expected.add(addMember(organization, "member-" + i + "@neworg.org"));
|
expected.add(addMember(organization, "member-" + i + "@neworg.org"));
|
||||||
|
@ -326,19 +331,19 @@ public class OrganizationMemberTest extends AbstractOrganizationTest {
|
||||||
|
|
||||||
organization.delete().close();
|
organization.delete().close();
|
||||||
|
|
||||||
for (UserRepresentation member : expected) {
|
for (MemberRepresentation member : expected) {
|
||||||
try {
|
try {
|
||||||
organization.members().member(member.getId()).toRepresentation();
|
organization.members().member(member.getId()).toRepresentation();
|
||||||
fail("should be deleted");
|
fail("should be deleted");
|
||||||
} catch (NotFoundException ignore) {}
|
} catch (NotFoundException ignore) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (UserRepresentation member : expected) {
|
for (MemberRepresentation member : expected) {
|
||||||
// users should exist as they are not managed by the organization
|
// users should exist as they are not managed by the organization
|
||||||
testRealm().users().get(member.getId()).toRepresentation();
|
testRealm().users().get(member.getId()).toRepresentation();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (UserRepresentation member : expected) {
|
for (MemberRepresentation member : expected) {
|
||||||
try {
|
try {
|
||||||
// user no longer bound to the organization
|
// user no longer bound to the organization
|
||||||
organization.members().getOrganization(member.getId());
|
organization.members().getOrganization(member.getId());
|
||||||
|
@ -361,7 +366,7 @@ public class OrganizationMemberTest extends AbstractOrganizationTest {
|
||||||
expected.add(addMember(organization, "thejoker@neworg.org", "Jack", "White"));
|
expected.add(addMember(organization, "thejoker@neworg.org", "Jack", "White"));
|
||||||
|
|
||||||
// exact search - username/e-mail, first name, last name.
|
// exact search - username/e-mail, first name, last name.
|
||||||
List<UserRepresentation> existing = organization.members().search("brucewayne@neworg.org", true, null, null);
|
List<MemberRepresentation> existing = organization.members().search("brucewayne@neworg.org", true, null, null);
|
||||||
assertThat(existing, hasSize(1));
|
assertThat(existing, hasSize(1));
|
||||||
assertThat(existing.get(0).getUsername(), is(equalTo("brucewayne@neworg.org")));
|
assertThat(existing.get(0).getUsername(), is(equalTo("brucewayne@neworg.org")));
|
||||||
assertThat(existing.get(0).getEmail(), is(equalTo("brucewayne@neworg.org")));
|
assertThat(existing.get(0).getEmail(), is(equalTo("brucewayne@neworg.org")));
|
||||||
|
|
Loading…
Reference in a new issue