Integrate registration with terms and conditions required action
Closes #25891 Signed-off-by: rmartinc <rmartinc@redhat.com>
This commit is contained in:
parent
a8eca6add0
commit
e162974a8d
4 changed files with 76 additions and 4 deletions
|
@ -2982,4 +2982,5 @@ viewDocumentation=Refer to documentation
|
|||
viewGuides=View guides
|
||||
joinCommunity=Join community
|
||||
readBlog=Read blog
|
||||
customValue=Custom value
|
||||
customValue=Custom value
|
||||
termsAndConditionsUserAttribute=Terms and conditions accepted timestamp
|
||||
|
|
|
@ -25,6 +25,8 @@ import org.keycloak.authentication.FormAction;
|
|||
import org.keycloak.authentication.FormActionFactory;
|
||||
import org.keycloak.authentication.FormContext;
|
||||
import org.keycloak.authentication.ValidationContext;
|
||||
import org.keycloak.authentication.requiredactions.TermsAndConditions;
|
||||
import org.keycloak.common.util.Time;
|
||||
import org.keycloak.events.Details;
|
||||
import org.keycloak.events.Errors;
|
||||
import org.keycloak.events.EventType;
|
||||
|
@ -33,6 +35,7 @@ import org.keycloak.models.AuthenticationExecutionModel;
|
|||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RequiredActionProviderModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.utils.FormMessage;
|
||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||
|
@ -137,6 +140,17 @@ public class RegistrationUserCreation implements FormAction, FormActionFactory {
|
|||
|
||||
user.setEnabled(true);
|
||||
|
||||
if ("on".equals(formData.getFirst(RegistrationTermsAndConditions.FIELD))) {
|
||||
// if accepted terms and conditions checkbox, remove action and add the attribute if enabled
|
||||
RequiredActionProviderModel tacModel = context.getRealm().getRequiredActionProviderByAlias(
|
||||
UserModel.RequiredAction.TERMS_AND_CONDITIONS.name());
|
||||
if (tacModel != null && tacModel.isEnabled()) {
|
||||
user.setSingleAttribute(TermsAndConditions.USER_ATTRIBUTE, Integer.toString(Time.currentTime()));
|
||||
context.getAuthenticationSession().removeRequiredAction(UserModel.RequiredAction.TERMS_AND_CONDITIONS);
|
||||
user.removeRequiredAction(UserModel.RequiredAction.TERMS_AND_CONDITIONS);
|
||||
}
|
||||
}
|
||||
|
||||
context.setUser(user);
|
||||
|
||||
context.getAuthenticationSession().setClientNote(OIDCLoginProtocol.LOGIN_HINT_PARAM, username);
|
||||
|
|
|
@ -30,6 +30,7 @@ import java.util.regex.Pattern;
|
|||
import java.util.stream.Collectors;
|
||||
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.authentication.requiredactions.TermsAndConditions;
|
||||
import org.keycloak.common.Profile;
|
||||
import org.keycloak.component.AmphibianProviderFactory;
|
||||
import org.keycloak.component.ComponentModel;
|
||||
|
@ -38,6 +39,7 @@ import org.keycloak.models.KeycloakContext;
|
|||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RequiredActionProviderModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
import org.keycloak.provider.ProviderConfigurationBuilder;
|
||||
|
@ -173,6 +175,13 @@ public class DeclarativeUserProfileProviderFactory implements UserProfileProvide
|
|||
return realm.isInternationalizationEnabled();
|
||||
}
|
||||
|
||||
private static boolean isTermAndConditionsEnabled(AttributeContext context) {
|
||||
RealmModel realm = context.getSession().getContext().getRealm();
|
||||
RequiredActionProviderModel tacModel = realm.getRequiredActionProviderByAlias(
|
||||
UserModel.RequiredAction.TERMS_AND_CONDITIONS.name());
|
||||
return tacModel != null && tacModel.isEnabled();
|
||||
}
|
||||
|
||||
private static boolean isNewUser(AttributeContext c) {
|
||||
return c.getUser() == null;
|
||||
}
|
||||
|
@ -440,6 +449,11 @@ public class DeclarativeUserProfileProviderFactory implements UserProfileProvide
|
|||
metadata.addAttribute(UserModel.LOCALE, -1, DeclarativeUserProfileProviderFactory::isInternationalizationEnabled, DeclarativeUserProfileProviderFactory::isInternationalizationEnabled)
|
||||
.setRequired(AttributeMetadata.ALWAYS_FALSE);
|
||||
|
||||
metadata.addAttribute(TermsAndConditions.USER_ATTRIBUTE, -1, AttributeMetadata.ALWAYS_FALSE,
|
||||
DeclarativeUserProfileProviderFactory::isTermAndConditionsEnabled)
|
||||
.setAttributeDisplayName("${termsAndConditionsUserAttribute}")
|
||||
.setRequired(AttributeMetadata.ALWAYS_FALSE);
|
||||
|
||||
return metadata;
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,6 @@ package org.keycloak.testsuite.forms;
|
|||
import org.hamcrest.Matchers;
|
||||
import org.jboss.arquillian.graphene.page.Page;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Assume;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.authentication.AuthenticationFlow;
|
||||
|
@ -28,14 +27,18 @@ import org.keycloak.authentication.forms.RegistrationPassword;
|
|||
import org.keycloak.authentication.forms.RegistrationRecaptcha;
|
||||
import org.keycloak.authentication.forms.RegistrationTermsAndConditions;
|
||||
import org.keycloak.authentication.forms.RegistrationUserCreation;
|
||||
import org.keycloak.authentication.requiredactions.TermsAndConditions;
|
||||
import org.keycloak.common.util.Time;
|
||||
import org.keycloak.events.Details;
|
||||
import org.keycloak.events.Errors;
|
||||
import org.keycloak.events.EventType;
|
||||
import org.keycloak.models.AuthenticationExecutionModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.utils.DefaultAuthenticationFlows;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.representations.idm.EventRepresentation;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.representations.idm.RequiredActionProviderRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
|
||||
import org.keycloak.testsuite.AssertEvents;
|
||||
|
@ -436,7 +439,7 @@ public class RegisterTest extends AbstractTestRealmKeycloakTest {
|
|||
assertUserRegistered(userId, username, email);
|
||||
}
|
||||
|
||||
private void assertUserRegistered(String userId, String username, String email) {
|
||||
private UserRepresentation assertUserRegistered(String userId, String username, String email) {
|
||||
events.expectLogin().detail("username", username.toLowerCase()).user(userId).assertEvent();
|
||||
|
||||
UserRepresentation user = getUser(userId);
|
||||
|
@ -445,6 +448,7 @@ public class RegisterTest extends AbstractTestRealmKeycloakTest {
|
|||
// test that timestamp is current with 10s tollerance
|
||||
assertTrue((System.currentTimeMillis() - user.getCreatedTimestamp()) < 10000);
|
||||
assertUserBasicRegisterAttributes(userId, username, email, "firstName", "lastName");
|
||||
return user;
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -761,12 +765,51 @@ public class RegisterTest extends AbstractTestRealmKeycloakTest {
|
|||
|
||||
String userId = events.expectRegister("registerUserSuccessTermsAcceptance", "registerUserSuccessTermsAcceptance@email")
|
||||
.assertEvent().getUserId();
|
||||
assertUserRegistered(userId, "registerUserSuccessTermsAcceptance", "registerUserSuccessTermsAcceptance@email");
|
||||
UserRepresentation user = assertUserRegistered(userId, "registerUserSuccessTermsAcceptance", "registerUserSuccessTermsAcceptance@email");
|
||||
Assert.assertNull(user.getAttributes());
|
||||
} finally {
|
||||
configureRegistrationFlowWithCustomRegistrationPageForm(UUID.randomUUID().toString());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void registerUserSuccessTermsAcceptanceWithRequiredActionEnabled() {
|
||||
configureRegistrationFlowWithCustomRegistrationPageForm(UUID.randomUUID().toString(),
|
||||
AuthenticationExecutionModel.Requirement.REQUIRED);
|
||||
|
||||
// configure Terms and Conditions required action as enabled and default
|
||||
RequiredActionProviderRepresentation tacRep = testRealm().flows().getRequiredAction(UserModel.RequiredAction.TERMS_AND_CONDITIONS.name());
|
||||
Assert.assertNotNull(tacRep);
|
||||
tacRep.setEnabled(true);
|
||||
tacRep.setDefaultAction(true);
|
||||
testRealm().flows().updateRequiredAction(UserModel.RequiredAction.TERMS_AND_CONDITIONS.name(), tacRep);
|
||||
|
||||
try {
|
||||
loginPage.open();
|
||||
loginPage.clickRegister();
|
||||
registerPage.assertCurrent();
|
||||
|
||||
int currentTime = Time.currentTime();
|
||||
registerPage.register("firstName", "lastName", "registerUserSuccessTermsAcceptance2@email",
|
||||
"registerUserSuccessTermsAcceptance2", "password", "password", null, true, null);
|
||||
|
||||
assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
||||
|
||||
String userId = events.expectRegister("registerUserSuccessTermsAcceptance2", "registerUserSuccessTermsAcceptance2@email")
|
||||
.assertEvent().getUserId();
|
||||
UserRepresentation user = assertUserRegistered(userId, "registerUserSuccessTermsAcceptance2", "registerUserSuccessTermsAcceptance2@email");
|
||||
Assert.assertNotNull(user.getAttributes());
|
||||
Assert.assertNotNull(user.getAttributes().get(TermsAndConditions.USER_ATTRIBUTE));
|
||||
Assert.assertEquals(1, user.getAttributes().get(TermsAndConditions.USER_ATTRIBUTE).size());
|
||||
Assert.assertTrue(Integer.parseInt(user.getAttributes().get(TermsAndConditions.USER_ATTRIBUTE).get(0)) >= currentTime);
|
||||
} finally {
|
||||
tacRep.setEnabled(false);
|
||||
tacRep.setDefaultAction(false);
|
||||
testRealm().flows().updateRequiredAction(UserModel.RequiredAction.TERMS_AND_CONDITIONS.name(), tacRep);
|
||||
configureRegistrationFlowWithCustomRegistrationPageForm(UUID.randomUUID().toString());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRegisterShouldFailBeforeUserCreationWhenUserIsInContext() {
|
||||
loginPage.open();
|
||||
|
|
Loading…
Reference in a new issue