Resolve SA before resolving users from username or email

Closes #48592

Signed-off-by: Pedro Igor <pigor.craveiro@gmail.com>
This commit is contained in:
Pedro Igor
2026-05-07 02:32:43 -03:00
committed by GitHub
parent 8add9b0c00
commit 1ccce63aa4
2 changed files with 52 additions and 3 deletions
@@ -189,15 +189,22 @@ public class DefaultEvaluation implements Evaluation {
}
RealmModel realm = session.getContext().getRealm();
user = session.users().getUserById(realm, id);
if (Objects.isNull(user)) {
// in case the id references a service account
ClientModel client = realm.getClientById(id);
if (client != null) {
user = session.users().getServiceAccount(client);
}
}
if (Objects.isNull(user)) {
user = session.users().getUserByUsername(realm, id);
}
if (Objects.isNull(user)) {
user = session.users().getUserByEmail(realm, id);
}
if (Objects.isNull(user)) {
user = session.users().getServiceAccount(realm.getClientById(id));
}
}
cache.put(id, user);
@@ -50,6 +50,7 @@ import org.keycloak.common.Profile.Feature;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
@@ -674,6 +675,47 @@ public class PolicyEvaluationTest extends AbstractAuthzTest {
};
}
@Test
public void testResolveServiceAccountByClientId() {
testingClient.server().run(PolicyEvaluationTest::testResolveServiceAccountByClientId);
}
public static void testResolveServiceAccountByClientId(KeycloakSession session) {
RealmModel realm = session.realms().getRealmByName("authz-test");
session.getContext().setRealm(realm);
AuthorizationProvider authorization = session.getProvider(AuthorizationProvider.class);
ClientModel clientModel = session.clients().getClientByClientId(realm, "resource-server-test");
StoreFactory storeFactory = authorization.getStoreFactory();
ResourceServer resourceServer = storeFactory.getResourceServerStore().findByClient(clientModel);
// get the service account user and assign a realm role
UserModel serviceAccount = session.users().getServiceAccount(clientModel);
assertNotNull(serviceAccount);
RoleModel roleA = realm.getRole("role-a");
assertNotNull(roleA);
serviceAccount.grantRole(roleA);
// create a simple user policy (type doesn't matter, we test through the Realm interface)
UserPolicyRepresentation policyRepresentation = new UserPolicyRepresentation();
policyRepresentation.setName("testResolveServiceAccountByClientId");
policyRepresentation.addUser(serviceAccount.getId());
Policy policy = storeFactory.getPolicyStore().create(resourceServer, policyRepresentation);
DefaultEvaluation evaluation = createEvaluation(session, authorization, resourceServer, policy);
// resolve service account using the client's internal ID (the fix scenario)
Assertions.assertTrue(evaluation.getRealm().isUserInRealmRole(clientModel.getId(), "role-a"),
"Service account should be resolved by client internal ID");
// resolve service account using the service account username (regression check)
Assertions.assertTrue(evaluation.getRealm().isUserInRealmRole(serviceAccount.getUsername(), "role-a"),
"Service account should still be resolved by username");
// non-existent ID should not cause errors (NPE check)
Assertions.assertFalse(evaluation.getRealm().isUserInRealmRole("non-existent-id", "role-a"),
"Non-existent ID should return false without errors");
}
@Test
public void testEvaluation() {
RealmResource realmApi = realmsResouce().realm("authz-test");