Return next action if the current action is not supported in AIA
Closes #33513 Signed-off-by: rmartinc <rmartinc@redhat.com>
This commit is contained in:
parent
8719e2d0d7
commit
44b1290917
2 changed files with 45 additions and 7 deletions
|
@ -1052,7 +1052,7 @@ public class AuthenticationManager {
|
||||||
|
|
||||||
final var kcAction = authSession.getClientNote(Constants.KC_ACTION);
|
final var kcAction = authSession.getClientNote(Constants.KC_ACTION);
|
||||||
final var nextApplicableAction =
|
final var nextApplicableAction =
|
||||||
getFirstApplicableRequiredAction(realm, authSession, user, kcAction);
|
getFirstApplicableRequiredAction(realm, authSession, user, kcAction, new HashSet<>());
|
||||||
if (nextApplicableAction != null) {
|
if (nextApplicableAction != null) {
|
||||||
return nextApplicableAction.getAlias();
|
return nextApplicableAction.getAlias();
|
||||||
}
|
}
|
||||||
|
@ -1232,7 +1232,7 @@ public class AuthenticationManager {
|
||||||
HttpRequest request, EventBuilder event, RealmModel realm, UserModel user, Set<String> ignoredActions) {
|
HttpRequest request, EventBuilder event, RealmModel realm, UserModel user, Set<String> ignoredActions) {
|
||||||
final var kcAction = authSession.getClientNote(Constants.KC_ACTION);
|
final var kcAction = authSession.getClientNote(Constants.KC_ACTION);
|
||||||
final var firstApplicableRequiredAction =
|
final var firstApplicableRequiredAction =
|
||||||
getFirstApplicableRequiredAction(realm, authSession, user, kcAction);
|
getFirstApplicableRequiredAction(realm, authSession, user, kcAction, ignoredActions);
|
||||||
|
|
||||||
if (firstApplicableRequiredAction != null) {
|
if (firstApplicableRequiredAction != null) {
|
||||||
return executeAction(session, authSession, firstApplicableRequiredAction, request, event, realm, user,
|
return executeAction(session, authSession, firstApplicableRequiredAction, request, event, realm, user,
|
||||||
|
@ -1265,11 +1265,13 @@ public class AuthenticationManager {
|
||||||
if (actionProvider.initiatedActionSupport() == InitiatedActionSupport.NOT_SUPPORTED) {
|
if (actionProvider.initiatedActionSupport() == InitiatedActionSupport.NOT_SUPPORTED) {
|
||||||
logger.debugv("Requested action {0} does not support being invoked with kc_action", factory.getId());
|
logger.debugv("Requested action {0} does not support being invoked with kc_action", factory.getId());
|
||||||
setKcActionStatus(factory.getId(), RequiredActionContext.KcActionStatus.ERROR, authSession);
|
setKcActionStatus(factory.getId(), RequiredActionContext.KcActionStatus.ERROR, authSession);
|
||||||
return null;
|
ignoredActions.add(factory.getId());
|
||||||
|
return nextActionAfterAuthentication(session, authSession, session.getContext().getConnection(), request, session.getContext().getUri(), event, ignoredActions);
|
||||||
} else if (!model.isEnabled()) {
|
} else if (!model.isEnabled()) {
|
||||||
logger.debugv("Requested action {0} is disabled and can't be invoked with kc_action", factory.getId());
|
logger.debugv("Requested action {0} is disabled and can't be invoked with kc_action", factory.getId());
|
||||||
setKcActionStatus(factory.getId(), RequiredActionContext.KcActionStatus.ERROR, authSession);
|
setKcActionStatus(factory.getId(), RequiredActionContext.KcActionStatus.ERROR, authSession);
|
||||||
return null;
|
ignoredActions.add(factory.getId());
|
||||||
|
return nextActionAfterAuthentication(session, authSession, session.getContext().getConnection(), request, session.getContext().getUri(), event, ignoredActions);
|
||||||
} else {
|
} else {
|
||||||
authSession.setClientNote(Constants.KC_ACTION_EXECUTING, factory.getId());
|
authSession.setClientNote(Constants.KC_ACTION_EXECUTING, factory.getId());
|
||||||
}
|
}
|
||||||
|
@ -1311,9 +1313,9 @@ public class AuthenticationManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static RequiredActionProviderModel getFirstApplicableRequiredAction(final RealmModel realm,
|
private static RequiredActionProviderModel getFirstApplicableRequiredAction(final RealmModel realm,
|
||||||
final AuthenticationSessionModel authSession, final UserModel user, final String kcAction) {
|
final AuthenticationSessionModel authSession, final UserModel user, final String kcAction, final Set<String> ignoredActions) {
|
||||||
final var applicableRequiredActionsSorted =
|
final var applicableRequiredActionsSorted =
|
||||||
getApplicableRequiredActionsSorted(realm, authSession, user, kcAction);
|
getApplicableRequiredActionsSorted(realm, authSession, user, kcAction, ignoredActions);
|
||||||
|
|
||||||
final RequiredActionProviderModel firstApplicableRequiredAction;
|
final RequiredActionProviderModel firstApplicableRequiredAction;
|
||||||
if (applicableRequiredActionsSorted.isEmpty()) {
|
if (applicableRequiredActionsSorted.isEmpty()) {
|
||||||
|
@ -1328,7 +1330,7 @@ public class AuthenticationManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<RequiredActionProviderModel> getApplicableRequiredActionsSorted(final RealmModel realm,
|
private static List<RequiredActionProviderModel> getApplicableRequiredActionsSorted(final RealmModel realm,
|
||||||
final AuthenticationSessionModel authSession, final UserModel user, final String kcActionAlias) {
|
final AuthenticationSessionModel authSession, final UserModel user, final String kcActionAlias, final Set<String> ignoredActions) {
|
||||||
final Set<String> nonInitiatedActionAliases = new HashSet<>();
|
final Set<String> nonInitiatedActionAliases = new HashSet<>();
|
||||||
nonInitiatedActionAliases.addAll(user.getRequiredActionsStream().toList());
|
nonInitiatedActionAliases.addAll(user.getRequiredActionsStream().toList());
|
||||||
nonInitiatedActionAliases.addAll(authSession.getRequiredActions());
|
nonInitiatedActionAliases.addAll(authSession.getRequiredActions());
|
||||||
|
@ -1336,6 +1338,7 @@ public class AuthenticationManager {
|
||||||
final var applicableNonInitiatedActions = nonInitiatedActionAliases.stream()
|
final var applicableNonInitiatedActions = nonInitiatedActionAliases.stream()
|
||||||
.map(alias -> getApplicableRequiredAction(realm, alias))
|
.map(alias -> getApplicableRequiredAction(realm, alias))
|
||||||
.filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
|
.filter(model -> !ignoredActions.contains(model.getProviderId()))
|
||||||
.collect(Collectors.toMap(RequiredActionProviderModel::getAlias, Function.identity()));
|
.collect(Collectors.toMap(RequiredActionProviderModel::getAlias, Function.identity()));
|
||||||
|
|
||||||
RequiredActionProviderModel kcAction = null;
|
RequiredActionProviderModel kcAction = null;
|
||||||
|
|
|
@ -16,9 +16,11 @@
|
||||||
*/
|
*/
|
||||||
package org.keycloak.testsuite.actions;
|
package org.keycloak.testsuite.actions;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import org.jboss.arquillian.graphene.page.Page;
|
import org.jboss.arquillian.graphene.page.Page;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.keycloak.admin.client.resource.RealmResource;
|
||||||
import org.keycloak.authentication.requiredactions.TermsAndConditions;
|
import org.keycloak.authentication.requiredactions.TermsAndConditions;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
|
@ -27,6 +29,8 @@ import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
|
||||||
import org.keycloak.testsuite.AssertEvents;
|
import org.keycloak.testsuite.AssertEvents;
|
||||||
import org.keycloak.testsuite.pages.AppPage;
|
import org.keycloak.testsuite.pages.AppPage;
|
||||||
import org.keycloak.testsuite.pages.LoginPage;
|
import org.keycloak.testsuite.pages.LoginPage;
|
||||||
|
import org.keycloak.testsuite.pages.VerifyEmailPage;
|
||||||
|
import org.keycloak.testsuite.updaters.UserAttributeUpdater;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
@ -49,6 +53,9 @@ public class AppInitiatedActionTest extends AbstractTestRealmKeycloakTest {
|
||||||
@Page
|
@Page
|
||||||
protected LoginPage loginPage;
|
protected LoginPage loginPage;
|
||||||
|
|
||||||
|
@Page
|
||||||
|
protected VerifyEmailPage verifyEmailPage;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void executeUnknownAction() {
|
public void executeUnknownAction() {
|
||||||
oauth.kcAction("nosuch").openLoginForm();
|
oauth.kcAction("nosuch").openLoginForm();
|
||||||
|
@ -93,4 +100,32 @@ public class AppInitiatedActionTest extends AbstractTestRealmKeycloakTest {
|
||||||
testRealm().flows().updateRequiredAction("CONFIGURE_TOTP", configureTotp);
|
testRealm().flows().updateRequiredAction("CONFIGURE_TOTP", configureTotp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void executeActionWithVerifyEmailUnsupportedAIA() throws IOException {
|
||||||
|
RealmResource realm = testRealm();
|
||||||
|
RequiredActionProviderRepresentation model = realm.flows().getRequiredAction(UserModel.RequiredAction.VERIFY_EMAIL.name());
|
||||||
|
int prevPriority = model.getPriority();
|
||||||
|
|
||||||
|
try (UserAttributeUpdater userUpdater = UserAttributeUpdater
|
||||||
|
.forUserByUsername(realm, "test-user@localhost")
|
||||||
|
.setRequiredActions(UserModel.RequiredAction.VERIFY_EMAIL).update()) {
|
||||||
|
// Set max priority for verify email (AIA not supported) to be executed before update password
|
||||||
|
model.setPriority(1);
|
||||||
|
realm.flows().updateRequiredAction(UserModel.RequiredAction.VERIFY_EMAIL.name(), model);
|
||||||
|
|
||||||
|
oauth.kcAction(UserModel.RequiredAction.UPDATE_PASSWORD.name()).openLoginForm();
|
||||||
|
loginPage.login("test-user@localhost", "password");
|
||||||
|
|
||||||
|
// the update password should be displayed
|
||||||
|
passwordUpdatePage.assertCurrent();
|
||||||
|
passwordUpdatePage.changePassword("password", "password");
|
||||||
|
|
||||||
|
// once the AIA password is executed the verify profile should be displayed for the login
|
||||||
|
verifyEmailPage.assertCurrent();
|
||||||
|
} finally {
|
||||||
|
model.setPriority(prevPriority);
|
||||||
|
realm.flows().updateRequiredAction(UserModel.RequiredAction.VERIFY_EMAIL.name(), model);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue