KEYCLOAK-1959 Role offline_access was effective only when explicitly added to user
This commit is contained in:
parent
802a39b1ce
commit
b4520baee5
2 changed files with 77 additions and 1 deletions
|
@ -44,6 +44,8 @@ import java.util.Arrays;
|
|||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
|
@ -325,6 +327,21 @@ public class TokenManager {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add all roles specified in scope parameter directly into requestedRoles, even if they are available just through composite role
|
||||
List<RoleModel> scopeRoles = new LinkedList<>();
|
||||
for (String scopeParamPart : scopeParamRoles) {
|
||||
RoleModel scopeParamRole = getRoleFromScopeParam(client.getRealm(), scopeParamPart);
|
||||
if (scopeParamRole != null) {
|
||||
for (RoleModel role : roles) {
|
||||
if (role.hasRole(scopeParamRole)) {
|
||||
scopeRoles.add(scopeParamRole);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
roles.addAll(scopeRoles);
|
||||
requestedRoles = roles;
|
||||
}
|
||||
|
||||
|
@ -341,6 +358,17 @@ public class TokenManager {
|
|||
}
|
||||
}
|
||||
|
||||
// For now, just use "roleName" for realm roles and "clientId/roleName" for client roles
|
||||
private static RoleModel getRoleFromScopeParam(RealmModel realm, String scopeParamRole) {
|
||||
String[] parts = scopeParamRole.split("/");
|
||||
if (parts.length == 1) {
|
||||
return realm.getRole(parts[0]);
|
||||
} else {
|
||||
ClientModel roleClient = realm.getClientByClientId(parts[0]);
|
||||
return roleClient!=null ? roleClient.getRole(parts[1]) : null;
|
||||
}
|
||||
}
|
||||
|
||||
public void verifyAccess(AccessToken token, AccessToken newToken) throws OAuthErrorException {
|
||||
if (token.getRealmAccess() != null) {
|
||||
if (newToken.getRealmAccess() == null) throw new OAuthErrorException(OAuthErrorException.INVALID_SCOPE, "User no long has permission for realm roles");
|
||||
|
|
|
@ -30,6 +30,7 @@ import org.keycloak.models.Constants;
|
|||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.protocol.oidc.TokenManager;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
import org.keycloak.representations.RefreshToken;
|
||||
import org.keycloak.services.managers.ClientManager;
|
||||
|
@ -285,7 +286,6 @@ public class OfflineTokenTest {
|
|||
|
||||
Assert.assertEquals(userId, refreshedToken.getSubject());
|
||||
|
||||
Assert.assertEquals(2, refreshedToken.getRealmAccess().getRoles().size());
|
||||
Assert.assertTrue(refreshedToken.getRealmAccess().isUserInRole("user"));
|
||||
Assert.assertTrue(refreshedToken.getRealmAccess().isUserInRole(Constants.OFFLINE_ACCESS_ROLE));
|
||||
|
||||
|
@ -384,6 +384,54 @@ public class OfflineTokenTest {
|
|||
testRefreshWithOfflineToken(token2, offlineToken2, offlineTokenString2, token2.getSessionState(), serviceAccountUserId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void offlineTokenAllowedWithCompositeRole() throws Exception {
|
||||
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
|
||||
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
ClientModel offlineClient = appRealm.getClientByClientId("offline-client");
|
||||
UserModel testUser = session.users().getUserByUsername("test-user@localhost", appRealm);
|
||||
RoleModel offlineAccess = appRealm.getRole(Constants.OFFLINE_ACCESS_ROLE);
|
||||
|
||||
// Test access
|
||||
Assert.assertFalse(TokenManager.getAccess(null, true, offlineClient, testUser).contains(offlineAccess));
|
||||
Assert.assertTrue(TokenManager.getAccess(OAuth2Constants.OFFLINE_ACCESS, true, offlineClient, testUser).contains(offlineAccess));
|
||||
|
||||
// Grant offline_access role indirectly through composite role
|
||||
RoleModel composite = appRealm.addRole("composite");
|
||||
composite.addCompositeRole(offlineAccess);
|
||||
|
||||
testUser.deleteRoleMapping(offlineAccess);
|
||||
testUser.grantRole(composite);
|
||||
|
||||
// Test access
|
||||
Assert.assertFalse(TokenManager.getAccess(null, true, offlineClient, testUser).contains(offlineAccess));
|
||||
Assert.assertTrue(TokenManager.getAccess(OAuth2Constants.OFFLINE_ACCESS, true, offlineClient, testUser).contains(offlineAccess));
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// Integration test
|
||||
offlineTokenDirectGrantFlow();
|
||||
|
||||
// Revert changes
|
||||
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
|
||||
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
RoleModel composite = appRealm.getRole("composite");
|
||||
RoleModel offlineAccess = appRealm.getRole(Constants.OFFLINE_ACCESS_ROLE);
|
||||
UserModel testUser = session.users().getUserByUsername("test-user@localhost", appRealm);
|
||||
|
||||
testUser.deleteRoleMapping(composite);
|
||||
appRealm.removeRole(composite);
|
||||
testUser.grantRole(offlineAccess);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testServlet() {
|
||||
OfflineTokenServlet.tokenInfo = null;
|
||||
|
|
Loading…
Reference in a new issue