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

View file

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

View file

@ -20,88 +20,81 @@ import java.util.stream.Stream;
import org.keycloak.models.ModelDuplicateException; import org.keycloak.models.ModelDuplicateException;
import org.keycloak.models.OrganizationModel; import org.keycloak.models.OrganizationModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel; import org.keycloak.models.UserModel;
import org.keycloak.provider.Provider; 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 { public interface OrganizationProvider extends Provider {
/** /**
* Creates a new organization with given {@code name} to the given realm. * Creates a new organization with given {@code name} to the given realm.
* The internal ID of the organization will be created automatically. * The internal ID of the organization will be created automatically.
* @param realm Realm owning this organization.
* @param name String name of the organization. * @param name String name of the organization.
* @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 createOrganization(RealmModel realm, String name); OrganizationModel create(String name);
/** /**
* Returns a {@link OrganizationModel} by its {@code id}; * Returns a {@link OrganizationModel} by its {@code id};
* *
* @param realm the realm
* @param id the id of an organization * @param id the id of an organization
* @return the organization with the given {@code id} * @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. * Removes the given organization from the given realm.
* *
* @param realm Realm.
* @param organization Organization to be removed. * @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 * @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. * 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}. * Adds the give {@link UserModel} as a member of the given {@link OrganizationModel}.
* *
* @param realm the realm
* @param organization the organization * @param organization the organization
* @param user the user * @param user the user
* @return {@code true} if the user was added as a member. Otherwise, returns {@code false} * @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. * Returns the organizations of the given realm as a stream.
* @param realm Realm.
* @return Stream of the organizations. Never returns {@code null}. * @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}. * Returns the members of a given {@code organization}.
* *
* @param realm the realm
* @param organization the organization * @param organization the organization
* @return the organization with the given {@code id} * @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}. * Returns the member of an {@code organization} by its {@code id}.
* *
* @param realm the realm
* @param organization the organization * @param organization the organization
* @param id the member id * @param id the member id
* @return the organization with the given {@code 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. * Returns the {@link OrganizationModel} that a {@code member} belongs to.
* *
* @param realm the realm
* @param member the member of a organization * @param member the member of a organization
* @return the organization the {@code member} belongs to * @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); void setName(String name);
String getName(); String getName();
RealmModel getRealm();
} }

View file

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

View file

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

View file

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