Allowing client registration access token rotation deactivation

This commit is contained in:
Réda Housni Alaoui 2022-11-20 20:07:17 +01:00 committed by Marek Posolda
parent e374e309c6
commit dbe0c27bcf
6 changed files with 189 additions and 2 deletions

View file

@ -0,0 +1,26 @@
/*
* Copyright 2022 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.models;
public class ClientRegistrationAccessTokenConstants {
public static final String ROTATION_ENABLED = "client.registration.access.token.enabled";
private ClientRegistrationAccessTokenConstants(){}
}

View file

@ -0,0 +1,49 @@
/*
* Copyright 2022 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.executor;
import org.keycloak.models.ClientRegistrationAccessTokenConstants;
import org.keycloak.models.KeycloakSession;
import org.keycloak.representations.idm.ClientPolicyExecutorConfigurationRepresentation;
import org.keycloak.services.clientpolicy.ClientPolicyContext;
import org.keycloak.services.clientpolicy.ClientPolicyException;
public class RegistrationAccessTokenRotationDisabledExecutor implements ClientPolicyExecutorProvider<ClientPolicyExecutorConfigurationRepresentation> {
private final String providerId;
private final KeycloakSession session;
public RegistrationAccessTokenRotationDisabledExecutor(String providerId, KeycloakSession session) {
this.providerId = providerId;
this.session = session;
}
@Override
public String getProviderId() {
return providerId;
}
@Override
public void executeOnEvent(ClientPolicyContext context) throws ClientPolicyException {
if (session.getAttribute(ClientRegistrationAccessTokenConstants.ROTATION_ENABLED) == null){
return;
}
session.setAttribute(ClientRegistrationAccessTokenConstants.ROTATION_ENABLED, false);
}
}

View file

@ -0,0 +1,70 @@
/*
* Copyright 2022 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.executor;
import java.util.Collections;
import java.util.List;
import org.keycloak.Config;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.provider.ProviderConfigProperty;
public class RegistrationAccessTokenRotationDisabledExecutorFactory implements ClientPolicyExecutorProviderFactory {
public static final String PROVIDER_ID = "registration-access-token-rotation-disabled";
@Override
public String getHelpText() {
return "Disables registration access rotation for the client.";
}
@Override
public List<ProviderConfigProperty> getConfigProperties() {
return Collections.emptyList();
}
@Override
public ClientPolicyExecutorProvider create(KeycloakSession session) {
return new RegistrationAccessTokenRotationDisabledExecutor(getId(), session);
}
@Override
public void init(Config.Scope config) {
}
@Override
public void postInit(KeycloakSessionFactory factory) {
}
@Override
public void close() {
}
@Override
public String getId() {
return PROVIDER_ID;
}
@Override
public boolean isSupported() {
return true;
}
}

View file

@ -22,6 +22,7 @@ import org.keycloak.events.EventBuilder;
import org.keycloak.events.EventType; import org.keycloak.events.EventType;
import org.keycloak.models.ClientInitialAccessModel; import org.keycloak.models.ClientInitialAccessModel;
import org.keycloak.models.ClientModel; import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientRegistrationAccessTokenConstants;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ModelDuplicateException; import org.keycloak.models.ModelDuplicateException;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
@ -142,6 +143,7 @@ public abstract class AbstractClientRegistrationProvider implements ClientRegist
event.event(EventType.CLIENT_UPDATE).client(clientId); event.event(EventType.CLIENT_UPDATE).client(clientId);
ClientModel client = session.getContext().getRealm().getClientByClientId(clientId); ClientModel client = session.getContext().getRealm().getClientByClientId(clientId);
session.setAttribute(ClientRegistrationAccessTokenConstants.ROTATION_ENABLED, true);
RegistrationAuth registrationAuth = auth.requireUpdate(context, client); RegistrationAuth registrationAuth = auth.requireUpdate(context, client);
if (!client.getClientId().equals(rep.getClientId())) { if (!client.getClientId().equals(rep.getClientId())) {
@ -165,9 +167,15 @@ public abstract class AbstractClientRegistrationProvider implements ClientRegist
} }
if (auth.isRegistrationAccessToken()) { if (auth.isRegistrationAccessToken()) {
String registrationAccessToken = ClientRegistrationTokenUtils.updateRegistrationAccessToken(session, client, auth.getRegistrationAuth()); String registrationAccessToken;
if ((boolean) session.getAttribute(ClientRegistrationAccessTokenConstants.ROTATION_ENABLED)) {
registrationAccessToken = ClientRegistrationTokenUtils.updateRegistrationAccessToken(session, client, auth.getRegistrationAuth());
} else {
registrationAccessToken = ClientRegistrationTokenUtils.updateTokenSignature(session, auth);
}
rep.setRegistrationAccessToken(registrationAccessToken); rep.setRegistrationAccessToken(registrationAccessToken);
} }
session.removeAttribute(ClientRegistrationAccessTokenConstants.ROTATION_ENABLED);
try { try {
session.getContext().setClient(client); session.getContext().setClient(client);

View file

@ -18,4 +18,5 @@ org.keycloak.services.clientpolicy.executor.RejectResourceOwnerPasswordCredentia
org.keycloak.services.clientpolicy.executor.ClientSecretRotationExecutorFactory org.keycloak.services.clientpolicy.executor.ClientSecretRotationExecutorFactory
org.keycloak.services.clientpolicy.executor.RejectRequestExecutorFactory org.keycloak.services.clientpolicy.executor.RejectRequestExecutorFactory
org.keycloak.services.clientpolicy.executor.IntentClientBindCheckExecutorFactory org.keycloak.services.clientpolicy.executor.IntentClientBindCheckExecutorFactory
org.keycloak.services.clientpolicy.executor.SuppressRefreshTokenRotationExecutorFactory org.keycloak.services.clientpolicy.executor.SuppressRefreshTokenRotationExecutorFactory
org.keycloak.services.clientpolicy.executor.RegistrationAccessTokenRotationDisabledExecutorFactory

View file

@ -111,6 +111,7 @@ import org.keycloak.services.clientpolicy.executor.FullScopeDisabledExecutorFact
import org.keycloak.services.clientpolicy.executor.HolderOfKeyEnforcerExecutorFactory; import org.keycloak.services.clientpolicy.executor.HolderOfKeyEnforcerExecutorFactory;
import org.keycloak.services.clientpolicy.executor.IntentClientBindCheckExecutorFactory; import org.keycloak.services.clientpolicy.executor.IntentClientBindCheckExecutorFactory;
import org.keycloak.services.clientpolicy.executor.PKCEEnforcerExecutorFactory; import org.keycloak.services.clientpolicy.executor.PKCEEnforcerExecutorFactory;
import org.keycloak.services.clientpolicy.executor.RegistrationAccessTokenRotationDisabledExecutorFactory;
import org.keycloak.services.clientpolicy.executor.RejectRequestExecutorFactory; import org.keycloak.services.clientpolicy.executor.RejectRequestExecutorFactory;
import org.keycloak.services.clientpolicy.executor.RejectResourceOwnerPasswordCredentialsGrantExecutorFactory; import org.keycloak.services.clientpolicy.executor.RejectResourceOwnerPasswordCredentialsGrantExecutorFactory;
import org.keycloak.services.clientpolicy.executor.SecureClientAuthenticatorExecutorFactory; import org.keycloak.services.clientpolicy.executor.SecureClientAuthenticatorExecutorFactory;
@ -3391,6 +3392,38 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
assertEquals("no claim for an intent value for ID token" , oauth.getCurrentFragment().get(OAuth2Constants.ERROR_DESCRIPTION)); assertEquals("no claim for an intent value for ID token" , oauth.getCurrentFragment().get(OAuth2Constants.ERROR_DESCRIPTION));
} }
@Test
public void testRegistrationAccessTokenRotationDisabledExecutor() throws Exception {
// register profiles - client autoConfigured to disable registration access token rotation
String json = new ClientProfilesBuilder().addProfile(
new ClientProfileBuilder().createProfile(PROFILE_NAME, "Test Profile")
.addExecutor(
RegistrationAccessTokenRotationDisabledExecutorFactory.PROVIDER_ID,
new ClientPolicyExecutorConfigurationRepresentation()
)
.toRepresentation()
).toString();
updateProfiles(json);
// register policies
json = new ClientPoliciesBuilder().addPolicy(
new ClientPolicyBuilder().createPolicy(POLICY_NAME, "Test Policy", Boolean.TRUE)
.addCondition(AnyClientConditionFactory.PROVIDER_ID,
createAnyClientConditionConfig())
.addProfile(PROFILE_NAME)
.toRepresentation()
).toString();
updatePolicies(json);
String clientId = createClientDynamically(generateSuffixedName(CLIENT_NAME), r -> {});
OIDCClientRepresentation createdClient = getClientDynamically(clientId);
updateClientDynamically(clientId, clientRep ->
clientRep.setTokenEndpointAuthMethod(OIDCLoginProtocol.CLIENT_SECRET_BASIC));
assertEquals(createdClient.getRegistrationAccessToken(), getClientDynamically(clientId).getRegistrationAccessToken());
}
private void openVerificationPage(String verificationUri) { private void openVerificationPage(String verificationUri) {
driver.navigate().to(verificationUri); driver.navigate().to(verificationUri);
} }