Ability to declare a default "First broker login flow" per Realm

Closes #25823

Signed-off-by: Réda Housni Alaoui <reda-alaoui@hey.com>
Co-authored-by: Jon Koops <jonkoops@gmail.com>
This commit is contained in:
Réda Housni Alaoui 2024-02-28 16:17:51 +01:00 committed by GitHub
parent 9cced05049
commit a3b3ee4b87
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 127 additions and 28 deletions

View file

@ -206,6 +206,7 @@ public class RealmRepresentation {
protected String resetCredentialsFlow; protected String resetCredentialsFlow;
protected String clientAuthenticationFlow; protected String clientAuthenticationFlow;
protected String dockerAuthenticationFlow; protected String dockerAuthenticationFlow;
protected String firstBrokerLoginFlow;
protected Map<String, String> attributes; protected Map<String, String> attributes;
@ -1328,6 +1329,15 @@ public class RealmRepresentation {
return this; return this;
} }
public String getFirstBrokerLoginFlow() {
return firstBrokerLoginFlow;
}
public RealmRepresentation setFirstBrokerLoginFlow(String firstBrokerLoginFlow) {
this.firstBrokerLoginFlow = firstBrokerLoginFlow;
return this;
}
public String getKeycloakVersion() { public String getKeycloakVersion() {
return keycloakVersion; return keycloakVersion;
} }

View file

