Add an endpoint to the organizations endpoint to return the organizations for a given user

Closes #32158

Signed-off-by: Pedro Igor <pigor.craveiro@gmail.com>
This commit is contained in:
Pedro Igor 2024-08-15 08:37:02 -03:00
parent b2999ada2d
commit 4376a3c757
6 changed files with 75 additions and 1 deletions

View file

@ -86,4 +86,9 @@ public interface OrganizationMembersResource {
@GET @GET
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
Long count(); Long count();
@Path("{id}/organizations")
@GET
@Produces(MediaType.APPLICATION_JSON)
List<OrganizationRepresentation> getOrganizations(@PathParam("id") String id);
} }

View file

@ -0,0 +1,36 @@
/*
* 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.admin.client.resource;
import java.util.List;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import org.keycloak.representations.idm.OrganizationRepresentation;
public interface OrganizationsMembersResource {
@Path("{id}/organizations")
@GET
@Produces(MediaType.APPLICATION_JSON)
List<OrganizationRepresentation> getOrganizations(@PathParam("id") String id);
}

View file

@ -105,4 +105,7 @@ public interface OrganizationsResource {
@QueryParam("first") Integer first, @QueryParam("first") Integer first,
@QueryParam("max") Integer max @QueryParam("max") Integer max
); );
@Path("members")
OrganizationsMembersResource members();
} }

View file

@ -190,7 +190,7 @@ public class OrganizationMemberResource {
throw ErrorResponse.error("id cannot be null", Status.BAD_REQUEST); throw ErrorResponse.error("id cannot be null", Status.BAD_REQUEST);
} }
UserModel member = getMember(id); UserModel member = getUser(id);
return provider.getByMember(member).map((org) -> { return provider.getByMember(member).map((org) -> {
OrganizationRepresentation organization = new OrganizationRepresentation(); OrganizationRepresentation organization = new OrganizationRepresentation();
@ -222,6 +222,16 @@ public class OrganizationMemberResource {
return member; return member;
} }
private UserModel getUser(String id) {
UserModel user = session.users().getUserById(realm, id);
if (user == null) {
throw new NotFoundException();
}
return user;
}
private MemberRepresentation toRepresentation(UserModel member) { private MemberRepresentation toRepresentation(UserModel member) {
MemberRepresentation result = new MemberRepresentation(ModelToRepresentation.toRepresentation(session, realm, member)); MemberRepresentation result = new MemberRepresentation(ModelToRepresentation.toRepresentation(session, realm, member));
result.setMembershipType(provider.isManagedMember(organization, member) ? MembershipType.MANAGED : MembershipType.UNMANAGED); result.setMembershipType(provider.isManagedMember(organization, member) ? MembershipType.MANAGED : MembershipType.UNMANAGED);

View file

@ -162,4 +162,14 @@ public class OrganizationsResource {
return new OrganizationResource(session, organizationModel, adminEvent); return new OrganizationResource(session, organizationModel, adminEvent);
} }
@Path("members/{id}/organizations")
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
@Tag(name = KeycloakOpenAPI.Admin.Tags.ORGANIZATIONS)
@Operation(summary = "Returns the organizations associated with the user that has the specified id")
public Stream<OrganizationRepresentation> getOrganizations(@PathParam("id") String id) {
return new OrganizationMemberResource(session, null, adminEvent).getOrganizations(id);
}
} }

View file

@ -115,10 +115,20 @@ public class OrganizationMemberTest extends AbstractOrganizationTest {
public void testGetMemberOrganization() { public void testGetMemberOrganization() {
OrganizationResource organization = testRealm().organizations().get(createOrganization().getId()); OrganizationResource organization = testRealm().organizations().get(createOrganization().getId());
UserRepresentation member = addMember(organization); UserRepresentation member = addMember(organization);
OrganizationRepresentation orgB = createOrganization("orgb");
testRealm().organizations().get(orgB.getId()).members().addMember(member.getId()).close();
OrganizationRepresentation expected = organization.toRepresentation(); OrganizationRepresentation expected = organization.toRepresentation();
List<OrganizationRepresentation> actual = organization.members().member(member.getId()).getOrganizations(); List<OrganizationRepresentation> actual = organization.members().member(member.getId()).getOrganizations();
assertNotNull(actual); assertNotNull(actual);
assertEquals(2, actual.size());
assertTrue(actual.stream().map(OrganizationRepresentation::getId).anyMatch(expected.getId()::equals)); 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());
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));
} }
@Test @Test