mirror of
https://github.com/keycloak/keycloak.git
synced 2026-01-09 23:12:06 -03:30
Remove FGAP from standard token exchange v2
Closes #37108 Signed-off-by: Giuseppe Graziano <g.graziano94@gmail.com>
This commit is contained in:
parent
1afcf515aa
commit
f2d931ba44
@ -74,7 +74,7 @@ public class Profile {
|
||||
SCRIPTS("Write custom authenticators using JavaScript", Type.PREVIEW),
|
||||
|
||||
TOKEN_EXCHANGE("Token Exchange Service", Type.PREVIEW, 1),
|
||||
TOKEN_EXCHANGE_STANDARD_V2("Standard Token Exchange version 2", Type.EXPERIMENTAL, 2, Feature.ADMIN_FINE_GRAINED_AUTHZ), // TODO: Switch v2 token exchanges to depend admin-fine-grained-authz-v2
|
||||
TOKEN_EXCHANGE_STANDARD_V2("Standard Token Exchange version 2", Type.EXPERIMENTAL, 2),
|
||||
TOKEN_EXCHANGE_FEDERATED_V2("Federated Token Exchange for external-internal and internal-external token exchange", Type.EXPERIMENTAL, 2, Feature.ADMIN_FINE_GRAINED_AUTHZ),
|
||||
TOKEN_EXCHANGE_SUBJECT_IMPERSONATION_V2("Subject impersonation Token Exchange", Type.EXPERIMENTAL, 2, Feature.ADMIN_FINE_GRAINED_AUTHZ),
|
||||
|
||||
|
||||
@ -206,8 +206,7 @@ public abstract class AbstractTokenExchangeProvider implements TokenExchangeProv
|
||||
|
||||
}
|
||||
|
||||
protected Response exchangeClientToClient(UserModel targetUser, UserSessionModel targetUserSession,
|
||||
AccessToken token, boolean disallowOnHolderOfTokenMismatch) {
|
||||
protected String getRequestedTokenType() {
|
||||
String requestedTokenType = formParams.getFirst(OAuth2Constants.REQUESTED_TOKEN_TYPE);
|
||||
if (requestedTokenType == null) {
|
||||
requestedTokenType = OAuth2Constants.REFRESH_TOKEN_TYPE;
|
||||
@ -217,13 +216,13 @@ public abstract class AbstractTokenExchangeProvider implements TokenExchangeProv
|
||||
event.detail(Details.REASON, "requested_token_type unsupported");
|
||||
event.error(Errors.INVALID_REQUEST);
|
||||
throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_REQUEST, "requested_token_type unsupported", Response.Status.BAD_REQUEST);
|
||||
|
||||
}
|
||||
return requestedTokenType;
|
||||
}
|
||||
|
||||
protected List<ClientModel> getTargetAudienceClients() {
|
||||
List<String> audienceParams = params.getAudience();
|
||||
ClientModel tokenHolder = token == null ? null : realm.getClientByClientId(token.getIssuedFor());
|
||||
List<ClientModel> targetAudienceClients = new ArrayList<>();
|
||||
|
||||
if (audienceParams != null) {
|
||||
for (String audience : audienceParams) {
|
||||
ClientModel targetClient = realm.getClientByClientId(audience);
|
||||
@ -237,12 +236,10 @@ public abstract class AbstractTokenExchangeProvider implements TokenExchangeProv
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Assume client itself is audience in case audience parameter not provided
|
||||
if (targetAudienceClients.isEmpty()) {
|
||||
targetAudienceClients.add(client);
|
||||
}
|
||||
|
||||
for (ClientModel targetClient : targetAudienceClients) {
|
||||
if (targetClient.isConsentRequired()) {
|
||||
event.detail(Details.REASON, "audience requires consent");
|
||||
@ -257,7 +254,11 @@ public abstract class AbstractTokenExchangeProvider implements TokenExchangeProv
|
||||
throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_CLIENT, "Client disabled", Response.Status.BAD_REQUEST);
|
||||
}
|
||||
}
|
||||
return targetAudienceClients;
|
||||
}
|
||||
|
||||
protected void validateAudience(AccessToken token, boolean disallowOnHolderOfTokenMismatch, List<ClientModel> targetAudienceClients) {
|
||||
ClientModel tokenHolder = token == null ? null : realm.getClientByClientId(token.getIssuedFor());
|
||||
for (ClientModel targetClient : targetAudienceClients) {
|
||||
boolean isClientTheAudience = targetClient.equals(client);
|
||||
if (isClientTheAudience) {
|
||||
@ -281,7 +282,14 @@ public abstract class AbstractTokenExchangeProvider implements TokenExchangeProv
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected Response exchangeClientToClient(UserModel targetUser, UserSessionModel targetUserSession,
|
||||
AccessToken token, boolean disallowOnHolderOfTokenMismatch) {
|
||||
|
||||
String requestedTokenType = getRequestedTokenType();
|
||||
List<ClientModel> targetAudienceClients = getTargetAudienceClients();
|
||||
validateAudience(token, disallowOnHolderOfTokenMismatch, targetAudienceClients);
|
||||
String scope = getRequestedScope(token, targetAudienceClients);
|
||||
|
||||
try {
|
||||
@ -300,7 +308,7 @@ public abstract class AbstractTokenExchangeProvider implements TokenExchangeProv
|
||||
throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_REQUEST, "requested_token_type unsupported", Response.Status.BAD_REQUEST);
|
||||
}
|
||||
|
||||
private void forbiddenIfClientIsNotWithinTokenAudience(AccessToken token, ClientModel tokenHolder) {
|
||||
protected void forbiddenIfClientIsNotWithinTokenAudience(AccessToken token, ClientModel tokenHolder) {
|
||||
if (token != null && !token.hasAudience(client.getClientId())) {
|
||||
event.detail(Details.REASON, "client is not within the token audience");
|
||||
event.error(Errors.NOT_ALLOWED);
|
||||
@ -308,7 +316,7 @@ public abstract class AbstractTokenExchangeProvider implements TokenExchangeProv
|
||||
}
|
||||
}
|
||||
|
||||
private void forbiddenIfClientIsNotTokenHolder(boolean disallowOnHolderOfTokenMismatch, ClientModel tokenHolder) {
|
||||
protected void forbiddenIfClientIsNotTokenHolder(boolean disallowOnHolderOfTokenMismatch, ClientModel tokenHolder) {
|
||||
if (disallowOnHolderOfTokenMismatch && !client.equals(tokenHolder)) {
|
||||
event.detail(Details.REASON, "client is not the token holder");
|
||||
event.error(Errors.NOT_ALLOWED);
|
||||
|
||||
@ -121,6 +121,30 @@ public class StandardTokenExchangeProvider extends AbstractTokenExchangeProvider
|
||||
return exchangeClientToClient(tokenUser, tokenSession, token, true);
|
||||
}
|
||||
|
||||
protected void validateAudience(AccessToken token, boolean disallowOnHolderOfTokenMismatch, List<ClientModel> targetAudienceClients) {
|
||||
ClientModel tokenHolder = token == null ? null : realm.getClientByClientId(token.getIssuedFor());
|
||||
//reject if the requester-client is not in the audience of the subject token
|
||||
if (!client.equals(tokenHolder)) {
|
||||
forbiddenIfClientIsNotWithinTokenAudience(token, null);
|
||||
}
|
||||
for (ClientModel targetClient : targetAudienceClients) {
|
||||
boolean isClientTheAudience = targetClient.equals(client);
|
||||
if (isClientTheAudience) {
|
||||
if (client.isPublicClient()) {
|
||||
// public clients can only exchange on to themselves if they are the token holder
|
||||
forbiddenIfClientIsNotTokenHolder(disallowOnHolderOfTokenMismatch, tokenHolder);
|
||||
} else if (!client.equals(tokenHolder)) {
|
||||
// confidential clients can only exchange to themselves if they are within the token audience
|
||||
forbiddenIfClientIsNotWithinTokenAudience(token, tokenHolder);
|
||||
}
|
||||
} else {
|
||||
if (client.isPublicClient()) {
|
||||
// public clients can not exchange tokens from other client
|
||||
forbiddenIfClientIsNotTokenHolder(disallowOnHolderOfTokenMismatch, tokenHolder);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For now, include "scope" parameter as is
|
||||
@Override
|
||||
@ -128,13 +152,11 @@ public class StandardTokenExchangeProvider extends AbstractTokenExchangeProvider
|
||||
return params.getScope();
|
||||
}
|
||||
|
||||
|
||||
protected void setClientToContext(List<ClientModel> targetAudienceClients) {
|
||||
// The client requesting exchange is set in the context
|
||||
session.getContext().setClient(client);
|
||||
}
|
||||
|
||||
|
||||
protected Response exchangeClientToOIDCClient(UserModel targetUser, UserSessionModel targetUserSession, String requestedTokenType,
|
||||
List<ClientModel> targetAudienceClients, String scope) {
|
||||
RootAuthenticationSessionModel rootAuthSession = new AuthenticationSessionManager(session).createAuthenticationSession(realm, false);
|
||||
|
||||
@ -95,7 +95,7 @@ public abstract class AbstractStandardTokenExchangeTest extends AbstractKeycloak
|
||||
return accessToken;
|
||||
}
|
||||
|
||||
private String getSessionIdFromToken(String accessToken) throws Exception {
|
||||
protected String getSessionIdFromToken(String accessToken) throws Exception {
|
||||
return TokenVerifier.create(accessToken, AccessToken.class)
|
||||
.parse()
|
||||
.getToken()
|
||||
|
||||
@ -48,7 +48,6 @@ import static org.keycloak.testsuite.auth.page.AuthRealm.TEST;
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
@EnableFeature(value = Profile.Feature.TOKEN_EXCHANGE_STANDARD_V2, skipRestart = true)
|
||||
@EnableFeature(value = Profile.Feature.ADMIN_FINE_GRAINED_AUTHZ, skipRestart = true) // TODO: Remove as we may not need to use FGAP for token exchange
|
||||
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
||||
public class ClientTokenExchangeAudienceAndScopesTest extends AbstractKeycloakTest {
|
||||
|
||||
|
||||
@ -19,10 +19,9 @@
|
||||
|
||||
package org.keycloak.testsuite.oauth.tokenexchange;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.TokenVerifier;
|
||||
import org.keycloak.common.Profile;
|
||||
import org.keycloak.models.ClientModel;
|
||||
@ -40,13 +39,13 @@ import org.keycloak.testsuite.arquillian.annotation.UncaughtServerErrorExpected;
|
||||
import org.keycloak.testsuite.util.oauth.AccessTokenResponse;
|
||||
import org.keycloak.testsuite.util.oauth.OAuthClient;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.keycloak.testsuite.auth.page.AuthRealm.TEST;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
@EnableFeature(value = Profile.Feature.TOKEN_EXCHANGE_STANDARD_V2, skipRestart = true)
|
||||
@EnableFeature(value = Profile.Feature.ADMIN_FINE_GRAINED_AUTHZ, skipRestart = true) // TODO: Replace with admin-fine-grained-authz V2
|
||||
public class StandardTokenExchangeV2Test extends AbstractStandardTokenExchangeTest {
|
||||
|
||||
@Override
|
||||
@ -86,6 +85,43 @@ public class StandardTokenExchangeV2Test extends AbstractStandardTokenExchangeTe
|
||||
return accessToken;
|
||||
}
|
||||
|
||||
@Test
|
||||
@UncaughtServerErrorExpected
|
||||
public void testExchange() throws Exception {
|
||||
setupRealm();
|
||||
oauth.realm(TEST);
|
||||
String accessToken = getInitialAccessTokenForClientExchanger();
|
||||
{
|
||||
OAuthClient.AccessTokenResponse response = oauth.doTokenExchange(TEST, accessToken, "target", "client-exchanger", "secret");
|
||||
Assert.assertEquals(OAuth2Constants.REFRESH_TOKEN_TYPE, response.getIssuedTokenType());
|
||||
String exchangedTokenString = response.getAccessToken();
|
||||
TokenVerifier<AccessToken> verifier = TokenVerifier.create(exchangedTokenString, AccessToken.class);
|
||||
AccessToken exchangedToken = verifier.parse().getToken();
|
||||
Assert.assertEquals(getSessionIdFromToken(accessToken), exchangedToken.getSessionId());
|
||||
Assert.assertEquals("client-exchanger", exchangedToken.getIssuedFor());
|
||||
Assert.assertEquals("target", exchangedToken.getAudience()[0]);
|
||||
Assert.assertEquals(exchangedToken.getPreferredUsername(), "user");
|
||||
assertTrue(exchangedToken.getRealmAccess().isUserInRole("example"));
|
||||
}
|
||||
{
|
||||
OAuthClient.AccessTokenResponse response = oauth.doTokenExchange(TEST, accessToken, "target", "legal", "secret");
|
||||
Assert.assertEquals(OAuth2Constants.REFRESH_TOKEN_TYPE, response.getIssuedTokenType());
|
||||
String exchangedTokenString = response.getAccessToken();
|
||||
TokenVerifier<AccessToken> verifier = TokenVerifier.create(exchangedTokenString, AccessToken.class);
|
||||
AccessToken exchangedToken = verifier.parse().getToken();
|
||||
Assert.assertEquals(getSessionIdFromToken(accessToken), exchangedToken.getSessionId());
|
||||
Assert.assertEquals("legal", exchangedToken.getIssuedFor());
|
||||
Assert.assertEquals("target", exchangedToken.getAudience()[0]);
|
||||
Assert.assertEquals(exchangedToken.getPreferredUsername(), "user");
|
||||
assertTrue(exchangedToken.getRealmAccess().isUserInRole("example"));
|
||||
}
|
||||
{
|
||||
//exchange not allowed due the illegal client is not in the client-exchanger audience
|
||||
OAuthClient.AccessTokenResponse response = oauth.doTokenExchange(TEST, accessToken, "target", "illegal", "secret");
|
||||
Assert.assertEquals(403, response.getStatusCode());
|
||||
}
|
||||
}
|
||||
|
||||
// Scope parameter is different with V2. TODO: Should write differently this test for V2
|
||||
@Test
|
||||
@UncaughtServerErrorExpected
|
||||
@ -102,4 +138,19 @@ public class StandardTokenExchangeV2Test extends AbstractStandardTokenExchangeTe
|
||||
|
||||
}
|
||||
|
||||
//token exchange for public client will be not supported for v2 TODO: Should write differently this test for V2
|
||||
@Test
|
||||
@Ignore
|
||||
@UncaughtServerErrorExpected
|
||||
public void testExchangeFromPublicClient() {
|
||||
}
|
||||
|
||||
|
||||
//token exchange for public client will be not supported for v2 TODO: Should write differently this test for V2
|
||||
@Test
|
||||
@Ignore
|
||||
@UncaughtServerErrorExpected
|
||||
public void testPublicClientNotAllowed() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -21,6 +21,7 @@ package org.keycloak.testsuite.oauth.tokenexchange;
|
||||
|
||||
import org.keycloak.authorization.model.Policy;
|
||||
import org.keycloak.authorization.model.ResourceServer;
|
||||
import org.keycloak.common.Profile;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ImpersonationConstants;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
@ -60,12 +61,9 @@ public class TokenExchangeTestUtils {
|
||||
RealmModel realm = session.realms().getRealmByName(TEST);
|
||||
RoleModel exampleRole = realm.getRole("example");
|
||||
|
||||
AdminPermissionManagement management = AdminPermissions.management(session, realm);
|
||||
ClientModel target = realm.getClientByClientId("target");
|
||||
assertNotNull(target);
|
||||
|
||||
RoleModel impersonateRole = management.getRealmPermissionsClient().getRole(ImpersonationConstants.IMPERSONATION_ROLE);
|
||||
|
||||
ClientModel differentScopeClient = realm.addClient("different-scope-client");
|
||||
differentScopeClient.setClientId("different-scope-client");
|
||||
differentScopeClient.setPublicClient(false);
|
||||
@ -84,10 +82,18 @@ public class TokenExchangeTestUtils {
|
||||
clientExchanger.setSecret("secret");
|
||||
clientExchanger.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
|
||||
clientExchanger.setFullScopeAllowed(false);
|
||||
clientExchanger.addScopeMapping(impersonateRole);
|
||||
|
||||
if (Profile.isFeatureEnabled(Profile.Feature.ADMIN_FINE_GRAINED_AUTHZ) || Profile.isFeatureEnabled(Profile.Feature.ADMIN_FINE_GRAINED_AUTHZ_V2)) {
|
||||
AdminPermissionManagement management = AdminPermissions.management(session, realm);
|
||||
RoleModel impersonateRole = management.getRealmPermissionsClient().getRole(ImpersonationConstants.IMPERSONATION_ROLE);
|
||||
clientExchanger.addScopeMapping(impersonateRole);
|
||||
}
|
||||
|
||||
clientExchanger.addProtocolMapper(UserSessionNoteMapper.createUserSessionNoteMapper(IMPERSONATOR_ID));
|
||||
clientExchanger.addProtocolMapper(UserSessionNoteMapper.createUserSessionNoteMapper(IMPERSONATOR_USERNAME));
|
||||
clientExchanger.addProtocolMapper(AudienceProtocolMapper.createClaimMapper("different-scope-client-audience", differentScopeClient.getClientId(), null, true, false, true));
|
||||
clientExchanger.addProtocolMapper(AudienceProtocolMapper.createClaimMapper("allowed-exchanger1", null, "legal", true, false, true));
|
||||
clientExchanger.addProtocolMapper(AudienceProtocolMapper.createClaimMapper("allowed-exchanger2", null, "no-refresh-token", true, false, true));
|
||||
|
||||
ClientModel illegal = realm.addClient("illegal");
|
||||
illegal.setClientId("illegal");
|
||||
@ -176,9 +182,12 @@ public class TokenExchangeTestUtils {
|
||||
clientRep.addClient(serviceAccount.getId());
|
||||
clientRep.addClient(differentScopeClient.getId());
|
||||
|
||||
ResourceServer server = management.realmResourceServer();
|
||||
Policy clientPolicy = management.authz().getStoreFactory().getPolicyStore().create(server, clientRep);
|
||||
management.clients().exchangeToPermission(target).addAssociatedPolicy(clientPolicy);
|
||||
if (Profile.isFeatureEnabled(Profile.Feature.ADMIN_FINE_GRAINED_AUTHZ) || Profile.isFeatureEnabled(Profile.Feature.ADMIN_FINE_GRAINED_AUTHZ_V2)) {
|
||||
AdminPermissionManagement management = AdminPermissions.management(session, realm);
|
||||
ResourceServer server = management.realmResourceServer();
|
||||
Policy clientPolicy = management.authz().getStoreFactory().getPolicyStore().create(server, clientRep);
|
||||
management.clients().exchangeToPermission(target).addAssociatedPolicy(clientPolicy);
|
||||
}
|
||||
|
||||
// permission for user impersonation for a client
|
||||
|
||||
@ -188,17 +197,26 @@ public class TokenExchangeTestUtils {
|
||||
clientImpersonateRep.addClient(directPublic.getId());
|
||||
clientImpersonateRep.addClient(directUntrustedPublic.getId());
|
||||
clientImpersonateRep.addClient(directNoSecret.getId());
|
||||
server = management.realmResourceServer();
|
||||
Policy clientImpersonatePolicy = management.authz().getStoreFactory().getPolicyStore().create(server, clientImpersonateRep);
|
||||
management.users().setPermissionsEnabled(true);
|
||||
management.users().adminImpersonatingPermission().addAssociatedPolicy(clientImpersonatePolicy);
|
||||
management.users().adminImpersonatingPermission().setDecisionStrategy(DecisionStrategy.AFFIRMATIVE);
|
||||
|
||||
if (Profile.isFeatureEnabled(Profile.Feature.ADMIN_FINE_GRAINED_AUTHZ) || Profile.isFeatureEnabled(Profile.Feature.ADMIN_FINE_GRAINED_AUTHZ_V2)) {
|
||||
AdminPermissionManagement management = AdminPermissions.management(session, realm);
|
||||
ResourceServer server = management.realmResourceServer();
|
||||
Policy clientImpersonatePolicy = management.authz().getStoreFactory().getPolicyStore().create(server, clientImpersonateRep);
|
||||
management.users().setPermissionsEnabled(true);
|
||||
management.users().adminImpersonatingPermission().addAssociatedPolicy(clientImpersonatePolicy);
|
||||
management.users().adminImpersonatingPermission().setDecisionStrategy(DecisionStrategy.AFFIRMATIVE);
|
||||
}
|
||||
|
||||
UserModel user = session.users().addUser(realm, "user");
|
||||
user.setEnabled(true);
|
||||
user.credentialManager().updateCredential(UserCredentialModel.password("password"));
|
||||
user.grantRole(exampleRole);
|
||||
user.grantRole(impersonateRole);
|
||||
|
||||
if (Profile.isFeatureEnabled(Profile.Feature.ADMIN_FINE_GRAINED_AUTHZ) || Profile.isFeatureEnabled(Profile.Feature.ADMIN_FINE_GRAINED_AUTHZ_V2)) {
|
||||
AdminPermissionManagement management = AdminPermissions.management(session, realm);
|
||||
RoleModel impersonateRole = management.getRealmPermissionsClient().getRole(ImpersonationConstants.IMPERSONATION_ROLE);
|
||||
user.grantRole(impersonateRole);
|
||||
}
|
||||
|
||||
UserModel bad = session.users().addUser(realm, "bad-impersonator");
|
||||
bad.setEnabled(true);
|
||||
@ -221,7 +239,7 @@ public class TokenExchangeTestUtils {
|
||||
public static void addDirectExchanger(KeycloakSession session) {
|
||||
RealmModel realm = session.realms().getRealmByName(TEST);
|
||||
RoleModel exampleRole = realm.addRole("example");
|
||||
AdminPermissionManagement management = AdminPermissions.management(session, realm);
|
||||
|
||||
|
||||
ClientModel target = realm.addClient("target");
|
||||
target.setName("target");
|
||||
@ -243,18 +261,19 @@ public class TokenExchangeTestUtils {
|
||||
directExchanger.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
|
||||
directExchanger.setFullScopeAllowed(false);
|
||||
|
||||
// permission for client to client exchange to "target" client
|
||||
management.clients().setPermissionsEnabled(target, true);
|
||||
|
||||
ClientPolicyRepresentation clientImpersonateRep = new ClientPolicyRepresentation();
|
||||
clientImpersonateRep.setName("clientImpersonatorsDirect");
|
||||
clientImpersonateRep.addClient(directExchanger.getId());
|
||||
|
||||
ResourceServer server = management.realmResourceServer();
|
||||
Policy clientImpersonatePolicy = management.authz().getStoreFactory().getPolicyStore().create(server, clientImpersonateRep);
|
||||
management.users().setPermissionsEnabled(true);
|
||||
management.users().adminImpersonatingPermission().addAssociatedPolicy(clientImpersonatePolicy);
|
||||
management.users().adminImpersonatingPermission().setDecisionStrategy(DecisionStrategy.AFFIRMATIVE);
|
||||
if (Profile.isFeatureEnabled(Profile.Feature.ADMIN_FINE_GRAINED_AUTHZ) || Profile.isFeatureEnabled(Profile.Feature.ADMIN_FINE_GRAINED_AUTHZ_V2)) {
|
||||
AdminPermissionManagement management = AdminPermissions.management(session, realm);
|
||||
management.clients().setPermissionsEnabled(target, true);
|
||||
ResourceServer server = management.realmResourceServer();
|
||||
Policy clientImpersonatePolicy = management.authz().getStoreFactory().getPolicyStore().create(server, clientImpersonateRep);
|
||||
management.users().setPermissionsEnabled(true);
|
||||
management.users().adminImpersonatingPermission().addAssociatedPolicy(clientImpersonatePolicy);
|
||||
management.users().adminImpersonatingPermission().setDecisionStrategy(DecisionStrategy.AFFIRMATIVE);
|
||||
}
|
||||
|
||||
UserModel impersonatedUser = session.users().addUser(realm, "impersonated-user");
|
||||
impersonatedUser.setEnabled(true);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user