@ -4,6 +4,7 @@ import Masthead from "../../Masthead";
const masthead = new Masthead(); const masthead = new Masthead();
export enum LoginFlowOption { export enum LoginFlowOption {
empty = "",
none = "None", none = "None",
browser = "browser", browser = "browser",
directGrant = "direct grant", directGrant = "direct grant",
@ -68,7 +69,7 @@ export default class ProviderBaseGeneralSettingsPage extends PageObject {
#doNotStoreUsers = "#doNotStoreUsers"; #doNotStoreUsers = "#doNotStoreUsers";
#accountLinkingOnlySwitch = "#accountLinkingOnly"; #accountLinkingOnlySwitch = "#accountLinkingOnly";
#hideOnLoginPageSwitch = "#hideOnLoginPage"; #hideOnLoginPageSwitch = "#hideOnLoginPage";
#firstLoginFlowSelect = "#firstBrokerLoginFlowAlias"; #firstLoginFlowSelect = "#firstBrokerLoginFlowAliasOverride";
#postLoginFlowSelect = "#postBrokerLoginFlowAlias"; #postLoginFlowSelect = "#postBrokerLoginFlowAlias";
#syncModeSelect = "#syncMode"; #syncModeSelect = "#syncMode";
#essentialClaimSwitch = "#filteredByClaim"; #essentialClaimSwitch = "#filteredByClaim";
@ -496,9 +497,7 @@ export default class ProviderBaseGeneralSettingsPage extends PageObject {
this.assertAccountLinkingOnlySwitchTurnedOn(false); this.assertAccountLinkingOnlySwitchTurnedOn(false);
this.assertHideOnLoginPageSwitchTurnedOn(false); this.assertHideOnLoginPageSwitchTurnedOn(false);
this.assertFirstLoginFlowSelectOptionEqual( this.assertFirstLoginFlowSelectOptionEqual(LoginFlowOption.empty);
LoginFlowOption.firstBrokerLogin,
);
this.assertPostLoginFlowSelectOptionEqual(LoginFlowOption.none); this.assertPostLoginFlowSelectOptionEqual(LoginFlowOption.none);
this.assertSyncModeSelectOptionEqual(SyncModeOption.import); this.assertSyncModeSelectOptionEqual(SyncModeOption.import);
this.assertClientAssertSigAlgSelectOptionEqual( this.assertClientAssertSigAlgSelectOptionEqual(

View file

@ -357,6 +357,7 @@ algorithmNotSpecified=Algorithm not specified
jwtX509HeadersEnabled=Add X.509 Headers to the JWT jwtX509HeadersEnabled=Add X.509 Headers to the JWT
rememberMe=Remember me rememberMe=Remember me
flow.registration=Registration flow flow.registration=Registration flow
flow.firstBrokerLogin=First broker login flow
showLess=Show less showLess=Show less
registeredClusterNodes=Registered cluster nodes registeredClusterNodes=Registered cluster nodes
connectionAndAuthenticationSettings=Connection and authentication settings connectionAndAuthenticationSettings=Connection and authentication settings
@ -950,6 +951,7 @@ homeURL=Home URL
eventTypes.REVOKE_GRANT_ERROR.name=Revoke grant error eventTypes.REVOKE_GRANT_ERROR.name=Revoke grant error
contentSecurityPolicyReportOnly=Content-Security-Policy-Report-Only contentSecurityPolicyReportOnly=Content-Security-Policy-Report-Only
firstBrokerLoginFlowAlias=First login flow firstBrokerLoginFlowAlias=First login flow
firstBrokerLoginFlowAliasOverride=First login flow override
missingAttributes=No {{label}} have been defined yet. Click the below button to add {{label}}, key and value are required for a key pair. missingAttributes=No {{label}} have been defined yet. Click the below button to add {{label}}, key and value are required for a key pair.
testConnectionError=Error\! {{error}} testConnectionError=Error\! {{error}}
authenticatedAccessPoliciesHelp=Those Policies are used when Client Registration Service is invoked by authenticated request. This means that the request contains Initial Access Token or Bearer Token. authenticatedAccessPoliciesHelp=Those Policies are used when Client Registration Service is invoked by authenticated request. This means that the request contains Initial Access Token or Bearer Token.

View file

@ -57,6 +57,7 @@ export const REALM_FLOWS = new Map<string, string>([
["resetCredentialsFlow", "reset credentials"], ["resetCredentialsFlow", "reset credentials"],
["clientAuthenticationFlow", "clients"], ["clientAuthenticationFlow", "clients"],
["dockerAuthenticationFlow", "docker auth"], ["dockerAuthenticationFlow", "docker auth"],
["firstBrokerLoginFlow", "firstBrokerLogin"],
]); ]);
const AliasRenderer = ({ id, alias, usedBy, builtIn }: AuthenticationType) => { const AliasRenderer = ({ id, alias, usedBy, builtIn }: AuthenticationType) => {

View file

@ -26,7 +26,8 @@ const LoginFlow = ({
field, field,
label, label,
defaultValue, defaultValue,
}: FieldProps & { defaultValue: string }) => { labelForEmpty = "none",
}: FieldProps & { defaultValue: string; labelForEmpty?: string }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { control } = useFormContext(); const { control } = useFormContext();
@ -59,7 +60,7 @@ const LoginFlow = ({
field.onChange(value as string); field.onChange(value as string);
setOpen(false); setOpen(false);
}} }}
selections={field.value || t("none")} selections={field.value || t(labelForEmpty)}
variant={SelectVariant.single} variant={SelectVariant.single}
aria-label={t(label)} aria-label={t(label)}
isOpen={open} isOpen={open}
@ -68,7 +69,7 @@ const LoginFlow = ({
...(defaultValue === "" ...(defaultValue === ""
? [ ? [
<SelectOption key="empty" value=""> <SelectOption key="empty" value="">
{t("none")} {t(labelForEmpty)}
</SelectOption>, </SelectOption>,
] ]
: []), : []),
@ -232,8 +233,9 @@ export const AdvancedSettings = ({ isOIDC, isSAML }: AdvancedSettingsProps) => {
)} )}
<LoginFlow <LoginFlow
field="firstBrokerLoginFlowAlias" field="firstBrokerLoginFlowAlias"
label="firstBrokerLoginFlowAlias" label="firstBrokerLoginFlowAliasOverride"
defaultValue="fist broker login" defaultValue=""
labelForEmpty=""
/> />
<LoginFlow <LoginFlow
field="postBrokerLoginFlowAlias" field="postBrokerLoginFlowAlias"

View file

@ -1266,6 +1266,18 @@ public class RealmAdapter implements CachedRealmModel {
updated.setDockerAuthenticationFlow(flow); updated.setDockerAuthenticationFlow(flow);
} }
@Override
public AuthenticationFlowModel getFirstBrokerLoginFlow() {
if (isUpdated()) return updated.getFirstBrokerLoginFlow();
return cached.getFirstBrokerLoginFlow();
}
@Override
public void setFirstBrokerLoginFlow(AuthenticationFlowModel flow) {
getDelegateForUpdate();
updated.setFirstBrokerLoginFlow(flow);
}
@Override @Override
public Stream<AuthenticationFlowModel> getAuthenticationFlowsStream() { public Stream<AuthenticationFlowModel> getAuthenticationFlowsStream() {
if (isUpdated()) return updated.getAuthenticationFlowsStream(); if (isUpdated()) return updated.getAuthenticationFlowsStream();

View file

@ -144,6 +144,7 @@ public class CachedRealm extends AbstractExtendableRevisioned {
protected AuthenticationFlowModel resetCredentialsFlow; protected AuthenticationFlowModel resetCredentialsFlow;
protected AuthenticationFlowModel clientAuthenticationFlow; protected AuthenticationFlowModel clientAuthenticationFlow;
protected AuthenticationFlowModel dockerAuthenticationFlow; protected AuthenticationFlowModel dockerAuthenticationFlow;
protected AuthenticationFlowModel firstBrokerLoginFlow;
protected boolean eventsEnabled; protected boolean eventsEnabled;
protected long eventsExpiration; protected long eventsExpiration;
@ -302,6 +303,7 @@ public class CachedRealm extends AbstractExtendableRevisioned {
resetCredentialsFlow = model.getResetCredentialsFlow(); resetCredentialsFlow = model.getResetCredentialsFlow();
clientAuthenticationFlow = model.getClientAuthenticationFlow(); clientAuthenticationFlow = model.getClientAuthenticationFlow();
dockerAuthenticationFlow = model.getDockerAuthenticationFlow(); dockerAuthenticationFlow = model.getDockerAuthenticationFlow();
firstBrokerLoginFlow = model.getFirstBrokerLoginFlow();
model.getComponentsStream().forEach(component -> model.getComponentsStream().forEach(component ->
componentsByParentAndType.add(component.getParentId() + component.getProviderType(), component) componentsByParentAndType.add(component.getParentId() + component.getProviderType(), component)
@ -687,6 +689,10 @@ public class CachedRealm extends AbstractExtendableRevisioned {
return dockerAuthenticationFlow; return dockerAuthenticationFlow;
} }
public AuthenticationFlowModel getFirstBrokerLoginFlow() {
return firstBrokerLoginFlow;
}
public List<String> getDefaultGroups() { public List<String> getDefaultGroups() {
return defaultGroups; return defaultGroups;
} }

View file

@ -1568,6 +1568,18 @@ public class RealmAdapter implements StorageProviderRealmModel, JpaModel<RealmEn
realm.setDockerAuthenticationFlow(flow.getId()); realm.setDockerAuthenticationFlow(flow.getId());
} }
@Override
public AuthenticationFlowModel getFirstBrokerLoginFlow() {
String flowId = getAttribute(RealmAttributes.FIRST_BROKER_LOGIN_FLOW_ID);
if (flowId == null) return null;
return getAuthenticationFlowById(flowId);
}
@Override
public void setFirstBrokerLoginFlow(AuthenticationFlowModel flow) {
setAttribute(RealmAttributes.FIRST_BROKER_LOGIN_FLOW_ID, flow.getId());
}
@Override @Override
public Stream<AuthenticationFlowModel> getAuthenticationFlowsStream() { public Stream<AuthenticationFlowModel> getAuthenticationFlowsStream() {
return realm.getAuthenticationFlows().stream().map(this::entityToModel); return realm.getAuthenticationFlows().stream().map(this::entityToModel);

View file

@ -54,4 +54,6 @@ public interface RealmAttributes {
String ADMIN_EVENTS_EXPIRATION = "adminEventsExpiration"; String ADMIN_EVENTS_EXPIRATION = "adminEventsExpiration";
String FIRST_BROKER_LOGIN_FLOW_ID = "firstBrokerLoginFlowId";
} }

View file

@ -21,10 +21,12 @@ package org.keycloak.migration.migrators;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.keycloak.migration.ModelVersion; import org.keycloak.migration.ModelVersion;
import org.keycloak.models.AuthenticationFlowModel;
import org.keycloak.models.KeycloakContext; import org.keycloak.models.KeycloakContext;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.LDAPConstants; import org.keycloak.models.LDAPConstants;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.utils.DefaultAuthenticationFlows;
import org.keycloak.models.utils.DefaultKeyProviders; import org.keycloak.models.utils.DefaultKeyProviders;
import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.userprofile.config.UPConfig; import org.keycloak.representations.userprofile.config.UPConfig;
@ -60,6 +62,7 @@ public class MigrateTo24_0_0 implements Migration {
updateUserProfileSettings(session); updateUserProfileSettings(session);
updateLdapProviderConfig(session); updateLdapProviderConfig(session);
createHS512ComponentModelKey(session); createHS512ComponentModelKey(session);
bindFirstBrokerLoginFlow(session);
} finally { } finally {
context.setRealm(null); context.setRealm(null);
} }
@ -103,4 +106,16 @@ public class MigrateTo24_0_0 implements Migration {
RealmModel realm = session.getContext().getRealm(); RealmModel realm = session.getContext().getRealm();
DefaultKeyProviders.createSecretProvider(realm); DefaultKeyProviders.createSecretProvider(realm);
} }
private void bindFirstBrokerLoginFlow(KeycloakSession session) {
RealmModel realm = session.getContext().getRealm();
String flowAlias = DefaultAuthenticationFlows.FIRST_BROKER_LOGIN_FLOW;
AuthenticationFlowModel flow = realm.getFlowByAlias(flowAlias);
if (flow == null) {
LOG.debugf("No flow found for alias '%s'. Skipping.", flowAlias);
return;
}
realm.setFirstBrokerLoginFlow(flow);
LOG.debugf("Flow '%s' has been bound to realm %s as 'First broker login' flow", realm.getName());
}
} }

View file

@ -871,6 +871,9 @@ public class DefaultExportImportManager implements ExportImportManager {
if (rep.getDockerAuthenticationFlow() != null) { if (rep.getDockerAuthenticationFlow() != null) {
realm.setDockerAuthenticationFlow(realm.getFlowByAlias(rep.getDockerAuthenticationFlow())); realm.setDockerAuthenticationFlow(realm.getFlowByAlias(rep.getDockerAuthenticationFlow()));
} }
if (rep.getFirstBrokerLoginFlow() != null) {
realm.setFirstBrokerLoginFlow(realm.getFlowByAlias(rep.getFirstBrokerLoginFlow()));
}
} }
@Override @Override
@ -1363,10 +1366,15 @@ public class DefaultExportImportManager implements ExportImportManager {
} else { } else {
newRealm.setClientAuthenticationFlow(newRealm.getFlowByAlias(rep.getClientAuthenticationFlow())); newRealm.setClientAuthenticationFlow(newRealm.getFlowByAlias(rep.getClientAuthenticationFlow()));
} }
if (rep.getFirstBrokerLoginFlow() == null) {
// Added in 1.7 AuthenticationFlowModel firstBrokerLoginFlow = newRealm.getFlowByAlias(DefaultAuthenticationFlows.FIRST_BROKER_LOGIN_FLOW);
if (newRealm.getFlowByAlias(DefaultAuthenticationFlows.FIRST_BROKER_LOGIN_FLOW) == null) { if (firstBrokerLoginFlow == null) {
DefaultAuthenticationFlows.firstBrokerLoginFlow(newRealm, true); DefaultAuthenticationFlows.firstBrokerLoginFlow(newRealm, true);
} else {
newRealm.setFirstBrokerLoginFlow(firstBrokerLoginFlow);
}
} else {
newRealm.setFirstBrokerLoginFlow(newRealm.getFlowByAlias(rep.getFirstBrokerLoginFlow()));
} }
// Added in 2.2 // Added in 2.2

