From 882f5ffea45713f6f931e6775266f0e6efffee65 Mon Sep 17 00:00:00 2001 From: Takashi Norimatsu Date: Mon, 18 Jan 2021 08:42:17 +0900 Subject: [PATCH] KEYCLOAK-15533 Client Policy : Extends Policy Interface to Migrate Client Registration Policies Co-authored-by: Hryhorii Hevorkian Co-authored-by: Andrii Murashkin --- .../clientpolicy/ClientPolicyEvent.java | 4 + .../oidc/endpoints/AuthorizationEndpoint.java | 2 +- .../oidc/endpoints/LogoutEndpoint.java | 2 +- .../oidc/endpoints/TokenEndpoint.java | 4 +- .../endpoints/TokenIntrospectionEndpoint.java | 2 +- .../endpoints/TokenRevocationEndpoint.java | 2 +- .../oidc/endpoints/UserInfoEndpoint.java | 2 +- .../DefaultClientPolicyManager.java | 5 +- .../condition/ClientScopesCondition.java | 4 +- .../ClientUpdateContextCondition.java | 6 +- .../ClientUpdateSourceGroupsCondition.java | 18 +- .../ClientUpdateSourceRolesCondition.java | 18 +- .../AbstractAdminClientCRUDContext.java} | 25 +- .../AbstractDynamicClientCRUDContext.java | 59 +++++ .../context/AdminClientRegisterContext.java | 42 ++++ .../context/AdminClientRegisteredContext.java | 42 ++++ .../context/AdminClientUnregisterContext.java | 42 ++++ .../AdminClientUpdateContext.java | 41 +-- .../context/AdminClientUpdatedContext.java | 50 ++++ .../context/AdminClientViewContext.java | 42 ++++ .../AuthorizationRequestContext.java | 2 +- .../ClientCRUDContext.java} | 36 ++- .../DynamicClientRegisterContext.java | 43 +--- .../DynamicClientRegisteredContext.java | 44 ++++ .../DynamicClientUnregisterContext.java | 44 ++++ .../DynamicClientUpdateContext.java | 46 +--- .../context/DynamicClientUpdatedContext.java | 44 ++++ .../context/DynamicClientViewContext.java | 44 ++++ .../{ => context}/LogoutRequestContext.java | 2 +- .../{ => context}/TokenIntrospectContext.java | 2 +- .../{ => context}/TokenRefreshContext.java | 2 +- .../{ => context}/TokenRequestContext.java | 2 +- .../{ => context}/TokenRevokeContext.java | 2 +- .../{ => context}/UserInfoRequestContext.java | 2 +- ...ntingClientRegistrationPolicyExecutor.java | 4 +- .../executor/HolderOfKeyEnforceExecutor.java | 14 +- .../executor/PKCEEnforceExecutor.java | 4 +- .../SecureRedirectUriEnforceExecutor.java | 16 +- .../executor/SecureRequestObjectExecutor.java | 2 +- .../executor/SecureResponseTypeExecutor.java | 2 +- .../SecureSessionEnforceExecutor.java | 2 +- ...SecureSigningAlgorithmEnforceExecutor.java | 8 +- .../AbstractClientRegistrationProvider.java | 11 + .../ClientRegistrationAuth.java | 12 +- .../resources/admin/ClientResource.java | 27 +- .../resources/admin/ClientsResource.java | 11 +- .../executor/TestRaiseExeptionExecutor.java | 63 +++++ .../TestRaiseExeptionExecutorFactory.java | 72 ++++++ ...ecutor.ClientPolicyExecutorProviderFactory | 1 + .../client/AbstractClientPoliciesTest.java | 63 ++++- .../testsuite/client/ClientPoliciesTest.java | 233 ++++++++++++------ 51 files changed, 971 insertions(+), 301 deletions(-) rename services/src/main/java/org/keycloak/services/clientpolicy/{AdminClientRegisterContext.java => context/AbstractAdminClientCRUDContext.java} (59%) create mode 100644 services/src/main/java/org/keycloak/services/clientpolicy/context/AbstractDynamicClientCRUDContext.java create mode 100644 services/src/main/java/org/keycloak/services/clientpolicy/context/AdminClientRegisterContext.java create mode 100644 services/src/main/java/org/keycloak/services/clientpolicy/context/AdminClientRegisteredContext.java create mode 100644 services/src/main/java/org/keycloak/services/clientpolicy/context/AdminClientUnregisterContext.java rename services/src/main/java/org/keycloak/services/clientpolicy/{ => context}/AdminClientUpdateContext.java (52%) create mode 100644 services/src/main/java/org/keycloak/services/clientpolicy/context/AdminClientUpdatedContext.java create mode 100644 services/src/main/java/org/keycloak/services/clientpolicy/context/AdminClientViewContext.java rename services/src/main/java/org/keycloak/services/clientpolicy/{ => context}/AuthorizationRequestContext.java (97%) rename services/src/main/java/org/keycloak/services/clientpolicy/{ClientUpdateContext.java => context/ClientCRUDContext.java} (56%) rename services/src/main/java/org/keycloak/services/clientpolicy/{ => context}/DynamicClientRegisterContext.java (52%) create mode 100644 services/src/main/java/org/keycloak/services/clientpolicy/context/DynamicClientRegisteredContext.java create mode 100644 services/src/main/java/org/keycloak/services/clientpolicy/context/DynamicClientUnregisterContext.java rename services/src/main/java/org/keycloak/services/clientpolicy/{ => context}/DynamicClientUpdateContext.java (54%) create mode 100644 services/src/main/java/org/keycloak/services/clientpolicy/context/DynamicClientUpdatedContext.java create mode 100644 services/src/main/java/org/keycloak/services/clientpolicy/context/DynamicClientViewContext.java rename services/src/main/java/org/keycloak/services/clientpolicy/{ => context}/LogoutRequestContext.java (96%) rename services/src/main/java/org/keycloak/services/clientpolicy/{ => context}/TokenIntrospectContext.java (96%) rename services/src/main/java/org/keycloak/services/clientpolicy/{ => context}/TokenRefreshContext.java (96%) rename services/src/main/java/org/keycloak/services/clientpolicy/{ => context}/TokenRequestContext.java (96%) rename services/src/main/java/org/keycloak/services/clientpolicy/{ => context}/TokenRevokeContext.java (96%) rename services/src/main/java/org/keycloak/services/clientpolicy/{ => context}/UserInfoRequestContext.java (95%) create mode 100644 testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/services/clientpolicy/executor/TestRaiseExeptionExecutor.java create mode 100644 testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/services/clientpolicy/executor/TestRaiseExeptionExecutorFactory.java create mode 100644 testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/META-INF/services/org.keycloak.services.clientpolicy.executor.ClientPolicyExecutorProviderFactory diff --git a/server-spi/src/main/java/org/keycloak/services/clientpolicy/ClientPolicyEvent.java b/server-spi/src/main/java/org/keycloak/services/clientpolicy/ClientPolicyEvent.java index 87988ea1c9..aa5a6db4bc 100644 --- a/server-spi/src/main/java/org/keycloak/services/clientpolicy/ClientPolicyEvent.java +++ b/server-spi/src/main/java/org/keycloak/services/clientpolicy/ClientPolicyEvent.java @@ -20,7 +20,11 @@ package org.keycloak.services.clientpolicy; public enum ClientPolicyEvent { REGISTER, + REGISTERED, UPDATE, + UPDATED, + VIEW, + UNREGISTER, AUTHORIZATION_REQUEST, TOKEN_REQUEST, TOKEN_REFRESH, diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/AuthorizationEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/AuthorizationEndpoint.java index ec46a1f0d7..874351e9b8 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/AuthorizationEndpoint.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/AuthorizationEndpoint.java @@ -44,10 +44,10 @@ import org.keycloak.protocol.oidc.utils.RedirectUtils; import org.keycloak.services.ErrorPageException; import org.keycloak.services.ServicesLogger; import org.keycloak.services.Urls; -import org.keycloak.services.clientpolicy.AuthorizationRequestContext; import org.keycloak.services.clientpolicy.ClientPolicyContext; import org.keycloak.services.clientpolicy.ClientPolicyException; import org.keycloak.services.clientpolicy.DefaultClientPolicyManager; +import org.keycloak.services.clientpolicy.context.AuthorizationRequestContext; import org.keycloak.services.messages.Messages; import org.keycloak.services.resources.LoginActionsService; import org.keycloak.services.util.CacheControlUtil; diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/LogoutEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/LogoutEndpoint.java index 49e1198c9b..f3fa3b2fa3 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/LogoutEndpoint.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/LogoutEndpoint.java @@ -46,7 +46,7 @@ import org.keycloak.representations.RefreshToken; import org.keycloak.services.ErrorPage; import org.keycloak.services.ErrorResponseException; import org.keycloak.services.clientpolicy.ClientPolicyException; -import org.keycloak.services.clientpolicy.LogoutRequestContext; +import org.keycloak.services.clientpolicy.context.LogoutRequestContext; import org.keycloak.services.managers.AuthenticationManager; import org.keycloak.services.managers.UserSessionManager; import org.keycloak.services.messages.Messages; diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java index f0710af27e..3dffa04c0b 100644 --- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java @@ -85,8 +85,8 @@ import org.keycloak.services.CorsErrorResponseException; import org.keycloak.services.ServicesLogger; import org.keycloak.services.Urls; import org.keycloak.services.clientpolicy.ClientPolicyException; -import org.keycloak.services.clientpolicy.TokenRefreshContext; -import org.keycloak.services.clientpolicy.TokenRequestContext; +import org.keycloak.services.clientpolicy.context.TokenRefreshContext; +import org.keycloak.services.clientpolicy.context.TokenRequestContext; import org.keycloak.services.managers.AppAuthManager; import org.keycloak.services.managers.AuthenticationManager; import org.keycloak.services.managers.AuthenticationSessionManager; diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenIntrospectionEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenIntrospectionEndpoint.java index c6dd404667..2f67de4686 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenIntrospectionEndpoint.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenIntrospectionEndpoint.java @@ -33,7 +33,7 @@ import org.keycloak.services.CorsErrorResponseException; import org.keycloak.services.ErrorResponseException; import org.keycloak.services.clientpolicy.ClientPolicyException; import org.keycloak.services.clientpolicy.DefaultClientPolicyManager; -import org.keycloak.services.clientpolicy.TokenIntrospectContext; +import org.keycloak.services.clientpolicy.context.TokenIntrospectContext; import javax.ws.rs.POST; import javax.ws.rs.core.Context; diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenRevocationEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenRevocationEndpoint.java index 06b40b33b3..a1b13b71ba 100644 --- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenRevocationEndpoint.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenRevocationEndpoint.java @@ -48,7 +48,7 @@ import org.keycloak.protocol.oidc.utils.AuthorizeClientUtil; import org.keycloak.representations.AccessToken; import org.keycloak.services.CorsErrorResponseException; import org.keycloak.services.clientpolicy.ClientPolicyException; -import org.keycloak.services.clientpolicy.TokenRevokeContext; +import org.keycloak.services.clientpolicy.context.TokenRevokeContext; import org.keycloak.services.managers.UserSessionCrossDCManager; import org.keycloak.services.managers.UserSessionManager; import org.keycloak.services.resources.Cors; diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/UserInfoEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/UserInfoEndpoint.java index 70692a0dd2..bde802b5d9 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/UserInfoEndpoint.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/UserInfoEndpoint.java @@ -49,7 +49,7 @@ import org.keycloak.representations.AccessToken; import org.keycloak.services.CorsErrorResponseException; import org.keycloak.services.Urls; import org.keycloak.services.clientpolicy.ClientPolicyException; -import org.keycloak.services.clientpolicy.UserInfoRequestContext; +import org.keycloak.services.clientpolicy.context.UserInfoRequestContext; import org.keycloak.services.managers.AppAuthManager; import org.keycloak.services.managers.AuthenticationManager; import org.keycloak.services.managers.UserSessionCrossDCManager; diff --git a/services/src/main/java/org/keycloak/services/clientpolicy/DefaultClientPolicyManager.java b/services/src/main/java/org/keycloak/services/clientpolicy/DefaultClientPolicyManager.java index 90600e1053..1ea5638a40 100644 --- a/services/src/main/java/org/keycloak/services/clientpolicy/DefaultClientPolicyManager.java +++ b/services/src/main/java/org/keycloak/services/clientpolicy/DefaultClientPolicyManager.java @@ -17,7 +17,10 @@ package org.keycloak.services.clientpolicy; -import java.util.*; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; import java.util.stream.Collectors; import org.jboss.logging.Logger; diff --git a/services/src/main/java/org/keycloak/services/clientpolicy/condition/ClientScopesCondition.java b/services/src/main/java/org/keycloak/services/clientpolicy/condition/ClientScopesCondition.java index 42d78db78a..9f9c263059 100644 --- a/services/src/main/java/org/keycloak/services/clientpolicy/condition/ClientScopesCondition.java +++ b/services/src/main/java/org/keycloak/services/clientpolicy/condition/ClientScopesCondition.java @@ -30,12 +30,12 @@ import org.keycloak.models.AuthenticatedClientSessionModel; import org.keycloak.models.ClientModel; import org.keycloak.models.KeycloakSession; import org.keycloak.protocol.oidc.endpoints.request.AuthorizationEndpointRequest; -import org.keycloak.services.clientpolicy.AuthorizationRequestContext; import org.keycloak.services.clientpolicy.ClientPolicyContext; import org.keycloak.services.clientpolicy.ClientPolicyException; import org.keycloak.services.clientpolicy.ClientPolicyLogger; import org.keycloak.services.clientpolicy.ClientPolicyVote; -import org.keycloak.services.clientpolicy.TokenRequestContext; +import org.keycloak.services.clientpolicy.context.AuthorizationRequestContext; +import org.keycloak.services.clientpolicy.context.TokenRequestContext; public class ClientScopesCondition extends AbstractClientPolicyConditionProvider { diff --git a/services/src/main/java/org/keycloak/services/clientpolicy/condition/ClientUpdateContextCondition.java b/services/src/main/java/org/keycloak/services/clientpolicy/condition/ClientUpdateContextCondition.java index 9f73ec70f8..69a90da4b6 100644 --- a/services/src/main/java/org/keycloak/services/clientpolicy/condition/ClientUpdateContextCondition.java +++ b/services/src/main/java/org/keycloak/services/clientpolicy/condition/ClientUpdateContextCondition.java @@ -28,7 +28,7 @@ import org.keycloak.services.clientpolicy.ClientPolicyContext; import org.keycloak.services.clientpolicy.ClientPolicyException; import org.keycloak.services.clientpolicy.ClientPolicyLogger; import org.keycloak.services.clientpolicy.ClientPolicyVote; -import org.keycloak.services.clientpolicy.ClientUpdateContext; +import org.keycloak.services.clientpolicy.context.ClientCRUDContext; import org.keycloak.services.clientregistration.ClientRegistrationTokenUtils; import org.keycloak.util.TokenUtil; @@ -45,7 +45,7 @@ public class ClientUpdateContextCondition extends AbstractClientPolicyConditionP switch (context.getEvent()) { case REGISTER: case UPDATE: - if (isAuthMethodMatched((ClientUpdateContext)context)) return ClientPolicyVote.YES; + if (isAuthMethodMatched((ClientCRUDContext)context)) return ClientPolicyVote.YES; return ClientPolicyVote.NO; default: return ClientPolicyVote.ABSTAIN; @@ -72,7 +72,7 @@ public class ClientUpdateContextCondition extends AbstractClientPolicyConditionP return isMatched; } - private boolean isAuthMethodMatched(ClientUpdateContext context) { + private boolean isAuthMethodMatched(ClientCRUDContext context) { String authMethod = null; if (context.getToken() == null) { diff --git a/services/src/main/java/org/keycloak/services/clientpolicy/condition/ClientUpdateSourceGroupsCondition.java b/services/src/main/java/org/keycloak/services/clientpolicy/condition/ClientUpdateSourceGroupsCondition.java index f3c4307330..62c5adf955 100644 --- a/services/src/main/java/org/keycloak/services/clientpolicy/condition/ClientUpdateSourceGroupsCondition.java +++ b/services/src/main/java/org/keycloak/services/clientpolicy/condition/ClientUpdateSourceGroupsCondition.java @@ -29,15 +29,15 @@ import org.keycloak.models.GroupModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.UserModel; import org.keycloak.representations.JsonWebToken; -import org.keycloak.services.clientpolicy.AdminClientRegisterContext; -import org.keycloak.services.clientpolicy.AdminClientUpdateContext; import org.keycloak.services.clientpolicy.ClientPolicyContext; import org.keycloak.services.clientpolicy.ClientPolicyException; import org.keycloak.services.clientpolicy.ClientPolicyLogger; import org.keycloak.services.clientpolicy.ClientPolicyVote; -import org.keycloak.services.clientpolicy.ClientUpdateContext; -import org.keycloak.services.clientpolicy.DynamicClientRegisterContext; -import org.keycloak.services.clientpolicy.DynamicClientUpdateContext; +import org.keycloak.services.clientpolicy.context.AdminClientRegisterContext; +import org.keycloak.services.clientpolicy.context.AdminClientUpdateContext; +import org.keycloak.services.clientpolicy.context.ClientCRUDContext; +import org.keycloak.services.clientpolicy.context.DynamicClientRegisterContext; +import org.keycloak.services.clientpolicy.context.DynamicClientUpdateContext; public class ClientUpdateSourceGroupsCondition extends AbstractClientPolicyConditionProvider { @@ -52,17 +52,17 @@ public class ClientUpdateSourceGroupsCondition extends AbstractClientPolicyCondi switch (context.getEvent()) { case REGISTER: if (context instanceof AdminClientRegisterContext) { - return getVoteForGroupsMatched(((ClientUpdateContext)context).getAuthenticatedUser()); + return getVoteForGroupsMatched(((ClientCRUDContext)context).getAuthenticatedUser()); } else if (context instanceof DynamicClientRegisterContext) { - return getVoteForGroupsMatched(((ClientUpdateContext)context).getToken()); + return getVoteForGroupsMatched(((ClientCRUDContext)context).getToken()); } else { throw new ClientPolicyException(OAuthErrorException.SERVER_ERROR, "unexpected context type."); } case UPDATE: if (context instanceof AdminClientUpdateContext) { - return getVoteForGroupsMatched(((ClientUpdateContext)context).getAuthenticatedUser()); + return getVoteForGroupsMatched(((ClientCRUDContext)context).getAuthenticatedUser()); } else if (context instanceof DynamicClientUpdateContext) { - return getVoteForGroupsMatched(((ClientUpdateContext)context).getToken()); + return getVoteForGroupsMatched(((ClientCRUDContext)context).getToken()); } else { throw new ClientPolicyException(OAuthErrorException.SERVER_ERROR, "unexpected context type."); } diff --git a/services/src/main/java/org/keycloak/services/clientpolicy/condition/ClientUpdateSourceRolesCondition.java b/services/src/main/java/org/keycloak/services/clientpolicy/condition/ClientUpdateSourceRolesCondition.java index 5d8f06c799..c5708a2abb 100644 --- a/services/src/main/java/org/keycloak/services/clientpolicy/condition/ClientUpdateSourceRolesCondition.java +++ b/services/src/main/java/org/keycloak/services/clientpolicy/condition/ClientUpdateSourceRolesCondition.java @@ -30,15 +30,15 @@ import org.keycloak.models.RealmModel; import org.keycloak.models.RoleModel; import org.keycloak.models.UserModel; import org.keycloak.representations.JsonWebToken; -import org.keycloak.services.clientpolicy.AdminClientRegisterContext; -import org.keycloak.services.clientpolicy.AdminClientUpdateContext; import org.keycloak.services.clientpolicy.ClientPolicyContext; import org.keycloak.services.clientpolicy.ClientPolicyException; import org.keycloak.services.clientpolicy.ClientPolicyLogger; import org.keycloak.services.clientpolicy.ClientPolicyVote; -import org.keycloak.services.clientpolicy.ClientUpdateContext; -import org.keycloak.services.clientpolicy.DynamicClientRegisterContext; -import org.keycloak.services.clientpolicy.DynamicClientUpdateContext; +import org.keycloak.services.clientpolicy.context.AdminClientRegisterContext; +import org.keycloak.services.clientpolicy.context.AdminClientUpdateContext; +import org.keycloak.services.clientpolicy.context.ClientCRUDContext; +import org.keycloak.services.clientpolicy.context.DynamicClientRegisterContext; +import org.keycloak.services.clientpolicy.context.DynamicClientUpdateContext; public class ClientUpdateSourceRolesCondition extends AbstractClientPolicyConditionProvider { @@ -53,17 +53,17 @@ public class ClientUpdateSourceRolesCondition extends AbstractClientPolicyCondit switch (context.getEvent()) { case REGISTER: if (context instanceof AdminClientRegisterContext) { - return getVoteForRolesMatched(((ClientUpdateContext)context).getAuthenticatedUser()); + return getVoteForRolesMatched(((ClientCRUDContext)context).getAuthenticatedUser()); } else if (context instanceof DynamicClientRegisterContext) { - return getVoteForRolesMatched(((ClientUpdateContext)context).getToken()); + return getVoteForRolesMatched(((ClientCRUDContext)context).getToken()); } else { throw new ClientPolicyException(OAuthErrorException.SERVER_ERROR, "unexpected context type."); } case UPDATE: if (context instanceof AdminClientUpdateContext) { - return getVoteForRolesMatched(((ClientUpdateContext)context).getAuthenticatedUser()); + return getVoteForRolesMatched(((ClientCRUDContext)context).getAuthenticatedUser()); } else if (context instanceof DynamicClientUpdateContext) { - return getVoteForRolesMatched(((ClientUpdateContext)context).getToken()); + return getVoteForRolesMatched(((ClientCRUDContext)context).getToken()); } else { throw new ClientPolicyException(OAuthErrorException.SERVER_ERROR, "unexpected context type."); } diff --git a/services/src/main/java/org/keycloak/services/clientpolicy/AdminClientRegisterContext.java b/services/src/main/java/org/keycloak/services/clientpolicy/context/AbstractAdminClientCRUDContext.java similarity index 59% rename from services/src/main/java/org/keycloak/services/clientpolicy/AdminClientRegisterContext.java rename to services/src/main/java/org/keycloak/services/clientpolicy/context/AbstractAdminClientCRUDContext.java index a0b5a24233..3f85355076 100644 --- a/services/src/main/java/org/keycloak/services/clientpolicy/AdminClientRegisterContext.java +++ b/services/src/main/java/org/keycloak/services/clientpolicy/context/AbstractAdminClientCRUDContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Red Hat, Inc. and/or its affiliates + * Copyright 2021 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"); @@ -15,36 +15,21 @@ * limitations under the License. */ -package org.keycloak.services.clientpolicy; +package org.keycloak.services.clientpolicy.context; import org.keycloak.models.ClientModel; import org.keycloak.models.UserModel; import org.keycloak.representations.JsonWebToken; -import org.keycloak.representations.idm.ClientRepresentation; -import org.keycloak.services.clientpolicy.ClientPolicyEvent; import org.keycloak.services.resources.admin.AdminAuth; -public class AdminClientRegisterContext implements ClientUpdateContext { +abstract class AbstractAdminClientCRUDContext implements ClientCRUDContext { - private final ClientRepresentation clientRepresentation; - private final AdminAuth adminAuth; + protected final AdminAuth adminAuth; - public AdminClientRegisterContext(ClientRepresentation clientRepresentation, - AdminAuth adminAuth) { - this.clientRepresentation = clientRepresentation; + public AbstractAdminClientCRUDContext(AdminAuth adminAuth) { this.adminAuth = adminAuth; } - @Override - public ClientPolicyEvent getEvent() { - return ClientPolicyEvent.REGISTER; - } - - @Override - public ClientRepresentation getProposedClientRepresentation() { - return clientRepresentation; - } - @Override public ClientModel getAuthenticatedClient() { return adminAuth.getClient(); diff --git a/services/src/main/java/org/keycloak/services/clientpolicy/context/AbstractDynamicClientCRUDContext.java b/services/src/main/java/org/keycloak/services/clientpolicy/context/AbstractDynamicClientCRUDContext.java new file mode 100644 index 0000000000..89de264f89 --- /dev/null +++ b/services/src/main/java/org/keycloak/services/clientpolicy/context/AbstractDynamicClientCRUDContext.java @@ -0,0 +1,59 @@ +/* + * Copyright 2021 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.services.clientpolicy.context; + +import org.keycloak.models.ClientModel; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.models.UserModel; +import org.keycloak.representations.JsonWebToken; + +abstract class AbstractDynamicClientCRUDContext implements ClientCRUDContext { + + private final JsonWebToken token; + private ClientModel authenticatedClient; + private UserModel authenticatedUser; + + public AbstractDynamicClientCRUDContext(KeycloakSession session, JsonWebToken token, RealmModel realm) { + this.token = token; + if (token == null) { + return; + } + if (token.getIssuedFor() != null) { + this.authenticatedClient = realm.getClientByClientId(token.getIssuedFor()); + } + if (token.getSubject() != null) { + this.authenticatedUser = session.users().getUserById(token.getSubject(), realm); + } + } + + @Override + public ClientModel getAuthenticatedClient() { + return authenticatedClient; + } + + @Override + public UserModel getAuthenticatedUser() { + return authenticatedUser; + } + + @Override + public JsonWebToken getToken() { + return token; + } +} diff --git a/services/src/main/java/org/keycloak/services/clientpolicy/context/AdminClientRegisterContext.java b/services/src/main/java/org/keycloak/services/clientpolicy/context/AdminClientRegisterContext.java new file mode 100644 index 0000000000..9b677d4aec --- /dev/null +++ b/services/src/main/java/org/keycloak/services/clientpolicy/context/AdminClientRegisterContext.java @@ -0,0 +1,42 @@ +/* + * Copyright 2020 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.services.clientpolicy.context; + +import org.keycloak.representations.idm.ClientRepresentation; +import org.keycloak.services.clientpolicy.ClientPolicyEvent; +import org.keycloak.services.resources.admin.AdminAuth; + +public class AdminClientRegisterContext extends AbstractAdminClientCRUDContext { + + private final ClientRepresentation proposedClientRepresentation; + + public AdminClientRegisterContext(ClientRepresentation proposedClientRepresentation, AdminAuth adminAuth) { + super(adminAuth); + this.proposedClientRepresentation = proposedClientRepresentation; + } + + @Override + public ClientPolicyEvent getEvent() { + return ClientPolicyEvent.REGISTER; + } + + @Override + public ClientRepresentation getProposedClientRepresentation() { + return proposedClientRepresentation; + } +} diff --git a/services/src/main/java/org/keycloak/services/clientpolicy/context/AdminClientRegisteredContext.java b/services/src/main/java/org/keycloak/services/clientpolicy/context/AdminClientRegisteredContext.java new file mode 100644 index 0000000000..576dbaa29d --- /dev/null +++ b/services/src/main/java/org/keycloak/services/clientpolicy/context/AdminClientRegisteredContext.java @@ -0,0 +1,42 @@ +/* + * Copyright 2021 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.services.clientpolicy.context; + +import org.keycloak.models.ClientModel; +import org.keycloak.services.clientpolicy.ClientPolicyEvent; +import org.keycloak.services.resources.admin.AdminAuth; + +public class AdminClientRegisteredContext extends AbstractAdminClientCRUDContext { + + private final ClientModel registeredClient; + + public AdminClientRegisteredContext(ClientModel registeredClient, AdminAuth adminAuth) { + super(adminAuth); + this.registeredClient = registeredClient; + } + + @Override + public ClientPolicyEvent getEvent() { + return ClientPolicyEvent.REGISTERED; + } + + @Override + public ClientModel getTargetClient() { + return registeredClient; + } +} diff --git a/services/src/main/java/org/keycloak/services/clientpolicy/context/AdminClientUnregisterContext.java b/services/src/main/java/org/keycloak/services/clientpolicy/context/AdminClientUnregisterContext.java new file mode 100644 index 0000000000..4a9ffde6a5 --- /dev/null +++ b/services/src/main/java/org/keycloak/services/clientpolicy/context/AdminClientUnregisterContext.java @@ -0,0 +1,42 @@ +/* + * Copyright 2021 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.services.clientpolicy.context; + +import org.keycloak.models.ClientModel; +import org.keycloak.services.clientpolicy.ClientPolicyEvent; +import org.keycloak.services.resources.admin.AdminAuth; + +public class AdminClientUnregisterContext extends AbstractAdminClientCRUDContext { + + private final ClientModel targetClient; + + public AdminClientUnregisterContext(ClientModel targetClient, AdminAuth adminAuth) { + super(adminAuth); + this.targetClient = targetClient; + } + + @Override + public ClientPolicyEvent getEvent() { + return ClientPolicyEvent.UNREGISTER; + } + + @Override + public ClientModel getTargetClient() { + return this.targetClient; + } +} diff --git a/services/src/main/java/org/keycloak/services/clientpolicy/AdminClientUpdateContext.java b/services/src/main/java/org/keycloak/services/clientpolicy/context/AdminClientUpdateContext.java similarity index 52% rename from services/src/main/java/org/keycloak/services/clientpolicy/AdminClientUpdateContext.java rename to services/src/main/java/org/keycloak/services/clientpolicy/context/AdminClientUpdateContext.java index ce035f9385..b6da434afa 100644 --- a/services/src/main/java/org/keycloak/services/clientpolicy/AdminClientUpdateContext.java +++ b/services/src/main/java/org/keycloak/services/clientpolicy/context/AdminClientUpdateContext.java @@ -15,26 +15,22 @@ * limitations under the License. */ -package org.keycloak.services.clientpolicy; +package org.keycloak.services.clientpolicy.context; import org.keycloak.models.ClientModel; -import org.keycloak.models.UserModel; -import org.keycloak.representations.JsonWebToken; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.services.clientpolicy.ClientPolicyEvent; import org.keycloak.services.resources.admin.AdminAuth; -public class AdminClientUpdateContext implements ClientUpdateContext { +public class AdminClientUpdateContext extends AbstractAdminClientCRUDContext { - private final ClientRepresentation clientRepresentation; - private final AdminAuth adminAuth; - private final ClientModel client; + private final ClientRepresentation proposedClientRepresentation; + private final ClientModel targetClient; - public AdminClientUpdateContext(ClientRepresentation clientRepresentation, - AdminAuth adminAuth, ClientModel client) { - this.clientRepresentation = clientRepresentation; - this.adminAuth = adminAuth; - this.client = client; + public AdminClientUpdateContext(ClientRepresentation proposedClientRepresentation, ClientModel targetClient, AdminAuth adminAuth) { + super(adminAuth); + this.proposedClientRepresentation = proposedClientRepresentation; + this.targetClient = targetClient; } @Override @@ -44,26 +40,11 @@ public class AdminClientUpdateContext implements ClientUpdateContext { @Override public ClientRepresentation getProposedClientRepresentation() { - return clientRepresentation; + return proposedClientRepresentation; } @Override - public ClientModel getClientToBeUpdated() { - return client; - } - - @Override - public ClientModel getAuthenticatedClient() { - return adminAuth.getClient(); - } - - @Override - public UserModel getAuthenticatedUser() { - return adminAuth.getUser(); - } - - @Override - public JsonWebToken getToken() { - return adminAuth.getToken(); + public ClientModel getTargetClient() { + return targetClient; } } diff --git a/services/src/main/java/org/keycloak/services/clientpolicy/context/AdminClientUpdatedContext.java b/services/src/main/java/org/keycloak/services/clientpolicy/context/AdminClientUpdatedContext.java new file mode 100644 index 0000000000..9dfda2c131 --- /dev/null +++ b/services/src/main/java/org/keycloak/services/clientpolicy/context/AdminClientUpdatedContext.java @@ -0,0 +1,50 @@ +/* + * Copyright 2021 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.services.clientpolicy.context; + +import org.keycloak.models.ClientModel; +import org.keycloak.representations.idm.ClientRepresentation; +import org.keycloak.services.clientpolicy.ClientPolicyEvent; +import org.keycloak.services.resources.admin.AdminAuth; + +public class AdminClientUpdatedContext extends AbstractAdminClientCRUDContext { + + private final ClientRepresentation proposedClientRepresentation; + private final ClientModel updatedClient; + + public AdminClientUpdatedContext(ClientRepresentation roposedClientRepresentation, ClientModel updatedClient, AdminAuth adminAuth) { + super(adminAuth); + this.proposedClientRepresentation = roposedClientRepresentation; + this.updatedClient = updatedClient; + } + + @Override + public ClientPolicyEvent getEvent() { + return ClientPolicyEvent.UPDATED; + } + + @Override + public ClientRepresentation getProposedClientRepresentation() { + return proposedClientRepresentation; + } + + @Override + public ClientModel getTargetClient() { + return updatedClient; + } +} diff --git a/services/src/main/java/org/keycloak/services/clientpolicy/context/AdminClientViewContext.java b/services/src/main/java/org/keycloak/services/clientpolicy/context/AdminClientViewContext.java new file mode 100644 index 0000000000..1427b42ad1 --- /dev/null +++ b/services/src/main/java/org/keycloak/services/clientpolicy/context/AdminClientViewContext.java @@ -0,0 +1,42 @@ +/* + * Copyright 2021 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.services.clientpolicy.context; + +import org.keycloak.models.ClientModel; +import org.keycloak.services.clientpolicy.ClientPolicyEvent; +import org.keycloak.services.resources.admin.AdminAuth; + +public class AdminClientViewContext extends AbstractAdminClientCRUDContext { + + private final ClientModel targetClient; + + public AdminClientViewContext(ClientModel targetClient, AdminAuth adminAuth) { + super(adminAuth); + this.targetClient = targetClient; + } + + @Override + public ClientPolicyEvent getEvent() { + return ClientPolicyEvent.VIEW; + } + + @Override + public ClientModel getTargetClient() { + return targetClient; + } +} diff --git a/services/src/main/java/org/keycloak/services/clientpolicy/AuthorizationRequestContext.java b/services/src/main/java/org/keycloak/services/clientpolicy/context/AuthorizationRequestContext.java similarity index 97% rename from services/src/main/java/org/keycloak/services/clientpolicy/AuthorizationRequestContext.java rename to services/src/main/java/org/keycloak/services/clientpolicy/context/AuthorizationRequestContext.java index 75e46d3647..8733563838 100644 --- a/services/src/main/java/org/keycloak/services/clientpolicy/AuthorizationRequestContext.java +++ b/services/src/main/java/org/keycloak/services/clientpolicy/context/AuthorizationRequestContext.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.services.clientpolicy; +package org.keycloak.services.clientpolicy.context; import javax.ws.rs.core.MultivaluedMap; diff --git a/services/src/main/java/org/keycloak/services/clientpolicy/ClientUpdateContext.java b/services/src/main/java/org/keycloak/services/clientpolicy/context/ClientCRUDContext.java similarity index 56% rename from services/src/main/java/org/keycloak/services/clientpolicy/ClientUpdateContext.java rename to services/src/main/java/org/keycloak/services/clientpolicy/context/ClientCRUDContext.java index d96467347c..9ac429e484 100644 --- a/services/src/main/java/org/keycloak/services/clientpolicy/ClientUpdateContext.java +++ b/services/src/main/java/org/keycloak/services/clientpolicy/context/ClientCRUDContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Red Hat, Inc. and/or its affiliates + * Copyright 2021 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"); @@ -15,31 +15,35 @@ * limitations under the License. */ -package org.keycloak.services.clientpolicy; +package org.keycloak.services.clientpolicy.context; import org.keycloak.models.ClientModel; import org.keycloak.models.UserModel; import org.keycloak.representations.JsonWebToken; import org.keycloak.representations.idm.ClientRepresentation; +import org.keycloak.services.clientpolicy.ClientPolicyContext; /** - * Represents the context in the client registration/update by Dynamic Client Registration or Admin REST API. + * Represents the context in the request to register/read/update/unregister client by Dynamic Client Registration or Admin REST API. */ -public interface ClientUpdateContext extends ClientPolicyContext { +public interface ClientCRUDContext extends ClientPolicyContext { /** - * returns {@link ClientRepresentation} for creating or updating the current client. + * returns {@link ClientRepresentation} for creating the new client or updating the existing client. * * @return {@link ClientRepresentation} */ - ClientRepresentation getProposedClientRepresentation(); + default ClientRepresentation getProposedClientRepresentation() { + return null; + } /** - * returns {@link ClientModel} of the current client to be updated. + * returns {@link ClientModel} of the existing client to be updated/read/updated/deleted. + * on REGISTER event, it returns null. * * @return {@link ClientModel} */ - default ClientModel getClientToBeUpdated() { + default ClientModel getTargetClient() { return null; } @@ -48,19 +52,25 @@ public interface ClientUpdateContext extends ClientPolicyContext { * * @return {@link UserModel} */ - UserModel getAuthenticatedUser(); + default UserModel getAuthenticatedUser() { + return null; + } + /** * returns {@link UserModel} of the authenticated client. * * @return {@link UserModel} */ - ClientModel getAuthenticatedClient(); + default ClientModel getAuthenticatedClient() { + return null; + } /** - * returns {@link JsonWebToken} of the token accompanied with registration/update client + * returns {@link JsonWebToken} of the token accompanied with the request to register/read/update/unregister client * * @return {@link JsonWebToken} */ - JsonWebToken getToken(); - + default JsonWebToken getToken() { + return null; + } } diff --git a/services/src/main/java/org/keycloak/services/clientpolicy/DynamicClientRegisterContext.java b/services/src/main/java/org/keycloak/services/clientpolicy/context/DynamicClientRegisterContext.java similarity index 52% rename from services/src/main/java/org/keycloak/services/clientpolicy/DynamicClientRegisterContext.java rename to services/src/main/java/org/keycloak/services/clientpolicy/context/DynamicClientRegisterContext.java index f2ff5cb51e..6f7e9b7715 100644 --- a/services/src/main/java/org/keycloak/services/clientpolicy/DynamicClientRegisterContext.java +++ b/services/src/main/java/org/keycloak/services/clientpolicy/context/DynamicClientRegisterContext.java @@ -15,35 +15,21 @@ * limitations under the License. */ -package org.keycloak.services.clientpolicy; +package org.keycloak.services.clientpolicy.context; -import org.keycloak.models.ClientModel; import org.keycloak.models.RealmModel; -import org.keycloak.models.UserModel; import org.keycloak.representations.JsonWebToken; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.services.clientpolicy.ClientPolicyEvent; import org.keycloak.services.clientregistration.ClientRegistrationContext; -public class DynamicClientRegisterContext implements ClientUpdateContext { +public class DynamicClientRegisterContext extends AbstractDynamicClientCRUDContext { - private final ClientRegistrationContext context; - private JsonWebToken token; - private UserModel user; - private ClientModel client; + private final ClientRepresentation proposedClientRepresentation; - public DynamicClientRegisterContext(ClientRegistrationContext context, - JsonWebToken token, RealmModel realm) { - this.context = context; - this.token = token; - if (token != null) { - if (token.getSubject() != null) { - this.user = context.getSession().users().getUserById(realm, token.getSubject()); - } - if (token.getIssuedFor() != null) { - this.client = realm.getClientByClientId(token.getIssuedFor()); - } - } + public DynamicClientRegisterContext(ClientRegistrationContext context, JsonWebToken token, RealmModel realm) { + super(context.getSession(), token, realm); + this.proposedClientRepresentation = context.getClient(); } @Override @@ -53,21 +39,6 @@ public class DynamicClientRegisterContext implements ClientUpdateContext { @Override public ClientRepresentation getProposedClientRepresentation() { - return context.getClient(); - } - - @Override - public ClientModel getAuthenticatedClient() { - return client; - } - - @Override - public UserModel getAuthenticatedUser() { - return user; - } - - @Override - public JsonWebToken getToken() { - return token; + return proposedClientRepresentation; } } diff --git a/services/src/main/java/org/keycloak/services/clientpolicy/context/DynamicClientRegisteredContext.java b/services/src/main/java/org/keycloak/services/clientpolicy/context/DynamicClientRegisteredContext.java new file mode 100644 index 0000000000..70ec084d24 --- /dev/null +++ b/services/src/main/java/org/keycloak/services/clientpolicy/context/DynamicClientRegisteredContext.java @@ -0,0 +1,44 @@ +/* + * Copyright 2021 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.services.clientpolicy.context; + +import org.keycloak.models.ClientModel; +import org.keycloak.models.RealmModel; +import org.keycloak.representations.JsonWebToken; +import org.keycloak.services.clientpolicy.ClientPolicyEvent; +import org.keycloak.services.clientregistration.ClientRegistrationContext; + +public class DynamicClientRegisteredContext extends AbstractDynamicClientCRUDContext { + + private final ClientModel registeredClient; + + public DynamicClientRegisteredContext(ClientRegistrationContext context, ClientModel registeredClient, JsonWebToken token, RealmModel realm) { + super(context.getSession(), token, realm); + this.registeredClient = registeredClient; + } + + @Override + public ClientPolicyEvent getEvent() { + return ClientPolicyEvent.REGISTERED; + } + + @Override + public ClientModel getTargetClient() { + return registeredClient; + } +} diff --git a/services/src/main/java/org/keycloak/services/clientpolicy/context/DynamicClientUnregisterContext.java b/services/src/main/java/org/keycloak/services/clientpolicy/context/DynamicClientUnregisterContext.java new file mode 100644 index 0000000000..7b2c9be544 --- /dev/null +++ b/services/src/main/java/org/keycloak/services/clientpolicy/context/DynamicClientUnregisterContext.java @@ -0,0 +1,44 @@ +/* + * Copyright 2021 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.services.clientpolicy.context; + +import org.keycloak.models.ClientModel; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.representations.JsonWebToken; +import org.keycloak.services.clientpolicy.ClientPolicyEvent; + +public class DynamicClientUnregisterContext extends AbstractDynamicClientCRUDContext { + + private final ClientModel targetClient; + + public DynamicClientUnregisterContext(KeycloakSession session, ClientModel targetClient, JsonWebToken token, RealmModel realm) { + super(session, token, realm); + this.targetClient = targetClient; + } + + @Override + public ClientPolicyEvent getEvent() { + return ClientPolicyEvent.UNREGISTER; + } + + @Override + public ClientModel getTargetClient() { + return this.targetClient; + } +} diff --git a/services/src/main/java/org/keycloak/services/clientpolicy/DynamicClientUpdateContext.java b/services/src/main/java/org/keycloak/services/clientpolicy/context/DynamicClientUpdateContext.java similarity index 54% rename from services/src/main/java/org/keycloak/services/clientpolicy/DynamicClientUpdateContext.java rename to services/src/main/java/org/keycloak/services/clientpolicy/context/DynamicClientUpdateContext.java index 87c5107d03..4a757ff855 100644 --- a/services/src/main/java/org/keycloak/services/clientpolicy/DynamicClientUpdateContext.java +++ b/services/src/main/java/org/keycloak/services/clientpolicy/context/DynamicClientUpdateContext.java @@ -15,37 +15,24 @@ * limitations under the License. */ -package org.keycloak.services.clientpolicy; +package org.keycloak.services.clientpolicy.context; import org.keycloak.models.ClientModel; import org.keycloak.models.RealmModel; -import org.keycloak.models.UserModel; import org.keycloak.representations.JsonWebToken; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.services.clientpolicy.ClientPolicyEvent; import org.keycloak.services.clientregistration.ClientRegistrationContext; -public class DynamicClientUpdateContext implements ClientUpdateContext { +public class DynamicClientUpdateContext extends AbstractDynamicClientCRUDContext { - private final ClientRegistrationContext context; private final ClientModel clientToBeUpdated; - private JsonWebToken token; - private UserModel user; - private ClientModel client; + private final ClientRepresentation proposedClientRepresentation; - public DynamicClientUpdateContext(ClientRegistrationContext context, - ClientModel client, JsonWebToken token, RealmModel realm) { - this.context = context; - this.clientToBeUpdated = client; - this.token = token; - if (token != null) { - if (token.getSubject() != null) { - this.user = context.getSession().users().getUserById(realm, token.getSubject()); - } - if (token.getIssuedFor() != null) { - this.client = realm.getClientByClientId(token.getIssuedFor()); - } - } + public DynamicClientUpdateContext(ClientRegistrationContext context, ClientModel proposedClientRepresentation, JsonWebToken token, RealmModel realm) { + super(context.getSession(), token, realm); + this.clientToBeUpdated = proposedClientRepresentation; + this.proposedClientRepresentation = context.getClient(); } @Override @@ -55,26 +42,11 @@ public class DynamicClientUpdateContext implements ClientUpdateContext { @Override public ClientRepresentation getProposedClientRepresentation() { - return context.getClient(); + return proposedClientRepresentation; } @Override - public ClientModel getClientToBeUpdated() { + public ClientModel getTargetClient() { return clientToBeUpdated; } - - @Override - public ClientModel getAuthenticatedClient() { - return client; - } - - @Override - public UserModel getAuthenticatedUser() { - return user; - } - - @Override - public JsonWebToken getToken() { - return token; - } } diff --git a/services/src/main/java/org/keycloak/services/clientpolicy/context/DynamicClientUpdatedContext.java b/services/src/main/java/org/keycloak/services/clientpolicy/context/DynamicClientUpdatedContext.java new file mode 100644 index 0000000000..27ea675fe4 --- /dev/null +++ b/services/src/main/java/org/keycloak/services/clientpolicy/context/DynamicClientUpdatedContext.java @@ -0,0 +1,44 @@ +/* + * Copyright 2021 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.services.clientpolicy.context; + +import org.keycloak.models.ClientModel; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.representations.JsonWebToken; +import org.keycloak.services.clientpolicy.ClientPolicyEvent; + +public class DynamicClientUpdatedContext extends AbstractDynamicClientCRUDContext { + + private final ClientModel updatedClient; + + public DynamicClientUpdatedContext(KeycloakSession session, ClientModel updatedClient, JsonWebToken token, RealmModel realm) { + super(session, token, realm); + this.updatedClient = updatedClient; + } + + @Override + public ClientPolicyEvent getEvent() { + return ClientPolicyEvent.UPDATED; + } + + @Override + public ClientModel getTargetClient() { + return updatedClient; + } +} diff --git a/services/src/main/java/org/keycloak/services/clientpolicy/context/DynamicClientViewContext.java b/services/src/main/java/org/keycloak/services/clientpolicy/context/DynamicClientViewContext.java new file mode 100644 index 0000000000..e1f899506c --- /dev/null +++ b/services/src/main/java/org/keycloak/services/clientpolicy/context/DynamicClientViewContext.java @@ -0,0 +1,44 @@ +/* + * Copyright 2021 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.services.clientpolicy.context; + +import org.keycloak.models.ClientModel; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.representations.JsonWebToken; +import org.keycloak.services.clientpolicy.ClientPolicyEvent; + +public class DynamicClientViewContext extends AbstractDynamicClientCRUDContext { + + private final ClientModel targetClient; + + public DynamicClientViewContext(KeycloakSession session, ClientModel targetClient, JsonWebToken token, RealmModel realm) { + super(session, token, realm); + this.targetClient = targetClient; + } + + @Override + public ClientPolicyEvent getEvent() { + return ClientPolicyEvent.VIEW; + } + + @Override + public ClientModel getTargetClient() { + return targetClient; + } +} diff --git a/services/src/main/java/org/keycloak/services/clientpolicy/LogoutRequestContext.java b/services/src/main/java/org/keycloak/services/clientpolicy/context/LogoutRequestContext.java similarity index 96% rename from services/src/main/java/org/keycloak/services/clientpolicy/LogoutRequestContext.java rename to services/src/main/java/org/keycloak/services/clientpolicy/context/LogoutRequestContext.java index 3eef068897..4a236e4cc5 100644 --- a/services/src/main/java/org/keycloak/services/clientpolicy/LogoutRequestContext.java +++ b/services/src/main/java/org/keycloak/services/clientpolicy/context/LogoutRequestContext.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.services.clientpolicy; +package org.keycloak.services.clientpolicy.context; import javax.ws.rs.core.MultivaluedMap; diff --git a/services/src/main/java/org/keycloak/services/clientpolicy/TokenIntrospectContext.java b/services/src/main/java/org/keycloak/services/clientpolicy/context/TokenIntrospectContext.java similarity index 96% rename from services/src/main/java/org/keycloak/services/clientpolicy/TokenIntrospectContext.java rename to services/src/main/java/org/keycloak/services/clientpolicy/context/TokenIntrospectContext.java index 591adcce7d..53d2e3feae 100644 --- a/services/src/main/java/org/keycloak/services/clientpolicy/TokenIntrospectContext.java +++ b/services/src/main/java/org/keycloak/services/clientpolicy/context/TokenIntrospectContext.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.services.clientpolicy; +package org.keycloak.services.clientpolicy.context; import javax.ws.rs.core.MultivaluedMap; diff --git a/services/src/main/java/org/keycloak/services/clientpolicy/TokenRefreshContext.java b/services/src/main/java/org/keycloak/services/clientpolicy/context/TokenRefreshContext.java similarity index 96% rename from services/src/main/java/org/keycloak/services/clientpolicy/TokenRefreshContext.java rename to services/src/main/java/org/keycloak/services/clientpolicy/context/TokenRefreshContext.java index 8f54c25533..e322f147bb 100644 --- a/services/src/main/java/org/keycloak/services/clientpolicy/TokenRefreshContext.java +++ b/services/src/main/java/org/keycloak/services/clientpolicy/context/TokenRefreshContext.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.services.clientpolicy; +package org.keycloak.services.clientpolicy.context; import javax.ws.rs.core.MultivaluedMap; diff --git a/services/src/main/java/org/keycloak/services/clientpolicy/TokenRequestContext.java b/services/src/main/java/org/keycloak/services/clientpolicy/context/TokenRequestContext.java similarity index 96% rename from services/src/main/java/org/keycloak/services/clientpolicy/TokenRequestContext.java rename to services/src/main/java/org/keycloak/services/clientpolicy/context/TokenRequestContext.java index fc1e554b2d..7d35f8ad15 100644 --- a/services/src/main/java/org/keycloak/services/clientpolicy/TokenRequestContext.java +++ b/services/src/main/java/org/keycloak/services/clientpolicy/context/TokenRequestContext.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.services.clientpolicy; +package org.keycloak.services.clientpolicy.context; import javax.ws.rs.core.MultivaluedMap; diff --git a/services/src/main/java/org/keycloak/services/clientpolicy/TokenRevokeContext.java b/services/src/main/java/org/keycloak/services/clientpolicy/context/TokenRevokeContext.java similarity index 96% rename from services/src/main/java/org/keycloak/services/clientpolicy/TokenRevokeContext.java rename to services/src/main/java/org/keycloak/services/clientpolicy/context/TokenRevokeContext.java index 03d74a4411..16c70e86f8 100644 --- a/services/src/main/java/org/keycloak/services/clientpolicy/TokenRevokeContext.java +++ b/services/src/main/java/org/keycloak/services/clientpolicy/context/TokenRevokeContext.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.services.clientpolicy; +package org.keycloak.services.clientpolicy.context; import javax.ws.rs.core.MultivaluedMap; diff --git a/services/src/main/java/org/keycloak/services/clientpolicy/UserInfoRequestContext.java b/services/src/main/java/org/keycloak/services/clientpolicy/context/UserInfoRequestContext.java similarity index 95% rename from services/src/main/java/org/keycloak/services/clientpolicy/UserInfoRequestContext.java rename to services/src/main/java/org/keycloak/services/clientpolicy/context/UserInfoRequestContext.java index ddf4d3f5c0..f8c80b5cbf 100644 --- a/services/src/main/java/org/keycloak/services/clientpolicy/UserInfoRequestContext.java +++ b/services/src/main/java/org/keycloak/services/clientpolicy/context/UserInfoRequestContext.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.services.clientpolicy; +package org.keycloak.services.clientpolicy.context; import org.keycloak.services.clientpolicy.ClientPolicyContext; import org.keycloak.services.clientpolicy.ClientPolicyEvent; diff --git a/services/src/main/java/org/keycloak/services/clientpolicy/executor/AbstractAugumentingClientRegistrationPolicyExecutor.java b/services/src/main/java/org/keycloak/services/clientpolicy/executor/AbstractAugumentingClientRegistrationPolicyExecutor.java index 3a83c3e908..893fd01b47 100644 --- a/services/src/main/java/org/keycloak/services/clientpolicy/executor/AbstractAugumentingClientRegistrationPolicyExecutor.java +++ b/services/src/main/java/org/keycloak/services/clientpolicy/executor/AbstractAugumentingClientRegistrationPolicyExecutor.java @@ -23,7 +23,7 @@ import org.keycloak.models.KeycloakSession; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.services.clientpolicy.ClientPolicyContext; import org.keycloak.services.clientpolicy.ClientPolicyException; -import org.keycloak.services.clientpolicy.ClientUpdateContext; +import org.keycloak.services.clientpolicy.context.ClientCRUDContext; import org.keycloak.services.clientpolicy.executor.ClientPolicyExecutorProvider; public abstract class AbstractAugumentingClientRegistrationPolicyExecutor implements ClientPolicyExecutorProvider { @@ -46,7 +46,7 @@ public abstract class AbstractAugumentingClientRegistrationPolicyExecutor implem switch (context.getEvent()) { case REGISTER: case UPDATE: - ClientUpdateContext clientUpdateContext = (ClientUpdateContext)context; + ClientCRUDContext clientUpdateContext = (ClientCRUDContext)context; augment(clientUpdateContext.getProposedClientRepresentation()); validate(clientUpdateContext.getProposedClientRepresentation()); break; diff --git a/services/src/main/java/org/keycloak/services/clientpolicy/executor/HolderOfKeyEnforceExecutor.java b/services/src/main/java/org/keycloak/services/clientpolicy/executor/HolderOfKeyEnforceExecutor.java index 85975855e7..d083a32e10 100644 --- a/services/src/main/java/org/keycloak/services/clientpolicy/executor/HolderOfKeyEnforceExecutor.java +++ b/services/src/main/java/org/keycloak/services/clientpolicy/executor/HolderOfKeyEnforceExecutor.java @@ -27,7 +27,12 @@ import org.keycloak.protocol.oidc.OIDCAdvancedConfigWrapper; import org.keycloak.representations.AccessToken; import org.keycloak.representations.RefreshToken; import org.keycloak.representations.idm.ClientRepresentation; -import org.keycloak.services.clientpolicy.*; +import org.keycloak.services.clientpolicy.ClientPolicyContext; +import org.keycloak.services.clientpolicy.ClientPolicyException; +import org.keycloak.services.clientpolicy.context.LogoutRequestContext; +import org.keycloak.services.clientpolicy.context.TokenRefreshContext; +import org.keycloak.services.clientpolicy.context.TokenRevokeContext; +import org.keycloak.services.clientpolicy.context.UserInfoRequestContext; import org.keycloak.services.util.MtlsHoKTokenUtil; import javax.ws.rs.core.MultivaluedMap; @@ -75,29 +80,26 @@ public class HolderOfKeyEnforceExecutor extends AbstractAugumentingClientRegistr HttpRequest request = session.getContext().getContextObject(HttpRequest.class); switch (context.getEvent()) { - case TOKEN_REQUEST: AccessToken.CertConf certConf = MtlsHoKTokenUtil.bindTokenWithClientCertificate(request, session); if (certConf == null) { throw new ClientPolicyException(OAuthErrorException.INVALID_REQUEST, "Client Certification missing for MTLS HoK Token Binding"); } break; - case TOKEN_REFRESH: checkTokenRefresh((TokenRefreshContext) context, request); break; - case TOKEN_REVOKE: checkTokenRevoke((TokenRevokeContext) context, request); break; - case USERINFO_REQUEST: checkUserInfo((UserInfoRequestContext) context, request); break; - case LOGOUT_REQUEST: checkLogout((LogoutRequestContext) context, request); break; + default: + return; } } diff --git a/services/src/main/java/org/keycloak/services/clientpolicy/executor/PKCEEnforceExecutor.java b/services/src/main/java/org/keycloak/services/clientpolicy/executor/PKCEEnforceExecutor.java index cf00a14b39..17ce256d30 100644 --- a/services/src/main/java/org/keycloak/services/clientpolicy/executor/PKCEEnforceExecutor.java +++ b/services/src/main/java/org/keycloak/services/clientpolicy/executor/PKCEEnforceExecutor.java @@ -38,10 +38,10 @@ import org.keycloak.protocol.oidc.utils.OAuth2Code; import org.keycloak.protocol.oidc.utils.OAuth2CodeParser; import org.keycloak.protocol.oidc.utils.OIDCResponseType; import org.keycloak.representations.idm.ClientRepresentation; -import org.keycloak.services.clientpolicy.AuthorizationRequestContext; import org.keycloak.services.clientpolicy.ClientPolicyContext; import org.keycloak.services.clientpolicy.ClientPolicyException; -import org.keycloak.services.clientpolicy.TokenRequestContext; +import org.keycloak.services.clientpolicy.context.AuthorizationRequestContext; +import org.keycloak.services.clientpolicy.context.TokenRequestContext; import org.keycloak.services.clientpolicy.executor.AbstractAugumentingClientRegistrationPolicyExecutor; public class PKCEEnforceExecutor extends AbstractAugumentingClientRegistrationPolicyExecutor { diff --git a/services/src/main/java/org/keycloak/services/clientpolicy/executor/SecureRedirectUriEnforceExecutor.java b/services/src/main/java/org/keycloak/services/clientpolicy/executor/SecureRedirectUriEnforceExecutor.java index ebbcb3e65b..509f16f5be 100644 --- a/services/src/main/java/org/keycloak/services/clientpolicy/executor/SecureRedirectUriEnforceExecutor.java +++ b/services/src/main/java/org/keycloak/services/clientpolicy/executor/SecureRedirectUriEnforceExecutor.java @@ -24,15 +24,15 @@ import org.jboss.logging.Logger; import org.keycloak.OAuthErrorException; import org.keycloak.component.ComponentModel; import org.keycloak.models.KeycloakSession; -import org.keycloak.services.clientpolicy.AdminClientRegisterContext; -import org.keycloak.services.clientpolicy.AdminClientUpdateContext; -import org.keycloak.services.clientpolicy.AuthorizationRequestContext; import org.keycloak.services.clientpolicy.ClientPolicyContext; import org.keycloak.services.clientpolicy.ClientPolicyException; import org.keycloak.services.clientpolicy.ClientPolicyLogger; -import org.keycloak.services.clientpolicy.ClientUpdateContext; -import org.keycloak.services.clientpolicy.DynamicClientRegisterContext; -import org.keycloak.services.clientpolicy.DynamicClientUpdateContext; +import org.keycloak.services.clientpolicy.context.AdminClientRegisterContext; +import org.keycloak.services.clientpolicy.context.AdminClientUpdateContext; +import org.keycloak.services.clientpolicy.context.AuthorizationRequestContext; +import org.keycloak.services.clientpolicy.context.ClientCRUDContext; +import org.keycloak.services.clientpolicy.context.DynamicClientRegisterContext; +import org.keycloak.services.clientpolicy.context.DynamicClientUpdateContext; public class SecureRedirectUriEnforceExecutor implements ClientPolicyExecutorProvider { @@ -61,14 +61,14 @@ public class SecureRedirectUriEnforceExecutor implements ClientPolicyExecutorPro switch (context.getEvent()) { case REGISTER: if (context instanceof AdminClientRegisterContext || context instanceof DynamicClientRegisterContext) { - confirmSecureRedirectUris(((ClientUpdateContext)context).getProposedClientRepresentation().getRedirectUris()); + confirmSecureRedirectUris(((ClientCRUDContext)context).getProposedClientRepresentation().getRedirectUris()); } else { throw new ClientPolicyException(OAuthErrorException.INVALID_REQUEST, "not allowed input format."); } return; case UPDATE: if (context instanceof AdminClientUpdateContext || context instanceof DynamicClientUpdateContext) { - confirmSecureRedirectUris(((ClientUpdateContext)context).getProposedClientRepresentation().getRedirectUris()); + confirmSecureRedirectUris(((ClientCRUDContext)context).getProposedClientRepresentation().getRedirectUris()); } else { throw new ClientPolicyException(OAuthErrorException.INVALID_REQUEST, "not allowed input format."); } diff --git a/services/src/main/java/org/keycloak/services/clientpolicy/executor/SecureRequestObjectExecutor.java b/services/src/main/java/org/keycloak/services/clientpolicy/executor/SecureRequestObjectExecutor.java index 0101044c15..1b8c0f2bd1 100644 --- a/services/src/main/java/org/keycloak/services/clientpolicy/executor/SecureRequestObjectExecutor.java +++ b/services/src/main/java/org/keycloak/services/clientpolicy/executor/SecureRequestObjectExecutor.java @@ -32,10 +32,10 @@ import org.keycloak.protocol.oidc.endpoints.request.AuthorizationEndpointRequest import org.keycloak.protocol.oidc.endpoints.request.AuthzEndpointRequestParser; import org.keycloak.protocol.oidc.utils.OIDCResponseType; import org.keycloak.services.Urls; -import org.keycloak.services.clientpolicy.AuthorizationRequestContext; import org.keycloak.services.clientpolicy.ClientPolicyContext; import org.keycloak.services.clientpolicy.ClientPolicyException; import org.keycloak.services.clientpolicy.ClientPolicyLogger; +import org.keycloak.services.clientpolicy.context.AuthorizationRequestContext; import com.fasterxml.jackson.databind.JsonNode; diff --git a/services/src/main/java/org/keycloak/services/clientpolicy/executor/SecureResponseTypeExecutor.java b/services/src/main/java/org/keycloak/services/clientpolicy/executor/SecureResponseTypeExecutor.java index 375d8f4306..2db1aecf01 100644 --- a/services/src/main/java/org/keycloak/services/clientpolicy/executor/SecureResponseTypeExecutor.java +++ b/services/src/main/java/org/keycloak/services/clientpolicy/executor/SecureResponseTypeExecutor.java @@ -23,10 +23,10 @@ import org.keycloak.component.ComponentModel; import org.keycloak.models.KeycloakSession; import org.keycloak.protocol.oidc.endpoints.request.AuthorizationEndpointRequest; import org.keycloak.protocol.oidc.utils.OIDCResponseType; -import org.keycloak.services.clientpolicy.AuthorizationRequestContext; import org.keycloak.services.clientpolicy.ClientPolicyContext; import org.keycloak.services.clientpolicy.ClientPolicyException; import org.keycloak.services.clientpolicy.ClientPolicyLogger; +import org.keycloak.services.clientpolicy.context.AuthorizationRequestContext; public class SecureResponseTypeExecutor implements ClientPolicyExecutorProvider { diff --git a/services/src/main/java/org/keycloak/services/clientpolicy/executor/SecureSessionEnforceExecutor.java b/services/src/main/java/org/keycloak/services/clientpolicy/executor/SecureSessionEnforceExecutor.java index 1ca7bbc179..2729b53d6c 100644 --- a/services/src/main/java/org/keycloak/services/clientpolicy/executor/SecureSessionEnforceExecutor.java +++ b/services/src/main/java/org/keycloak/services/clientpolicy/executor/SecureSessionEnforceExecutor.java @@ -23,10 +23,10 @@ import org.keycloak.component.ComponentModel; import org.keycloak.models.KeycloakSession; import org.keycloak.protocol.oidc.endpoints.request.AuthorizationEndpointRequest; import org.keycloak.protocol.oidc.utils.OIDCResponseType; -import org.keycloak.services.clientpolicy.AuthorizationRequestContext; import org.keycloak.services.clientpolicy.ClientPolicyContext; import org.keycloak.services.clientpolicy.ClientPolicyException; import org.keycloak.services.clientpolicy.ClientPolicyLogger; +import org.keycloak.services.clientpolicy.context.AuthorizationRequestContext; import org.keycloak.util.TokenUtil; public class SecureSessionEnforceExecutor implements ClientPolicyExecutorProvider { diff --git a/services/src/main/java/org/keycloak/services/clientpolicy/executor/SecureSigningAlgorithmEnforceExecutor.java b/services/src/main/java/org/keycloak/services/clientpolicy/executor/SecureSigningAlgorithmEnforceExecutor.java index fffe96c6de..e2f865b3a1 100644 --- a/services/src/main/java/org/keycloak/services/clientpolicy/executor/SecureSigningAlgorithmEnforceExecutor.java +++ b/services/src/main/java/org/keycloak/services/clientpolicy/executor/SecureSigningAlgorithmEnforceExecutor.java @@ -28,13 +28,13 @@ import org.keycloak.crypto.Algorithm; import org.keycloak.models.KeycloakSession; import org.keycloak.protocol.oidc.OIDCConfigAttributes; import org.keycloak.representations.idm.ClientRepresentation; -import org.keycloak.services.clientpolicy.AdminClientRegisterContext; -import org.keycloak.services.clientpolicy.AdminClientUpdateContext; import org.keycloak.services.clientpolicy.ClientPolicyContext; import org.keycloak.services.clientpolicy.ClientPolicyException; import org.keycloak.services.clientpolicy.ClientPolicyLogger; -import org.keycloak.services.clientpolicy.DynamicClientRegisterContext; -import org.keycloak.services.clientpolicy.DynamicClientUpdateContext; +import org.keycloak.services.clientpolicy.context.AdminClientRegisterContext; +import org.keycloak.services.clientpolicy.context.AdminClientUpdateContext; +import org.keycloak.services.clientpolicy.context.DynamicClientRegisterContext; +import org.keycloak.services.clientpolicy.context.DynamicClientUpdateContext; public class SecureSigningAlgorithmEnforceExecutor implements ClientPolicyExecutorProvider { diff --git a/services/src/main/java/org/keycloak/services/clientregistration/AbstractClientRegistrationProvider.java b/services/src/main/java/org/keycloak/services/clientregistration/AbstractClientRegistrationProvider.java index 94edd380a3..53a6e73815 100755 --- a/services/src/main/java/org/keycloak/services/clientregistration/AbstractClientRegistrationProvider.java +++ b/services/src/main/java/org/keycloak/services/clientregistration/AbstractClientRegistrationProvider.java @@ -30,6 +30,9 @@ import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.oidc.OIDCClientRepresentation; import org.keycloak.services.ErrorResponseException; import org.keycloak.services.ForbiddenException; +import org.keycloak.services.clientpolicy.ClientPolicyException; +import org.keycloak.services.clientpolicy.context.DynamicClientRegisteredContext; +import org.keycloak.services.clientpolicy.context.DynamicClientUpdatedContext; import org.keycloak.services.clientregistration.policy.ClientRegistrationPolicyManager; import org.keycloak.services.clientregistration.policy.RegistrationAuth; import org.keycloak.services.managers.ClientManager; @@ -70,6 +73,7 @@ public abstract class AbstractClientRegistrationProvider implements ClientRegist RepresentationToModel.createResourceServer(clientModel, session, true); } + session.clientPolicy().triggerOnEvent(new DynamicClientRegisteredContext(context, clientModel, auth.getJwt(), realm)); ClientRegistrationPolicyManager.triggerAfterRegister(context, registrationAuth, clientModel); client = ModelToRepresentation.toRepresentation(clientModel, session); @@ -90,6 +94,8 @@ public abstract class AbstractClientRegistrationProvider implements ClientRegist return client; } catch (ModelDuplicateException e) { throw new ErrorResponseException(ErrorCodes.INVALID_CLIENT_METADATA, "Client Identifier in use", Response.Status.BAD_REQUEST); + } catch (ClientPolicyException cpe) { + throw new ErrorResponseException(cpe.getError(), cpe.getErrorDetail(), Response.Status.BAD_REQUEST); } } @@ -134,6 +140,11 @@ public abstract class AbstractClientRegistrationProvider implements ClientRegist rep.setRegistrationAccessToken(registrationAccessToken); } + try { + session.clientPolicy().triggerOnEvent(new DynamicClientUpdatedContext(session, client, auth.getJwt(), client.getRealm())); + } catch (ClientPolicyException cpe) { + throw new ErrorResponseException(cpe.getError(), cpe.getErrorDetail(), Response.Status.BAD_REQUEST); + } ClientRegistrationPolicyManager.triggerAfterUpdate(context, registrationAuth, client); event.client(client.getClientId()).success(); diff --git a/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationAuth.java b/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationAuth.java index ddf93fa7a7..32e8467c2e 100644 --- a/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationAuth.java +++ b/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationAuth.java @@ -37,8 +37,10 @@ import org.keycloak.protocol.oidc.utils.AuthorizeClientUtil; import org.keycloak.representations.JsonWebToken; import org.keycloak.services.ErrorResponseException; import org.keycloak.services.clientpolicy.ClientPolicyException; -import org.keycloak.services.clientpolicy.DynamicClientRegisterContext; -import org.keycloak.services.clientpolicy.DynamicClientUpdateContext; +import org.keycloak.services.clientpolicy.context.DynamicClientRegisterContext; +import org.keycloak.services.clientpolicy.context.DynamicClientUnregisterContext; +import org.keycloak.services.clientpolicy.context.DynamicClientUpdateContext; +import org.keycloak.services.clientpolicy.context.DynamicClientViewContext; import org.keycloak.services.clientregistration.policy.ClientRegistrationPolicyException; import org.keycloak.services.clientregistration.policy.ClientRegistrationPolicyManager; import org.keycloak.services.clientregistration.policy.RegistrationAuth; @@ -199,8 +201,9 @@ public class ClientRegistrationAuth { if (authenticated) { try { + session.clientPolicy().triggerOnEvent(new DynamicClientViewContext(session, client, jwt, realm)); ClientRegistrationPolicyManager.triggerBeforeView(session, provider, authType, client); - } catch (ClientRegistrationPolicyException crpe) { + } catch (ClientRegistrationPolicyException | ClientPolicyException crpe) { throw forbidden(crpe.getMessage()); } } else { @@ -230,8 +233,9 @@ public class ClientRegistrationAuth { RegistrationAuth chainType = requireUpdateAuth(client); try { + session.clientPolicy().triggerOnEvent(new DynamicClientUnregisterContext(session, client, jwt, realm)); ClientRegistrationPolicyManager.triggerBeforeRemove(session, provider, chainType, client); - } catch (ClientRegistrationPolicyException crpe) { + } catch (ClientRegistrationPolicyException | ClientPolicyException crpe) { throw forbidden(crpe.getMessage()); } } diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java index 98d44a2652..65cb65da3f 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java @@ -50,8 +50,11 @@ import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.representations.idm.UserSessionRepresentation; import org.keycloak.services.ErrorResponse; import org.keycloak.services.ErrorResponseException; -import org.keycloak.services.clientpolicy.AdminClientUpdateContext; import org.keycloak.services.clientpolicy.ClientPolicyException; +import org.keycloak.services.clientpolicy.context.AdminClientUnregisterContext; +import org.keycloak.services.clientpolicy.context.AdminClientUpdateContext; +import org.keycloak.services.clientpolicy.context.AdminClientUpdatedContext; +import org.keycloak.services.clientpolicy.context.AdminClientViewContext; import org.keycloak.services.clientregistration.ClientRegistrationTokenUtils; import org.keycloak.services.clientregistration.policy.RegistrationAuth; import org.keycloak.services.managers.ClientManager; @@ -130,12 +133,8 @@ public class ClientResource { auth.clients().requireConfigure(client); try { - session.clientPolicy().triggerOnEvent(new AdminClientUpdateContext(rep, auth.adminAuth(), client)); - } catch (ClientPolicyException cpe) { - throw new ErrorResponseException(cpe.getError(), cpe.getErrorDetail(), Response.Status.BAD_REQUEST); - } + session.clientPolicy().triggerOnEvent(new AdminClientUpdateContext(rep, client, auth.adminAuth())); - try { updateClientFromRep(rep, client, session); ValidationUtil.validateClient(session, client, false, r -> { @@ -146,10 +145,14 @@ public class ClientResource { Response.Status.BAD_REQUEST); }); + session.clientPolicy().triggerOnEvent(new AdminClientUpdatedContext(rep, client, auth.adminAuth())); + adminEvent.operation(OperationType.UPDATE).resourcePath(session.getContext().getUri()).representation(rep).success(); return Response.noContent().build(); } catch (ModelDuplicateException e) { return ErrorResponse.exists("Client already exists"); + } catch (ClientPolicyException cpe) { + throw new ErrorResponseException(cpe.getError(), cpe.getErrorDetail(), Response.Status.BAD_REQUEST); } } @@ -162,6 +165,12 @@ public class ClientResource { @NoCache @Produces(MediaType.APPLICATION_JSON) public ClientRepresentation getClient() { + try { + session.clientPolicy().triggerOnEvent(new AdminClientViewContext(client, auth.adminAuth())); + } catch (ClientPolicyException cpe) { + throw new ErrorResponseException(cpe.getError(), cpe.getErrorDetail(), Response.Status.BAD_REQUEST); + } + auth.clients().requireView(client); ClientRepresentation representation = ModelToRepresentation.toRepresentation(client, session); @@ -206,6 +215,12 @@ public class ClientResource { throw new NotFoundException("Could not find client"); } + try { + session.clientPolicy().triggerOnEvent(new AdminClientUnregisterContext(client, auth.adminAuth())); + } catch (ClientPolicyException cpe) { + throw new ErrorResponseException(cpe.getError(), cpe.getErrorDetail(), Response.Status.BAD_REQUEST); + } + new ClientManager(new RealmManager(session)).removeClient(realm, client); adminEvent.operation(OperationType.DELETE).resourcePath(session.getContext().getUri()).success(); } diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientsResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientsResource.java index e2ebfea1d2..6a8a0c3173 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/ClientsResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientsResource.java @@ -34,8 +34,9 @@ import org.keycloak.representations.idm.authorization.ResourceServerRepresentati import org.keycloak.services.ErrorResponse; import org.keycloak.services.ErrorResponseException; import org.keycloak.services.ForbiddenException; -import org.keycloak.services.clientpolicy.AdminClientRegisterContext; import org.keycloak.services.clientpolicy.ClientPolicyException; +import org.keycloak.services.clientpolicy.context.AdminClientRegisterContext; +import org.keycloak.services.clientpolicy.context.AdminClientRegisteredContext; import org.keycloak.services.managers.ClientManager; import org.keycloak.services.managers.RealmManager; import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator; @@ -163,11 +164,7 @@ public class ClientsResource { try { session.clientPolicy().triggerOnEvent(new AdminClientRegisterContext(rep, auth.adminAuth())); - } catch (ClientPolicyException cpe) { - throw new ErrorResponseException(cpe.getError(), cpe.getErrorDetail(), Response.Status.BAD_REQUEST); - } - try { ClientModel clientModel = ClientManager.createClient(session, realm, rep); if (TRUE.equals(rep.isServiceAccountsEnabled())) { @@ -200,9 +197,13 @@ public class ClientsResource { Response.Status.BAD_REQUEST); }); + session.clientPolicy().triggerOnEvent(new AdminClientRegisteredContext(clientModel, auth.adminAuth())); + return Response.created(session.getContext().getUri().getAbsolutePathBuilder().path(clientModel.getId()).build()).build(); } catch (ModelDuplicateException e) { return ErrorResponse.exists("Client " + rep.getClientId() + " already exists"); + } catch (ClientPolicyException cpe) { + throw new ErrorResponseException(cpe.getError(), cpe.getErrorDetail(), Response.Status.BAD_REQUEST); } } diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/services/clientpolicy/executor/TestRaiseExeptionExecutor.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/services/clientpolicy/executor/TestRaiseExeptionExecutor.java new file mode 100644 index 0000000000..4e3f0ebd8c --- /dev/null +++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/services/clientpolicy/executor/TestRaiseExeptionExecutor.java @@ -0,0 +1,63 @@ +/* + * Copyright 2021 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.testsuite.services.clientpolicy.executor; + +import java.util.List; + +import org.jboss.logging.Logger; +import org.keycloak.component.ComponentModel; +import org.keycloak.models.KeycloakSession; +import org.keycloak.services.clientpolicy.ClientPolicyContext; +import org.keycloak.services.clientpolicy.ClientPolicyEvent; +import org.keycloak.services.clientpolicy.ClientPolicyException; +import org.keycloak.services.clientpolicy.ClientPolicyLogger; +import org.keycloak.services.clientpolicy.executor.ClientPolicyExecutorProvider; + +public class TestRaiseExeptionExecutor implements ClientPolicyExecutorProvider { + + private static final Logger logger = Logger.getLogger(TestRaiseExeptionExecutor.class); + + protected final KeycloakSession session; + protected final ComponentModel componentModel; + + public TestRaiseExeptionExecutor(KeycloakSession session, ComponentModel componentModel) { + this.session = session; + this.componentModel = componentModel; + } + + @Override + public String getName() { + return componentModel.getName(); + } + + @Override + public String getProviderId() { + return componentModel.getProviderId(); + } + + @Override + public void executeOnEvent(ClientPolicyContext context) throws ClientPolicyException { + if (isThrowExceptionNeeded(context.getEvent())) throw new ClientPolicyException(context.getEvent().toString(), "Exception thrown intentionally"); + } + + private boolean isThrowExceptionNeeded(ClientPolicyEvent event) { + ClientPolicyLogger.log(logger, "Client Policy Trigger Event = " + event); + List l = componentModel.getConfig().get(TestRaiseExeptionExecutorFactory.TARGET_CP_EVENTS); + return l != null && l.stream().anyMatch(i->i.equals(event.toString())); + } +} diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/services/clientpolicy/executor/TestRaiseExeptionExecutorFactory.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/services/clientpolicy/executor/TestRaiseExeptionExecutorFactory.java new file mode 100644 index 0000000000..5516a814d0 --- /dev/null +++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/services/clientpolicy/executor/TestRaiseExeptionExecutorFactory.java @@ -0,0 +1,72 @@ +/* + * Copyright 2021 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.testsuite.services.clientpolicy.executor; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.keycloak.Config.Scope; +import org.keycloak.component.ComponentModel; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.KeycloakSessionFactory; +import org.keycloak.provider.ProviderConfigProperty; +import org.keycloak.services.clientpolicy.executor.ClientPolicyExecutorProvider; +import org.keycloak.services.clientpolicy.executor.ClientPolicyExecutorProviderFactory; + +public class TestRaiseExeptionExecutorFactory implements ClientPolicyExecutorProviderFactory { + + public static final String PROVIDER_ID = "test-raise-exception-executor"; + + public static final String TARGET_CP_EVENTS = "target-cp-events"; + private static final ProviderConfigProperty TARGET_CP_EVENTS_PROPERTY = new ProviderConfigProperty( + TARGET_CP_EVENTS, null, null, ProviderConfigProperty.MULTIVALUED_STRING_TYPE, null); + + @Override + public ClientPolicyExecutorProvider create(KeycloakSession session, ComponentModel model) { + return new TestRaiseExeptionExecutor(session, model); + } + + @Override + public void init(Scope config) { + } + + @Override + public void postInit(KeycloakSessionFactory factory) { + } + + @Override + public void close() { + } + + @Override + public String getId() { + return PROVIDER_ID; + } + + @Override + public String getHelpText() { + return null; + } + + @Override + public List getConfigProperties() { + return Arrays.asList(TARGET_CP_EVENTS_PROPERTY); + } + +} diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/META-INF/services/org.keycloak.services.clientpolicy.executor.ClientPolicyExecutorProviderFactory b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/META-INF/services/org.keycloak.services.clientpolicy.executor.ClientPolicyExecutorProviderFactory new file mode 100644 index 0000000000..c8c284c8ac --- /dev/null +++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/META-INF/services/org.keycloak.services.clientpolicy.executor.ClientPolicyExecutorProviderFactory @@ -0,0 +1 @@ +org.keycloak.testsuite.services.clientpolicy.executor.TestRaiseExeptionExecutorFactory \ No newline at end of file diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/AbstractClientPoliciesTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/AbstractClientPoliciesTest.java index 52b342ff6d..6f522d8cd2 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/AbstractClientPoliciesTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/AbstractClientPoliciesTest.java @@ -18,6 +18,7 @@ package org.keycloak.testsuite.client; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; import java.io.IOException; import java.net.URI; @@ -42,6 +43,7 @@ import java.util.Map; import java.util.UUID; import java.util.function.Consumer; +import javax.ws.rs.BadRequestException; import javax.ws.rs.core.Response; import org.apache.http.HttpResponse; @@ -423,10 +425,10 @@ public abstract class AbstractClientPoliciesTest extends AbstractKeycloakTest { // Client CRUD operation by Admin REST API primitives - protected String createClientByAdmin(String clientId, Consumer op) throws ClientPolicyException { + protected String createClientByAdmin(String clientName, Consumer op) throws ClientPolicyException { ClientRepresentation clientRep = new ClientRepresentation(); - clientRep.setClientId(clientId); - clientRep.setName(clientId); + clientRep.setClientId(clientName); + clientRep.setName(clientName); clientRep.setProtocol("openid-connect"); clientRep.setBearerOnly(Boolean.FALSE); clientRep.setPublicClient(Boolean.FALSE); @@ -435,7 +437,14 @@ public abstract class AbstractClientPoliciesTest extends AbstractKeycloakTest { op.accept(clientRep); Response resp = adminClient.realm(REALM_NAME).clients().create(clientRep); if (resp.getStatus() == Response.Status.BAD_REQUEST.getStatusCode()) { - throw new ClientPolicyException(Errors.INVALID_REGISTRATION, "registration error by admin"); + String respBody = resp.readEntity(String.class); + Map responseJson = null; + try { + responseJson = JsonSerialization.readValue(respBody, Map.class); + } catch (IOException e) { + fail(); + } + throw new ClientPolicyException(responseJson.get(OAuth2Constants.ERROR), responseJson.get(OAuth2Constants.ERROR_DESCRIPTION)); } resp.close(); assertEquals(Response.Status.CREATED.getStatusCode(), resp.getStatus()); @@ -445,21 +454,55 @@ public abstract class AbstractClientPoliciesTest extends AbstractKeycloakTest { return cId; } - protected ClientRepresentation getClientByAdmin(String cId) { + protected ClientRepresentation getClientByAdmin(String cId) throws ClientPolicyException { ClientResource clientResource = adminClient.realm(REALM_NAME).clients().get(cId); - return clientResource.toRepresentation(); + try { + return clientResource.toRepresentation(); + } catch (BadRequestException bre) { + processClientPolicyExceptionByAdmin(bre); + } + return null; } - protected void updateClientByAdmin(String cId, Consumer op) { + protected ClientRepresentation getClientByAdminWithName(String clientName) { + return adminClient.realm(REALM_NAME).clients().findByClientId(clientName).get(0); + } + + protected void updateClientByAdmin(String cId, Consumer op) throws ClientPolicyException { ClientResource clientResource = adminClient.realm(REALM_NAME).clients().get(cId); ClientRepresentation clientRep = clientResource.toRepresentation(); op.accept(clientRep); - clientResource.update(clientRep); + try { + clientResource.update(clientRep); + } catch (BadRequestException bre) { + processClientPolicyExceptionByAdmin(bre); + } } - protected void deleteClientByAdmin(String cId) { + protected void deleteClientByAdmin(String cId) throws ClientPolicyException { ClientResource clientResource = adminClient.realm(REALM_NAME).clients().get(cId); - clientResource.remove(); + try { + clientResource.remove(); + } catch (BadRequestException bre) { + processClientPolicyExceptionByAdmin(bre); + } + } + + private void processClientPolicyExceptionByAdmin(BadRequestException bre) throws ClientPolicyException { + Response resp = bre.getResponse(); + if (resp.getStatus() != Response.Status.BAD_REQUEST.getStatusCode()) { + resp.close(); + return; + } + + String respBody = resp.readEntity(String.class); + Map responseJson = null; + try { + responseJson = JsonSerialization.readValue(respBody, Map.class); + } catch (IOException e) { + fail(); + } + throw new ClientPolicyException(responseJson.get(OAuth2Constants.ERROR), responseJson.get(OAuth2Constants.ERROR_DESCRIPTION)); } // Registration/Initial Access Token acquisition for Dynamic Client Registration diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/ClientPoliciesTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/ClientPoliciesTest.java index 67d7105f24..1162a88eb8 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/ClientPoliciesTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/ClientPoliciesTest.java @@ -33,8 +33,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; - -import javax.ws.rs.BadRequestException; +import java.util.stream.Collectors; import org.apache.http.HttpResponse; import org.apache.http.client.methods.CloseableHttpResponse; @@ -58,6 +57,7 @@ import org.keycloak.events.EventType; import org.keycloak.jose.jws.Algorithm; import org.keycloak.models.AdminRoles; import org.keycloak.models.Constants; +import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.protocol.oidc.OIDCAdvancedConfigWrapper; import org.keycloak.protocol.oidc.OIDCConfigAttributes; import org.keycloak.protocol.oidc.OIDCLoginProtocol; @@ -72,6 +72,7 @@ import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.representations.oidc.OIDCClientRepresentation; import org.keycloak.representations.oidc.TokenMetadataRepresentation; +import org.keycloak.services.clientpolicy.ClientPolicyEvent; import org.keycloak.services.clientpolicy.ClientPolicyException; import org.keycloak.services.clientpolicy.DefaultClientPolicyProviderFactory; import org.keycloak.services.clientpolicy.condition.AbstractClientPolicyConditionProviderFactory; @@ -100,6 +101,7 @@ import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude.A import org.keycloak.testsuite.client.resources.TestApplicationResourceUrls; import org.keycloak.testsuite.rest.resource.TestingOIDCEndpointsApplicationResource.AuthorizationEndpointRequestObject; import org.keycloak.testsuite.services.clientpolicy.condition.TestRaiseExeptionConditionFactory; +import org.keycloak.testsuite.services.clientpolicy.executor.TestRaiseExeptionExecutorFactory; import org.keycloak.testsuite.util.MutualTLSUtils; import org.keycloak.testsuite.util.OAuthClient; @@ -161,8 +163,8 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest { clientRep.setClientAuthenticatorType(ClientIdAndSecretAuthenticator.PROVIDER_ID); }); fail(); - } catch (ClientPolicyException e) { - assertEquals(Errors.INVALID_REGISTRATION, e.getMessage()); + } catch (ClientPolicyException cpe) { + assertEquals(OAuthErrorException.INVALID_CLIENT_METADATA, cpe.getError()); } } @@ -181,8 +183,8 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest { try { createClientByAdmin(generateSuffixedName(CLIENT_NAME), (ClientRepresentation clientRep) -> {}); fail(); - } catch (ClientPolicyException e) { - assertEquals(Errors.INVALID_REGISTRATION, e.getMessage()); + } catch (ClientPolicyException cpe) { + assertEquals(OAuthErrorException.INVALID_CLIENT_METADATA, cpe.getError()); } } @@ -198,8 +200,8 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest { clientRep.setClientAuthenticatorType(ClientIdAndSecretAuthenticator.PROVIDER_ID); }); fail(); - } catch (BadRequestException bre) { - assertEquals("HTTP 400 Bad Request", bre.getMessage()); + } catch (ClientPolicyException cpe) { + assertEquals(OAuthErrorException.INVALID_CLIENT_METADATA, cpe.getError()); } assertEquals(JWTClientSecretAuthenticator.PROVIDER_ID, getClientByAdmin(cId).getClientAuthenticatorType()); } @@ -323,7 +325,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest { String conditionName = ClientRolesCondition_NAME; createCondition(conditionName, ClientRolesConditionFactory.PROVIDER_ID, null, (ComponentRepresentation provider) -> { - setConditionClientRoles(provider, new ArrayList<>(Arrays.asList(SAMPLE_CLIENT_ROLE))); + setConditionClientRoles(provider, Arrays.asList(SAMPLE_CLIENT_ROLE)); }); registerCondition(conditionName, policyName); logger.info("... Registered Condition : " + conditionName); @@ -331,7 +333,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest { failLoginByNotFollowingPKCE(clientId); updateCondition(conditionName, (ComponentRepresentation provider) -> { - setConditionClientRoles(provider, new ArrayList<>(Arrays.asList("anothor-client-role"))); + setConditionClientRoles(provider, Arrays.asList("anothor-client-role")); }); successfulLoginAndLogout(clientId, clientSecret); @@ -349,14 +351,14 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest { String roleConditionName = ClientRolesCondition_NAME; createCondition(roleConditionName, ClientRolesConditionFactory.PROVIDER_ID, null, (ComponentRepresentation provider) -> { - setConditionClientRoles(provider, new ArrayList<>(Arrays.asList(SAMPLE_CLIENT_ROLE))); + setConditionClientRoles(provider, Arrays.asList(SAMPLE_CLIENT_ROLE)); }); registerCondition(roleConditionName, policyName); logger.info("... Registered Condition : " + roleConditionName); String updateConditionName = ClientUpdateContextCondition_NAME; createCondition(updateConditionName, ClientUpdateContextConditionFactory.PROVIDER_ID, null, (ComponentRepresentation provider) -> { - setConditionRegistrationMethods(provider, new ArrayList<>(Arrays.asList(ClientUpdateContextConditionFactory.BY_AUTHENTICATED_USER))); + setConditionRegistrationMethods(provider, Arrays.asList(ClientUpdateContextConditionFactory.BY_AUTHENTICATED_USER)); }); registerCondition(updateConditionName, policyName); logger.info("... Registered Condition : " + updateConditionName); @@ -431,21 +433,21 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest { String roleConditionAlphaName = generateSuffixedName(ClientRolesCondition_NAME); createCondition(roleConditionAlphaName, ClientRolesConditionFactory.PROVIDER_ID, null, (ComponentRepresentation provider) -> { - setConditionClientRoles(provider, new ArrayList<>(Arrays.asList(roleAlphaName, roleZetaName))); + setConditionClientRoles(provider, Arrays.asList(roleAlphaName, roleZetaName)); }); registerCondition(roleConditionAlphaName, policyAlphaName); logger.info("... Registered Condition : " + roleConditionAlphaName); String updateConditionAlphaName = generateSuffixedName(ClientUpdateContextCondition_NAME); createCondition(updateConditionAlphaName, ClientUpdateContextConditionFactory.PROVIDER_ID, null, (ComponentRepresentation provider) -> { - setConditionRegistrationMethods(provider, new ArrayList<>(Arrays.asList(ClientUpdateContextConditionFactory.BY_AUTHENTICATED_USER))); + setConditionRegistrationMethods(provider, Arrays.asList(ClientUpdateContextConditionFactory.BY_AUTHENTICATED_USER)); }); registerCondition(updateConditionAlphaName, policyAlphaName); logger.info("... Registered Condition : " + updateConditionAlphaName); String clientAuthExecutorAlphaName = generateSuffixedName(SecureClientAuthEnforceExecutor_NAME); createExecutor(clientAuthExecutorAlphaName, SecureClientAuthEnforceExecutorFactory.PROVIDER_ID, null, (ComponentRepresentation provider) -> { - setExecutorAcceptedClientAuthMethods(provider, new ArrayList<>(Arrays.asList(ClientIdAndSecretAuthenticator.PROVIDER_ID))); + setExecutorAcceptedClientAuthMethods(provider, Arrays.asList(ClientIdAndSecretAuthenticator.PROVIDER_ID)); setExecutorAugmentActivate(provider); setExecutorAugmentedClientAuthMethod(provider, ClientIdAndSecretAuthenticator.PROVIDER_ID); }); @@ -458,7 +460,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest { String roleConditionBetaName = generateSuffixedName(ClientRolesCondition_NAME); createCondition(roleConditionBetaName, ClientRolesConditionFactory.PROVIDER_ID, null, (ComponentRepresentation provider) -> { - setConditionClientRoles(provider, new ArrayList<>(Arrays.asList(roleBetaName, roleZetaName))); + setConditionClientRoles(provider, Arrays.asList(roleBetaName, roleZetaName)); }); registerCondition(roleConditionBetaName, policyBetaName); logger.info("... Registered Condition : " + roleConditionBetaName); @@ -507,8 +509,8 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest { try { createClientByAdmin(generateSuffixedName(CLIENT_NAME), (ClientRepresentation clientRep) -> {}); fail(); - } catch (ClientPolicyException e) { - assertEquals(Errors.INVALID_REGISTRATION, e.getMessage()); + } catch (ClientPolicyException cpe) { + assertEquals(OAuthErrorException.SERVER_ERROR, cpe.getError()); } } @@ -611,15 +613,15 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest { String conditionName = ClientUpdateSourceHostsCondition_NAME; createCondition(conditionName, ClientUpdateSourceHostsConditionFactory.PROVIDER_ID, null, (ComponentRepresentation provider) -> { - setConditionClientUpdateSourceHosts(provider, new ArrayList<>(Arrays.asList("localhost", "127.0.0.1"))); + setConditionClientUpdateSourceHosts(provider, Arrays.asList("localhost", "127.0.0.1")); }); registerCondition(conditionName, policyName); logger.info("... Registered Condition : " + conditionName); String executorName = SecureClientAuthEnforceExecutor_NAME; createExecutor(executorName, SecureClientAuthEnforceExecutorFactory.PROVIDER_ID, null, (ComponentRepresentation provider) -> { - setExecutorAcceptedClientAuthMethods(provider, new ArrayList<>(Arrays.asList( - JWTClientAuthenticator.PROVIDER_ID, JWTClientSecretAuthenticator.PROVIDER_ID, X509ClientAuthenticator.PROVIDER_ID))); + setExecutorAcceptedClientAuthMethods(provider, Arrays.asList( + JWTClientAuthenticator.PROVIDER_ID, JWTClientSecretAuthenticator.PROVIDER_ID, X509ClientAuthenticator.PROVIDER_ID)); }); registerExecutor(executorName, policyName); logger.info("... Registered Executor : " + executorName); @@ -631,12 +633,12 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest { clientRep.setSecret(clientSecret); }); fail(); - } catch (ClientPolicyException e) { - assertEquals(Errors.INVALID_REGISTRATION, e.getMessage()); + } catch (ClientPolicyException cpe) { + assertEquals(OAuthErrorException.INVALID_CLIENT_METADATA, cpe.getError()); } updateCondition(conditionName, (ComponentRepresentation provider) -> { - setConditionClientUpdateSourceHosts(provider, new ArrayList<>(Arrays.asList("example.com"))); + setConditionClientUpdateSourceHosts(provider, Arrays.asList("example.com")); }); try { createClientByAdmin(clientId, (ClientRepresentation clientRep) -> { @@ -655,14 +657,14 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest { String conditionName = ClientUpdateSourceGroupsCondition_NAME; createCondition(conditionName, ClientUpdateSourceGroupsConditionFactory.PROVIDER_ID, null, (ComponentRepresentation provider) -> { - setConditionClientUpdateSourceGroups(provider, new ArrayList<>(Arrays.asList("topGroup"))); + setConditionClientUpdateSourceGroups(provider, Arrays.asList("topGroup")); }); registerCondition(conditionName, policyName); logger.info("... Registered Condition : " + conditionName); String executorName = SecureClientAuthEnforceExecutor_NAME; createExecutor(executorName, SecureClientAuthEnforceExecutorFactory.PROVIDER_ID, null, (ComponentRepresentation provider) -> { - setExecutorAcceptedClientAuthMethods(provider, new ArrayList<>(Arrays.asList(JWTClientAuthenticator.PROVIDER_ID))); + setExecutorAcceptedClientAuthMethods(provider,Arrays.asList(JWTClientAuthenticator.PROVIDER_ID)); }); registerExecutor(executorName, policyName); logger.info("... Registered Executor : " + executorName); @@ -690,14 +692,14 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest { String conditionName = ClientUpdateSourceRolesCondition_NAME; createCondition(conditionName, ClientUpdateSourceRolesConditionFactory.PROVIDER_ID, null, (ComponentRepresentation provider) -> { - setConditionUpdatingClientSourceRoles(provider, new ArrayList<>(Arrays.asList(AdminRoles.CREATE_CLIENT))); + setConditionUpdatingClientSourceRoles(provider, Arrays.asList(AdminRoles.CREATE_CLIENT)); }); registerCondition(conditionName, policyName); logger.info("... Registered Condition : " + conditionName); String executorName = SecureClientAuthEnforceExecutor_NAME; createExecutor(executorName, SecureClientAuthEnforceExecutorFactory.PROVIDER_ID, null, (ComponentRepresentation provider) -> { - setExecutorAcceptedClientAuthMethods(provider, new ArrayList<>(Arrays.asList(JWTClientAuthenticator.PROVIDER_ID))); + setExecutorAcceptedClientAuthMethods(provider, Arrays.asList(JWTClientAuthenticator.PROVIDER_ID)); }); registerExecutor(executorName, policyName); logger.info("... Registered Executor : " + executorName); @@ -725,7 +727,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest { String conditionName = ClientScopesCondition_NAME; createCondition(conditionName, ClientScopesConditionFactory.PROVIDER_ID, null, (ComponentRepresentation provider) -> { - setConditionClientScopes(provider, new ArrayList<>(Arrays.asList("offline_access", "microprofile-jwt"))); + setConditionClientScopes(provider, Arrays.asList("offline_access", "microprofile-jwt")); }); registerCondition(conditionName, policyName); logger.info("... Registered Condition : " + conditionName); @@ -759,7 +761,6 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest { } } - @Test public void testClientAccessTypeCondition() throws ClientRegistrationException, ClientPolicyException { String policyName = POLICY_NAME; @@ -768,7 +769,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest { String conditionName = ClientAccessTypeCondition_NAME; createCondition(conditionName, ClientAccessTypeConditionFactory.PROVIDER_ID, null, (ComponentRepresentation provider) -> { - setConditionClientAccessType(provider, new ArrayList<>(Arrays.asList(ClientAccessTypeConditionFactory.TYPE_CONFIDENTIAL))); + setConditionClientAccessType(provider, Arrays.asList(ClientAccessTypeConditionFactory.TYPE_CONFIDENTIAL)); }); registerCondition(conditionName, policyName); logger.info("... Registered Condition : " + conditionName); @@ -805,7 +806,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest { String conditionName = ClientRolesCondition_NAME; createCondition(conditionName, ClientRolesConditionFactory.PROVIDER_ID, null, (ComponentRepresentation provider) -> { - setConditionClientRoles(provider, new ArrayList<>(Arrays.asList(SAMPLE_CLIENT_ROLE))); + setConditionClientRoles(provider, Arrays.asList(SAMPLE_CLIENT_ROLE)); }); registerCondition(conditionName, policyName); logger.info("... Registered Condition : " + conditionName); @@ -869,7 +870,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest { String conditionName = ClientRolesCondition_NAME; createCondition(conditionName, ClientRolesConditionFactory.PROVIDER_ID, null, (ComponentRepresentation provider) -> { - setConditionClientRoles(provider, new ArrayList<>(Arrays.asList(SAMPLE_CLIENT_ROLE))); + setConditionClientRoles(provider, Arrays.asList(SAMPLE_CLIENT_ROLE)); }); registerCondition(conditionName, policyName); logger.info("... Registered Condition : " + conditionName); @@ -972,7 +973,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest { String roleBetaName = "sample-client-role-beta"; String conditionName = ClientRolesCondition_NAME; createCondition(conditionName, ClientRolesConditionFactory.PROVIDER_ID, null, (ComponentRepresentation provider) -> { - setConditionClientRoles(provider, new ArrayList<>(Arrays.asList(roleBetaName))); + setConditionClientRoles(provider, Arrays.asList(roleBetaName)); }); registerCondition(conditionName, policyName); logger.info("... Registered Condition : " + conditionName); @@ -1023,10 +1024,10 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest { String conditionName = ClientUpdateContextCondition_NAME; createCondition(conditionName, ClientUpdateContextConditionFactory.PROVIDER_ID, null, (ComponentRepresentation provider) -> { - setConditionRegistrationMethods(provider, new ArrayList<>(Arrays.asList( + setConditionRegistrationMethods(provider, Arrays.asList( ClientUpdateContextConditionFactory.BY_AUTHENTICATED_USER, ClientUpdateContextConditionFactory.BY_INITIAL_ACCESS_TOKEN, - ClientUpdateContextConditionFactory.BY_REGISTRATION_ACCESS_TOKEN))); + ClientUpdateContextConditionFactory.BY_REGISTRATION_ACCESS_TOKEN)); }); registerCondition(conditionName, policyName); logger.info("... Registered Condition : " + conditionName); @@ -1044,8 +1045,8 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest { clientRep.getAttributes().put(OIDCConfigAttributes.USER_INFO_RESPONSE_SIGNATURE_ALG, Algorithm.none.name()); }); fail(); - } catch (ClientPolicyException e) { - assertEquals(Errors.INVALID_REGISTRATION, e.getMessage()); + } catch (ClientPolicyException cpe) { + assertEquals(OAuthErrorException.INVALID_REQUEST, cpe.getError()); } // create by Admin REST API - success @@ -1062,32 +1063,32 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest { try { updateClientByAdmin(cAppAdminId, (ClientRepresentation clientRep) -> { clientRep.setAttributes(new HashMap<>()); - clientRep.getAttributes().put(OIDCConfigAttributes.ACCESS_TOKEN_SIGNED_RESPONSE_ALG, Algorithm.RS512.name()); + clientRep.getAttributes().put(OIDCConfigAttributes.ACCESS_TOKEN_SIGNED_RESPONSE_ALG, org.keycloak.crypto.Algorithm.RS512); }); - } catch (BadRequestException bre) { - assertEquals("HTTP 400 Bad Request", bre.getMessage()); + } catch (ClientPolicyException cpe) { + assertEquals(Errors.INVALID_REQUEST, cpe.getError()); } ClientRepresentation cRep = getClientByAdmin(cAppAdminId); - assertEquals(Algorithm.ES256.name(), cRep.getAttributes().get(OIDCConfigAttributes.ACCESS_TOKEN_SIGNED_RESPONSE_ALG)); + assertEquals(org.keycloak.crypto.Algorithm.ES256, cRep.getAttributes().get(OIDCConfigAttributes.ACCESS_TOKEN_SIGNED_RESPONSE_ALG)); // update by Admin REST API - success updateClientByAdmin(cAppAdminId, (ClientRepresentation clientRep) -> { clientRep.setAttributes(new HashMap<>()); - clientRep.getAttributes().put(OIDCConfigAttributes.ACCESS_TOKEN_SIGNED_RESPONSE_ALG, Algorithm.PS384.name()); + clientRep.getAttributes().put(OIDCConfigAttributes.ACCESS_TOKEN_SIGNED_RESPONSE_ALG, org.keycloak.crypto.Algorithm.PS384); }); cRep = getClientByAdmin(cAppAdminId); - assertEquals(Algorithm.PS384.name(), cRep.getAttributes().get(OIDCConfigAttributes.ACCESS_TOKEN_SIGNED_RESPONSE_ALG)); + assertEquals(org.keycloak.crypto.Algorithm.PS384, cRep.getAttributes().get(OIDCConfigAttributes.ACCESS_TOKEN_SIGNED_RESPONSE_ALG)); // create dynamically - fail try { createClientByAdmin(generateSuffixedName("App-in-Dynamic"), (ClientRepresentation clientRep) -> { clientRep.setSecret("secret"); clientRep.setAttributes(new HashMap<>()); - clientRep.getAttributes().put(OIDCConfigAttributes.USER_INFO_RESPONSE_SIGNATURE_ALG, Algorithm.RS384.name()); + clientRep.getAttributes().put(OIDCConfigAttributes.USER_INFO_RESPONSE_SIGNATURE_ALG, org.keycloak.crypto.Algorithm.RS384); }); fail(); - } catch (ClientPolicyException e) { - assertEquals(Errors.INVALID_REGISTRATION, e.getMessage()); + } catch (ClientPolicyException cpe) { + assertEquals(OAuthErrorException.INVALID_REQUEST, cpe.getError()); } // create dynamically - success @@ -1103,7 +1104,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest { // update dynamically - fail try { updateClientDynamically(cAppDynamicClientId, (OIDCClientRepresentation clientRep) -> { - clientRep.setIdTokenSignedResponseAlg(Algorithm.RS256.name()); + clientRep.setIdTokenSignedResponseAlg(org.keycloak.crypto.Algorithm.RS256); }); fail(); } catch (ClientRegistrationException e) { @@ -1126,10 +1127,10 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest { String conditionName = ClientUpdateContextCondition_NAME; createCondition(conditionName, ClientUpdateContextConditionFactory.PROVIDER_ID, null, (ComponentRepresentation provider) -> { - setConditionRegistrationMethods(provider, new ArrayList<>(Arrays.asList( + setConditionRegistrationMethods(provider, Arrays.asList( ClientUpdateContextConditionFactory.BY_AUTHENTICATED_USER, ClientUpdateContextConditionFactory.BY_INITIAL_ACCESS_TOKEN, - ClientUpdateContextConditionFactory.BY_REGISTRATION_ACCESS_TOKEN))); + ClientUpdateContextConditionFactory.BY_REGISTRATION_ACCESS_TOKEN)); }); registerCondition(conditionName, policyName); logger.info("... Registered Condition : " + conditionName); @@ -1148,9 +1149,9 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest { assertEquals(ERR_MSG_CLIENT_REG_FAIL, e.getMessage()); } updateCondition(conditionName, (ComponentRepresentation provider) -> { - setConditionRegistrationMethods(provider, new ArrayList<>(Arrays.asList( + setConditionRegistrationMethods(provider, Arrays.asList( ClientUpdateContextConditionFactory.BY_AUTHENTICATED_USER, - ClientUpdateContextConditionFactory.BY_REGISTRATION_ACCESS_TOKEN))); + ClientUpdateContextConditionFactory.BY_REGISTRATION_ACCESS_TOKEN)); }); try { createClientDynamically(generateSuffixedName(CLIENT_NAME), (OIDCClientRepresentation clientRep) -> {}); @@ -1172,7 +1173,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest { String conditionName = ClientRolesCondition_NAME; createCondition(conditionName, ClientRolesConditionFactory.PROVIDER_ID, null, (ComponentRepresentation provider) -> { - setConditionClientRoles(provider, new ArrayList<>(Arrays.asList(roleAlphaName, roleZetaName))); + setConditionClientRoles(provider, Arrays.asList(roleAlphaName, roleZetaName)); }); registerCondition(conditionName, policyName); logger.info("... Registered Condition : " + conditionName); @@ -1317,6 +1318,73 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest { } } + @Test + public void testExtendedClientPolicyIntefacesForClientRegistrationPolicyMigration() throws ClientRegistrationException, ClientPolicyException { + String policyName = "MyPolicy"; + String clientName = "ByAdmin-App" + KeycloakModelUtils.generateId().substring(0, 7); + String executorName = "TestRaiseExeptionExecutor"; + + createPolicy(policyName, DefaultClientPolicyProviderFactory.PROVIDER_ID, null, null, null); + logger.info("... Created Policy : " + policyName); + + createCondition("AnyClientConditionFactory", AnyClientConditionFactory.PROVIDER_ID, null, (ComponentRepresentation provider) -> { + }); + registerCondition("AnyClientConditionFactory", policyName); + logger.info("... Registered Condition : AnyClientConditionFactory"); + + createExecutor(executorName, TestRaiseExeptionExecutorFactory.PROVIDER_ID, null, (ComponentRepresentation provider) -> { + provider.getConfig().put(TestRaiseExeptionExecutorFactory.TARGET_CP_EVENTS, Arrays.asList(ClientPolicyEvent.REGISTERED.toString())); + }); + registerExecutor(executorName, policyName); + logger.info("... Registered Executor : " + executorName); + + String clientId = null; + try { + try { + createClientByAdmin(clientName, (ClientRepresentation clientRep) -> { + }); + fail(); + } catch (ClientPolicyException cpe) { + assertEquals(ClientPolicyEvent.REGISTERED.toString(), cpe.getError()); + } + + updateTargetCPEvents(executorName, Arrays.asList(ClientPolicyEvent.UPDATED)); + + clientId = getClientByAdminWithName(clientName).getId(); + assertEquals(true, getClientByAdmin(clientId).isEnabled()); + try { + updateClientByAdmin(clientId, (ClientRepresentation clientRep) -> { + clientRep.setEnabled(false); + }); + fail(); + } catch (ClientPolicyException cpe) { + assertEquals(ClientPolicyEvent.UPDATED.toString(), cpe.getError()); + } + assertEquals(false, getClientByAdmin(clientId).isEnabled()); + + updateTargetCPEvents(executorName, Arrays.asList(ClientPolicyEvent.VIEW)); + try { + getClientByAdmin(clientId); + } catch (ClientPolicyException cpe) { + assertEquals(ClientPolicyEvent.VIEW.toString(), cpe.getError()); + } + + updateTargetCPEvents(executorName, Arrays.asList(ClientPolicyEvent.UNREGISTER)); + try { + deleteClientByAdmin(clientId); + } catch (ClientPolicyException cpe) { + assertEquals(ClientPolicyEvent.UNREGISTER.toString(), cpe.getError()); + } + } finally { + updateExecutor(executorName, (ComponentRepresentation provider) -> { + provider.getConfig().put(TestRaiseExeptionExecutorFactory.TARGET_CP_EVENTS, Collections.singletonList((String)null)); + }); + deleteClientByAdmin(clientId); + } + + // TODO : For dynamic client registration, the existing test scheme can not distinguish when the exception happens on which event so that the migrated client policy executors test them afterwards. + } + private void checkMtlsFlow() throws IOException { // Check login. OAuthClient.AuthorizationEndpointResponse loginResponse = oauth.doLogin(TEST_USER_NAME, TEST_USER_PASSWORD); @@ -1438,18 +1506,20 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest { createPolicy(policyName, DefaultClientPolicyProviderFactory.PROVIDER_ID, null, null, null); logger.info("... Created Policy : " + policyName); - createCondition(ClientUpdateContextCondition_NAME, ClientUpdateContextConditionFactory.PROVIDER_ID, null, (ComponentRepresentation provider) -> { - setConditionRegistrationMethods(provider, new ArrayList<>(Arrays.asList(ClientUpdateContextConditionFactory.BY_AUTHENTICATED_USER))); + String conditionName = ClientUpdateContextCondition_NAME; + createCondition(conditionName, ClientUpdateContextConditionFactory.PROVIDER_ID, null, (ComponentRepresentation provider) -> { + setConditionRegistrationMethods(provider, Arrays.asList(ClientUpdateContextConditionFactory.BY_AUTHENTICATED_USER)); }); - registerCondition(ClientUpdateContextCondition_NAME, policyName); - logger.info("... Registered Condition : " + ClientUpdateContextCondition_NAME); + registerCondition(conditionName, policyName); + logger.info("... Registered Condition : " + conditionName); - createExecutor(SecureClientAuthEnforceExecutor_NAME, SecureClientAuthEnforceExecutorFactory.PROVIDER_ID, null, (ComponentRepresentation provider) -> { - setExecutorAcceptedClientAuthMethods(provider, new ArrayList<>(Arrays.asList( - JWTClientAuthenticator.PROVIDER_ID, JWTClientSecretAuthenticator.PROVIDER_ID, X509ClientAuthenticator.PROVIDER_ID))); + String executorName = SecureClientAuthEnforceExecutor_NAME; + createExecutor(executorName, SecureClientAuthEnforceExecutorFactory.PROVIDER_ID, null, (ComponentRepresentation provider) -> { + setExecutorAcceptedClientAuthMethods(provider, Arrays.asList( + JWTClientAuthenticator.PROVIDER_ID, JWTClientSecretAuthenticator.PROVIDER_ID, X509ClientAuthenticator.PROVIDER_ID)); }); - registerExecutor(SecureClientAuthEnforceExecutor_NAME, policyName); - logger.info("... Registered Executor : " + SecureClientAuthEnforceExecutor_NAME); + registerExecutor(executorName, policyName); + logger.info("... Registered Executor : " + executorName); } @@ -1458,31 +1528,35 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest { createPolicy(policyName, DefaultClientPolicyProviderFactory.PROVIDER_ID, null, null, null); logger.info("... Created Policy : " + policyName); - createCondition(ClientUpdateContextCondition_NAME, ClientUpdateContextConditionFactory.PROVIDER_ID, null, (ComponentRepresentation provider) -> { - setConditionRegistrationMethods(provider, new ArrayList<>(Arrays.asList(ClientUpdateContextConditionFactory.BY_INITIAL_ACCESS_TOKEN))); + String conditionName = ClientUpdateContextCondition_NAME; + createCondition(conditionName, ClientUpdateContextConditionFactory.PROVIDER_ID, null, (ComponentRepresentation provider) -> { + setConditionRegistrationMethods(provider, Arrays.asList(ClientUpdateContextConditionFactory.BY_INITIAL_ACCESS_TOKEN)); }); - registerCondition(ClientUpdateContextCondition_NAME, policyName); - logger.info("... Registered Condition : " + ClientUpdateContextCondition_NAME); + registerCondition(conditionName, policyName); + logger.info("... Registered Condition : " + conditionName); - createCondition(ClientRolesCondition_NAME, ClientRolesConditionFactory.PROVIDER_ID, null, (ComponentRepresentation provider) -> { - setConditionClientRoles(provider, new ArrayList<>(Arrays.asList(SAMPLE_CLIENT_ROLE))); + conditionName = ClientRolesCondition_NAME; + createCondition(conditionName, ClientRolesConditionFactory.PROVIDER_ID, null, (ComponentRepresentation provider) -> { + setConditionClientRoles(provider, Arrays.asList(SAMPLE_CLIENT_ROLE)); }); - registerCondition(ClientRolesCondition_NAME, policyName); - logger.info("... Registered Condition : " + ClientRolesCondition_NAME); + registerCondition(conditionName, policyName); + logger.info("... Registered Condition : " + conditionName); - createExecutor(SecureClientAuthEnforceExecutor_NAME, SecureClientAuthEnforceExecutorFactory.PROVIDER_ID, null, (ComponentRepresentation provider) -> { - setExecutorAcceptedClientAuthMethods(provider, new ArrayList<>(Arrays.asList(ClientIdAndSecretAuthenticator.PROVIDER_ID, JWTClientAuthenticator.PROVIDER_ID))); + String executorName = SecureClientAuthEnforceExecutor_NAME; + createExecutor(executorName, SecureClientAuthEnforceExecutorFactory.PROVIDER_ID, null, (ComponentRepresentation provider) -> { + setExecutorAcceptedClientAuthMethods(provider, Arrays.asList(ClientIdAndSecretAuthenticator.PROVIDER_ID, JWTClientAuthenticator.PROVIDER_ID)); setExecutorAugmentedClientAuthMethod(provider, ClientIdAndSecretAuthenticator.PROVIDER_ID); setExecutorAugmentActivate(provider); }); - registerExecutor(SecureClientAuthEnforceExecutor_NAME, policyName); - logger.info("... Registered Executor : " + SecureClientAuthEnforceExecutor_NAME); + registerExecutor(executorName, policyName); + logger.info("... Registered Executor : " + executorName); - createExecutor(PKCEEnforceExecutor_NAME, PKCEEnforceExecutorFactory.PROVIDER_ID, null, (ComponentRepresentation provider) -> { + executorName = PKCEEnforceExecutor_NAME; + createExecutor(executorName, PKCEEnforceExecutorFactory.PROVIDER_ID, null, (ComponentRepresentation provider) -> { setExecutorAugmentActivate(provider); }); - registerExecutor(PKCEEnforceExecutor_NAME, policyName); - logger.info("... Registered Executor : " + PKCEEnforceExecutor_NAME); + registerExecutor(executorName, policyName); + logger.info("... Registered Executor : " + executorName); } private void successfulLoginAndLogout(String clientId, String clientSecret) { @@ -1587,4 +1661,9 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest { assertEquals(ERR_MSG_MISSING_NONCE, oauth.getCurrentQuery().get(OAuth2Constants.ERROR_DESCRIPTION)); } + private void updateTargetCPEvents(String executorName, List events) { + updateExecutor(executorName, (ComponentRepresentation provider) -> { + provider.getConfig().put(TestRaiseExeptionExecutorFactory.TARGET_CP_EVENTS, events.stream().map(i->i.toString()).collect(Collectors.toList())); + }); + } } \ No newline at end of file