mirror of
https://github.com/keycloak/keycloak.git
synced 2026-01-10 15:32:05 -03:30
Add re-authentication when updating email via UPDATE_EMAIL feature
Closes #39670 Signed-off-by: Martin Kanis <mkanis@redhat.com>
This commit is contained in:
parent
814e66ef7b
commit
f35c413b31
@ -38,17 +38,21 @@ import java.util.List;
|
||||
*/
|
||||
public interface RequiredActionFactory extends ProviderFactory<RequiredActionProvider> {
|
||||
|
||||
List<ProviderConfigProperty> MAX_AUTH_AGE_CONFIG_PROPERTIES = ProviderConfigurationBuilder.create()
|
||||
.property()
|
||||
.name(Constants.MAX_AUTH_AGE_KEY)
|
||||
.label("Maximum Age of Authentication")
|
||||
.helpText("Configures the duration in seconds this action can be used after the last authentication before the user is required to re-authenticate. " +
|
||||
"This parameter is used just in the context of AIA when the kc_action parameter is available in the request, which is for instance when user " +
|
||||
"himself updates his password in the account console.")
|
||||
.type(ProviderConfigProperty.STRING_TYPE)
|
||||
.defaultValue(MaxAuthAgePasswordPolicyProviderFactory.DEFAULT_MAX_AUTH_AGE)
|
||||
.add()
|
||||
.build();
|
||||
List<ProviderConfigProperty> MAX_AUTH_AGE_CONFIG_PROPERTIES = getMaxAuthAgePropertyConfig();
|
||||
|
||||
static List<ProviderConfigProperty> getMaxAuthAgePropertyConfig() {
|
||||
return ProviderConfigurationBuilder.create()
|
||||
.property()
|
||||
.name(Constants.MAX_AUTH_AGE_KEY)
|
||||
.label("Maximum Age of Authentication")
|
||||
.helpText("Configures the duration in seconds this action can be used after the last authentication before the user is required to re-authenticate. " +
|
||||
"This parameter is used just in the context of AIA when the kc_action parameter is available in the request, which is for instance when user " +
|
||||
"himself updates his password in the account console.")
|
||||
.type(ProviderConfigProperty.STRING_TYPE)
|
||||
.defaultValue(MaxAuthAgePasswordPolicyProviderFactory.DEFAULT_MAX_AUTH_AGE)
|
||||
.add()
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Display text used in admin console to reference this required action
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
|
||||
package org.keycloak.authentication.requiredactions;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@ -49,6 +50,7 @@ import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.utils.FormMessage;
|
||||
import org.keycloak.provider.EnvironmentDependentProviderFactory;
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
import org.keycloak.services.Urls;
|
||||
import org.keycloak.services.validation.Validation;
|
||||
import org.keycloak.sessions.AuthenticationSessionModel;
|
||||
@ -199,6 +201,17 @@ public class UpdateEmail implements RequiredActionProvider, RequiredActionFactor
|
||||
return UserModel.RequiredAction.UPDATE_EMAIL.name();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxAuthAge(KeycloakSession session) {
|
||||
// always require re-authentication
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ProviderConfigProperty> getConfigMetadata() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSupported(Config.Scope config) {
|
||||
return Profile.isFeatureEnabled(Profile.Feature.UPDATE_EMAIL);
|
||||
|
||||
@ -17,27 +17,52 @@
|
||||
package org.keycloak.testsuite.actions;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.keycloak.userprofile.UserProfileConstants.ROLE_USER;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.admin.client.resource.UserProfileResource;
|
||||
import org.keycloak.events.Details;
|
||||
import org.keycloak.events.EventType;
|
||||
import org.keycloak.models.Constants;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.representations.idm.RequiredActionProviderRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.representations.userprofile.config.UPAttribute;
|
||||
import org.keycloak.representations.userprofile.config.UPAttributePermissions;
|
||||
import org.keycloak.representations.userprofile.config.UPAttributeRequired;
|
||||
import org.keycloak.representations.userprofile.config.UPConfig;
|
||||
import org.keycloak.testsuite.arquillian.annotation.IgnoreBrowserDriver;
|
||||
import org.keycloak.testsuite.util.UIUtils;
|
||||
import org.keycloak.validate.validators.LengthValidator;
|
||||
import org.openqa.selenium.WebElement;
|
||||
import org.openqa.selenium.firefox.FirefoxDriver;
|
||||
import org.openqa.selenium.support.FindBy;
|
||||
|
||||
public class AppInitiatedActionUpdateEmailTest extends AbstractAppInitiatedActionUpdateEmailTest {
|
||||
|
||||
@FindBy(id = "update-email-btn")
|
||||
private WebElement updateEmailBtn;
|
||||
|
||||
@After
|
||||
public void after() {
|
||||
// update email required action max auth age back to default
|
||||
Optional<RequiredActionProviderRepresentation> updateEmailAction = testRealm().flows().getRequiredActions()
|
||||
.stream()
|
||||
.filter(requiredAction -> requiredAction.getProviderId().equals(UserModel.RequiredAction.UPDATE_EMAIL.name()))
|
||||
.findFirst();
|
||||
if (updateEmailAction.isPresent()) {
|
||||
updateEmailAction.get().getConfig().remove(Constants.MAX_AUTH_AGE_KEY);
|
||||
testRealm().flows().updateRequiredAction(UserModel.RequiredAction.UPDATE_EMAIL.name(), updateEmailAction.get());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateEmail() throws Exception {
|
||||
changeEmailUsingAIA("new@email.com");
|
||||
@ -104,4 +129,54 @@ public class AppInitiatedActionUpdateEmailTest extends AbstractAppInitiatedActio
|
||||
|
||||
emailUpdatePage.changeEmail(newEmail);
|
||||
}
|
||||
|
||||
@Test
|
||||
// only for firefox as it needs to go to the account console
|
||||
|
||||
@IgnoreBrowserDriver(value={FirefoxDriver.class}, negate=true)
|
||||
public void updateEmailReAuthentication() {
|
||||
appPage.open();
|
||||
appPage.openAccount();
|
||||
|
||||
loginPage.login("test-user@localhost", "password");
|
||||
|
||||
setTimeOffset(50);
|
||||
|
||||
UIUtils.clickLink(updateEmailBtn);
|
||||
|
||||
loginPage.assertCurrent();
|
||||
loginPage.login("password");
|
||||
|
||||
emailUpdatePage.assertCurrent();
|
||||
emailUpdatePage.changeEmail("test-user2@localhost");
|
||||
}
|
||||
|
||||
@Test
|
||||
// only for firefox as it needs to go to the account console
|
||||
// chrome doesn't work due to "change password popup"
|
||||
@IgnoreBrowserDriver(value={FirefoxDriver.class}, negate=true)
|
||||
public void updateEmailCustomMaxAgeReAuthentication() {
|
||||
RequiredActionProviderRepresentation updateEmailAction = testRealm().flows().getRequiredActions()
|
||||
.stream()
|
||||
.filter(requiredAction -> requiredAction.getProviderId().equals(UserModel.RequiredAction.UPDATE_EMAIL.name()))
|
||||
.findFirst()
|
||||
.orElseThrow();
|
||||
|
||||
// this custom config should be ignored and re-authentication should be always required
|
||||
updateEmailAction.getConfig().put(Constants.MAX_AUTH_AGE_KEY, "300");
|
||||
testRealm().flows().updateRequiredAction(UserModel.RequiredAction.UPDATE_EMAIL.name(), updateEmailAction);
|
||||
|
||||
appPage.open();
|
||||
appPage.openAccount();
|
||||
|
||||
loginPage.login("test-user@localhost", "password");
|
||||
|
||||
UIUtils.clickLink(updateEmailBtn);
|
||||
|
||||
loginPage.assertCurrent();
|
||||
loginPage.login("password");
|
||||
|
||||
emailUpdatePage.assertCurrent();
|
||||
emailUpdatePage.changeEmail("test-user2@localhost");
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user