View file

@ -42,7 +42,7 @@ public class AuthenticationMapper {
} }
final List<String> useAsDefault = Stream.of(realm.getBrowserFlow(), realm.getRegistrationFlow(), realm.getDirectGrantFlow(), final List<String> useAsDefault = Stream.of(realm.getBrowserFlow(), realm.getRegistrationFlow(), realm.getDirectGrantFlow(),
realm.getResetCredentialsFlow(), realm.getClientAuthenticationFlow(), realm.getDockerAuthenticationFlow()) realm.getResetCredentialsFlow(), realm.getClientAuthenticationFlow(), realm.getDockerAuthenticationFlow(), realm.getFirstBrokerLoginFlow())
.filter(f -> flow.getAlias().equals(f.getAlias())).map(AuthenticationFlowModel::getAlias).collect(Collectors.toList()); .filter(f -> flow.getAlias().equals(f.getAlias())).map(AuthenticationFlowModel::getAlias).collect(Collectors.toList());
if (!useAsDefault.isEmpty()) { if (!useAsDefault.isEmpty()) {

View file

@ -469,6 +469,7 @@ public class DefaultAuthenticationFlows {
firstBrokerLogin.setTopLevel(true); firstBrokerLogin.setTopLevel(true);
firstBrokerLogin.setBuiltIn(true); firstBrokerLogin.setBuiltIn(true);
firstBrokerLogin = realm.addAuthenticationFlow(firstBrokerLogin); firstBrokerLogin = realm.addAuthenticationFlow(firstBrokerLogin);
realm.setFirstBrokerLoginFlow(firstBrokerLogin);
AuthenticatorConfigModel reviewProfileConfig = new AuthenticatorConfigModel(); AuthenticatorConfigModel reviewProfileConfig = new AuthenticatorConfigModel();
reviewProfileConfig.setAlias(IDP_REVIEW_PROFILE_CONFIG_ALIAS); reviewProfileConfig.setAlias(IDP_REVIEW_PROFILE_CONFIG_ALIAS);

View file

@ -854,6 +854,7 @@ public final class KeycloakModelUtils {
if ((realmFlow = realm.getDirectGrantFlow()) != null && realmFlow.getId().equals(model.getId())) return true; if ((realmFlow = realm.getDirectGrantFlow()) != null && realmFlow.getId().equals(model.getId())) return true;
if ((realmFlow = realm.getResetCredentialsFlow()) != null && realmFlow.getId().equals(model.getId())) return true; if ((realmFlow = realm.getResetCredentialsFlow()) != null && realmFlow.getId().equals(model.getId())) return true;
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;
return realm.getIdentityProvidersStream().anyMatch(idp -> return realm.getIdentityProvidersStream().anyMatch(idp ->
Objects.equals(idp.getFirstBrokerLoginFlowId(), model.getId()) || Objects.equals(idp.getFirstBrokerLoginFlowId(), model.getId()) ||

View file

@ -115,6 +115,8 @@ public class ModelToRepresentation {
REALM_EXCLUDED_ATTRIBUTES.add(Constants.CLIENT_POLICIES); REALM_EXCLUDED_ATTRIBUTES.add(Constants.CLIENT_POLICIES);
REALM_EXCLUDED_ATTRIBUTES.add(Constants.CLIENT_PROFILES); REALM_EXCLUDED_ATTRIBUTES.add(Constants.CLIENT_PROFILES);
REALM_EXCLUDED_ATTRIBUTES.add("firstBrokerLoginFlowId");
} }
private static final Logger LOG = Logger.getLogger(ModelToRepresentation.class); private static final Logger LOG = Logger.getLogger(ModelToRepresentation.class);
@ -471,6 +473,7 @@ public class ModelToRepresentation {
if (realm.getResetCredentialsFlow() != null) rep.setResetCredentialsFlow(realm.getResetCredentialsFlow().getAlias()); if (realm.getResetCredentialsFlow() != null) rep.setResetCredentialsFlow(realm.getResetCredentialsFlow().getAlias());
if (realm.getClientAuthenticationFlow() != null) rep.setClientAuthenticationFlow(realm.getClientAuthenticationFlow().getAlias()); if (realm.getClientAuthenticationFlow() != null) rep.setClientAuthenticationFlow(realm.getClientAuthenticationFlow().getAlias());
if (realm.getDockerAuthenticationFlow() != null) rep.setDockerAuthenticationFlow(realm.getDockerAuthenticationFlow().getAlias()); if (realm.getDockerAuthenticationFlow() != null) rep.setDockerAuthenticationFlow(realm.getDockerAuthenticationFlow().getAlias());
if (realm.getFirstBrokerLoginFlow() != null) rep.setFirstBrokerLoginFlow(realm.getFirstBrokerLoginFlow().getAlias());
rep.setDefaultRole(toBriefRepresentation(realm.getDefaultRole())); rep.setDefaultRole(toBriefRepresentation(realm.getDefaultRole()));

View file

@ -838,21 +838,21 @@ public class RepresentationToModel {
identityProviderModel.setConfig(removeEmptyString(representation.getConfig())); identityProviderModel.setConfig(removeEmptyString(representation.getConfig()));
String flowAlias = representation.getFirstBrokerLoginFlowAlias(); String flowAlias = representation.getFirstBrokerLoginFlowAlias();
if (flowAlias == null) { if (flowAlias == null || flowAlias.trim().length() == 0) {
flowAlias = DefaultAuthenticationFlows.FIRST_BROKER_LOGIN_FLOW; identityProviderModel.setFirstBrokerLoginFlowId(null);
} else {
AuthenticationFlowModel flowModel = realm.getFlowByAlias(flowAlias);
if (flowModel == null) {
throw new ModelException("No available authentication flow with alias: " + flowAlias);
}
identityProviderModel.setFirstBrokerLoginFlowId(flowModel.getId());
} }
AuthenticationFlowModel flowModel = realm.getFlowByAlias(flowAlias);
if (flowModel == null) {
throw new ModelException("No available authentication flow with alias: " + flowAlias);
}
identityProviderModel.setFirstBrokerLoginFlowId(flowModel.getId());
flowAlias = representation.getPostBrokerLoginFlowAlias(); flowAlias = representation.getPostBrokerLoginFlowAlias();
if (flowAlias == null || flowAlias.trim().length() == 0) { if (flowAlias == null || flowAlias.trim().length() == 0) {
identityProviderModel.setPostBrokerLoginFlowId(null); identityProviderModel.setPostBrokerLoginFlowId(null);
} else { } else {
flowModel = realm.getFlowByAlias(flowAlias); AuthenticationFlowModel flowModel = realm.getFlowByAlias(flowAlias);
if (flowModel == null) { if (flowModel == null) {
throw new ModelException("No available authentication flow with alias: " + flowAlias); throw new ModelException("No available authentication flow with alias: " + flowAlias);
} }

View file

@ -1244,6 +1244,16 @@ public class IdentityBrokerStateTestHelpers {
} }
@Override
public AuthenticationFlowModel getFirstBrokerLoginFlow() {
return null;
}
@Override
public void setFirstBrokerLoginFlow(AuthenticationFlowModel flow) {
}
@Override @Override
public Stream<AuthenticationFlowModel> getAuthenticationFlowsStream() { public Stream<AuthenticationFlowModel> getAuthenticationFlowsStream() {
return null; return null;

View file

@ -375,6 +375,9 @@ public interface RealmModel extends RoleContainerModel {
AuthenticationFlowModel getDockerAuthenticationFlow(); AuthenticationFlowModel getDockerAuthenticationFlow();
void setDockerAuthenticationFlow(AuthenticationFlowModel flow); void setDockerAuthenticationFlow(AuthenticationFlowModel flow);
AuthenticationFlowModel getFirstBrokerLoginFlow();
void setFirstBrokerLoginFlow(AuthenticationFlowModel flow);
/** /**
* Returns authentications flows as a stream. * Returns authentications flows as a stream.
* @return Stream of {@link AuthenticationFlowModel}. Never returns {@code null}. * @return Stream of {@link AuthenticationFlowModel}. Never returns {@code null}.

View file

@ -849,7 +849,15 @@ public class LoginActionsService {
BrokeredIdentityContext brokerContext = serializedCtx.deserialize(session, authSession); BrokeredIdentityContext brokerContext = serializedCtx.deserialize(session, authSession);
final String identityProviderAlias = brokerContext.getIdpConfig().getAlias(); final String identityProviderAlias = brokerContext.getIdpConfig().getAlias();
String flowId = firstBrokerLogin ? brokerContext.getIdpConfig().getFirstBrokerLoginFlowId() : brokerContext.getIdpConfig().getPostBrokerLoginFlowId(); String flowId;
if (firstBrokerLogin) {
flowId = brokerContext.getIdpConfig().getFirstBrokerLoginFlowId();
if (flowId == null) {
flowId = realm.getFirstBrokerLoginFlow().getId();
}
} else {
flowId = brokerContext.getIdpConfig().getPostBrokerLoginFlowId();
}
if (flowId == null) { if (flowId == null) {
ServicesLogger.LOGGER.flowNotConfigForIDP(identityProviderAlias); ServicesLogger.LOGGER.flowNotConfigForIDP(identityProviderAlias);
String message = "Flow not configured for identity provider"; String message = "Flow not configured for identity provider";

View file

@ -204,6 +204,7 @@ public class IdentityProviderTest extends AbstractAdminTest {
assertTrue(representation.isEnabled()); assertTrue(representation.isEnabled());
assertFalse(representation.isStoreToken()); assertFalse(representation.isStoreToken());
assertFalse(representation.isTrustEmail()); assertFalse(representation.isTrustEmail());
assertNull(representation.getFirstBrokerLoginFlowAlias());
assertEquals("some secret value", testingClient.testing("admin-client-test").getIdentityProviderConfig("new-identity-provider").get("clientSecret")); assertEquals("some secret value", testingClient.testing("admin-client-test").getIdentityProviderConfig("new-identity-provider").get("clientSecret"));
@ -1022,7 +1023,7 @@ public class IdentityProviderTest extends AbstractAdminTest {
Assert.assertEquals("alias", "saml", idp.getAlias()); Assert.assertEquals("alias", "saml", idp.getAlias());
Assert.assertEquals("providerId", "saml", idp.getProviderId()); Assert.assertEquals("providerId", "saml", idp.getProviderId());
Assert.assertEquals("enabled",enabled, idp.isEnabled()); Assert.assertEquals("enabled",enabled, idp.isEnabled());
Assert.assertEquals("firstBrokerLoginFlowAlias", "first broker login",idp.getFirstBrokerLoginFlowAlias()); Assert.assertNull("firstBrokerLoginFlowAlias", idp.getFirstBrokerLoginFlowAlias());
assertSamlConfig(idp.getConfig()); assertSamlConfig(idp.getConfig());
} }

View file

@ -16,6 +16,7 @@
*/ */
package org.keycloak.testsuite.saml; package org.keycloak.testsuite.saml;
import java.util.Map;
import org.keycloak.admin.client.resource.ClientsResource; import org.keycloak.admin.client.resource.ClientsResource;
import org.keycloak.admin.client.resource.RealmResource; import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.admin.client.resource.UserResource; import org.keycloak.admin.client.resource.UserResource;
@ -135,15 +136,17 @@ public class BrokerTest extends AbstractSamlTest {
final ClientsResource clients = realm.clients(); final ClientsResource clients = realm.clients();
AuthenticationExecutionInfoRepresentation reviewProfileAuthenticator = null; AuthenticationExecutionInfoRepresentation reviewProfileAuthenticator = null;
String firstBrokerLoginFlowAlias = null; final String firstBrokerLoginFlowAlias = UUID.randomUUID().toString();
realm.flows().copy(realm.toRepresentation().getFirstBrokerLoginFlow(), Map.of("newName", firstBrokerLoginFlowAlias)).close();
final IdentityProviderRepresentation rep = addIdentityProvider("https://saml.idp/saml"); final IdentityProviderRepresentation rep = addIdentityProvider("https://saml.idp/saml");
rep.setFirstBrokerLoginFlowAlias(firstBrokerLoginFlowAlias);
rep.getConfig().put(SAMLIdentityProviderConfig.NAME_ID_POLICY_FORMAT, "undefined"); rep.getConfig().put(SAMLIdentityProviderConfig.NAME_ID_POLICY_FORMAT, "undefined");
rep.getConfig().put(SAMLIdentityProviderConfig.PRINCIPAL_TYPE, SamlPrincipalType.ATTRIBUTE.toString()); rep.getConfig().put(SAMLIdentityProviderConfig.PRINCIPAL_TYPE, SamlPrincipalType.ATTRIBUTE.toString());
rep.getConfig().put(SAMLIdentityProviderConfig.PRINCIPAL_ATTRIBUTE, "mail"); rep.getConfig().put(SAMLIdentityProviderConfig.PRINCIPAL_ATTRIBUTE, "mail");
try (IdentityProviderCreator idp = new IdentityProviderCreator(realm, rep)) { try (IdentityProviderCreator idp = new IdentityProviderCreator(realm, rep)) {
IdentityProviderRepresentation idpRepresentation = idp.identityProvider().toRepresentation();
firstBrokerLoginFlowAlias = idpRepresentation.getFirstBrokerLoginFlowAlias();
List<AuthenticationExecutionInfoRepresentation> executions = realm.flows().getExecutions(firstBrokerLoginFlowAlias); List<AuthenticationExecutionInfoRepresentation> executions = realm.flows().getExecutions(firstBrokerLoginFlowAlias);
reviewProfileAuthenticator = executions.stream() reviewProfileAuthenticator = executions.stream()
.filter(ex -> Objects.equals(ex.getProviderId(), IdpReviewProfileAuthenticatorFactory.PROVIDER_ID)) .filter(ex -> Objects.equals(ex.getProviderId(), IdpReviewProfileAuthenticatorFactory.PROVIDER_ID))