Merge pull request #4377 from patriot1burke/master
token exchange permissions
This commit is contained in:
commit
cc9fb4c12e
13 changed files with 410 additions and 103 deletions
|
@ -101,7 +101,6 @@ public interface OAuth2Constants {
|
||||||
String REFRESH_TOKEN_TYPE="urn:ietf:params:oauth:token-type:refresh_token";
|
String REFRESH_TOKEN_TYPE="urn:ietf:params:oauth:token-type:refresh_token";
|
||||||
String JWT_TOKEN_TYPE="urn:ietf:params:oauth:token-type:jwt";
|
String JWT_TOKEN_TYPE="urn:ietf:params:oauth:token-type:jwt";
|
||||||
String ID_TOKEN_TYPE="urn:ietf:params:oauth:token-type:id_token";
|
String ID_TOKEN_TYPE="urn:ietf:params:oauth:token-type:id_token";
|
||||||
String TOKEN_EXCHANGER ="token-exchanger";
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,83 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2016 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.authorization.common;
|
||||||
|
|
||||||
|
import org.keycloak.authorization.attribute.Attributes;
|
||||||
|
import org.keycloak.authorization.identity.Identity;
|
||||||
|
import org.keycloak.common.util.MultivaluedHashMap;
|
||||||
|
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 java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class ClientModelIdentity implements Identity {
|
||||||
|
protected RealmModel realm;
|
||||||
|
protected ClientModel client;
|
||||||
|
protected UserModel serviceAccount;
|
||||||
|
|
||||||
|
public ClientModelIdentity(KeycloakSession session, ClientModel client) {
|
||||||
|
this.realm = client.getRealm();
|
||||||
|
this.client = client;
|
||||||
|
this.serviceAccount = session.users().getServiceAccount(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return client.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Attributes getAttributes() {
|
||||||
|
MultivaluedHashMap map = new MultivaluedHashMap<String, String>();
|
||||||
|
if (serviceAccount != null) map.addAll(serviceAccount.getAttributes());
|
||||||
|
return Attributes.from(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasRealmRole(String roleName) {
|
||||||
|
if (serviceAccount == null) return false;
|
||||||
|
RoleModel role = realm.getRole(roleName);
|
||||||
|
if (role == null) return false;
|
||||||
|
return serviceAccount.hasRole(role);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasClientRole(String clientId, String roleName) {
|
||||||
|
if (serviceAccount == null) return false;
|
||||||
|
ClientModel client = realm.getClientByClientId(clientId);
|
||||||
|
RoleModel role = client.getRole(roleName);
|
||||||
|
if (role == null) return false;
|
||||||
|
return serviceAccount.hasRole(role);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasRole(String roleName) {
|
||||||
|
throw new RuntimeException("Should not execute");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasClientRole(String roleName) {
|
||||||
|
throw new RuntimeException("Should not execute");
|
||||||
|
}
|
||||||
|
}
|
|
@ -683,6 +683,18 @@ public class TokenManager {
|
||||||
this.clientSession = clientSession;
|
this.clientSession = clientSession;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public AccessToken getAccessToken() {
|
||||||
|
return accessToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RefreshToken getRefreshToken() {
|
||||||
|
return refreshToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IDToken getIdToken() {
|
||||||
|
return idToken;
|
||||||
|
}
|
||||||
|
|
||||||
public AccessTokenResponseBuilder accessToken(AccessToken accessToken) {
|
public AccessTokenResponseBuilder accessToken(AccessToken accessToken) {
|
||||||
this.accessToken = accessToken;
|
this.accessToken = accessToken;
|
||||||
return this;
|
return this;
|
||||||
|
|
|
@ -593,45 +593,28 @@ public class TokenEndpoint {
|
||||||
throw new ErrorResponseException(OAuthErrorException.INVALID_CLIENT, "Client requires user consent", Response.Status.BAD_REQUEST);
|
throw new ErrorResponseException(OAuthErrorException.INVALID_CLIENT, "Client requires user consent", Response.Status.BAD_REQUEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean allowed = false;
|
boolean exchangeFromAllowed = false;
|
||||||
UserModel serviceAccount = session.users().getServiceAccount(client);
|
|
||||||
if (serviceAccount != null) {
|
|
||||||
if (authResult.getToken().getAudience() == null) {
|
|
||||||
logger.debug("Client doesn't have service account");
|
|
||||||
}
|
|
||||||
boolean tokenAllowed = false;
|
|
||||||
for (String aud : authResult.getToken().getAudience()) {
|
for (String aud : authResult.getToken().getAudience()) {
|
||||||
ClientModel audClient = realm.getClientByClientId(aud);
|
ClientModel audClient = realm.getClientByClientId(aud);
|
||||||
if (audClient == null) continue;
|
if (audClient == null) continue;
|
||||||
if (audClient.equals(client)) {
|
if (audClient.equals(client)) {
|
||||||
tokenAllowed = true;
|
exchangeFromAllowed = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
RoleModel audExchanger = audClient.getRole(OAuth2Constants.TOKEN_EXCHANGER);
|
if (AdminPermissions.management(session, realm).clients().canExchangeFrom(client, audClient)) {
|
||||||
if (audExchanger != null && serviceAccount.hasRole(audExchanger)) {
|
exchangeFromAllowed = true;
|
||||||
tokenAllowed = true;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!tokenAllowed) {
|
if (!exchangeFromAllowed) {
|
||||||
logger.debug("Client does not have exchange rights for audience of token");
|
logger.debug("Client does not have exchange rights for audience of provided token");
|
||||||
} else {
|
event.error(Errors.NOT_ALLOWED);
|
||||||
RoleModel targetExchangable = targetClient.getRole(OAuth2Constants.TOKEN_EXCHANGER);
|
throw new ErrorResponseException(OAuthErrorException.ACCESS_DENIED, "Client not allowed to exchange", Response.Status.FORBIDDEN);
|
||||||
RoleModel realmExchangeable = AdminPermissions.management(session, realm).getRealmManagementClient().getRole(OAuth2Constants.TOKEN_EXCHANGER);
|
}
|
||||||
allowed = (targetExchangable != null && serviceAccount.hasRole(targetExchangable)) || (realmExchangeable != null && serviceAccount.hasRole(realmExchangeable));
|
if (!AdminPermissions.management(session, realm).clients().canExchangeTo(client, targetClient)) {
|
||||||
if (!allowed) {
|
logger.debug("Client does not have exchange rights for target audience");
|
||||||
logger.debug("Client does not have exchange rights for target audience");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
logger.debug("Client doesn't have service account");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!allowed) {
|
|
||||||
event.error(Errors.NOT_ALLOWED);
|
event.error(Errors.NOT_ALLOWED);
|
||||||
throw new ErrorResponseException(OAuthErrorException.ACCESS_DENIED, "Client not allowed to exchange", Response.Status.FORBIDDEN);
|
throw new ErrorResponseException(OAuthErrorException.ACCESS_DENIED, "Client not allowed to exchange", Response.Status.FORBIDDEN);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AuthenticationSessionModel authSession = new AuthenticationSessionManager(session).createAuthenticationSession(realm, targetClient, false);
|
AuthenticationSessionModel authSession = new AuthenticationSessionManager(session).createAuthenticationSession(realm, targetClient, false);
|
||||||
|
@ -656,6 +639,8 @@ public class TokenEndpoint {
|
||||||
TokenManager.AccessTokenResponseBuilder responseBuilder = tokenManager.responseBuilder(realm, targetClient, event, session, userSession, clientSession)
|
TokenManager.AccessTokenResponseBuilder responseBuilder = tokenManager.responseBuilder(realm, targetClient, event, session, userSession, clientSession)
|
||||||
.generateAccessToken()
|
.generateAccessToken()
|
||||||
.generateRefreshToken();
|
.generateRefreshToken();
|
||||||
|
responseBuilder.getAccessToken().issuedFor(client.getClientId());
|
||||||
|
responseBuilder.getRefreshToken().issuedFor(client.getClientId());
|
||||||
|
|
||||||
String scopeParam = clientSession.getNote(OAuth2Constants.SCOPE);
|
String scopeParam = clientSession.getNote(OAuth2Constants.SCOPE);
|
||||||
if (TokenUtil.isOIDCRequest(scopeParam)) {
|
if (TokenUtil.isOIDCRequest(scopeParam)) {
|
||||||
|
|
|
@ -27,6 +27,8 @@ import org.keycloak.models.ClientModel;
|
||||||
public interface AdminPermissionManagement {
|
public interface AdminPermissionManagement {
|
||||||
public static final String MANAGE_SCOPE = "manage";
|
public static final String MANAGE_SCOPE = "manage";
|
||||||
public static final String VIEW_SCOPE = "view";
|
public static final String VIEW_SCOPE = "view";
|
||||||
|
public static final String EXCHANGE_FROM_SCOPE="exchange-from";
|
||||||
|
public static final String EXCHANGE_TO_SCOPE="exchange-to";
|
||||||
|
|
||||||
ClientModel getRealmManagementClient();
|
ClientModel getRealmManagementClient();
|
||||||
|
|
||||||
|
|
|
@ -41,6 +41,14 @@ public interface ClientPermissionManagement {
|
||||||
|
|
||||||
Map<String, String> getPermissions(ClientModel client);
|
Map<String, String> getPermissions(ClientModel client);
|
||||||
|
|
||||||
|
boolean canExchangeFrom(ClientModel authorizedClient, ClientModel from);
|
||||||
|
|
||||||
|
boolean canExchangeTo(ClientModel authorizedClient, ClientModel to);
|
||||||
|
|
||||||
|
Policy exchangeFromPermission(ClientModel client);
|
||||||
|
|
||||||
|
Policy exchangeToPermission(ClientModel client);
|
||||||
|
|
||||||
Policy mapRolesPermission(ClientModel client);
|
Policy mapRolesPermission(ClientModel client);
|
||||||
|
|
||||||
Policy mapRolesClientScopePermission(ClientModel client);
|
Policy mapRolesClientScopePermission(ClientModel client);
|
||||||
|
|
|
@ -18,23 +18,35 @@ package org.keycloak.services.resources.admin.permissions;
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
import org.keycloak.authorization.AuthorizationProvider;
|
import org.keycloak.authorization.AuthorizationProvider;
|
||||||
|
import org.keycloak.authorization.attribute.Attributes;
|
||||||
|
import org.keycloak.authorization.common.ClientModelIdentity;
|
||||||
|
import org.keycloak.authorization.common.DefaultEvaluationContext;
|
||||||
|
import org.keycloak.authorization.identity.Identity;
|
||||||
import org.keycloak.authorization.model.Policy;
|
import org.keycloak.authorization.model.Policy;
|
||||||
import org.keycloak.authorization.model.Resource;
|
import org.keycloak.authorization.model.Resource;
|
||||||
import org.keycloak.authorization.model.ResourceServer;
|
import org.keycloak.authorization.model.ResourceServer;
|
||||||
import org.keycloak.authorization.model.Scope;
|
import org.keycloak.authorization.model.Scope;
|
||||||
|
import org.keycloak.authorization.policy.evaluation.EvaluationContext;
|
||||||
import org.keycloak.models.AdminRoles;
|
import org.keycloak.models.AdminRoles;
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
import org.keycloak.models.ClientTemplateModel;
|
import org.keycloak.models.ClientTemplateModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.RoleModel;
|
import org.keycloak.models.RoleModel;
|
||||||
|
import org.keycloak.representations.AccessToken;
|
||||||
import org.keycloak.services.ForbiddenException;
|
import org.keycloak.services.ForbiddenException;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static org.keycloak.services.resources.admin.permissions.AdminPermissionManagement.EXCHANGE_FROM_SCOPE;
|
||||||
|
import static org.keycloak.services.resources.admin.permissions.AdminPermissionManagement.EXCHANGE_TO_SCOPE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages default policies for all users.
|
* Manages default policies for all users.
|
||||||
*
|
*
|
||||||
|
@ -79,6 +91,14 @@ class ClientPermissions implements ClientPermissionEvaluator, ClientPermissionMa
|
||||||
return MAP_ROLES_COMPOSITE_SCOPE + ".permission.client." + client.getId();
|
return MAP_ROLES_COMPOSITE_SCOPE + ".permission.client." + client.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String getExchangeToPermissionName(ClientModel client) {
|
||||||
|
return EXCHANGE_TO_SCOPE + ".permission.client." + client.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getExchangeFromPermissionName(ClientModel client) {
|
||||||
|
return EXCHANGE_FROM_SCOPE + ".permission.client." + client.getId();
|
||||||
|
}
|
||||||
|
|
||||||
private void initialize(ClientModel client) {
|
private void initialize(ClientModel client) {
|
||||||
ResourceServer server = root.findOrCreateResourceServer(client);
|
ResourceServer server = root.findOrCreateResourceServer(client);
|
||||||
Scope manageScope = manageScope(server);
|
Scope manageScope = manageScope(server);
|
||||||
|
@ -93,18 +113,11 @@ class ClientPermissions implements ClientPermissionEvaluator, ClientPermissionMa
|
||||||
if (mapRoleScope == null) {
|
if (mapRoleScope == null) {
|
||||||
mapRoleScope = authz.getStoreFactory().getScopeStore().create(MAP_ROLES_SCOPE, server);
|
mapRoleScope = authz.getStoreFactory().getScopeStore().create(MAP_ROLES_SCOPE, server);
|
||||||
}
|
}
|
||||||
Scope mapRoleClientScope = authz.getStoreFactory().getScopeStore().findByName(MAP_ROLES_CLIENT_SCOPE, server.getId());
|
Scope mapRoleClientScope = root.initializeScope(MAP_ROLES_CLIENT_SCOPE, server);
|
||||||
if (mapRoleClientScope == null) {
|
Scope mapRoleCompositeScope = root.initializeScope(MAP_ROLES_COMPOSITE_SCOPE, server);
|
||||||
mapRoleClientScope = authz.getStoreFactory().getScopeStore().create(MAP_ROLES_CLIENT_SCOPE, server);
|
Scope configureScope = root.initializeScope(CONFIGURE_SCOPE, server);
|
||||||
}
|
Scope exchangeFromScope = root.initializeScope(EXCHANGE_FROM_SCOPE, server);
|
||||||
Scope mapRoleCompositeScope = authz.getStoreFactory().getScopeStore().findByName(MAP_ROLES_COMPOSITE_SCOPE, server.getId());
|
Scope exchangeToScope = root.initializeScope(EXCHANGE_TO_SCOPE, server);
|
||||||
if (mapRoleCompositeScope == null) {
|
|
||||||
mapRoleCompositeScope = authz.getStoreFactory().getScopeStore().create(MAP_ROLES_COMPOSITE_SCOPE, server);
|
|
||||||
}
|
|
||||||
Scope configureScope = authz.getStoreFactory().getScopeStore().findByName(CONFIGURE_SCOPE, server.getId());
|
|
||||||
if (configureScope == null) {
|
|
||||||
configureScope = authz.getStoreFactory().getScopeStore().create(CONFIGURE_SCOPE, server);
|
|
||||||
}
|
|
||||||
|
|
||||||
String resourceName = getResourceName(client);
|
String resourceName = getResourceName(client);
|
||||||
Resource resource = authz.getStoreFactory().getResourceStore().findByName(resourceName, server.getId());
|
Resource resource = authz.getStoreFactory().getResourceStore().findByName(resourceName, server.getId());
|
||||||
|
@ -118,6 +131,8 @@ class ClientPermissions implements ClientPermissionEvaluator, ClientPermissionMa
|
||||||
scopeset.add(mapRoleScope);
|
scopeset.add(mapRoleScope);
|
||||||
scopeset.add(mapRoleClientScope);
|
scopeset.add(mapRoleClientScope);
|
||||||
scopeset.add(mapRoleCompositeScope);
|
scopeset.add(mapRoleCompositeScope);
|
||||||
|
scopeset.add(exchangeFromScope);
|
||||||
|
scopeset.add(exchangeToScope);
|
||||||
resource.updateScopes(scopeset);
|
resource.updateScopes(scopeset);
|
||||||
}
|
}
|
||||||
String managePermissionName = getManagePermissionName(client);
|
String managePermissionName = getManagePermissionName(client);
|
||||||
|
@ -150,6 +165,16 @@ class ClientPermissions implements ClientPermissionEvaluator, ClientPermissionMa
|
||||||
if (mapRoleCompositePermission == null) {
|
if (mapRoleCompositePermission == null) {
|
||||||
Helper.addEmptyScopePermission(authz, server, mapRoleCompositePermissionName, resource, mapRoleCompositeScope);
|
Helper.addEmptyScopePermission(authz, server, mapRoleCompositePermissionName, resource, mapRoleCompositeScope);
|
||||||
}
|
}
|
||||||
|
String exchangeToPermissionName = getExchangeToPermissionName(client);
|
||||||
|
Policy exchangeToPermission = authz.getStoreFactory().getPolicyStore().findByName(exchangeToPermissionName, server.getId());
|
||||||
|
if (exchangeToPermission == null) {
|
||||||
|
Helper.addEmptyScopePermission(authz, server, exchangeToPermissionName, resource, exchangeToScope);
|
||||||
|
}
|
||||||
|
String exchangeFromPermissionName = getExchangeFromPermissionName(client);
|
||||||
|
Policy exchangeFromPermission = authz.getStoreFactory().getPolicyStore().findByName(exchangeFromPermissionName, server.getId());
|
||||||
|
if (exchangeFromPermission == null) {
|
||||||
|
Helper.addEmptyScopePermission(authz, server, exchangeFromPermissionName, resource, exchangeFromScope);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void deletePolicy(String name, ResourceServer server) {
|
private void deletePolicy(String name, ResourceServer server) {
|
||||||
|
@ -169,6 +194,8 @@ class ClientPermissions implements ClientPermissionEvaluator, ClientPermissionMa
|
||||||
deletePolicy(getMapRolesClientScopePermissionName(client), server);
|
deletePolicy(getMapRolesClientScopePermissionName(client), server);
|
||||||
deletePolicy(getMapRolesCompositePermissionName(client), server);
|
deletePolicy(getMapRolesCompositePermissionName(client), server);
|
||||||
deletePolicy(getConfigurePermissionName(client), server);
|
deletePolicy(getConfigurePermissionName(client), server);
|
||||||
|
deletePolicy(getExchangeToPermissionName(client), server);
|
||||||
|
deletePolicy(getExchangeFromPermissionName(client), server);
|
||||||
Resource resource = authz.getStoreFactory().getResourceStore().findByName(getResourceName(client), server.getId());;
|
Resource resource = authz.getStoreFactory().getResourceStore().findByName(getResourceName(client), server.getId());;
|
||||||
if (resource != null) authz.getStoreFactory().getResourceStore().delete(resource.getId());
|
if (resource != null) authz.getStoreFactory().getResourceStore().delete(resource.getId());
|
||||||
}
|
}
|
||||||
|
@ -196,6 +223,14 @@ class ClientPermissions implements ClientPermissionEvaluator, ClientPermissionMa
|
||||||
return authz.getStoreFactory().getScopeStore().findByName(AdminPermissionManagement.MANAGE_SCOPE, server.getId());
|
return authz.getStoreFactory().getScopeStore().findByName(AdminPermissionManagement.MANAGE_SCOPE, server.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Scope exchangeFromScope(ResourceServer server) {
|
||||||
|
return authz.getStoreFactory().getScopeStore().findByName(EXCHANGE_FROM_SCOPE, server.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Scope exchangeToScope(ResourceServer server) {
|
||||||
|
return authz.getStoreFactory().getScopeStore().findByName(EXCHANGE_TO_SCOPE, server.getId());
|
||||||
|
}
|
||||||
|
|
||||||
private Scope configureScope(ResourceServer server) {
|
private Scope configureScope(ResourceServer server) {
|
||||||
return authz.getStoreFactory().getScopeStore().findByName(CONFIGURE_SCOPE, server.getId());
|
return authz.getStoreFactory().getScopeStore().findByName(CONFIGURE_SCOPE, server.getId());
|
||||||
}
|
}
|
||||||
|
@ -271,16 +306,119 @@ class ClientPermissions implements ClientPermissionEvaluator, ClientPermissionMa
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, String> getPermissions(ClientModel client) {
|
public Map<String, String> getPermissions(ClientModel client) {
|
||||||
Map<String, String> scopes = new HashMap<>();
|
initialize(client);
|
||||||
scopes.put(MAP_ROLES_SCOPE, mapRolesPermission(client).getId());
|
Map<String, String> scopes = new LinkedHashMap<>();
|
||||||
scopes.put(MAP_ROLES_CLIENT_SCOPE, mapRolesClientScopePermission(client).getId());
|
|
||||||
scopes.put(MAP_ROLES_COMPOSITE_SCOPE, mapRolesCompositePermission(client).getId());
|
|
||||||
scopes.put(AdminPermissionManagement.VIEW_SCOPE, viewPermission(client).getId());
|
scopes.put(AdminPermissionManagement.VIEW_SCOPE, viewPermission(client).getId());
|
||||||
scopes.put(AdminPermissionManagement.MANAGE_SCOPE, managePermission(client).getId());
|
scopes.put(AdminPermissionManagement.MANAGE_SCOPE, managePermission(client).getId());
|
||||||
scopes.put(CONFIGURE_SCOPE, configurePermission(client).getId());
|
scopes.put(CONFIGURE_SCOPE, configurePermission(client).getId());
|
||||||
|
scopes.put(MAP_ROLES_SCOPE, mapRolesPermission(client).getId());
|
||||||
|
scopes.put(MAP_ROLES_CLIENT_SCOPE, mapRolesClientScopePermission(client).getId());
|
||||||
|
scopes.put(MAP_ROLES_COMPOSITE_SCOPE, mapRolesCompositePermission(client).getId());
|
||||||
|
scopes.put(EXCHANGE_FROM_SCOPE, exchangeFromPermission(client).getId());
|
||||||
|
scopes.put(EXCHANGE_TO_SCOPE, exchangeToPermission(client).getId());
|
||||||
return scopes;
|
return scopes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canExchangeFrom(ClientModel authorizedClient, ClientModel from) {
|
||||||
|
if (!authorizedClient.equals(from)) {
|
||||||
|
ResourceServer server = resourceServer(from);
|
||||||
|
if (server == null) {
|
||||||
|
logger.debug("No resource server set up for target client");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Resource resource = authz.getStoreFactory().getResourceStore().findByName(getResourceName(from), server.getId());
|
||||||
|
if (resource == null) {
|
||||||
|
logger.debug("No resource object set up for target client");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Policy policy = authz.getStoreFactory().getPolicyStore().findByName(getExchangeFromPermissionName(from), server.getId());
|
||||||
|
if (policy == null) {
|
||||||
|
logger.debug("No permission object set up for target client");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<Policy> associatedPolicies = policy.getAssociatedPolicies();
|
||||||
|
// if no policies attached to permission then just do default behavior
|
||||||
|
if (associatedPolicies == null || associatedPolicies.isEmpty()) {
|
||||||
|
logger.debug("No policies set up for permission on target client");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Scope scope = exchangeFromScope(server);
|
||||||
|
if (scope == null) {
|
||||||
|
logger.debug(EXCHANGE_FROM_SCOPE + " not initialized");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ClientModelIdentity identity = new ClientModelIdentity(session, authorizedClient);
|
||||||
|
EvaluationContext context = new DefaultEvaluationContext(identity, session) {
|
||||||
|
@Override
|
||||||
|
public Map<String, Collection<String>> getBaseAttributes() {
|
||||||
|
Map<String, Collection<String>> attributes = super.getBaseAttributes();
|
||||||
|
attributes.put("kc.client.id", Arrays.asList(authorizedClient.getClientId()));
|
||||||
|
return attributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
return root.evaluatePermission(resource, scope, server, context);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canExchangeTo(ClientModel authorizedClient, ClientModel to) {
|
||||||
|
|
||||||
|
if (!authorizedClient.equals(to)) {
|
||||||
|
ResourceServer server = resourceServer(to);
|
||||||
|
if (server == null) {
|
||||||
|
logger.debug("No resource server set up for target client");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Resource resource = authz.getStoreFactory().getResourceStore().findByName(getResourceName(to), server.getId());
|
||||||
|
if (resource == null) {
|
||||||
|
logger.debug("No resource object set up for target client");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Policy policy = authz.getStoreFactory().getPolicyStore().findByName(getExchangeToPermissionName(to), server.getId());
|
||||||
|
if (policy == null) {
|
||||||
|
logger.debug("No permission object set up for target client");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<Policy> associatedPolicies = policy.getAssociatedPolicies();
|
||||||
|
// if no policies attached to permission then just do default behavior
|
||||||
|
if (associatedPolicies == null || associatedPolicies.isEmpty()) {
|
||||||
|
logger.debug("No policies set up for permission on target client");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Scope scope = exchangeToScope(server);
|
||||||
|
if (scope == null) {
|
||||||
|
logger.debug(EXCHANGE_TO_SCOPE + " not initialized");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ClientModelIdentity identity = new ClientModelIdentity(session, authorizedClient);
|
||||||
|
EvaluationContext context = new DefaultEvaluationContext(identity, session) {
|
||||||
|
@Override
|
||||||
|
public Map<String, Collection<String>> getBaseAttributes() {
|
||||||
|
Map<String, Collection<String>> attributes = super.getBaseAttributes();
|
||||||
|
attributes.put("kc.client.id", Arrays.asList(authorizedClient.getClientId()));
|
||||||
|
return attributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
return root.evaluatePermission(resource, scope, server, context);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canManage(ClientModel client) {
|
public boolean canManage(ClientModel client) {
|
||||||
|
@ -463,6 +601,20 @@ class ClientPermissions implements ClientPermissionEvaluator, ClientPermissionMa
|
||||||
return root.evaluatePermission(resource, scope, server);
|
return root.evaluatePermission(resource, scope, server);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Policy exchangeFromPermission(ClientModel client) {
|
||||||
|
ResourceServer server = resourceServer(client);
|
||||||
|
if (server == null) return null;
|
||||||
|
return authz.getStoreFactory().getPolicyStore().findByName(getExchangeFromPermissionName(client), server.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Policy exchangeToPermission(ClientModel client) {
|
||||||
|
ResourceServer server = resourceServer(client);
|
||||||
|
if (server == null) return null;
|
||||||
|
return authz.getStoreFactory().getPolicyStore().findByName(getExchangeToPermissionName(client), server.getId());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Policy mapRolesPermission(ClientModel client) {
|
public Policy mapRolesPermission(ClientModel client) {
|
||||||
ResourceServer server = resourceServer(client);
|
ResourceServer server = resourceServer(client);
|
||||||
|
|
|
@ -31,6 +31,7 @@ import org.keycloak.services.ForbiddenException;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
@ -242,11 +243,12 @@ class GroupPermissions implements GroupPermissionEvaluator, GroupPermissionManag
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, String> getPermissions(GroupModel group) {
|
public Map<String, String> getPermissions(GroupModel group) {
|
||||||
Map<String, String> scopes = new HashMap<>();
|
initialize(group);
|
||||||
|
Map<String, String> scopes = new LinkedHashMap<>();
|
||||||
scopes.put(AdminPermissionManagement.VIEW_SCOPE, viewPermission(group).getId());
|
scopes.put(AdminPermissionManagement.VIEW_SCOPE, viewPermission(group).getId());
|
||||||
scopes.put(AdminPermissionManagement.MANAGE_SCOPE, managePermission(group).getId());
|
scopes.put(AdminPermissionManagement.MANAGE_SCOPE, managePermission(group).getId());
|
||||||
scopes.put(MANAGE_MEMBERS_SCOPE, manageMembersPermission(group).getId());
|
|
||||||
scopes.put(VIEW_MEMBERS_SCOPE, viewMembersPermission(group).getId());
|
scopes.put(VIEW_MEMBERS_SCOPE, viewMembersPermission(group).getId());
|
||||||
|
scopes.put(MANAGE_MEMBERS_SCOPE, manageMembersPermission(group).getId());
|
||||||
scopes.put(MANAGE_MEMBERSHIP_SCOPE, manageMembershipPermission(group).getId());
|
scopes.put(MANAGE_MEMBERSHIP_SCOPE, manageMembershipPermission(group).getId());
|
||||||
return scopes;
|
return scopes;
|
||||||
}
|
}
|
||||||
|
|
|
@ -277,6 +277,14 @@ class MgmtPermissions implements AdminPermissionEvaluator, AdminPermissionManage
|
||||||
return scope;
|
return scope;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Scope initializeScope(String name, ResourceServer server) {
|
||||||
|
Scope scope = authz.getStoreFactory().getScopeStore().findByName(name, server.getId());
|
||||||
|
if (scope == null) {
|
||||||
|
scope = authz.getStoreFactory().getScopeStore().create(name, server);
|
||||||
|
}
|
||||||
|
return scope;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public Scope realmManageScope() {
|
public Scope realmManageScope() {
|
||||||
|
@ -307,10 +315,14 @@ class MgmtPermissions implements AdminPermissionEvaluator, AdminPermissionManage
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean evaluatePermission(Resource resource, Scope scope, ResourceServer resourceServer, Identity identity) {
|
public boolean evaluatePermission(Resource resource, Scope scope, ResourceServer resourceServer, Identity identity) {
|
||||||
|
EvaluationContext context = new DefaultEvaluationContext(identity, session);
|
||||||
|
return evaluatePermission(resource, scope, resourceServer, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean evaluatePermission(Resource resource, Scope scope, ResourceServer resourceServer, EvaluationContext context) {
|
||||||
RealmModel oldRealm = session.getContext().getRealm();
|
RealmModel oldRealm = session.getContext().getRealm();
|
||||||
try {
|
try {
|
||||||
session.getContext().setRealm(realm);
|
session.getContext().setRealm(realm);
|
||||||
EvaluationContext context = new DefaultEvaluationContext(identity, session);
|
|
||||||
DecisionResult decisionCollector = new DecisionResult();
|
DecisionResult decisionCollector = new DecisionResult();
|
||||||
List<ResourcePermission> permissions = Permissions.permission(resourceServer, resource, scope);
|
List<ResourcePermission> permissions = Permissions.permission(resourceServer, resource, scope);
|
||||||
PermissionEvaluator from = authz.evaluators().from(permissions, context);
|
PermissionEvaluator from = authz.evaluators().from(permissions, context);
|
||||||
|
|
|
@ -36,6 +36,7 @@ import org.keycloak.services.ForbiddenException;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
@ -87,7 +88,8 @@ class RolePermissions implements RolePermissionEvaluator, RolePermissionManageme
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, String> getPermissions(RoleModel role) {
|
public Map<String, String> getPermissions(RoleModel role) {
|
||||||
Map<String, String> scopes = new HashMap<>();
|
initialize(role);
|
||||||
|
Map<String, String> scopes = new LinkedHashMap<>();
|
||||||
scopes.put(RolePermissionManagement.MAP_ROLE_SCOPE, mapRolePermission(role).getId());
|
scopes.put(RolePermissionManagement.MAP_ROLE_SCOPE, mapRolePermission(role).getId());
|
||||||
scopes.put(RolePermissionManagement.MAP_ROLE_CLIENT_SCOPE_SCOPE, mapClientScopePermission(role).getId());
|
scopes.put(RolePermissionManagement.MAP_ROLE_CLIENT_SCOPE_SCOPE, mapClientScopePermission(role).getId());
|
||||||
scopes.put(RolePermissionManagement.MAP_ROLE_COMPOSITE_SCOPE, mapCompositePermission(role).getId());
|
scopes.put(RolePermissionManagement.MAP_ROLE_COMPOSITE_SCOPE, mapCompositePermission(role).getId());
|
||||||
|
|
|
@ -34,6 +34,7 @@ import org.keycloak.services.ForbiddenException;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
@ -121,9 +122,10 @@ class UserPermissions implements UserPermissionEvaluator, UserPermissionManageme
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, String> getPermissions() {
|
public Map<String, String> getPermissions() {
|
||||||
Map<String, String> scopes = new HashMap<>();
|
initialize();
|
||||||
scopes.put(AdminPermissionManagement.MANAGE_SCOPE, managePermission().getId());
|
Map<String, String> scopes = new LinkedHashMap<>();
|
||||||
scopes.put(AdminPermissionManagement.VIEW_SCOPE, viewPermission().getId());
|
scopes.put(AdminPermissionManagement.VIEW_SCOPE, viewPermission().getId());
|
||||||
|
scopes.put(AdminPermissionManagement.MANAGE_SCOPE, managePermission().getId());
|
||||||
scopes.put(MAP_ROLES_SCOPE, mapRolesPermission().getId());
|
scopes.put(MAP_ROLES_SCOPE, mapRolesPermission().getId());
|
||||||
scopes.put(MANAGE_GROUP_MEMBERSHIP_SCOPE, manageGroupMembershipPermission().getId());
|
scopes.put(MANAGE_GROUP_MEMBERSHIP_SCOPE, manageGroupMembershipPermission().getId());
|
||||||
scopes.put(IMPERSONATE_SCOPE, adminImpersonatingPermission().getId());
|
scopes.put(IMPERSONATE_SCOPE, adminImpersonatingPermission().getId());
|
||||||
|
|
|
@ -23,6 +23,8 @@ import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.keycloak.OAuth2Constants;
|
import org.keycloak.OAuth2Constants;
|
||||||
import org.keycloak.TokenVerifier;
|
import org.keycloak.TokenVerifier;
|
||||||
|
import org.keycloak.authorization.model.Policy;
|
||||||
|
import org.keycloak.authorization.model.ResourceServer;
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
|
@ -32,6 +34,9 @@ import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||||
import org.keycloak.representations.AccessToken;
|
import org.keycloak.representations.AccessToken;
|
||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
|
import org.keycloak.representations.idm.authorization.ClientPolicyRepresentation;
|
||||||
|
import org.keycloak.representations.idm.authorization.UserPolicyRepresentation;
|
||||||
|
import org.keycloak.services.resources.admin.permissions.AdminPermissionManagement;
|
||||||
import org.keycloak.services.resources.admin.permissions.AdminPermissions;
|
import org.keycloak.services.resources.admin.permissions.AdminPermissions;
|
||||||
import org.keycloak.testsuite.AbstractKeycloakTest;
|
import org.keycloak.testsuite.AbstractKeycloakTest;
|
||||||
import org.keycloak.testsuite.Assert;
|
import org.keycloak.testsuite.Assert;
|
||||||
|
@ -68,7 +73,6 @@ public class TokenExchangeTest extends AbstractKeycloakTest {
|
||||||
|
|
||||||
public static void setupRealm(KeycloakSession session) {
|
public static void setupRealm(KeycloakSession session) {
|
||||||
RealmModel realm = session.realms().getRealmByName(TEST);
|
RealmModel realm = session.realms().getRealmByName(TEST);
|
||||||
RoleModel realmExchangeable = AdminPermissions.management(session, realm).getRealmManagementClient().addRole(OAuth2Constants.TOKEN_EXCHANGER);
|
|
||||||
|
|
||||||
RoleModel exampleRole = realm.addRole("example");
|
RoleModel exampleRole = realm.addRole("example");
|
||||||
|
|
||||||
|
@ -79,48 +83,62 @@ public class TokenExchangeTest extends AbstractKeycloakTest {
|
||||||
target.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
|
target.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
|
||||||
target.setFullScopeAllowed(false);
|
target.setFullScopeAllowed(false);
|
||||||
target.addScopeMapping(exampleRole);
|
target.addScopeMapping(exampleRole);
|
||||||
RoleModel targetExchangeable = target.addRole(OAuth2Constants.TOKEN_EXCHANGER);
|
|
||||||
|
|
||||||
target = realm.addClient("realm-exchanger");
|
ClientModel clientExchanger = realm.addClient("client-exchanger");
|
||||||
target.setClientId("realm-exchanger");
|
clientExchanger.setClientId("client-exchanger");
|
||||||
target.setDirectAccessGrantsEnabled(true);
|
clientExchanger.setPublicClient(false);
|
||||||
target.setEnabled(true);
|
clientExchanger.setDirectAccessGrantsEnabled(true);
|
||||||
target.setSecret("secret");
|
clientExchanger.setEnabled(true);
|
||||||
target.setServiceAccountsEnabled(true);
|
clientExchanger.setSecret("secret");
|
||||||
target.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
|
clientExchanger.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
|
||||||
target.setFullScopeAllowed(false);
|
clientExchanger.setFullScopeAllowed(false);
|
||||||
new org.keycloak.services.managers.ClientManager(new org.keycloak.services.managers.RealmManager(session)).enableServiceAccount(target);
|
|
||||||
session.users().getServiceAccount(target).grantRole(realmExchangeable);
|
|
||||||
|
|
||||||
target = realm.addClient("client-exchanger");
|
ClientModel illegal = realm.addClient("illegal");
|
||||||
target.setClientId("client-exchanger");
|
illegal.setClientId("illegal");
|
||||||
target.setDirectAccessGrantsEnabled(true);
|
illegal.setPublicClient(false);
|
||||||
target.setEnabled(true);
|
illegal.setDirectAccessGrantsEnabled(true);
|
||||||
target.setSecret("secret");
|
illegal.setEnabled(true);
|
||||||
target.setServiceAccountsEnabled(true);
|
illegal.setSecret("secret");
|
||||||
target.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
|
illegal.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
|
||||||
target.setFullScopeAllowed(false);
|
illegal.setFullScopeAllowed(false);
|
||||||
new org.keycloak.services.managers.ClientManager(new org.keycloak.services.managers.RealmManager(session)).enableServiceAccount(target);
|
|
||||||
session.users().getServiceAccount(target).grantRole(targetExchangeable);
|
|
||||||
|
|
||||||
target = realm.addClient("account-not-allowed");
|
ClientModel illegalTo = realm.addClient("illegal-to");
|
||||||
target.setClientId("account-not-allowed");
|
illegalTo.setClientId("illegal-to");
|
||||||
target.setDirectAccessGrantsEnabled(true);
|
illegalTo.setPublicClient(false);
|
||||||
target.setEnabled(true);
|
illegalTo.setDirectAccessGrantsEnabled(true);
|
||||||
target.setSecret("secret");
|
illegalTo.setEnabled(true);
|
||||||
target.setServiceAccountsEnabled(true);
|
illegalTo.setSecret("secret");
|
||||||
target.setFullScopeAllowed(false);
|
illegalTo.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
|
||||||
target.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
|
illegalTo.setFullScopeAllowed(false);
|
||||||
new org.keycloak.services.managers.ClientManager(new org.keycloak.services.managers.RealmManager(session)).enableServiceAccount(target);
|
|
||||||
|
ClientModel legal = realm.addClient("legal");
|
||||||
|
legal.setClientId("legal");
|
||||||
|
legal.setPublicClient(false);
|
||||||
|
legal.setDirectAccessGrantsEnabled(true);
|
||||||
|
legal.setEnabled(true);
|
||||||
|
legal.setSecret("secret");
|
||||||
|
legal.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
|
||||||
|
legal.setFullScopeAllowed(false);
|
||||||
|
|
||||||
|
AdminPermissionManagement management = AdminPermissions.management(session, realm);
|
||||||
|
|
||||||
|
management.clients().setPermissionsEnabled(target, true);
|
||||||
|
ClientPolicyRepresentation clientRep = new ClientPolicyRepresentation();
|
||||||
|
clientRep.setName("to");
|
||||||
|
clientRep.addClient(clientExchanger.getId());
|
||||||
|
clientRep.addClient(legal.getId());
|
||||||
|
ResourceServer server = management.realmResourceServer();
|
||||||
|
Policy clientPolicy = management.authz().getStoreFactory().getPolicyStore().create(clientRep, server);
|
||||||
|
management.clients().exchangeToPermission(target).addAssociatedPolicy(clientPolicy);
|
||||||
|
|
||||||
|
management.clients().setPermissionsEnabled(clientExchanger, true);
|
||||||
|
ClientPolicyRepresentation client2Rep = new ClientPolicyRepresentation();
|
||||||
|
client2Rep.setName("from");
|
||||||
|
client2Rep.addClient(legal.getId());
|
||||||
|
client2Rep.addClient(illegalTo.getId());
|
||||||
|
Policy client2Policy = management.authz().getStoreFactory().getPolicyStore().create(client2Rep, server);
|
||||||
|
management.clients().exchangeFromPermission(clientExchanger).addAssociatedPolicy(client2Policy);
|
||||||
|
|
||||||
target = realm.addClient("no-account");
|
|
||||||
target.setClientId("no-account");
|
|
||||||
target.setDirectAccessGrantsEnabled(true);
|
|
||||||
target.setEnabled(true);
|
|
||||||
target.setSecret("secret");
|
|
||||||
target.setServiceAccountsEnabled(true);
|
|
||||||
target.setFullScopeAllowed(false);
|
|
||||||
target.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
|
|
||||||
|
|
||||||
UserModel user = session.users().addUser(realm, "user");
|
UserModel user = session.users().addUser(realm, "user");
|
||||||
user.setEnabled(true);
|
user.setEnabled(true);
|
||||||
|
@ -140,18 +158,46 @@ public class TokenExchangeTest extends AbstractKeycloakTest {
|
||||||
testingClient.server().run(TokenExchangeTest::setupRealm);
|
testingClient.server().run(TokenExchangeTest::setupRealm);
|
||||||
|
|
||||||
oauth.realm(TEST);
|
oauth.realm(TEST);
|
||||||
oauth.clientId("realm-exchanger");
|
oauth.clientId("client-exchanger");
|
||||||
|
|
||||||
OAuthClient.AccessTokenResponse response = oauth.doGrantAccessTokenRequest("secret", "user", "password");
|
OAuthClient.AccessTokenResponse response = oauth.doGrantAccessTokenRequest("secret", "user", "password");
|
||||||
String accessToken = response.getAccessToken();
|
String accessToken = response.getAccessToken();
|
||||||
|
TokenVerifier<AccessToken> accessTokenVerifier = TokenVerifier.create(accessToken, AccessToken.class);
|
||||||
|
AccessToken token = accessTokenVerifier.parse().getToken();
|
||||||
|
Assert.assertEquals(token.getPreferredUsername(), "user");
|
||||||
|
Assert.assertTrue(token.getRealmAccess() == null || !token.getRealmAccess().isUserInRole("example"));
|
||||||
|
|
||||||
response = oauth.doTokenExchange(TEST,accessToken, "target", "realm-exchanger", "secret");
|
{
|
||||||
|
response = oauth.doTokenExchange(TEST, accessToken, "target", "client-exchanger", "secret");
|
||||||
|
|
||||||
String exchangedTokenString = response.getAccessToken();
|
String exchangedTokenString = response.getAccessToken();
|
||||||
TokenVerifier<AccessToken> verifier = TokenVerifier.create(exchangedTokenString, AccessToken.class);
|
TokenVerifier<AccessToken> verifier = TokenVerifier.create(exchangedTokenString, AccessToken.class);
|
||||||
AccessToken exchangedToken = verifier.parse().getToken();
|
AccessToken exchangedToken = verifier.parse().getToken();
|
||||||
|
Assert.assertEquals("client-exchanger", exchangedToken.getIssuedFor());
|
||||||
|
Assert.assertEquals("target", exchangedToken.getAudience()[0]);
|
||||||
Assert.assertEquals(exchangedToken.getPreferredUsername(), "user");
|
Assert.assertEquals(exchangedToken.getPreferredUsername(), "user");
|
||||||
Assert.assertTrue(exchangedToken.getRealmAccess().isUserInRole("example"));
|
Assert.assertTrue(exchangedToken.getRealmAccess().isUserInRole("example"));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
response = oauth.doTokenExchange(TEST, accessToken, "target", "legal", "secret");
|
||||||
|
|
||||||
|
String exchangedTokenString = response.getAccessToken();
|
||||||
|
TokenVerifier<AccessToken> verifier = TokenVerifier.create(exchangedTokenString, AccessToken.class);
|
||||||
|
AccessToken exchangedToken = verifier.parse().getToken();
|
||||||
|
Assert.assertEquals("legal", exchangedToken.getIssuedFor());
|
||||||
|
Assert.assertEquals("target", exchangedToken.getAudience()[0]);
|
||||||
|
Assert.assertEquals(exchangedToken.getPreferredUsername(), "user");
|
||||||
|
Assert.assertTrue(exchangedToken.getRealmAccess().isUserInRole("example"));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
response = oauth.doTokenExchange(TEST, accessToken, "target", "illegal", "secret");
|
||||||
|
Assert.assertEquals(403, response.getStatusCode());
|
||||||
|
}
|
||||||
|
{
|
||||||
|
response = oauth.doTokenExchange(TEST, accessToken, "target", "illegal-to", "secret");
|
||||||
|
Assert.assertEquals(403, response.getStatusCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1340,6 +1340,8 @@ manage-permissions-group.tooltip=Fine grain permssions for admins that want to m
|
||||||
manage-authz-group-scope-description=Policies that decide if an admin can manage this group
|
manage-authz-group-scope-description=Policies that decide if an admin can manage this group
|
||||||
view-authz-group-scope-description=Policies that decide if an admin can view this group
|
view-authz-group-scope-description=Policies that decide if an admin can view this group
|
||||||
view-members-authz-group-scope-description=Policies that decide if an admin can manage the members of this group
|
view-members-authz-group-scope-description=Policies that decide if an admin can manage the members of this group
|
||||||
|
exchange-to-authz-client-scope-description=Policies that decide which clients are allowed exchange tokens for a token that is targeted to this client.
|
||||||
|
exchange-from-authz-client-scope-description=Policies that decide which clients are allowed to exchange tokens that were generated for this client.
|
||||||
manage-authz-client-scope-description=Policies that decide if an admin can manage this client
|
manage-authz-client-scope-description=Policies that decide if an admin can manage this client
|
||||||
configure-authz-client-scope-description=Reduced management permissions for admin. Cannot set scope, template, or protocol mappers.
|
configure-authz-client-scope-description=Reduced management permissions for admin. Cannot set scope, template, or protocol mappers.
|
||||||
view-authz-client-scope-description=Policies that decide if an admin can view this client
|
view-authz-client-scope-description=Policies that decide if an admin can view this client
|
||||||
|
|
Loading…
Reference in a new issue