Avoid adding organization flows if they are already exist
Closes #31182 Signed-off-by: Pedro Igor <pigor.craveiro@gmail.com>
This commit is contained in:
parent
25da859d18
commit
de1de06354
3 changed files with 113 additions and 132 deletions
|
@ -17,20 +17,12 @@
|
||||||
|
|
||||||
package org.keycloak.organization.jpa;
|
package org.keycloak.organization.jpa;
|
||||||
|
|
||||||
import org.keycloak.Config;
|
|
||||||
import org.keycloak.Config.Scope;
|
import org.keycloak.Config.Scope;
|
||||||
import org.keycloak.models.GroupModel;
|
import org.keycloak.models.GroupModel;
|
||||||
import org.keycloak.models.GroupModel.GroupEvent;
|
import org.keycloak.models.GroupModel.GroupEvent;
|
||||||
import org.keycloak.models.ModelValidationException;
|
import org.keycloak.models.ModelValidationException;
|
||||||
import org.keycloak.organization.authentication.authenticators.broker.IdpOrganizationAuthenticatorFactory;
|
|
||||||
import org.keycloak.organization.authentication.authenticators.browser.OrganizationAuthenticatorFactory;
|
|
||||||
import org.keycloak.models.AuthenticationExecutionModel;
|
|
||||||
import org.keycloak.models.AuthenticationFlowModel;
|
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.KeycloakSessionFactory;
|
import org.keycloak.models.KeycloakSessionFactory;
|
||||||
import org.keycloak.models.RealmModel;
|
|
||||||
import org.keycloak.models.RealmModel.RealmPostCreateEvent;
|
|
||||||
import org.keycloak.models.RealmModel.RealmRemovedEvent;
|
|
||||||
import org.keycloak.organization.OrganizationProvider;
|
import org.keycloak.organization.OrganizationProvider;
|
||||||
import org.keycloak.organization.OrganizationProviderFactory;
|
import org.keycloak.organization.OrganizationProviderFactory;
|
||||||
import org.keycloak.organization.utils.Organizations;
|
import org.keycloak.organization.utils.Organizations;
|
||||||
|
@ -65,133 +57,13 @@ public class JpaOrganizationProviderFactory implements OrganizationProviderFacto
|
||||||
return ID;
|
return ID;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleEvents(ProviderEvent event) {
|
private void handleEvents(ProviderEvent e) {
|
||||||
if (event instanceof RealmPostCreateEvent) {
|
if (e instanceof GroupEvent event) {
|
||||||
RealmModel realm = ((RealmPostCreateEvent) event).getCreatedRealm();
|
KeycloakSession session = event.getKeycloakSession();
|
||||||
configureAuthenticationFlows(realm);
|
GroupModel group = event.getGroup();
|
||||||
}
|
|
||||||
if (event instanceof GroupEvent) {
|
|
||||||
GroupEvent groupEvent = (GroupEvent) event;
|
|
||||||
KeycloakSession session = groupEvent.getKeycloakSession();
|
|
||||||
GroupModel group = groupEvent.getGroup();
|
|
||||||
if (!Organizations.canManageOrganizationGroup(session, group)) {
|
if (!Organizations.canManageOrganizationGroup(session, group)) {
|
||||||
throw new ModelValidationException("Can not update organization group");
|
throw new ModelValidationException("Can not update organization group");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void configureAuthenticationFlows(RealmModel realm) {
|
|
||||||
addOrganizationFirstBrokerFlowStep(realm);
|
|
||||||
addOrganizationBrowserFlowStep(realm);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addOrganizationFirstBrokerFlowStep(RealmModel realm) {
|
|
||||||
|
|
||||||
AuthenticationFlowModel firstBrokerLoginFlow = realm.getFirstBrokerLoginFlow();
|
|
||||||
if (firstBrokerLoginFlow == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (realm.getAuthenticationExecutionsStream(firstBrokerLoginFlow.getId())
|
|
||||||
.map(AuthenticationExecutionModel::getAuthenticator)
|
|
||||||
.anyMatch(IdpOrganizationAuthenticatorFactory.ID::equals)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Config.getAdminRealm().equals(realm.getName())) {
|
|
||||||
// do not add the org flows to the master realm for now.
|
|
||||||
AuthenticationFlowModel conditionalOrg = new AuthenticationFlowModel();
|
|
||||||
conditionalOrg.setTopLevel(false);
|
|
||||||
conditionalOrg.setBuiltIn(true);
|
|
||||||
conditionalOrg.setAlias("First Broker Login - Conditional Organization");
|
|
||||||
conditionalOrg.setDescription("Flow to determine if the authenticator that adds organization members is to be used");
|
|
||||||
conditionalOrg.setProviderId("basic-flow");
|
|
||||||
conditionalOrg = realm.addAuthenticationFlow(conditionalOrg);
|
|
||||||
AuthenticationExecutionModel execution = new AuthenticationExecutionModel();
|
|
||||||
execution.setParentFlow(firstBrokerLoginFlow.getId());
|
|
||||||
execution.setRequirement(AuthenticationExecutionModel.Requirement.CONDITIONAL);
|
|
||||||
execution.setFlowId(conditionalOrg.getId());
|
|
||||||
execution.setPriority(50);
|
|
||||||
execution.setAuthenticatorFlow(true);
|
|
||||||
realm.addAuthenticatorExecution(execution);
|
|
||||||
|
|
||||||
execution = new AuthenticationExecutionModel();
|
|
||||||
execution.setParentFlow(conditionalOrg.getId());
|
|
||||||
execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED);
|
|
||||||
execution.setAuthenticator("conditional-user-configured");
|
|
||||||
execution.setPriority(10);
|
|
||||||
execution.setAuthenticatorFlow(false);
|
|
||||||
realm.addAuthenticatorExecution(execution);
|
|
||||||
|
|
||||||
execution = new AuthenticationExecutionModel();
|
|
||||||
execution.setParentFlow(conditionalOrg.getId());
|
|
||||||
execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED);
|
|
||||||
execution.setAuthenticator(IdpOrganizationAuthenticatorFactory.ID);
|
|
||||||
execution.setPriority(20);
|
|
||||||
execution.setAuthenticatorFlow(false);
|
|
||||||
realm.addAuthenticatorExecution(execution);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addOrganizationBrowserFlowStep(RealmModel realm) {
|
|
||||||
|
|
||||||
AuthenticationFlowModel browserFlow = realm.getBrowserFlow();
|
|
||||||
if (browserFlow == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (realm.getAuthenticationExecutionsStream(browserFlow.getId())
|
|
||||||
.map(AuthenticationExecutionModel::getAuthenticator)
|
|
||||||
.anyMatch(OrganizationAuthenticatorFactory.ID::equals)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Config.getAdminRealm().equals(realm.getName())) {
|
|
||||||
// do not add the org flows to the master realm for now.
|
|
||||||
AuthenticationFlowModel organizations = new AuthenticationFlowModel();
|
|
||||||
organizations.setTopLevel(false);
|
|
||||||
organizations.setBuiltIn(true);
|
|
||||||
organizations.setAlias("Organization");
|
|
||||||
organizations.setProviderId("basic-flow");
|
|
||||||
organizations = realm.addAuthenticationFlow(organizations);
|
|
||||||
AuthenticationExecutionModel execution = new AuthenticationExecutionModel();
|
|
||||||
execution.setParentFlow(browserFlow.getId());
|
|
||||||
execution.setRequirement(AuthenticationExecutionModel.Requirement.ALTERNATIVE);
|
|
||||||
execution.setFlowId(organizations.getId());
|
|
||||||
execution.setPriority(26);
|
|
||||||
execution.setAuthenticatorFlow(true);
|
|
||||||
realm.addAuthenticatorExecution(execution);
|
|
||||||
|
|
||||||
AuthenticationFlowModel conditionalOrg = new AuthenticationFlowModel();
|
|
||||||
conditionalOrg.setTopLevel(false);
|
|
||||||
conditionalOrg.setBuiltIn(true);
|
|
||||||
conditionalOrg.setAlias("Browser - Conditional Organization");
|
|
||||||
conditionalOrg.setDescription("Flow to determine if the organization identity-first login is to be used");
|
|
||||||
conditionalOrg.setProviderId("basic-flow");
|
|
||||||
conditionalOrg = realm.addAuthenticationFlow(conditionalOrg);
|
|
||||||
execution = new AuthenticationExecutionModel();
|
|
||||||
execution.setParentFlow(organizations.getId());
|
|
||||||
execution.setRequirement(AuthenticationExecutionModel.Requirement.CONDITIONAL);
|
|
||||||
execution.setFlowId(conditionalOrg.getId());
|
|
||||||
execution.setPriority(10);
|
|
||||||
execution.setAuthenticatorFlow(true);
|
|
||||||
realm.addAuthenticatorExecution(execution);
|
|
||||||
|
|
||||||
execution = new AuthenticationExecutionModel();
|
|
||||||
execution.setParentFlow(conditionalOrg.getId());
|
|
||||||
execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED);
|
|
||||||
execution.setAuthenticator("conditional-user-configured");
|
|
||||||
execution.setPriority(10);
|
|
||||||
execution.setAuthenticatorFlow(false);
|
|
||||||
realm.addAuthenticatorExecution(execution);
|
|
||||||
|
|
||||||
execution = new AuthenticationExecutionModel();
|
|
||||||
execution.setParentFlow(conditionalOrg.getId());
|
|
||||||
execution.setRequirement(AuthenticationExecutionModel.Requirement.ALTERNATIVE);
|
|
||||||
execution.setAuthenticator(OrganizationAuthenticatorFactory.ID);
|
|
||||||
execution.setPriority(20);
|
|
||||||
execution.setAuthenticatorFlow(false);
|
|
||||||
realm.addAuthenticatorExecution(execution);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,9 @@
|
||||||
|
|
||||||
package org.keycloak.models.utils;
|
package org.keycloak.models.utils;
|
||||||
|
|
||||||
|
import org.keycloak.Config;
|
||||||
|
import org.keycloak.common.Profile;
|
||||||
|
import org.keycloak.common.Profile.Feature;
|
||||||
import org.keycloak.models.AuthenticationExecutionModel;
|
import org.keycloak.models.AuthenticationExecutionModel;
|
||||||
import org.keycloak.models.AuthenticationFlowModel;
|
import org.keycloak.models.AuthenticationFlowModel;
|
||||||
import org.keycloak.models.AuthenticatorConfigModel;
|
import org.keycloak.models.AuthenticatorConfigModel;
|
||||||
|
@ -379,6 +382,8 @@ public class DefaultAuthenticationFlows {
|
||||||
execution.setPriority(20);
|
execution.setPriority(20);
|
||||||
execution.setAuthenticatorFlow(false);
|
execution.setAuthenticatorFlow(false);
|
||||||
realm.addAuthenticatorExecution(execution);
|
realm.addAuthenticatorExecution(execution);
|
||||||
|
|
||||||
|
addOrganizationBrowserFlowStep(realm, browser);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void addIdentityProviderAuthenticator(RealmModel realm, String defaultProvider) {
|
public static void addIdentityProviderAuthenticator(RealmModel realm, String defaultProvider) {
|
||||||
|
@ -638,6 +643,8 @@ public class DefaultAuthenticationFlows {
|
||||||
execution.setPriority(20);
|
execution.setPriority(20);
|
||||||
execution.setAuthenticatorFlow(false);
|
execution.setAuthenticatorFlow(false);
|
||||||
realm.addAuthenticatorExecution(execution);
|
realm.addAuthenticatorExecution(execution);
|
||||||
|
|
||||||
|
addOrganizationFirstBrokerFlowStep(realm, firstBrokerLogin);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void samlEcpProfile(RealmModel realm) {
|
public static void samlEcpProfile(RealmModel realm) {
|
||||||
|
@ -682,4 +689,96 @@ public class DefaultAuthenticationFlows {
|
||||||
|
|
||||||
realm.addAuthenticatorExecution(execution);
|
realm.addAuthenticatorExecution(execution);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void addOrganizationFirstBrokerFlowStep(RealmModel realm, AuthenticationFlowModel flow) {
|
||||||
|
if (!Profile.isFeatureEnabled(Feature.ORGANIZATION)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!Config.getAdminRealm().equals(realm.getName())) {
|
||||||
|
// do not add the org flows to the master realm for now.
|
||||||
|
AuthenticationFlowModel conditionalOrg = new AuthenticationFlowModel();
|
||||||
|
conditionalOrg.setTopLevel(false);
|
||||||
|
conditionalOrg.setBuiltIn(true);
|
||||||
|
conditionalOrg.setAlias("First Broker Login - Conditional Organization");
|
||||||
|
conditionalOrg.setDescription("Flow to determine if the authenticator that adds organization members is to be used");
|
||||||
|
conditionalOrg.setProviderId("basic-flow");
|
||||||
|
conditionalOrg = realm.addAuthenticationFlow(conditionalOrg);
|
||||||
|
AuthenticationExecutionModel execution = new AuthenticationExecutionModel();
|
||||||
|
execution.setParentFlow(flow.getId());
|
||||||
|
execution.setRequirement(AuthenticationExecutionModel.Requirement.CONDITIONAL);
|
||||||
|
execution.setFlowId(conditionalOrg.getId());
|
||||||
|
execution.setPriority(50);
|
||||||
|
execution.setAuthenticatorFlow(true);
|
||||||
|
realm.addAuthenticatorExecution(execution);
|
||||||
|
|
||||||
|
execution = new AuthenticationExecutionModel();
|
||||||
|
execution.setParentFlow(conditionalOrg.getId());
|
||||||
|
execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED);
|
||||||
|
execution.setAuthenticator("conditional-user-configured");
|
||||||
|
execution.setPriority(10);
|
||||||
|
execution.setAuthenticatorFlow(false);
|
||||||
|
realm.addAuthenticatorExecution(execution);
|
||||||
|
|
||||||
|
execution = new AuthenticationExecutionModel();
|
||||||
|
execution.setParentFlow(conditionalOrg.getId());
|
||||||
|
execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED);
|
||||||
|
execution.setAuthenticator("idp-add-organization-member");
|
||||||
|
execution.setPriority(20);
|
||||||
|
execution.setAuthenticatorFlow(false);
|
||||||
|
realm.addAuthenticatorExecution(execution);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void addOrganizationBrowserFlowStep(RealmModel realm, AuthenticationFlowModel flow) {
|
||||||
|
if (!Profile.isFeatureEnabled(Feature.ORGANIZATION)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!Config.getAdminRealm().equals(realm.getName())) {
|
||||||
|
// do not add the org flows to the master realm for now.
|
||||||
|
AuthenticationFlowModel organizations = new AuthenticationFlowModel();
|
||||||
|
organizations.setTopLevel(false);
|
||||||
|
organizations.setBuiltIn(true);
|
||||||
|
organizations.setAlias("Organization");
|
||||||
|
organizations.setProviderId("basic-flow");
|
||||||
|
organizations = realm.addAuthenticationFlow(organizations);
|
||||||
|
AuthenticationExecutionModel execution = new AuthenticationExecutionModel();
|
||||||
|
execution.setParentFlow(flow.getId());
|
||||||
|
execution.setRequirement(AuthenticationExecutionModel.Requirement.ALTERNATIVE);
|
||||||
|
execution.setFlowId(organizations.getId());
|
||||||
|
execution.setPriority(26);
|
||||||
|
execution.setAuthenticatorFlow(true);
|
||||||
|
realm.addAuthenticatorExecution(execution);
|
||||||
|
|
||||||
|
AuthenticationFlowModel conditionalOrg = new AuthenticationFlowModel();
|
||||||
|
conditionalOrg.setTopLevel(false);
|
||||||
|
conditionalOrg.setBuiltIn(true);
|
||||||
|
conditionalOrg.setAlias("Browser - Conditional Organization");
|
||||||
|
conditionalOrg.setDescription("Flow to determine if the organization identity-first login is to be used");
|
||||||
|
conditionalOrg.setProviderId("basic-flow");
|
||||||
|
conditionalOrg = realm.addAuthenticationFlow(conditionalOrg);
|
||||||
|
execution = new AuthenticationExecutionModel();
|
||||||
|
execution.setParentFlow(organizations.getId());
|
||||||
|
execution.setRequirement(AuthenticationExecutionModel.Requirement.CONDITIONAL);
|
||||||
|
execution.setFlowId(conditionalOrg.getId());
|
||||||
|
execution.setPriority(10);
|
||||||
|
execution.setAuthenticatorFlow(true);
|
||||||
|
realm.addAuthenticatorExecution(execution);
|
||||||
|
|
||||||
|
execution = new AuthenticationExecutionModel();
|
||||||
|
execution.setParentFlow(conditionalOrg.getId());
|
||||||
|
execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED);
|
||||||
|
execution.setAuthenticator("conditional-user-configured");
|
||||||
|
execution.setPriority(10);
|
||||||
|
execution.setAuthenticatorFlow(false);
|
||||||
|
realm.addAuthenticatorExecution(execution);
|
||||||
|
|
||||||
|
execution = new AuthenticationExecutionModel();
|
||||||
|
execution.setParentFlow(conditionalOrg.getId());
|
||||||
|
execution.setRequirement(AuthenticationExecutionModel.Requirement.ALTERNATIVE);
|
||||||
|
execution.setAuthenticator("organization");
|
||||||
|
execution.setPriority(20);
|
||||||
|
execution.setAuthenticatorFlow(false);
|
||||||
|
realm.addAuthenticatorExecution(execution);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,10 +28,12 @@ import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
import jakarta.ws.rs.core.Response;
|
import jakarta.ws.rs.core.Response;
|
||||||
import org.hamcrest.Matchers;
|
import org.hamcrest.Matchers;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.keycloak.admin.client.resource.AuthenticationManagementResource;
|
||||||
import org.keycloak.admin.client.resource.OrganizationResource;
|
import org.keycloak.admin.client.resource.OrganizationResource;
|
||||||
import org.keycloak.admin.client.resource.RealmResource;
|
import org.keycloak.admin.client.resource.RealmResource;
|
||||||
import org.keycloak.admin.client.resource.UsersResource;
|
import org.keycloak.admin.client.resource.UsersResource;
|
||||||
|
@ -40,6 +42,8 @@ import org.keycloak.exportimport.ExportImportConfig;
|
||||||
import org.keycloak.exportimport.singlefile.SingleFileExportProviderFactory;
|
import org.keycloak.exportimport.singlefile.SingleFileExportProviderFactory;
|
||||||
import org.keycloak.exportimport.singlefile.SingleFileImportProviderFactory;
|
import org.keycloak.exportimport.singlefile.SingleFileImportProviderFactory;
|
||||||
import org.keycloak.models.OrganizationModel;
|
import org.keycloak.models.OrganizationModel;
|
||||||
|
import org.keycloak.models.utils.DefaultAuthenticationFlows;
|
||||||
|
import org.keycloak.representations.idm.AuthenticationExecutionInfoRepresentation;
|
||||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||||
import org.keycloak.representations.idm.OrganizationRepresentation;
|
import org.keycloak.representations.idm.OrganizationRepresentation;
|
||||||
import org.keycloak.representations.idm.PartialImportRepresentation;
|
import org.keycloak.representations.idm.PartialImportRepresentation;
|
||||||
|
@ -125,6 +129,12 @@ public class OrganizationExportTest extends AbstractOrganizationTest {
|
||||||
// login to the organization identity provider and run the configured first broker login flow
|
// login to the organization identity provider and run the configured first broker login flow
|
||||||
loginPage.login(email, bc.getUserPassword());
|
loginPage.login(email, bc.getUserPassword());
|
||||||
assertThat(appPage.getRequestType(),is(AppPage.RequestType.AUTH_RESPONSE));
|
assertThat(appPage.getRequestType(),is(AppPage.RequestType.AUTH_RESPONSE));
|
||||||
|
|
||||||
|
AuthenticationManagementResource flows = testRealm().flows();
|
||||||
|
List<AuthenticationExecutionInfoRepresentation> executions = flows.getExecutions(DefaultAuthenticationFlows.BROWSER_FLOW);
|
||||||
|
assertThat(executions.stream().filter(e -> "Organization".equals(e.getDisplayName())).count(), is(1L));
|
||||||
|
executions = flows.getExecutions(DefaultAuthenticationFlows.FIRST_BROKER_LOGIN_FLOW);
|
||||||
|
assertThat(executions.stream().filter(e -> "First Broker Login - Conditional Organization".equals(e.getDisplayName())).count(), is(1L));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
Loading…
Reference in a new issue