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:
parent
9cced05049
commit
a3b3ee4b87
21 changed files with 127 additions and 28 deletions
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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) => {
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -54,4 +54,6 @@ public interface RealmAttributes {
|
||||||
|
|
||||||
String ADMIN_EVENTS_EXPIRATION = "adminEventsExpiration";
|
String ADMIN_EVENTS_EXPIRATION = "adminEventsExpiration";
|
||||||
|
|
||||||
|
String FIRST_BROKER_LOGIN_FLOW_ID = "firstBrokerLoginFlowId";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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()) {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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()) ||
|
||||||
|
|
|
@ -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()));
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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}.
|
||||||
|
|
|
@ -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";
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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))
|
||||||
|
|
Loading…
Reference in a new issue