mirror of
https://github.com/keycloak/keycloak.git
synced 2025-12-21 14:30:05 -06:00
Add briefRepresentation to get organizations from user
Allow asking for the full representation in `GET /admin/realms/{realm}/organizations/members/{member-id}/organizations`
Closes #40438
Signed-off-by: Alexis Rico <sferadev@gmail.com>
This commit is contained in:
@@ -20,9 +20,11 @@ package org.keycloak.admin.client.resource;
|
||||
import java.util.List;
|
||||
|
||||
import jakarta.ws.rs.DELETE;
|
||||
import jakarta.ws.rs.DefaultValue;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import org.keycloak.representations.idm.MemberRepresentation;
|
||||
@@ -46,5 +48,6 @@ public interface OrganizationMemberResource {
|
||||
@Path("organizations")
|
||||
@GET
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
List<OrganizationRepresentation> getOrganizations();
|
||||
List<OrganizationRepresentation> getOrganizations(
|
||||
@QueryParam("briefRepresentation") @DefaultValue("true") boolean briefRepresentation);
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import java.util.List;
|
||||
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.DELETE;
|
||||
import jakarta.ws.rs.DefaultValue;
|
||||
import jakarta.ws.rs.FormParam;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.POST;
|
||||
@@ -139,5 +140,7 @@ public interface OrganizationMembersResource {
|
||||
@Path("{id}/organizations")
|
||||
@GET
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
List<OrganizationRepresentation> getOrganizations(@PathParam("id") String id);
|
||||
List<OrganizationRepresentation> getOrganizations(
|
||||
@PathParam("id") String id,
|
||||
@QueryParam("briefRepresentation") @DefaultValue("true") boolean briefRepresentation);
|
||||
}
|
||||
|
||||
@@ -19,10 +19,12 @@ package org.keycloak.admin.client.resource;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import jakarta.ws.rs.DefaultValue;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import org.keycloak.representations.idm.OrganizationRepresentation;
|
||||
|
||||
@@ -31,5 +33,7 @@ public interface OrganizationsMembersResource {
|
||||
@Path("{id}/organizations")
|
||||
@GET
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
List<OrganizationRepresentation> getOrganizations(@PathParam("id") String id);
|
||||
List<OrganizationRepresentation> getOrganizations(
|
||||
@PathParam("id") String id,
|
||||
@QueryParam("briefRepresentation") @DefaultValue("true") boolean briefRepresentation);
|
||||
}
|
||||
|
||||
@@ -241,14 +241,18 @@ public class OrganizationMemberResource {
|
||||
@APIResponse(responseCode = "200", description = "", content = @Content(schema = @Schema(implementation = OrganizationRepresentation.class, type = SchemaType.ARRAY))),
|
||||
@APIResponse(responseCode = "400", description = "Bad Request")
|
||||
})
|
||||
public Stream<OrganizationRepresentation> getOrganizations(@PathParam("member-id") String memberId) {
|
||||
public Stream<OrganizationRepresentation> getOrganizations(
|
||||
@PathParam("member-id") String memberId,
|
||||
@Parameter(description = "if false, return the full representation. Otherwise, only the basic fields are returned.")
|
||||
@QueryParam("briefRepresentation") @DefaultValue("true") boolean briefRepresentation) {
|
||||
if (StringUtil.isBlank(memberId)) {
|
||||
throw ErrorResponse.error("id cannot be null", Status.BAD_REQUEST);
|
||||
}
|
||||
|
||||
UserModel member = getUser(memberId);
|
||||
|
||||
return provider.getByMember(member).map(ModelToRepresentation::toRepresentation);
|
||||
return provider.getByMember(member)
|
||||
.map(model -> ModelToRepresentation.toRepresentation(model, briefRepresentation));
|
||||
}
|
||||
|
||||
@Path("count")
|
||||
|
||||
@@ -222,7 +222,10 @@ public class OrganizationsResource {
|
||||
@APIResponse(responseCode = "200", description = "", content = @Content(schema = @Schema(implementation = OrganizationRepresentation.class, type = SchemaType.ARRAY))),
|
||||
@APIResponse(responseCode = "400", description = "Bad Request")
|
||||
})
|
||||
public Stream<OrganizationRepresentation> getOrganizations(@PathParam("member-id") String memberId) {
|
||||
return new OrganizationMemberResource(session, null, adminEvent).getOrganizations(memberId);
|
||||
public Stream<OrganizationRepresentation> getOrganizations(
|
||||
@PathParam("member-id") String memberId,
|
||||
@Parameter(description = "if false, return the full representation. Otherwise, only the basic fields are returned.")
|
||||
@QueryParam("briefRepresentation") @DefaultValue("true") boolean briefRepresentation) {
|
||||
return new OrganizationMemberResource(session, null, adminEvent).getOrganizations(memberId, briefRepresentation);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,13 +123,13 @@ public class OrganizationMemberTest extends AbstractOrganizationTest {
|
||||
OrganizationRepresentation orgB = createOrganization("orgb");
|
||||
testRealm().organizations().get(orgB.getId()).members().addMember(member.getId()).close();
|
||||
OrganizationRepresentation expected = organization.toRepresentation();
|
||||
List<OrganizationRepresentation> actual = organization.members().member(member.getId()).getOrganizations();
|
||||
List<OrganizationRepresentation> actual = organization.members().member(member.getId()).getOrganizations(true);
|
||||
assertNotNull(actual);
|
||||
assertEquals(2, actual.size());
|
||||
assertTrue(actual.stream().map(OrganizationRepresentation::getId).anyMatch(expected.getId()::equals));
|
||||
assertTrue(actual.stream().map(OrganizationRepresentation::getId).anyMatch(orgB.getId()::equals));
|
||||
|
||||
actual = testRealm().organizations().members().getOrganizations(member.getId());
|
||||
actual = testRealm().organizations().members().getOrganizations(member.getId(), true);
|
||||
assertNotNull(actual);
|
||||
assertEquals(2, actual.size());
|
||||
assertTrue(actual.stream().map(OrganizationRepresentation::getId).anyMatch(expected.getId()::equals));
|
||||
@@ -332,7 +332,7 @@ public class OrganizationMemberTest extends AbstractOrganizationTest {
|
||||
for (MemberRepresentation member : expected) {
|
||||
try {
|
||||
// user no longer bound to the organization
|
||||
organization.members().member(member.getId()).getOrganizations();
|
||||
organization.members().member(member.getId()).getOrganizations(true);
|
||||
fail("should not be associated with the organization anymore");
|
||||
} catch (NotFoundException ignore) {
|
||||
}
|
||||
@@ -507,7 +507,7 @@ public class OrganizationMemberTest extends AbstractOrganizationTest {
|
||||
Assert.assertTrue(orgb.members().list(-1, -1).stream().map(UserRepresentation::getId).anyMatch(member.getId()::equals));
|
||||
String orgbId = orgb.toRepresentation().getId();
|
||||
String orgaId = orga.toRepresentation().getId();
|
||||
List<String> memberOfOrgs = orga.members().member(member.getId()).getOrganizations().stream().map(OrganizationRepresentation::getId).toList();
|
||||
List<String> memberOfOrgs = orga.members().member(member.getId()).getOrganizations(true).stream().map(OrganizationRepresentation::getId).toList();
|
||||
assertTrue(memberOfOrgs.contains(orgaId));
|
||||
assertTrue(memberOfOrgs.contains(orgbId));
|
||||
}
|
||||
@@ -583,6 +583,59 @@ public class OrganizationMemberTest extends AbstractOrganizationTest {
|
||||
assertThat(updatedUser.getEmail(), is(nullValue()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetMemberOrganizationsBriefVsFullRepresentation() {
|
||||
// Create an organization with attributes
|
||||
OrganizationResource organization = testRealm().organizations().get(createOrganization().getId());
|
||||
OrganizationRepresentation orgRep = organization.toRepresentation();
|
||||
orgRep.singleAttribute("testAttribute", "testValue");
|
||||
organization.update(orgRep).close();
|
||||
|
||||
UserRepresentation member = addMember(organization);
|
||||
|
||||
// Test brief representation (default, briefRepresentation=true)
|
||||
List<OrganizationRepresentation> briefOrgs = organization.members().member(member.getId()).getOrganizations(true);
|
||||
assertNotNull(briefOrgs);
|
||||
assertEquals(1, briefOrgs.size());
|
||||
OrganizationRepresentation briefRep = briefOrgs.get(0);
|
||||
assertEquals(orgRep.getId(), briefRep.getId());
|
||||
assertEquals(orgRep.getName(), briefRep.getName());
|
||||
assertEquals(orgRep.getAlias(), briefRep.getAlias());
|
||||
assertEquals(orgRep.getDescription(), briefRep.getDescription());
|
||||
assertEquals(orgRep.getRedirectUrl(), briefRep.getRedirectUrl());
|
||||
assertEquals(orgRep.isEnabled(), briefRep.isEnabled());
|
||||
assertNull("Brief representation should not include attributes", briefRep.getAttributes());
|
||||
|
||||
// Test full representation (briefRepresentation=false)
|
||||
List<OrganizationRepresentation> fullOrgs = organization.members().member(member.getId()).getOrganizations(false);
|
||||
assertNotNull(fullOrgs);
|
||||
assertEquals(1, fullOrgs.size());
|
||||
OrganizationRepresentation fullRep = fullOrgs.get(0);
|
||||
assertEquals(orgRep.getId(), fullRep.getId());
|
||||
assertEquals(orgRep.getName(), fullRep.getName());
|
||||
assertEquals(orgRep.getAlias(), fullRep.getAlias());
|
||||
assertEquals(orgRep.getDescription(), fullRep.getDescription());
|
||||
assertEquals(orgRep.getRedirectUrl(), fullRep.getRedirectUrl());
|
||||
assertEquals(orgRep.isEnabled(), fullRep.isEnabled());
|
||||
assertNotNull("Full representation should include attributes", fullRep.getAttributes());
|
||||
assertTrue("Full representation should include the test attribute",
|
||||
fullRep.getAttributes().containsKey("testAttribute"));
|
||||
assertEquals("testValue", fullRep.getAttributes().get("testAttribute").get(0));
|
||||
|
||||
// Test the global members API endpoint as well
|
||||
List<OrganizationRepresentation> briefOrgsGlobal = testRealm().organizations().members().getOrganizations(member.getId(), true);
|
||||
assertNotNull(briefOrgsGlobal);
|
||||
assertEquals(1, briefOrgsGlobal.size());
|
||||
assertNull("Brief representation should not include attributes", briefOrgsGlobal.get(0).getAttributes());
|
||||
|
||||
List<OrganizationRepresentation> fullOrgsGlobal = testRealm().organizations().members().getOrganizations(member.getId(), false);
|
||||
assertNotNull(fullOrgsGlobal);
|
||||
assertEquals(1, fullOrgsGlobal.size());
|
||||
assertNotNull("Full representation should include attributes", fullOrgsGlobal.get(0).getAttributes());
|
||||
assertTrue("Full representation should include the test attribute",
|
||||
fullOrgsGlobal.get(0).getAttributes().containsKey("testAttribute"));
|
||||
}
|
||||
|
||||
private void loginViaNonOrgIdP(String idpAlias) {
|
||||
oauth.clientId("broker-app");
|
||||
loginPage.open(bc.consumerRealmName());
|
||||
|
||||
@@ -89,7 +89,7 @@ public class OrganizationMemberWithLdapTest extends AbstractOrganizationTest {
|
||||
try (Response response = organization.members().addMember(ldapUser.getId())) {
|
||||
assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatus());
|
||||
}
|
||||
List<OrganizationRepresentation> orgMemberships = organization.members().member(ldapUser.getId()).getOrganizations();
|
||||
List<OrganizationRepresentation> orgMemberships = organization.members().member(ldapUser.getId()).getOrganizations(true);
|
||||
assertThat(orgMemberships, notNullValue());
|
||||
assertThat(orgMemberships, hasSize(1));
|
||||
assertThat(orgMemberships.get(0).getId(), equalTo(orgRepresentation.getId()));
|
||||
|
||||
Reference in New Issue
Block a user