From dc9e52db4fa80e601cba24f293a1620f07a43a79 Mon Sep 17 00:00:00 2001 From: Pedro Igor Date: Mon, 4 May 2026 06:27:46 -0300 Subject: [PATCH] Enforce resource server predicates if one was given despite the IS_ADMIN flag (#48385) Closes #48324 Signed-off-by: Pedro Igor --- .../jpa/store/JPAPermissionTicketStore.java | 31 ++--- .../authz/PermissionManagementTest.java | 109 ++++++++++++++++++ 2 files changed, 126 insertions(+), 14 deletions(-) diff --git a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAPermissionTicketStore.java b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAPermissionTicketStore.java index a1a30158ff24..38bbc31eb593 100644 --- a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAPermissionTicketStore.java +++ b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAPermissionTicketStore.java @@ -87,24 +87,27 @@ private List getPredicates(CriteriaBuilder builder, List predicates = new ArrayList<>(); KeycloakSession session = provider.getKeycloakSession(); - if (resourceServer != null && !Boolean.parseBoolean(attributes.get(PermissionTicket.FilterOption.IS_ADMIN))) { + if (resourceServer != null) { predicates.add(builder.equal(root.get("resourceServer").get("id"), resourceServer.getId())); - ClientModel resourceServerClient = session.clients().getClientById(session.getContext().getRealm(), resourceServer.getClientId()); - UserModel currentUser = session.getContext().getUser(); - if (resourceServerClient.isServiceAccountsEnabled()) { - UserModel serviceAccount = session.users().getServiceAccount(resourceServerClient); + if (!Boolean.parseBoolean(attributes.get(PermissionTicket.FilterOption.IS_ADMIN))) { + ClientModel resourceServerClient = session.clients().getClientById(session.getContext().getRealm(), resourceServer.getClientId()); + UserModel currentUser = session.getContext().getUser(); - if (serviceAccount != null && serviceAccount.equals(currentUser)) { - currentUser = null; + if (resourceServerClient.isServiceAccountsEnabled()) { + UserModel serviceAccount = session.users().getServiceAccount(resourceServerClient); + + if (serviceAccount != null && serviceAccount.equals(currentUser)) { + currentUser = null; + } } - } - if (currentUser != null) { - predicates.add(builder.or( - builder.equal(root.get("owner"), currentUser.getId()), - builder.equal(root.get("requester"), currentUser.getId()) - )); + if (currentUser != null) { + predicates.add(builder.or( + builder.equal(root.get("owner"), currentUser.getId()), + builder.equal(root.get("requester"), currentUser.getId()) + )); + } } } @@ -190,7 +193,7 @@ public PermissionTicket findById(ResourceServer resourceServer, String id) { PermissionTicketEntity entity = entityManager.find(PermissionTicketEntity.class, id); - if (entity == null || !resourceServer.getId().equals(entity.getResourceServer().getId())) { + if (entity == null || (resourceServer != null && !resourceServer.getId().equals(entity.getResourceServer().getId()))) { return null; } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/PermissionManagementTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/PermissionManagementTest.java index de405316b1fa..57ebebc43cb4 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/PermissionManagementTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/PermissionManagementTest.java @@ -39,6 +39,7 @@ import org.keycloak.representations.idm.authorization.PermissionTicketToken; import org.keycloak.representations.idm.authorization.ResourceRepresentation; import org.keycloak.representations.idm.authorization.ScopeRepresentation; +import org.keycloak.testsuite.util.ClientBuilder; import org.junit.Test; @@ -511,4 +512,112 @@ public void testPermissionCount() throws Exception { Long ticketCount = getAuthzClient().protection().permission().count(resource.getId(), null, null, null, null, true); assertEquals("Returned number of permissions tickets must match the amount of permission tickets.", Long.valueOf(4), ticketCount); } + + @Test + public void testISAdminFilterRespectsResourceServer() { + // create a second resource server + getRealm().clients().create( + ClientBuilder.create() + .clientId("resource-server-test-2") + .secret("secret") + .authorizationServicesEnabled(true) + .redirectUris("http://localhost/resource-server-test-2") + .defaultRoles("uma_protection") + .directAccessGrants() + .serviceAccountsEnabled(true) + .build() + ).close(); + + testingClient.server().run(session -> { + org.keycloak.models.RealmModel realm = session.realms().getRealmByName("authz-test"); + session.getContext().setRealm(realm); + org.keycloak.authorization.AuthorizationProvider authorization = + session.getProvider(org.keycloak.authorization.AuthorizationProvider.class); + org.keycloak.authorization.store.StoreFactory storeFactory = authorization.getStoreFactory(); + org.keycloak.authorization.store.PermissionTicketStore ticketStore = + storeFactory.getPermissionTicketStore(); + + org.keycloak.models.ClientModel client1 = session.clients() + .getClientByClientId(realm, "resource-server-test"); + org.keycloak.authorization.model.ResourceServer rs1 = + storeFactory.getResourceServerStore().findByClient(client1); + + org.keycloak.models.ClientModel client2 = session.clients() + .getClientByClientId(realm, "resource-server-test-2"); + org.keycloak.authorization.model.ResourceServer rs2 = + storeFactory.getResourceServerStore().findByClient(client2); + + org.keycloak.authorization.model.Resource resource1 = + storeFactory.getResourceStore().create(rs1, "resource-rs1", client1.getId()); + org.keycloak.authorization.model.Resource resource2 = + storeFactory.getResourceStore().create(rs2, "resource-rs2", client2.getId()); + + String requester = session.users().getUserByUsername(realm, "marta").getId(); + + org.keycloak.authorization.model.PermissionTicket ticket1 = + ticketStore.create(rs1, resource1, null, requester); + ticket1.setGrantedTimestamp(System.currentTimeMillis()); + + org.keycloak.authorization.model.PermissionTicket ticket2 = + ticketStore.create(rs2, resource2, null, requester); + ticket2.setGrantedTimestamp(System.currentTimeMillis()); + + java.util.Map filters = + new java.util.EnumMap<>(org.keycloak.authorization.model.PermissionTicket.FilterOption.class); + filters.put(org.keycloak.authorization.model.PermissionTicket.FilterOption.IS_ADMIN, "true"); + filters.put(org.keycloak.authorization.model.PermissionTicket.FilterOption.GRANTED, "true"); + + java.util.List rs1Tickets = + ticketStore.find(rs1, filters, null, null); + java.util.List rs2Tickets = + ticketStore.find(rs2, filters, null, null); + + assertEquals(1, rs1Tickets.size()); + assertEquals(rs1.getId(), rs1Tickets.get(0).getResourceServer().getId()); + + assertEquals(1, rs2Tickets.size()); + assertEquals(rs2.getId(), rs2Tickets.get(0).getResourceServer().getId()); + }); + + testingClient.server().run(session -> { + org.keycloak.models.RealmModel realm = session.realms().getRealmByName("authz-test"); + session.getContext().setRealm(realm); + org.keycloak.authorization.AuthorizationProvider authorization = + session.getProvider(org.keycloak.authorization.AuthorizationProvider.class); + org.keycloak.authorization.store.StoreFactory storeFactory = authorization.getStoreFactory(); + org.keycloak.authorization.store.PermissionTicketStore ticketStore = + storeFactory.getPermissionTicketStore(); + org.keycloak.models.ClientModel client1 = session.clients() + .getClientByClientId(realm, "resource-server-test"); + org.keycloak.authorization.model.ResourceServer rs1 = + storeFactory.getResourceServerStore().findByClient(client1); + + org.keycloak.models.ClientModel client2 = session.clients() + .getClientByClientId(realm, "resource-server-test-2"); + org.keycloak.authorization.model.ResourceServer rs2 = + storeFactory.getResourceServerStore().findByClient(client2); + java.util.Map filters = + new java.util.EnumMap<>(org.keycloak.authorization.model.PermissionTicket.FilterOption.class); + filters.put(org.keycloak.authorization.model.PermissionTicket.FilterOption.IS_ADMIN, "true"); + filters.put(org.keycloak.authorization.model.PermissionTicket.FilterOption.GRANTED, "true"); + + java.util.List rs1Tickets = + ticketStore.find(rs1, filters, null, null); + java.util.List rs2Tickets = + ticketStore.find(rs2, filters, null, null); + + assertEquals(1, rs1Tickets.size()); + assertEquals(rs1.getId(), rs1Tickets.get(0).getResourceServer().getId()); + + assertEquals(1, rs2Tickets.size()); + assertEquals(rs2.getId(), rs2Tickets.get(0).getResourceServer().getId()); + + rs1Tickets = + ticketStore.find(null, filters, null, null); + assertEquals(2, rs1Tickets.size()); + rs2Tickets = + ticketStore.find(null, filters, null, null); + assertEquals(2, rs2Tickets.size()); + }); + } }