Prevent removing flow that used by client flow overrides
Closes #30707 Signed-off-by: Lex Cao <lexcao@foxmail.com>
This commit is contained in:
parent
e97ffe7a32
commit
3818f8f575
4 changed files with 143 additions and 25 deletions
|
@ -30,15 +30,17 @@ import org.keycloak.common.util.Time;
|
||||||
import org.keycloak.component.ComponentModel;
|
import org.keycloak.component.ComponentModel;
|
||||||
import org.keycloak.crypto.Algorithm;
|
import org.keycloak.crypto.Algorithm;
|
||||||
import org.keycloak.deployment.DeployedConfigurationsManager;
|
import org.keycloak.deployment.DeployedConfigurationsManager;
|
||||||
|
import org.keycloak.models.AccountRoles;
|
||||||
import org.keycloak.models.AuthenticationExecutionModel;
|
import org.keycloak.models.AuthenticationExecutionModel;
|
||||||
import org.keycloak.models.AuthenticatorConfigModel;
|
|
||||||
import org.keycloak.models.AuthenticationFlowModel;
|
import org.keycloak.models.AuthenticationFlowModel;
|
||||||
|
import org.keycloak.models.AuthenticatorConfigModel;
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
import org.keycloak.models.ClientScopeModel;
|
import org.keycloak.models.ClientScopeModel;
|
||||||
import org.keycloak.models.ClientSecretConstants;
|
import org.keycloak.models.ClientSecretConstants;
|
||||||
import org.keycloak.models.Constants;
|
import org.keycloak.models.Constants;
|
||||||
import org.keycloak.models.GroupModel;
|
import org.keycloak.models.GroupModel;
|
||||||
import org.keycloak.models.GroupProvider;
|
import org.keycloak.models.GroupProvider;
|
||||||
|
import org.keycloak.models.GroupProviderFactory;
|
||||||
import org.keycloak.models.IdentityProviderModel;
|
import org.keycloak.models.IdentityProviderModel;
|
||||||
import org.keycloak.models.KeycloakContext;
|
import org.keycloak.models.KeycloakContext;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
@ -50,15 +52,18 @@ import org.keycloak.models.RoleModel;
|
||||||
import org.keycloak.models.ScopeContainerModel;
|
import org.keycloak.models.ScopeContainerModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.protocol.oidc.OIDCConfigAttributes;
|
import org.keycloak.protocol.oidc.OIDCConfigAttributes;
|
||||||
|
import org.keycloak.provider.Provider;
|
||||||
|
import org.keycloak.provider.ProviderFactory;
|
||||||
import org.keycloak.representations.idm.CertificateRepresentation;
|
import org.keycloak.representations.idm.CertificateRepresentation;
|
||||||
|
import org.keycloak.sessions.AuthenticationSessionModel;
|
||||||
|
import org.keycloak.sessions.RootAuthenticationSessionModel;
|
||||||
import org.keycloak.transaction.JtaTransactionManagerLookup;
|
import org.keycloak.transaction.JtaTransactionManagerLookup;
|
||||||
import org.keycloak.utils.KeycloakSessionUtil;
|
import org.keycloak.utils.KeycloakSessionUtil;
|
||||||
|
|
||||||
import javax.crypto.spec.SecretKeySpec;
|
|
||||||
import jakarta.transaction.InvalidTransactionException;
|
import jakarta.transaction.InvalidTransactionException;
|
||||||
import jakarta.transaction.SystemException;
|
import jakarta.transaction.SystemException;
|
||||||
import jakarta.transaction.Transaction;
|
import jakarta.transaction.Transaction;
|
||||||
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
import java.security.Key;
|
import java.security.Key;
|
||||||
import java.security.KeyPair;
|
import java.security.KeyPair;
|
||||||
import java.security.PrivateKey;
|
import java.security.PrivateKey;
|
||||||
|
@ -66,25 +71,18 @@ import java.security.PublicKey;
|
||||||
import java.security.cert.X509Certificate;
|
import java.security.cert.X509Certificate;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
import org.keycloak.models.AccountRoles;
|
|
||||||
import org.keycloak.models.GroupProviderFactory;
|
|
||||||
import org.keycloak.provider.Provider;
|
|
||||||
import org.keycloak.provider.ProviderFactory;
|
|
||||||
import org.keycloak.sessions.AuthenticationSessionModel;
|
|
||||||
import org.keycloak.sessions.RootAuthenticationSessionModel;
|
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set of helper methods, which are useful in various model implementations.
|
* Set of helper methods, which are useful in various model implementations.
|
||||||
|
@ -922,6 +920,17 @@ public final class KeycloakModelUtils {
|
||||||
if ((realmFlow = realm.getDockerAuthenticationFlow()) != null && realmFlow.getId().equals(model.getId())) return true;
|
if ((realmFlow = realm.getDockerAuthenticationFlow()) != null && realmFlow.getId().equals(model.getId())) return true;
|
||||||
if ((realmFlow = realm.getFirstBrokerLoginFlow()) != null && realmFlow.getId().equals(model.getId())) return true;
|
if ((realmFlow = realm.getFirstBrokerLoginFlow()) != null && realmFlow.getId().equals(model.getId())) return true;
|
||||||
|
|
||||||
|
Stream<ClientModel> browserFlowOverridingClients = realm.searchClientByAuthenticationFlowBindingOverrides(Collections.singletonMap("browser", model.getId()), 0, 1);
|
||||||
|
Stream<ClientModel> directGrantFlowOverridingClients = realm.searchClientByAuthenticationFlowBindingOverrides(Collections.singletonMap("direct_grant", model.getId()), 0, 1);
|
||||||
|
boolean usedByClient = Stream.concat(browserFlowOverridingClients, directGrantFlowOverridingClients)
|
||||||
|
.limit(1)
|
||||||
|
.findAny()
|
||||||
|
.isPresent();
|
||||||
|
|
||||||
|
if (usedByClient) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return realm.getIdentityProvidersStream().anyMatch(idp ->
|
return realm.getIdentityProvidersStream().anyMatch(idp ->
|
||||||
Objects.equals(idp.getFirstBrokerLoginFlowId(), model.getId()) ||
|
Objects.equals(idp.getFirstBrokerLoginFlowId(), model.getId()) ||
|
||||||
Objects.equals(idp.getPostBrokerLoginFlowId(), model.getId()));
|
Objects.equals(idp.getPostBrokerLoginFlowId(), model.getId()));
|
||||||
|
|
|
@ -217,12 +217,13 @@ public abstract class AbstractAuthenticationTest extends AbstractKeycloakTest {
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
void createFlow(AuthenticationFlowRepresentation flowRep) {
|
String createFlow(AuthenticationFlowRepresentation flowRep) {
|
||||||
Response response = authMgmtResource.createFlow(flowRep);
|
Response response = authMgmtResource.createFlow(flowRep);
|
||||||
org.keycloak.testsuite.Assert.assertEquals(201, response.getStatus());
|
org.keycloak.testsuite.Assert.assertEquals(201, response.getStatus());
|
||||||
response.close();
|
response.close();
|
||||||
String flowId = ApiUtil.getCreatedId(response);
|
String flowId = ApiUtil.getCreatedId(response);
|
||||||
getCleanup().addAuthenticationFlowId(flowId);
|
getCleanup().addAuthenticationFlowId(flowId);
|
||||||
assertAdminEvents.assertEvent(testRealmId, OperationType.CREATE, AssertAdminEvents.isExpectedPrefixFollowedByUuid(AdminEventPaths.authFlowsPath()), flowRep, ResourceType.AUTH_FLOW);
|
assertAdminEvents.assertEvent(testRealmId, OperationType.CREATE, AssertAdminEvents.isExpectedPrefixFollowedByUuid(AdminEventPaths.authFlowsPath()), flowRep, ResourceType.AUTH_FLOW);
|
||||||
|
return flowId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,8 @@ package org.keycloak.testsuite.admin.authentication;
|
||||||
|
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.keycloak.admin.client.resource.ClientResource;
|
||||||
|
import org.keycloak.admin.client.resource.IdentityProviderResource;
|
||||||
import org.keycloak.authentication.authenticators.browser.IdentityProviderAuthenticatorFactory;
|
import org.keycloak.authentication.authenticators.browser.IdentityProviderAuthenticatorFactory;
|
||||||
import org.keycloak.common.util.StreamUtil;
|
import org.keycloak.common.util.StreamUtil;
|
||||||
import org.keycloak.events.admin.OperationType;
|
import org.keycloak.events.admin.OperationType;
|
||||||
|
@ -28,7 +30,10 @@ import org.keycloak.representations.idm.AuthenticationExecutionExportRepresentat
|
||||||
import org.keycloak.representations.idm.AuthenticationExecutionInfoRepresentation;
|
import org.keycloak.representations.idm.AuthenticationExecutionInfoRepresentation;
|
||||||
import org.keycloak.representations.idm.AuthenticationFlowRepresentation;
|
import org.keycloak.representations.idm.AuthenticationFlowRepresentation;
|
||||||
import org.keycloak.representations.idm.AuthenticatorConfigRepresentation;
|
import org.keycloak.representations.idm.AuthenticatorConfigRepresentation;
|
||||||
|
import org.keycloak.representations.idm.ClientRepresentation;
|
||||||
|
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||||
import org.keycloak.representations.idm.OAuth2ErrorRepresentation;
|
import org.keycloak.representations.idm.OAuth2ErrorRepresentation;
|
||||||
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
import org.keycloak.testsuite.admin.ApiUtil;
|
import org.keycloak.testsuite.admin.ApiUtil;
|
||||||
import org.keycloak.testsuite.util.AdminEventPaths;
|
import org.keycloak.testsuite.util.AdminEventPaths;
|
||||||
import org.keycloak.testsuite.util.ContainerAssume;
|
import org.keycloak.testsuite.util.ContainerAssume;
|
||||||
|
@ -37,6 +42,7 @@ import jakarta.ws.rs.BadRequestException;
|
||||||
import jakarta.ws.rs.ClientErrorException;
|
import jakarta.ws.rs.ClientErrorException;
|
||||||
import jakarta.ws.rs.InternalServerErrorException;
|
import jakarta.ws.rs.InternalServerErrorException;
|
||||||
import jakarta.ws.rs.NotFoundException;
|
import jakarta.ws.rs.NotFoundException;
|
||||||
|
import jakarta.ws.rs.WebApplicationException;
|
||||||
import jakarta.ws.rs.core.Response;
|
import jakarta.ws.rs.core.Response;
|
||||||
import jakarta.ws.rs.core.Response.Status;
|
import jakarta.ws.rs.core.Response.Status;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -47,14 +53,17 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
import java.util.function.Supplier;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
import static org.hamcrest.Matchers.containsString;
|
import static org.hamcrest.Matchers.containsString;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
import static org.keycloak.testsuite.util.Matchers.body;
|
import static org.keycloak.testsuite.util.Matchers.body;
|
||||||
import static org.keycloak.testsuite.util.Matchers.statusCodeIs;
|
import static org.keycloak.testsuite.util.Matchers.statusCodeIs;
|
||||||
|
@ -70,7 +79,7 @@ public class FlowTest extends AbstractAuthenticationTest {
|
||||||
createFlow(newFlow("Foo", "Foo flow", "generic", true, false));
|
createFlow(newFlow("Foo", "Foo flow", "generic", true, false));
|
||||||
addFlowToParent("Foo", "child");
|
addFlowToParent("Foo", "child");
|
||||||
addFlowToParent("child", "grandchild");
|
addFlowToParent("child", "grandchild");
|
||||||
|
|
||||||
List<AuthenticationFlowRepresentation> flows = authMgmtResource.getFlows();
|
List<AuthenticationFlowRepresentation> flows = authMgmtResource.getFlows();
|
||||||
AuthenticationFlowRepresentation found = findFlowByAlias("Foo", flows);
|
AuthenticationFlowRepresentation found = findFlowByAlias("Foo", flows);
|
||||||
authMgmtResource.deleteFlow(found.getId());
|
authMgmtResource.deleteFlow(found.getId());
|
||||||
|
@ -78,7 +87,7 @@ public class FlowTest extends AbstractAuthenticationTest {
|
||||||
|
|
||||||
createFlow(newFlow("Foo", "Foo flow", "generic", true, false));
|
createFlow(newFlow("Foo", "Foo flow", "generic", true, false));
|
||||||
addFlowToParent("Foo", "child");
|
addFlowToParent("Foo", "child");
|
||||||
|
|
||||||
// Under the old code, this would throw an error because "grandchild"
|
// Under the old code, this would throw an error because "grandchild"
|
||||||
// was left in the database
|
// was left in the database
|
||||||
addFlowToParent("child", "grandchild");
|
addFlowToParent("child", "grandchild");
|
||||||
|
@ -106,7 +115,7 @@ public class FlowTest extends AbstractAuthenticationTest {
|
||||||
|
|
||||||
authMgmtResource.deleteFlow(findFlowByAlias("Foo", authMgmtResource.getFlows()).getId());
|
authMgmtResource.deleteFlow(findFlowByAlias("Foo", authMgmtResource.getFlows()).getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addFlowToParent(String parentAlias, String childAlias) {
|
private void addFlowToParent(String parentAlias, String childAlias) {
|
||||||
Map<String, Object> data = new HashMap<>();
|
Map<String, Object> data = new HashMap<>();
|
||||||
data.put("alias", childAlias);
|
data.put("alias", childAlias);
|
||||||
|
@ -114,13 +123,13 @@ public class FlowTest extends AbstractAuthenticationTest {
|
||||||
data.put("description", childAlias + " flow");
|
data.put("description", childAlias + " flow");
|
||||||
authMgmtResource.addExecutionFlow(parentAlias, data);
|
authMgmtResource.addExecutionFlow(parentAlias, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAddFlowWithRestrictedCharInAlias() {
|
public void testAddFlowWithRestrictedCharInAlias() {
|
||||||
Response resp = authMgmtResource.createFlow(newFlow("fo]o", "Browser flow", "basic-flow", true, false));
|
Response resp = authMgmtResource.createFlow(newFlow("fo]o", "Browser flow", "basic-flow", true, false));
|
||||||
Assert.assertEquals(400, resp.getStatus());
|
Assert.assertEquals(400, resp.getStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAddRemoveFlow() {
|
public void testAddRemoveFlow() {
|
||||||
|
|
||||||
|
@ -261,6 +270,97 @@ public class FlowTest extends AbstractAuthenticationTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRemoveUsedFlow() {
|
||||||
|
String flowAlias = "test";
|
||||||
|
String flowId = createFlow(newFlow(flowAlias, "Test flow", "generic", true, false));
|
||||||
|
|
||||||
|
Runnable assertRemoveFail = () -> {
|
||||||
|
try {
|
||||||
|
authMgmtResource.deleteFlow(flowId);
|
||||||
|
Assert.fail("Not expected to delete flow that in used.");
|
||||||
|
} catch (WebApplicationException e) {
|
||||||
|
OAuth2ErrorRepresentation error = e.getResponse().readEntity(OAuth2ErrorRepresentation.class);
|
||||||
|
Assert.assertEquals("For more on this error consult the server log at the debug level.", error.getErrorDescription());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
{
|
||||||
|
// used in realm flow
|
||||||
|
RealmRepresentation realm = realmResource.toRepresentation();
|
||||||
|
BiConsumer<Supplier<String>, Consumer<String>> assertRemoveFailInRealm =
|
||||||
|
(rollbackFlow, updateFlow) -> {
|
||||||
|
String rollbackValue = rollbackFlow.get();
|
||||||
|
try {
|
||||||
|
updateFlow.accept(flowAlias);
|
||||||
|
realmResource.update(realm);
|
||||||
|
|
||||||
|
assertRemoveFail.run();
|
||||||
|
} finally {
|
||||||
|
updateFlow.accept(rollbackValue);
|
||||||
|
realmResource.update(realm);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
assertRemoveFailInRealm.accept(realm::getBrowserFlow, realm::setBrowserFlow);
|
||||||
|
assertRemoveFailInRealm.accept(realm::getRegistrationFlow, realm::setRegistrationFlow);
|
||||||
|
assertRemoveFailInRealm.accept(realm::getClientAuthenticationFlow, realm::setClientAuthenticationFlow);
|
||||||
|
assertRemoveFailInRealm.accept(realm::getDirectGrantFlow, realm::setDirectGrantFlow);
|
||||||
|
assertRemoveFailInRealm.accept(realm::getResetCredentialsFlow, realm::setResetCredentialsFlow);
|
||||||
|
assertRemoveFailInRealm.accept(realm::getDockerAuthenticationFlow, realm::setDockerAuthenticationFlow);
|
||||||
|
assertRemoveFailInRealm.accept(realm::getFirstBrokerLoginFlow, realm::setFirstBrokerLoginFlow);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// used by client override
|
||||||
|
ClientRepresentation client = realmResource.clients().findByClientId("account").get(0);
|
||||||
|
ClientResource clientResource = realmResource.clients().get(client.getId());
|
||||||
|
|
||||||
|
try {
|
||||||
|
client.setAuthenticationFlowBindingOverrides(
|
||||||
|
Map.of("browser", flowId)
|
||||||
|
);
|
||||||
|
clientResource.update(client);
|
||||||
|
|
||||||
|
assertRemoveFail.run();
|
||||||
|
} finally {
|
||||||
|
client.setAuthenticationFlowBindingOverrides(
|
||||||
|
Map.of("browser", "")
|
||||||
|
);
|
||||||
|
clientResource.update(client);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// used by idp override
|
||||||
|
IdentityProviderRepresentation idp = new IdentityProviderRepresentation();
|
||||||
|
idp.setAlias("idp");
|
||||||
|
idp.setProviderId("oidc");
|
||||||
|
|
||||||
|
Response response = realmResource.identityProviders().create(idp);
|
||||||
|
Assert.assertNotNull(ApiUtil.getCreatedId(response));
|
||||||
|
response.close();
|
||||||
|
getCleanup().addIdentityProviderAlias(idp.getAlias());
|
||||||
|
|
||||||
|
IdentityProviderResource idpResource = realmResource.identityProviders().get("idp");
|
||||||
|
BiConsumer<Supplier<String>, Consumer<String>> assertRemoveFailByIdp =
|
||||||
|
(rollbackIdp, updateIdp) -> {
|
||||||
|
String rollbackValue = rollbackIdp.get();
|
||||||
|
try {
|
||||||
|
updateIdp.accept(flowAlias);
|
||||||
|
idpResource.update(idp);
|
||||||
|
|
||||||
|
assertRemoveFail.run();
|
||||||
|
} finally {
|
||||||
|
updateIdp.accept(rollbackValue);
|
||||||
|
idpResource.update(idp);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
assertRemoveFailByIdp.accept(idp::getFirstBrokerLoginFlowAlias, idp::setFirstBrokerLoginFlowAlias);
|
||||||
|
assertRemoveFailByIdp.accept(idp::getPostBrokerLoginFlowAlias, idp::setPostBrokerLoginFlowAlias);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCopyFlow() {
|
public void testCopyFlow() {
|
||||||
|
|
|
@ -27,12 +27,14 @@ import org.keycloak.admin.client.resource.ClientResource;
|
||||||
import org.keycloak.admin.client.resource.ClientsResource;
|
import org.keycloak.admin.client.resource.ClientsResource;
|
||||||
import org.keycloak.authentication.authenticators.browser.UsernamePasswordFormFactory;
|
import org.keycloak.authentication.authenticators.browser.UsernamePasswordFormFactory;
|
||||||
import org.keycloak.common.Profile;
|
import org.keycloak.common.Profile;
|
||||||
|
import org.keycloak.connections.jpa.JpaConnectionProvider;
|
||||||
import org.keycloak.events.Details;
|
import org.keycloak.events.Details;
|
||||||
import org.keycloak.models.AuthenticationExecutionModel;
|
import org.keycloak.models.AuthenticationExecutionModel;
|
||||||
import org.keycloak.models.AuthenticationFlowBindings;
|
import org.keycloak.models.AuthenticationFlowBindings;
|
||||||
import org.keycloak.models.AuthenticationFlowModel;
|
import org.keycloak.models.AuthenticationFlowModel;
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
|
import org.keycloak.models.jpa.entities.AuthenticationFlowEntity;
|
||||||
import org.keycloak.models.utils.TimeBasedOTP;
|
import org.keycloak.models.utils.TimeBasedOTP;
|
||||||
import org.keycloak.representations.idm.AuthenticationFlowRepresentation;
|
import org.keycloak.representations.idm.AuthenticationFlowRepresentation;
|
||||||
import org.keycloak.representations.idm.ClientRepresentation;
|
import org.keycloak.representations.idm.ClientRepresentation;
|
||||||
|
@ -50,6 +52,8 @@ import org.keycloak.testsuite.util.FlowUtil;
|
||||||
import org.keycloak.util.BasicAuthHelper;
|
import org.keycloak.util.BasicAuthHelper;
|
||||||
import org.openqa.selenium.By;
|
import org.openqa.selenium.By;
|
||||||
|
|
||||||
|
import jakarta.persistence.EntityManager;
|
||||||
|
import jakarta.persistence.LockModeType;
|
||||||
import jakarta.ws.rs.client.Client;
|
import jakarta.ws.rs.client.Client;
|
||||||
import jakarta.ws.rs.client.Entity;
|
import jakarta.ws.rs.client.Entity;
|
||||||
import jakarta.ws.rs.client.WebTarget;
|
import jakarta.ws.rs.client.WebTarget;
|
||||||
|
@ -61,7 +65,6 @@ import java.util.Map;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.keycloak.testsuite.forms.BrowserFlowTest.revertFlows;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test that clients can override auth flows
|
* Test that clients can override auth flows
|
||||||
|
@ -395,15 +398,20 @@ public class FlowOverrideTest extends AbstractFlowTest {
|
||||||
|
|
||||||
String newFlowAlias = "Copy of Browser Flow";
|
String newFlowAlias = "Copy of Browser Flow";
|
||||||
testingClient.server("test").run(session -> FlowUtil.inCurrentRealm(session).copyBrowserFlow(newFlowAlias));
|
testingClient.server("test").run(session -> FlowUtil.inCurrentRealm(session).copyBrowserFlow(newFlowAlias));
|
||||||
|
AuthenticationFlowRepresentation newFlow = findFlowByAlias(newFlowAlias);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 1. set a flow override for client
|
// 1. set a flow override for client
|
||||||
AuthenticationFlowRepresentation newFlow = findFlowByAlias(newFlowAlias);
|
|
||||||
clientRep.setAuthenticationFlowBindingOverrides(Map.of(binding, newFlow.getId()));
|
clientRep.setAuthenticationFlowBindingOverrides(Map.of(binding, newFlow.getId()));
|
||||||
clientResource.update(clientRep);
|
clientResource.update(clientRep);
|
||||||
|
|
||||||
// 2. remove the flow
|
// 2. remove the flow through database, since we could not delete the flow which is used by client
|
||||||
revertFlows(testRealm(), newFlowAlias);
|
testingClient.server("test").run(session -> {
|
||||||
|
EntityManager em = session.getProvider(JpaConnectionProvider.class).getEntityManager();
|
||||||
|
AuthenticationFlowEntity entity = em.find(AuthenticationFlowEntity.class, newFlow.getId(), LockModeType.PESSIMISTIC_WRITE);
|
||||||
|
em.remove(entity);
|
||||||
|
em.flush();
|
||||||
|
});
|
||||||
|
|
||||||
// 3. login with client
|
// 3. login with client
|
||||||
testNoOverride.accept(clientRep.getClientId());
|
testNoOverride.accept(clientRep.getClientId());
|
||||||
|
|
Loading…
Reference in a new issue