Changes the contract to make it simpler and rely on the realm available from the current session

Closes #28403

Signed-off-by: Pedro Igor <pigor.craveiro@gmail.com>
This commit is contained in:
Pedro Igor 2024-03-26 14:23:17 -03:00 committed by Alexander Schwartz
parent 9bb2402d3b
commit fefeb83588
8 changed files with 57 additions and 83 deletions

View file

@ -42,16 +42,21 @@ public class JpaOrganizationProvider implements OrganizationProvider {
private final EntityManager em;
private final GroupProvider groupProvider;
private final UserProvider userProvider;
private final RealmModel realm;
public JpaOrganizationProvider(KeycloakSession session) {
em = session.getProvider(JpaConnectionProvider.class).getEntityManager();
groupProvider = session.groups();
userProvider = session.users();
realm = session.getContext().getRealm();
if (realm == null) {
throw new IllegalArgumentException("Session not bound to a realm");
}
}
@Override
public OrganizationModel createOrganization(RealmModel realm, String name) {
GroupModel group = createOrganizationGroup(realm, name);
public OrganizationModel create(String name) {
GroupModel group = createOrganizationGroup(name);
OrganizationEntity entity = new OrganizationEntity();
entity.setId(KeycloakModelUtils.generateId());
@ -65,14 +70,14 @@ public class JpaOrganizationProvider implements OrganizationProvider {
}
@Override
public boolean removeOrganization(RealmModel realm, OrganizationModel organization) {
GroupModel group = getOrganizationGroup(realm, organization);
public boolean remove(OrganizationModel organization) {
GroupModel group = getOrganizationGroup(organization);
//TODO: won't scale, requires a better mechanism for bulk deleting users
userProvider.getGroupMembersStream(realm, group).forEach(userModel -> userProvider.removeUser(realm, userModel));
groupProvider.removeGroup(realm, group);
OrganizationAdapter adapter = getAdapter(realm, organization.getId());
OrganizationAdapter adapter = getAdapter(organization.getId());
em.remove(adapter.getEntity());
@ -80,18 +85,18 @@ public class JpaOrganizationProvider implements OrganizationProvider {
}
@Override
public void removeOrganizations(RealmModel realm) {
public void removeAll() {
//TODO: won't scale, requires a better mechanism for bulk deleting organizations within a realm
getOrganizationsStream(realm).forEach(organization -> removeOrganization(realm, organization));
getAllStream().forEach(this::remove);
}
@Override
public boolean addOrganizationMember(RealmModel realm, OrganizationModel organization, UserModel user) {
public boolean addMember(OrganizationModel organization, UserModel user) {
throwExceptionIfOrganizationIsNull(organization);
if (user == null) {
throw new ModelException("User can not be null");
}
OrganizationAdapter adapter = getAdapter(realm, organization.getId());
OrganizationAdapter adapter = getAdapter(organization.getId());
GroupModel group = groupProvider.getGroupById(realm, adapter.getGroupId());
if (user.isMemberOf(group)) {
@ -109,13 +114,12 @@ public class JpaOrganizationProvider implements OrganizationProvider {
}
@Override
public OrganizationModel getOrganizationById(RealmModel realm, String id) {
return getAdapter(realm, id, false);
public OrganizationModel getById(String id) {
return getAdapter(id, false);
}
@Override
public Stream<OrganizationModel> getOrganizationsStream(RealmModel realm) {
throwExceptionIfRealmIsNull(realm);
public Stream<OrganizationModel> getAllStream() {
TypedQuery<OrganizationEntity> query = em.createNamedQuery("getByRealm", OrganizationEntity.class);
query.setParameter("realmId", realm.getId());
@ -124,17 +128,16 @@ public class JpaOrganizationProvider implements OrganizationProvider {
}
@Override
public Stream<UserModel> getMembersStream(RealmModel realm, OrganizationModel organization) {
public Stream<UserModel> getMembersStream(OrganizationModel organization) {
throwExceptionIfOrganizationIsNull(organization);
OrganizationAdapter adapter = getAdapter(realm, organization.getId());
GroupModel group = getOrganizationGroup(realm, adapter);
OrganizationAdapter adapter = getAdapter(organization.getId());
GroupModel group = getOrganizationGroup(adapter);
return userProvider.getGroupMembersStream(realm, group);
}
@Override
public UserModel getMemberById(RealmModel realm, OrganizationModel organization, String id) {
throwExceptionIfRealmIsNull(realm);
public UserModel getMemberById(OrganizationModel organization, String id) {
throwExceptionIfOrganizationIsNull(organization);
UserModel user = userProvider.getUserById(realm, id);
@ -152,8 +155,7 @@ public class JpaOrganizationProvider implements OrganizationProvider {
}
@Override
public OrganizationModel getOrganizationByMember(RealmModel realm, UserModel member) {
throwExceptionIfRealmIsNull(realm);
public OrganizationModel getByMember(UserModel member) {
if (member == null) {
throw new ModelException("User can not be null");
}
@ -164,7 +166,7 @@ public class JpaOrganizationProvider implements OrganizationProvider {
return null;
}
return getOrganizationById(realm, orgId);
return getById(orgId);
}
@Override
@ -172,12 +174,11 @@ public class JpaOrganizationProvider implements OrganizationProvider {
}
private OrganizationAdapter getAdapter(RealmModel realm, String id) {
return getAdapter(realm, id, true);
private OrganizationAdapter getAdapter(String id) {
return getAdapter(id, true);
}
private OrganizationAdapter getAdapter(RealmModel realm, String id, boolean failIfNotFound) {
throwExceptionIfRealmIsNull(realm);
private OrganizationAdapter getAdapter(String id, boolean failIfNotFound) {
OrganizationEntity entity = em.find(OrganizationEntity.class, id);
if (entity == null) {
@ -194,8 +195,7 @@ public class JpaOrganizationProvider implements OrganizationProvider {
return new OrganizationAdapter(realm, entity);
}
private GroupModel createOrganizationGroup(RealmModel realm, String name) {
throwExceptionIfRealmIsNull(realm);
private GroupModel createOrganizationGroup(String name) {
if (name == null) {
throw new ModelException("name can not be null");
}
@ -214,9 +214,9 @@ public class JpaOrganizationProvider implements OrganizationProvider {
return "kc.org." + name;
}
private GroupModel getOrganizationGroup(RealmModel realm, OrganizationModel organization) {
private GroupModel getOrganizationGroup(OrganizationModel organization) {
throwExceptionIfOrganizationIsNull(organization);
OrganizationAdapter adapter = getAdapter(realm, organization.getId());
OrganizationAdapter adapter = getAdapter(organization.getId());
GroupModel group = groupProvider.getGroupById(realm, adapter.getGroupId());
@ -232,10 +232,4 @@ public class JpaOrganizationProvider implements OrganizationProvider {
throw new ModelException("organization can not be null");
}
}
private void throwExceptionIfRealmIsNull(RealmModel realm) {
if (realm == null) {
throw new ModelException("realm can not be null");
}
}
}

View file

@ -57,8 +57,7 @@ public class JpaOrganizationProviderFactory implements OrganizationProviderFacto
if (event instanceof RealmRemovedEvent) {
KeycloakSession session = ((RealmRemovedEvent) event).getKeycloakSession();
OrganizationProvider provider = session.getProvider(OrganizationProvider.class);
RealmModel realm = ((RealmRemovedEvent) event).getRealm();
provider.removeOrganizations(realm);
provider.removeAll();
}
}
}

View file

@ -37,8 +37,7 @@ public final class OrganizationAdapter implements OrganizationModel, JpaModel<Or
return entity.getId();
}
@Override
public RealmModel getRealm() {
RealmModel getRealm() {
return realm;
}

View file

@ -20,88 +20,81 @@ import java.util.stream.Stream;
import org.keycloak.models.ModelDuplicateException;
import org.keycloak.models.OrganizationModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.provider.Provider;
/**
* A {@link Provider} that manages organization and its data within the scope of a realm.
*/
public interface OrganizationProvider extends Provider {
/**
* Creates a new organization with given {@code name} to the given realm.
* The internal ID of the organization will be created automatically.
* @param realm Realm owning this organization.
* @param name String name of the organization.
* @throws ModelDuplicateException If there is already an organization with the given name
* @return Model of the created organization.
*/
OrganizationModel createOrganization(RealmModel realm, String name);
OrganizationModel create(String name);
/**
* Returns a {@link OrganizationModel} by its {@code id};
*
* @param realm the realm
* @param id the id of an organization
* @return the organization with the given {@code id}
*/
OrganizationModel getOrganizationById(RealmModel realm, String id);
OrganizationModel getById(String id);
/**
* Removes the given organization from the given realm.
*
* @param realm Realm.
* @param organization Organization to be removed.
* @return true if the organization was removed, false if group doesn't exist or doesn't belong to the given realm
*/
boolean removeOrganization(RealmModel realm, OrganizationModel organization);
boolean remove(OrganizationModel organization);
/**
* Removes all organizations from the given realm.
* @param realm Realm.
*/
void removeOrganizations(RealmModel realm);
void removeAll();
/**
* Adds the give {@link UserModel} as a member of the given {@link OrganizationModel}.
*
* @param realm the realm
* @param organization the organization
* @param user the user
* @return {@code true} if the user was added as a member. Otherwise, returns {@code false}
*/
boolean addOrganizationMember(RealmModel realm, OrganizationModel organization, UserModel user);
boolean addMember(OrganizationModel organization, UserModel user);
/**
* Returns the organizations of the given realm as a stream.
* @param realm Realm.
* @return Stream of the organizations. Never returns {@code null}.
*/
Stream<OrganizationModel> getOrganizationsStream(RealmModel realm);
Stream<OrganizationModel> getAllStream();
/**
* Returns the members of a given {@code organization}.
*
* @param realm the realm
* @param organization the organization
* @return the organization with the given {@code id}
*/
Stream<UserModel> getMembersStream(RealmModel realm, OrganizationModel organization);
Stream<UserModel> getMembersStream(OrganizationModel organization);
/**
* Returns the member of an {@code organization} by its {@code id}.
*
* @param realm the realm
* @param organization the organization
* @param id the member id
* @return the organization with the given {@code id}
*/
UserModel getMemberById(RealmModel realm, OrganizationModel organization, String id);
UserModel getMemberById(OrganizationModel organization, String id);
/**
* Returns the {@link OrganizationModel} that a {@code member} belongs to.
*
* @param realm the realm
* @param member the member of a organization
* @return the organization the {@code member} belongs to
*/
OrganizationModel getOrganizationByMember(RealmModel realm, UserModel member);
OrganizationModel getByMember(UserModel member);
}

View file

@ -26,6 +26,4 @@ public interface OrganizationModel {
void setName(String name);
String getName();
RealmModel getRealm();
}

View file

@ -94,7 +94,7 @@ public class OrganizationMemberResource {
OrganizationProvider provider = session.getProvider(OrganizationProvider.class);
try {
if (provider.addOrganizationMember(realm, organization, member)) {
if (provider.addMember(organization, member)) {
return Response.created(session.getContext().getUri().getAbsolutePathBuilder().path(member.getId()).build()).build();
}
} catch (ModelException me) {
@ -110,7 +110,7 @@ public class OrganizationMemberResource {
@GET
@Produces(MediaType.APPLICATION_JSON)
public Stream<UserRepresentation> getMembers() {
return provider.getMembersStream(realm, organization).map(this::toRepresentation);
return provider.getMembersStream(organization).map(this::toRepresentation);
}
@Path("{id}")
@ -152,7 +152,7 @@ public class OrganizationMemberResource {
}
UserModel member = getMember(id);
OrganizationModel organization = provider.getOrganizationByMember(realm, member);
OrganizationModel organization = provider.getByMember(member);
OrganizationRepresentation rep = new OrganizationRepresentation();
rep.setId(organization.getId());
@ -161,7 +161,7 @@ public class OrganizationMemberResource {
}
private UserModel getMember(String id) {
UserModel member = provider.getMemberById(realm, organization, id);
UserModel member = provider.getMemberById(organization, id);
if (member == null) {
throw new NotFoundException();

View file

@ -34,7 +34,6 @@ import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.ext.Provider;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.OrganizationModel;
import org.keycloak.models.RealmModel;
import org.keycloak.organization.OrganizationProvider;
import org.keycloak.representations.idm.OrganizationRepresentation;
import org.keycloak.services.resources.admin.AdminEventBuilder;
@ -68,8 +67,7 @@ public class OrganizationResource {
throw new BadRequestException();
}
RealmModel realm = session.getContext().getRealm();
OrganizationModel model = provider.createOrganization(realm, organization.getName());
OrganizationModel model = provider.create(organization.getName());
return Response.created(session.getContext().getUri().getAbsolutePathBuilder().path(model.getId()).build()).build();
}
@ -77,7 +75,7 @@ public class OrganizationResource {
@GET
@Produces(MediaType.APPLICATION_JSON)
public Stream<OrganizationRepresentation> get() {
return provider.getOrganizationsStream(session.getContext().getRealm()).map(this::toRepresentation);
return provider.getAllStream().map(this::toRepresentation);
}
@Path("{id}")
@ -88,7 +86,7 @@ public class OrganizationResource {
throw new BadRequestException();
}
return toRepresentation(getOrganization(session.getContext().getRealm(), id));
return toRepresentation(getOrganization(id));
}
@Path("{id}")
@ -98,8 +96,7 @@ public class OrganizationResource {
throw new BadRequestException();
}
RealmModel realm = session.getContext().getRealm();
provider.removeOrganization(realm, getOrganization(realm, id));
provider.remove(getOrganization(id));
return Response.noContent().build();
}
@ -108,8 +105,7 @@ public class OrganizationResource {
@PUT
@Consumes(MediaType.APPLICATION_JSON)
public Response update(@PathParam("id") String id, OrganizationRepresentation organization) {
RealmModel realm = session.getContext().getRealm();
OrganizationModel model = getOrganization(realm, id);
OrganizationModel model = getOrganization(id);
toModel(organization, model);
@ -118,18 +114,16 @@ public class OrganizationResource {
@Path("{id}/members")
public OrganizationMemberResource members(@PathParam("id") String id) {
RealmModel realm = session.getContext().getRealm();
OrganizationModel model = getOrganization(realm, id);
return new OrganizationMemberResource(session, model, auth, adminEvent);
OrganizationModel organization = getOrganization(id);
return new OrganizationMemberResource(session, organization, auth, adminEvent);
}
private OrganizationModel getOrganization(RealmModel realm, String id) {
private OrganizationModel getOrganization(String id) {
if (id == null) {
throw new BadRequestException();
}
OrganizationModel model = provider.getOrganizationById(realm, id);
OrganizationModel model = provider.getById(id);
if (model == null) {
throw new NotFoundException();

View file

@ -28,7 +28,6 @@ import org.keycloak.models.ClientSessionContext;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.OrganizationModel;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.organization.OrganizationProvider;
@ -70,11 +69,9 @@ public class OrganizationMembershipMapper extends AbstractOIDCProtocolMapper imp
@Override
protected void setClaim(IDToken token, ProtocolMapperModel mappingModel, UserSessionModel userSession, KeycloakSession keycloakSession, ClientSessionContext clientSessionCtx) {
RealmModel realm = keycloakSession.getContext().getRealm();
UserModel user = userSession.getUser();
OrganizationProvider organizationProvider = keycloakSession.getProvider(OrganizationProvider.class);
OrganizationModel organization = organizationProvider.getOrganizationByMember(realm, user);
OrganizationModel organization = organizationProvider.getByMember(user);
if (organization != null) {
Map<String, Map<String, Object>> claim = new HashMap<>();