[KEYCLOAK-18429] Support a dynamic update profile form

This commit is contained in:
Vlastimil Elias 2021-06-28 15:22:21 +02:00 committed by Pedro Igor
parent f32447bcc1
commit 04ff2c327b
25 changed files with 1004 additions and 387 deletions

View file

@ -26,6 +26,6 @@ public enum LoginFormsPages {
LOGIN_IDP_LINK_CONFIRM, LOGIN_IDP_LINK_EMAIL,
OAUTH_GRANT, LOGIN_RESET_PASSWORD, LOGIN_UPDATE_PASSWORD, LOGIN_SELECT_AUTHENTICATOR, REGISTER, REGISTER_USER_PROFILE, INFO, ERROR, ERROR_WEBAUTHN, LOGIN_UPDATE_PROFILE,
LOGIN_PAGE_EXPIRED, CODE, X509_CONFIRM, SAML_POST_FORM,
LOGIN_OAUTH2_DEVICE_VERIFY_USER_CODE, VERIFY_PROFILE;
LOGIN_OAUTH2_DEVICE_VERIFY_USER_CODE, UPDATE_USER_PROFILE;
}

View file

@ -32,6 +32,7 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.services.resources.IdentityBrokerService;
import org.keycloak.sessions.AuthenticationSessionModel;
import org.keycloak.userprofile.UserProfileContext;
import org.keycloak.util.JsonSerialization;
import java.io.IOException;
@ -66,6 +67,12 @@ public class SerializedBrokeredIdentityContext implements UpdateProfileContext {
return !emailAsUsername;
}
@JsonIgnore
@Override
public UserProfileContext getUserProfileContext() {
return UserProfileContext.IDP_REVIEW;
}
public String getId() {
return id;
}

View file

@ -27,6 +27,7 @@ import org.keycloak.authentication.RequiredActionProvider;
import org.keycloak.events.Details;
import org.keycloak.events.EventBuilder;
import org.keycloak.events.EventType;
import org.keycloak.forms.login.LoginFormsProvider;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.UserModel;
@ -57,9 +58,7 @@ public class UpdateProfile implements RequiredActionProvider, RequiredActionFact
@Override
public void requiredActionChallenge(RequiredActionContext context) {
Response challenge = context.form()
.createResponse(UserModel.RequiredAction.UPDATE_PROFILE);
context.challenge(challenge);
context.challenge(createResponse(context, null, null));
}
@Override
@ -94,14 +93,28 @@ public class UpdateProfile implements RequiredActionProvider, RequiredActionFact
} catch (ValidationException pve) {
List<FormMessage> errors = Validation.getFormErrorsFromValidation(pve.getErrors());
Response challenge = context.form()
.setErrors(errors)
.setFormData(formData)
.createResponse(UserModel.RequiredAction.UPDATE_PROFILE);
context.challenge(challenge);
context.challenge(createResponse(context, formData, errors));
}
}
protected UserModel.RequiredAction getResponseAction(){
return UserModel.RequiredAction.UPDATE_PROFILE;
}
protected Response createResponse(RequiredActionContext context, MultivaluedMap<String, String> formData, List<FormMessage> errors) {
LoginFormsProvider form = context.form();
if (errors != null && !errors.isEmpty()) {
form.setErrors(errors);
}
if(formData != null) {
form = form.setFormData(formData);
}
return form.createResponse(getResponseAction());
}
@Override
public void close() {

View file

@ -17,24 +17,19 @@
package org.keycloak.authentication.requiredactions;
import java.util.List;
import java.util.stream.Collectors;
import javax.ws.rs.HttpMethod;
import javax.ws.rs.core.MultivaluedHashMap;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import java.util.List;
import org.keycloak.Config;
import org.keycloak.OAuth2Constants;
import org.keycloak.authentication.DisplayTypeRequiredActionFactory;
import org.keycloak.authentication.InitiatedActionSupport;
import org.keycloak.authentication.RequiredActionContext;
import org.keycloak.authentication.RequiredActionFactory;
import org.keycloak.authentication.RequiredActionProvider;
import org.keycloak.events.EventBuilder;
import org.keycloak.events.EventType;
import org.keycloak.forms.login.LoginFormsProvider;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.FormMessage;
import org.keycloak.services.validation.Validation;
@ -46,11 +41,16 @@ import org.keycloak.userprofile.ValidationException;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class VerifyUserProfile implements RequiredActionProvider, RequiredActionFactory, DisplayTypeRequiredActionFactory {
public class VerifyUserProfile extends UpdateProfile {
@Override
public InitiatedActionSupport initiatedActionSupport() {
return InitiatedActionSupport.SUPPORTED;
return InitiatedActionSupport.NOT_SUPPORTED;
}
@Override
protected UserModel.RequiredAction getResponseAction(){
return UserModel.RequiredAction.VERIFY_PROFILE;
}
@Override
@ -72,7 +72,7 @@ public class VerifyUserProfile implements RequiredActionProvider, RequiredAction
public void requiredActionChallenge(RequiredActionContext context) {
UserProfileProvider provider = context.getSession().getProvider(UserProfileProvider.class);
UserProfile profile = provider.create(UserProfileContext.UPDATE_PROFILE, context.getUser());
try {
profile.validate();
context.success();
@ -86,31 +86,17 @@ public class VerifyUserProfile implements RequiredActionProvider, RequiredAction
parameters = context.getHttpRequest().getDecodedFormParameters();
}
context.challenge(createResponse(context, profile, parameters, errors));
context.challenge(createResponse(context, parameters, errors));
EventBuilder event = context.getEvent().clone();
event.event(EventType.VERIFY_PROFILE);
event.detail("fields_to_update", collectFields(errors));
event.success();
}
}
@Override
public void processAction(RequiredActionContext context) {
EventBuilder event = context.getEvent();
event.event(EventType.VERIFY_PROFILE);
MultivaluedMap<String, String> formData = context.getHttpRequest().getDecodedFormParameters();
UserProfileProvider provider = context.getSession().getProvider(UserProfileProvider.class);
UserProfile profile = provider.create(UserProfileContext.UPDATE_PROFILE, formData, context.getUser());
try {
profile.update();
context.success();
} catch (ValidationException ve) {
List<FormMessage> errors = Validation.getFormErrorsFromValidation(ve.getErrors());
context.challenge(createResponse(context, profile, formData, errors));
}
}
@Override
public void close() {
private String collectFields(List<FormMessage> errors) {
return errors.stream().map(FormMessage::getField).distinct().collect(Collectors.joining(","));
}
@Override
@ -118,23 +104,6 @@ public class VerifyUserProfile implements RequiredActionProvider, RequiredAction
return this;
}
@Override
public RequiredActionProvider createDisplay(KeycloakSession session, String displayType) {
if (displayType == null) return this;
if (!OAuth2Constants.DISPLAY_CONSOLE.equalsIgnoreCase(displayType)) return null;
return ConsoleUpdateProfile.SINGLETON;
}
@Override
public void init(Config.Scope config) {
}
@Override
public void postInit(KeycloakSessionFactory factory) {
}
@Override
public String getDisplayText() {
return "Verify Profile";
@ -146,15 +115,4 @@ public class VerifyUserProfile implements RequiredActionProvider, RequiredAction
return UserModel.RequiredAction.VERIFY_PROFILE.name();
}
private Response createResponse(RequiredActionContext context, UserProfile profile,
MultivaluedMap<String, String> formData, List<FormMessage> errors) {
LoginFormsProvider form = context.form();
if (!errors.isEmpty()) {
form.setErrors(errors);
}
return form.setFormData(formData)
.createResponse(UserModel.RequiredAction.VERIFY_PROFILE);
}
}

View file

@ -22,6 +22,8 @@ import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.keycloak.userprofile.UserProfileContext;
/**
* Abstraction, which allows to display updateProfile page in various contexts (Required action of already existing user, or first identity provider
* login when user doesn't yet exists in Keycloak DB)
@ -29,6 +31,8 @@ import java.util.stream.Stream;
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public interface UpdateProfileContext {
UserProfileContext getUserProfileContext();
boolean isEditUsernameAllowed();

View file

@ -19,10 +19,10 @@ package org.keycloak.authentication.requiredactions.util;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.userprofile.UserProfileContext;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
@ -37,11 +37,16 @@ public class UserUpdateProfileContext implements UpdateProfileContext {
this.realm = realm;
this.user = user;
}
@Override
public boolean isEditUsernameAllowed() {
return realm.isEditUsernameAllowed();
}
@Override
public UserProfileContext getUserProfileContext() {
return UserProfileContext.UPDATE_PROFILE;
}
@Override
public String getUsername() {

View file

@ -150,7 +150,11 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
this.attributes.put(UPDATE_PROFILE_CONTEXT_ATTR, userBasedContext);
actionMessage = Messages.UPDATE_PROFILE;
page = LoginFormsPages.LOGIN_UPDATE_PROFILE;
if(isDynamicUserProfile()) {
page = LoginFormsPages.UPDATE_USER_PROFILE;
} else {
page = LoginFormsPages.LOGIN_UPDATE_PROFILE;
}
break;
case UPDATE_PASSWORD:
boolean isRequestedByAdmin = user.getRequiredActionsStream().filter(Objects::nonNull).anyMatch(UPDATE_PASSWORD.toString()::contains);
@ -166,7 +170,7 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
this.attributes.put(UPDATE_PROFILE_CONTEXT_ATTR, verifyProfile);
actionMessage = Messages.UPDATE_PROFILE;
page = LoginFormsPages.VERIFY_PROFILE;
page = LoginFormsPages.UPDATE_USER_PROFILE;
break;
default:
return Response.serverError().build();
@ -254,7 +258,7 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
case SAML_POST_FORM:
attributes.put("samlPost", new SAMLPostFormBean(formData));
break;
case VERIFY_PROFILE:
case UPDATE_USER_PROFILE:
attributes.put("profile", new VerifyProfileBean(user, formData, session));
break;
}

View file

@ -74,8 +74,8 @@ public class Templates {
return "login-x509-info.ftl";
case SAML_POST_FORM:
return "saml-post-form.ftl";
case VERIFY_PROFILE:
return "verify-profile.ftl";
case UPDATE_USER_PROFILE:
return "update-user-profile.ftl";
default:
throw new IllegalArgumentException();
}

View file

@ -55,9 +55,9 @@ public abstract class AbstractUserProfileBean {
* Get attribute default value to be pre-filled into the form on first show.
*
* @param name of the attribute
* @return attribute default value (can be null or empty list)
* @return attribute default value (can be null)
*/
protected abstract List<String> getAttributeDefaultValue(String name);
protected abstract String getAttributeDefaultValue(String name);
/**
* Get context the template is used for, so view can be customized for distinct contexts.
@ -110,11 +110,12 @@ public abstract class AbstractUserProfileBean {
}
public String getValue() {
List<String> v = formData!=null ? formData.getOrDefault(getName(), getAttributeDefaultValue(getName())): getAttributeDefaultValue(getName());
List<String> v = formData != null ? formData.get(getName()) : null;
if (v == null || v.isEmpty()) {
return null;
return getAttributeDefaultValue(getName());
} else {
return v.get(0);
}
return v.get(0);
}
public boolean isRequired() {
@ -143,7 +144,7 @@ public abstract class AbstractUserProfileBean {
return annotations;
}
/**
* Get info about validators applied to attribute.
*

View file

@ -17,7 +17,6 @@
package org.keycloak.forms.login.freemarker.model;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.ws.rs.core.MultivaluedMap;
@ -53,7 +52,7 @@ public class RegisterBean extends AbstractUserProfileBean {
}
@Override
protected List<String> getAttributeDefaultValue(String name) {
protected String getAttributeDefaultValue(String name) {
return null;
}

View file

@ -1,9 +1,5 @@
package org.keycloak.forms.login.freemarker.model;
import static java.util.Collections.singletonList;
import java.util.List;
import javax.ws.rs.core.MultivaluedMap;
import org.keycloak.models.KeycloakSession;
@ -31,8 +27,8 @@ public class VerifyProfileBean extends AbstractUserProfileBean {
}
@Override
protected List<String> getAttributeDefaultValue(String name) {
return singletonList(user.getFirstAttribute(name));
protected String getAttributeDefaultValue(String name) {
return user.getFirstAttribute(name);
}
@Override

View file

@ -138,6 +138,7 @@ public class DeclarativeUserProfileProvider extends AbstractUserProfileProvider<
decoratedMetadata.addAttribute(UserModel.LAST_NAME, 2, new AttributeValidatorMetadata(BlankAttributeValidator.ID, BlankAttributeValidator.createConfig(Messages.MISSING_LAST_NAME))).setAttributeDisplayName("${lastName}");
return decoratedMetadata;
}
return decoratedMetadata;
}
ComponentModel model = getComponentModelOrCreate(session);

View file

@ -16,6 +16,8 @@
*/
package org.keycloak.testsuite.pages;
import org.openqa.selenium.By;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
@ -30,6 +32,12 @@ public class LoginUpdateProfileEditUsernameAllowedPage extends LoginUpdateProfil
update(firstName, lastName, email);
}
public void updateWithDepartment(String firstName, String lastName, String department, String email, String username) {
usernameInput.clear();
usernameInput.sendKeys(username);
super.updateWithDepartment(firstName, lastName, department, email);
}
public String getUsername() {
return usernameInput.getAttribute("value");
}
@ -37,6 +45,14 @@ public class LoginUpdateProfileEditUsernameAllowedPage extends LoginUpdateProfil
public boolean isCurrent() {
return PageUtils.getPageTitle(driver).equals("Update Account Information");
}
public boolean isUsernamePresent() {
try {
return driver.findElement(By.id("username")).isDisplayed();
} catch (NoSuchElementException nse) {
return false;
}
}
@Override
public void open() {

View file

@ -19,6 +19,7 @@ package org.keycloak.testsuite.pages;
import org.jboss.arquillian.graphene.page.Page;
import org.keycloak.testsuite.util.UIUtils;
import org.openqa.selenium.By;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
@ -42,6 +43,9 @@ public class LoginUpdateProfilePage extends AbstractPage {
@FindBy(id = "email")
private WebElement emailInput;
@FindBy(id = "department")
private WebElement departmentInput;
@FindBy(css = "input[type=\"submit\"]")
private WebElement submitButton;
@ -53,6 +57,10 @@ public class LoginUpdateProfilePage extends AbstractPage {
private WebElement loginAlertErrorMessage;
public void update(String firstName, String lastName, String email) {
updateWithDepartment(firstName, lastName, null, email);
}
public void updateWithDepartment(String firstName, String lastName, String department, String email) {
if (firstName != null) {
firstNameInput.clear();
firstNameInput.sendKeys(firstName);
@ -66,6 +74,11 @@ public class LoginUpdateProfilePage extends AbstractPage {
emailInput.sendKeys(email);
}
if(department != null) {
departmentInput.clear();
departmentInput.sendKeys(department);
}
clickLink(submitButton);
}
@ -92,6 +105,14 @@ public class LoginUpdateProfilePage extends AbstractPage {
public String getEmail() {
return emailInput.getAttribute("value");
}
public String getDepartment() {
return departmentInput.getAttribute("value");
}
public boolean isDepartmentEnabled() {
return departmentInput.isEnabled();
}
public boolean isCurrent() {
return PageUtils.getPageTitle(driver).equals("Update Account Information");
@ -100,6 +121,19 @@ public class LoginUpdateProfilePage extends AbstractPage {
public UpdateProfileErrors getInputErrors() {
return errorsPage;
}
public String getLabelForField(String fieldId) {
return driver.findElement(By.cssSelector("label[for="+fieldId+"]")).getText();
}
public boolean isDepartmentPresent() {
try {
isDepartmentEnabled();
return true;
} catch (NoSuchElementException e) {
return false;
}
}
@Override
public void open() {
@ -120,8 +154,14 @@ public class LoginUpdateProfilePage extends AbstractPage {
@FindBy(id = "input-error-firstname")
private WebElement inputErrorFirstName;
@FindBy(id = "input-error-firstName")
private WebElement inputErrorFirstNameDynamic;
@FindBy(id = "input-error-lastname")
private WebElement inputErrorLastName;
@FindBy(id = "input-error-lastName")
private WebElement inputErrorLastNameDynamic;
@FindBy(id = "input-error-email")
private WebElement inputErrorEmail;
@ -133,7 +173,11 @@ public class LoginUpdateProfilePage extends AbstractPage {
try {
return getTextFromElement(inputErrorFirstName);
} catch (NoSuchElementException e) {
return null;
try {
return getTextFromElement(inputErrorFirstNameDynamic);
} catch (NoSuchElementException ex) {
return null;
}
}
}
@ -141,7 +185,11 @@ public class LoginUpdateProfilePage extends AbstractPage {
try {
return getTextFromElement(inputErrorLastName);
} catch (NoSuchElementException e) {
return null;
try {
return getTextFromElement(inputErrorLastNameDynamic);
} catch (NoSuchElementException ex) {
return null;
}
}
}

View file

@ -47,6 +47,10 @@ public class AppInitiatedActionUpdateProfileTest extends AbstractAppInitiatedAct
@Page
protected ErrorPage errorPage;
protected boolean isDynamicForm() {
return false;
}
@Override
public void configureTestRealm(RealmRepresentation testRealm) {
}
@ -203,7 +207,10 @@ public class AppInitiatedActionUpdateProfileTest extends AbstractAppInitiatedAct
Assert.assertEquals("New last", updateProfilePage.getLastName());
Assert.assertEquals("new@email.com", updateProfilePage.getEmail());
Assert.assertEquals("Please specify first name.", updateProfilePage.getInputErrors().getFirstNameError());
if(isDynamicForm())
Assert.assertEquals("Please specify this field.", updateProfilePage.getInputErrors().getFirstNameError());
else
Assert.assertEquals("Please specify first name.", updateProfilePage.getInputErrors().getFirstNameError());
events.assertEmpty();
}
@ -225,7 +232,10 @@ public class AppInitiatedActionUpdateProfileTest extends AbstractAppInitiatedAct
Assert.assertEquals("", updateProfilePage.getLastName());
Assert.assertEquals("new@email.com", updateProfilePage.getEmail());
Assert.assertEquals("Please specify last name.", updateProfilePage.getInputErrors().getLastNameError());
if(isDynamicForm())
Assert.assertEquals("Please specify this field.", updateProfilePage.getInputErrors().getLastNameError());
else
Assert.assertEquals("Please specify last name.", updateProfilePage.getInputErrors().getLastNameError());
events.assertEmpty();
}

View file

@ -0,0 +1,48 @@
/*
* Copyright 2021 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.actions;
import org.junit.Before;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.testsuite.forms.VerifyProfileTest;
/**
* Only covers basic use cases for App Initialized actions. Complete dynamic user profile behavior is tested in {@link RequiredActionUpdateProfileWithUserProfileTest} as it shares same code as the App initialized action.
*
* @author Vlastimil Elias <velias@redhat.com>
*
*/
public class AppInitiatedActionUpdateProfileWithUserProfileTest extends AppInitiatedActionUpdateProfileTest {
@Override
protected boolean isDynamicForm() {
return true;
}
@Override
public void configureTestRealm(RealmRepresentation testRealm) {
super.configureTestRealm(testRealm);
VerifyProfileTest.enableDynamicUserProfile(testRealm);
}
@Before
public void beforeTest() {
VerifyProfileTest.setUserProfileConfiguration(testRealm(),null);
super.beforeTest();
}
}

View file

@ -63,6 +63,10 @@ public class RequiredActionUpdateProfileTest extends AbstractTestRealmKeycloakTe
@Page
protected ErrorPage errorPage;
protected boolean isDynamicForm() {
return false;
}
@Override
public void configureTestRealm(RealmRepresentation testRealm) {
@ -166,7 +170,10 @@ public class RequiredActionUpdateProfileTest extends AbstractTestRealmKeycloakTe
Assert.assertEquals("New last", updateProfilePage.getLastName());
Assert.assertEquals("new@email.com", updateProfilePage.getEmail());
Assert.assertEquals("Please specify first name.", updateProfilePage.getInputErrors().getFirstNameError());
if(isDynamicForm())
Assert.assertEquals("Please specify this field.", updateProfilePage.getInputErrors().getFirstNameError());
else
Assert.assertEquals("Please specify first name.", updateProfilePage.getInputErrors().getFirstNameError());
events.assertEmpty();
}
@ -188,7 +195,10 @@ public class RequiredActionUpdateProfileTest extends AbstractTestRealmKeycloakTe
Assert.assertEquals("", updateProfilePage.getLastName());
Assert.assertEquals("new@email.com", updateProfilePage.getEmail());
Assert.assertEquals("Please specify last name.", updateProfilePage.getInputErrors().getLastNameError());
if(isDynamicForm())
Assert.assertEquals("Please specify this field.", updateProfilePage.getInputErrors().getLastNameError());
else
Assert.assertEquals("Please specify last name.", updateProfilePage.getInputErrors().getLastNameError());
events.assertEmpty();
}

View file

@ -0,0 +1,502 @@
/*
* Copyright 2021 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.actions;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.keycloak.testsuite.forms.VerifyProfileTest.PERMISSIONS_ALL;
import static org.keycloak.testsuite.forms.VerifyProfileTest.PERMISSIONS_ADMIN_EDITABLE;
import static org.keycloak.testsuite.forms.VerifyProfileTest.PERMISSIONS_ADMIN_ONLY;
import static org.keycloak.testsuite.forms.VerifyProfileTest.SCOPE_DEPARTMENT;
import static org.keycloak.testsuite.forms.VerifyProfileTest.VALIDATIONS_LENGTH;
import static org.keycloak.testsuite.forms.VerifyProfileTest.ATTRIBUTE_DEPARTMENT;
import static org.keycloak.testsuite.forms.VerifyProfileTest.CONFIGURATION_FOR_USER_EDIT;
import java.util.ArrayList;
import java.util.Collections;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.keycloak.OAuth2Constants;
import org.keycloak.events.Details;
import org.keycloak.events.EventType;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.testsuite.forms.VerifyProfileTest;
import org.keycloak.testsuite.pages.AppPage.RequestType;
import org.keycloak.testsuite.util.ClientScopeBuilder;
import org.keycloak.testsuite.util.KeycloakModelUtils;
import org.openqa.selenium.By;
/**
*
* @author Vlastimil Elias <velias@redhat.com>
*
*/
public class RequiredActionUpdateProfileWithUserProfileTest extends RequiredActionUpdateProfileTest {
protected static final String PASSWORD = "password";
protected static final String USERNAME1 = "test-user@localhost";
private static ClientRepresentation client_scope_default;
private static ClientRepresentation client_scope_optional;
@Override
protected boolean isDynamicForm() {
return true;
}
@Override
public void configureTestRealm(RealmRepresentation testRealm) {
super.configureTestRealm(testRealm);
VerifyProfileTest.enableDynamicUserProfile(testRealm);
testRealm.setClientScopes(new ArrayList<>());
testRealm.getClientScopes().add(ClientScopeBuilder.create().name(SCOPE_DEPARTMENT).protocol("openid-connect").build());
testRealm.getClientScopes().add(ClientScopeBuilder.create().name("profile").protocol("openid-connect").build());
client_scope_default = KeycloakModelUtils.createClient(testRealm, "client-a");
client_scope_default.setDefaultClientScopes(Collections.singletonList(SCOPE_DEPARTMENT));
client_scope_default.setRedirectUris(Collections.singletonList("*"));
client_scope_optional = KeycloakModelUtils.createClient(testRealm, "client-b");
client_scope_optional.setOptionalClientScopes(Collections.singletonList(SCOPE_DEPARTMENT));
client_scope_optional.setRedirectUris(Collections.singletonList("*"));
}
@Before
public void beforeTest() {
VerifyProfileTest.setUserProfileConfiguration(testRealm(),null);
super.beforeTest();
}
@Test
public void testDisplayName() {
setUserProfileConfiguration("{\"attributes\": ["
+ "{\"name\": \"firstName\",\"displayName\":\"${firstName}\"," + PERMISSIONS_ALL + ", \"required\": {}},"
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + "},"
+ "{\"name\": \"department\", \"displayName\" : \"Department\", " + PERMISSIONS_ALL + ", \"required\":{}}"
+ "]}");
loginPage.open();
loginPage.login(USERNAME1, PASSWORD);
updateProfilePage.assertCurrent();
//assert field names
// i18n replaced
Assert.assertEquals("First name",updateProfilePage.getLabelForField("firstName"));
// attribute name used if no display name set
Assert.assertEquals("lastName",updateProfilePage.getLabelForField("lastName"));
// direct value in display name
Assert.assertEquals("Department",updateProfilePage.getLabelForField("department"));
}
@Test
public void testAttributeGuiOrder() {
setUserProfileConfiguration("{\"attributes\": ["
+ "{\"name\": \"lastName\"," + VerifyProfileTest.PERMISSIONS_ALL + "},"
+ "{\"name\": \"department\", " + VerifyProfileTest.PERMISSIONS_ALL + ", \"required\":{}},"
+ "{\"name\": \"username\", " + VerifyProfileTest.PERMISSIONS_ALL + "},"
+ "{\"name\": \"firstName\"," + VerifyProfileTest.PERMISSIONS_ALL + ", \"required\": {}},"
+ "{\"name\": \"email\", " + VerifyProfileTest.PERMISSIONS_ALL + "}"
+ "]}");
loginPage.open();
loginPage.login(USERNAME1, PASSWORD);
updateProfilePage.assertCurrent();
//assert fields location in form
Assert.assertTrue(
driver.findElement(
By.cssSelector("form#kc-update-profile-form > div:nth-child(1) > div:nth-child(2) > input#lastName")
).isDisplayed()
);
Assert.assertTrue(
driver.findElement(
By.cssSelector("form#kc-update-profile-form > div:nth-child(2) > div:nth-child(2) > input#department")
).isDisplayed()
);
Assert.assertTrue(
driver.findElement(
By.cssSelector("form#kc-update-profile-form > div:nth-child(3) > div:nth-child(2) > input#username")
).isDisplayed()
);
Assert.assertTrue(
driver.findElement(
By.cssSelector("form#kc-update-profile-form > div:nth-child(4) > div:nth-child(2) > input#firstName")
).isDisplayed()
);
Assert.assertTrue(
driver.findElement(
By.cssSelector("form#kc-update-profile-form > div:nth-child(5) > div:nth-child(2) > input#email")
).isDisplayed()
);
}
@Test
public void testUsernameOnlyIfEditAllowed() {
RealmRepresentation realm = testRealm().toRepresentation();
boolean r = realm.isEditUsernameAllowed();
try {
realm.setEditUsernameAllowed(false);
testRealm().update(realm);
loginPage.open();
loginPage.login(USERNAME1, PASSWORD);
assertFalse(updateProfilePage.isUsernamePresent());
realm.setEditUsernameAllowed(true);
testRealm().update(realm);
driver.navigate().refresh();
assertTrue(updateProfilePage.isUsernamePresent());
} finally {
realm.setEditUsernameAllowed(r);
testRealm().update(realm);
}
}
@Test
public void testOptionalAttribute() {
setUserProfileConfiguration("{\"attributes\": ["
+ "{\"name\": \"firstName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + "}"
+ "]}");
loginPage.open();
loginPage.login(USERNAME1, PASSWORD);
updateProfilePage.assertCurrent();
assertFalse(updateProfilePage.isCancelDisplayed());
updateProfilePage.update("New first", "", "new@email.com", USERNAME1);
events.expectRequiredAction(EventType.UPDATE_PROFILE).detail(Details.PREVIOUS_FIRST_NAME, "Tom").detail(Details.UPDATED_FIRST_NAME, "New first")
.detail(Details.PREVIOUS_LAST_NAME, "Brady")
.detail(Details.PREVIOUS_EMAIL, USERNAME1).detail(Details.UPDATED_EMAIL, "new@email.com")
.assertEvent();
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
events.expectLogin().assertEvent();
// assert user is really updated in persistent store
UserRepresentation user = ActionUtil.findUserWithAdminClient(adminClient, USERNAME1);
Assert.assertEquals("New first", user.getFirstName());
Assert.assertEquals("", user.getLastName());
Assert.assertEquals("new@email.com", user.getEmail());
Assert.assertEquals(USERNAME1, user.getUsername());
}
@Test
public void testCustomValidationLastName() {
setUserProfileConfiguration(CONFIGURATION_FOR_USER_EDIT);
updateUserByUsername(USERNAME1, "ExistingFirst", "La", "Department");
setUserProfileConfiguration("{\"attributes\": ["
+ "{\"name\": \"firstName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL +","+VALIDATIONS_LENGTH + "},"
+ "{\"name\": \"department\"," + PERMISSIONS_ADMIN_ONLY + "}"
+ "]}");
loginPage.open();
loginPage.login(USERNAME1, PASSWORD);
updateProfilePage.assertCurrent();
//submit with error
updateProfilePage.update("First", "L", USERNAME1, USERNAME1);
updateProfilePage.assertCurrent();
//submit OK
updateProfilePage.update("First", "Last", USERNAME1, USERNAME1);
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
UserRepresentation user = getUserByUsername(USERNAME1);
assertEquals("First", user.getFirstName());
assertEquals("Last", user.getLastName());
//check that not configured attribute is unchanged
assertEquals("Department", user.firstAttribute(ATTRIBUTE_DEPARTMENT));
}
@Test
public void testRequiredReadOnlyAttribute() {
setUserProfileConfiguration("{\"attributes\": ["
+ "{\"name\": \"firstName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + "},"
+ "{\"name\": \"department\"," + PERMISSIONS_ADMIN_EDITABLE + ", \"required\":{}}"
+ "]}");
loginPage.open();
loginPage.login(USERNAME1, PASSWORD);
updateProfilePage.assertCurrent();
Assert.assertEquals("Brady", updateProfilePage.getLastName());
Assert.assertFalse(updateProfilePage.isDepartmentEnabled());
//update of the other attributes must be successful in this case
updateProfilePage.update("First", "Last", USERNAME1, USERNAME1);
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
UserRepresentation user = getUserByUsername(USERNAME1);
assertEquals("First", user.getFirstName());
assertEquals("Last", user.getLastName());
}
@Test
public void testAttributeNotVisible() {
setUserProfileConfiguration("{\"attributes\": ["
+ "{\"name\": \"firstName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + "},"
+ "{\"name\": \"department\"," + PERMISSIONS_ADMIN_ONLY + ", \"required\":{}}"
+ "]}");
loginPage.open();
loginPage.login(USERNAME1, PASSWORD);
updateProfilePage.assertCurrent();
Assert.assertEquals("Brady", updateProfilePage.getLastName());
Assert.assertFalse("'department' field is visible" , updateProfilePage.isDepartmentPresent());
//update of the other attributes must be successful in this case
updateProfilePage.update("First", "Last", USERNAME1, USERNAME1);
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
UserRepresentation user = getUserByUsername(USERNAME1);
assertEquals("First", user.getFirstName());
assertEquals("Last", user.getLastName());
}
@Test
public void testRequiredAttribute() {
setUserProfileConfiguration("{\"attributes\": ["
+ "{\"name\": \"firstName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + "},"
+ "{\"name\": \"department\"," + PERMISSIONS_ALL + ", \"required\":{}}"
+ "]}");
loginPage.open();
loginPage.login(USERNAME1, PASSWORD);
updateProfilePage.assertCurrent();
//submit with error
updateProfilePage.updateWithDepartment("FirstCC", "LastCC", "", USERNAME1, USERNAME1);
updateProfilePage.assertCurrent();
//submit OK
updateProfilePage.updateWithDepartment("FirstCC", "LastCC", "DepartmentCC", USERNAME1, USERNAME1);
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
UserRepresentation user = getUserByUsername(USERNAME1);
assertEquals("FirstCC", user.getFirstName());
assertEquals("LastCC", user.getLastName());
assertEquals("DepartmentCC", user.firstAttribute(ATTRIBUTE_DEPARTMENT));
}
@Test
public void testAttributeRequiredForScope() {
setUserProfileConfiguration("{\"attributes\": ["
+ "{\"name\": \"firstName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + "},"
+ "{\"name\": \"department\"," + PERMISSIONS_ALL + ", \"required\":{\"scopes\":[\""+SCOPE_DEPARTMENT+"\"]}}"
+ "]}");
oauth.scope(SCOPE_DEPARTMENT).clientId(client_scope_optional.getClientId()).openLoginForm();
loginPage.assertCurrent();
loginPage.login(USERNAME1, PASSWORD);
updateProfilePage.assertCurrent();
//submit with error
updateProfilePage.updateWithDepartment("FirstCC", "LastCC", "", USERNAME1, USERNAME1);
updateProfilePage.assertCurrent();
//submit OK
updateProfilePage.updateWithDepartment("FirstCC", "LastCC", "DepartmentCC", USERNAME1, USERNAME1);
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
UserRepresentation user = getUserByUsername(USERNAME1);
assertEquals("FirstCC", user.getFirstName());
assertEquals("LastCC", user.getLastName());
assertEquals("DepartmentCC", user.firstAttribute(ATTRIBUTE_DEPARTMENT));
}
@Test
public void testAttributeRequiredForDefaultScope() {
setUserProfileConfiguration("{\"attributes\": ["
+ "{\"name\": \"firstName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + "},"
+ "{\"name\": \"department\"," + PERMISSIONS_ALL + ", \"required\":{\"scopes\":[\""+SCOPE_DEPARTMENT+"\"]}}"
+ "]}");
oauth.clientId(client_scope_default.getClientId()).openLoginForm();
loginPage.assertCurrent();
loginPage.login(USERNAME1, PASSWORD);
updateProfilePage.assertCurrent();
//submit with error
updateProfilePage.updateWithDepartment("FirstCC", "LastCC", "", USERNAME1, USERNAME1);
updateProfilePage.assertCurrent();
//submit OK
updateProfilePage.updateWithDepartment("FirstCC", "LastCC", "DepartmentCC", USERNAME1, USERNAME1);
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
UserRepresentation user = getUserByUsername(USERNAME1);
assertEquals("FirstCC", user.getFirstName());
assertEquals("LastCC", user.getLastName());
assertEquals("DepartmentCC", user.firstAttribute(ATTRIBUTE_DEPARTMENT));
}
@Test
public void testAttributeRequiredAndSelectedByScope() {
setUserProfileConfiguration("{\"attributes\": ["
+ "{\"name\": \"firstName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + "},"
+ "{\"name\": \"department\"," + PERMISSIONS_ALL + ", \"required\":{}, \"selector\":{\"scopes\":[\""+SCOPE_DEPARTMENT+"\"]}}"
+ "]}");
oauth.scope(SCOPE_DEPARTMENT).clientId(client_scope_optional.getClientId()).openLoginForm();
loginPage.assertCurrent();
loginPage.login(USERNAME1, PASSWORD);
updateProfilePage.assertCurrent();
//submit with error
updateProfilePage.updateWithDepartment("FirstCC", "LastCC", "", USERNAME1, USERNAME1);
updateProfilePage.assertCurrent();
//submit OK
updateProfilePage.updateWithDepartment("FirstCC", "LastCC", "DepartmentCC", USERNAME1, USERNAME1);
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
UserRepresentation user = getUserByUsername(USERNAME1);
assertEquals("FirstCC", user.getFirstName());
assertEquals("LastCC", user.getLastName());
assertEquals("DepartmentCC", user.firstAttribute(ATTRIBUTE_DEPARTMENT));
}
@Test
public void testAttributeNotRequiredAndSelectedByScopeCanBeUpdated() {
setUserProfileConfiguration("{\"attributes\": ["
+ "{\"name\": \"firstName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
+ "{\"name\": \"department\"," + PERMISSIONS_ALL + ", \"selector\":{\"scopes\":[\""+SCOPE_DEPARTMENT+"\"]}}"
+ "]}");
oauth.scope(SCOPE_DEPARTMENT).clientId(client_scope_optional.getClientId()).openLoginForm();
loginPage.assertCurrent();
loginPage.login(USERNAME1, PASSWORD);
updateProfilePage.assertCurrent();
Assert.assertTrue(updateProfilePage.isDepartmentPresent());
updateProfilePage.updateWithDepartment("FirstCC", "LastCC", "DepartmentCC", USERNAME1, USERNAME1);
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
UserRepresentation user = getUserByUsername(USERNAME1);
assertEquals("FirstCC", user.getFirstName());
assertEquals("LastCC", user.getLastName());
assertEquals("DepartmentCC", user.firstAttribute(ATTRIBUTE_DEPARTMENT));
}
@Test
public void testAttributeRequiredButNotSelectedByScopeIsNotRendered() {
setUserProfileConfiguration("{\"attributes\": ["
+ "{\"name\": \"firstName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
+ "{\"name\": \"department\"," + PERMISSIONS_ALL + ", \"required\":{}, \"selector\":{\"scopes\":[\""+SCOPE_DEPARTMENT+"\"]}}"
+ "]}");
oauth.clientId(client_scope_optional.getClientId()).openLoginForm();
loginPage.assertCurrent();
loginPage.login(USERNAME1, PASSWORD);
updateProfilePage.assertCurrent();
Assert.assertFalse(updateProfilePage.isDepartmentPresent());
updateProfilePage.update("FirstCC", "LastCC", USERNAME1, USERNAME1);
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
UserRepresentation user = getUserByUsername(USERNAME1);
assertEquals("FirstCC", user.getFirstName());
assertEquals("LastCC", user.getLastName());
}
protected void setUserProfileConfiguration(String configuration) {
VerifyProfileTest.setUserProfileConfiguration(testRealm(), configuration);
}
protected UserRepresentation getUserByUsername(String username) {
return VerifyProfileTest.getUserByUsername(testRealm(), username);
}
protected void updateUserByUsername(String username, String firstName, String lastName, String department) {
UserRepresentation ur = getUserByUsername(username);
ur.setFirstName(firstName);
ur.setLastName(lastName);
ur.singleAttribute(ATTRIBUTE_DEPARTMENT, department);
testRealm().users().get(ur.getId()).update(ur);
}
}

View file

@ -17,104 +17,76 @@
package org.keycloak.testsuite.forms;
import static org.junit.Assert.assertEquals;
import static org.keycloak.userprofile.DeclarativeUserProfileProvider.REALM_USER_PROFILE_ENABLED;
import static org.keycloak.testsuite.forms.VerifyProfileTest.PERMISSIONS_ALL;
import static org.keycloak.testsuite.forms.VerifyProfileTest.PERMISSIONS_ADMIN_EDITABLE;
import static org.keycloak.testsuite.forms.VerifyProfileTest.SCOPE_DEPARTMENT;
import static org.keycloak.testsuite.forms.VerifyProfileTest.VALIDATIONS_LENGTH;
import static org.keycloak.testsuite.forms.VerifyProfileTest.ATTRIBUTE_DEPARTMENT;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import javax.ws.rs.core.Response;
import org.jboss.arquillian.graphene.page.Page;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Before;
import org.junit.Test;
import org.keycloak.OAuth2Constants;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
import org.keycloak.testsuite.AssertEvents;
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude;
import org.keycloak.testsuite.pages.AccountUpdateProfilePage;
import org.keycloak.testsuite.pages.AppPage;
import org.keycloak.testsuite.pages.AppPage.RequestType;
import org.keycloak.testsuite.pages.LoginPage;
import org.keycloak.testsuite.pages.RegisterPage;
import org.keycloak.testsuite.pages.VerifyEmailPage;
import org.keycloak.testsuite.util.ClientScopeBuilder;
import org.keycloak.testsuite.util.GreenMailRule;
import org.keycloak.testsuite.util.KeycloakModelUtils;
import org.openqa.selenium.By;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
* @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
* @author Vlastimil Elias <velias@redhat.com>
*/
@AuthServerContainerExclude(AuthServerContainerExclude.AuthServer.REMOTE)
public class RegisterWithUserProfileTest extends AbstractTestRealmKeycloakTest {
@Rule
public AssertEvents events = new AssertEvents(this);
@Page
protected AppPage appPage;
@Page
protected LoginPage loginPage;
@Page
protected RegisterPage registerPage;
@Page
protected VerifyEmailPage verifyEmailPage;
@Page
protected AccountUpdateProfilePage accountPage;
@Rule
public GreenMailRule greenMail = new GreenMailRule();
public class RegisterWithUserProfileTest extends RegisterTest {
private static final String SCOPE_LAST_NAME = "lastName";
private static ClientRepresentation client_scope_default;
private static ClientRepresentation client_scope_optional;
public static String UP_CONFIG_BASIC_ATTRIBUTES = "{\"name\": \"username\"," + VerifyProfileTest.PERMISSIONS_ALL + ", \"required\": {}},"
+ "{\"name\": \"email\"," + VerifyProfileTest.PERMISSIONS_ALL + ", \"required\": {}},";
public static String UP_CONFIG_BASIC_ATTRIBUTES = "{\"name\": \"username\"," + PERMISSIONS_ALL + ", \"required\": {}},"
+ "{\"name\": \"email\"," + PERMISSIONS_ALL + ", \"required\": {}},";
@Override
public void configureTestRealm(RealmRepresentation testRealm) {
super.configureTestRealm(testRealm);
VerifyProfileTest.enableDynamicUserProfile(testRealm);
testRealm.setClientScopes(new ArrayList<>());
testRealm.getClientScopes().add(ClientScopeBuilder.create().name(SCOPE_LAST_NAME).protocol("openid-connect").build());
testRealm.getClientScopes().add(ClientScopeBuilder.create().name(VerifyProfileTest.SCOPE_DEPARTMENT).protocol("openid-connect").build());
testRealm.getClientScopes().add(ClientScopeBuilder.create().name(SCOPE_DEPARTMENT).protocol("openid-connect").build());
List<String> scopes = new ArrayList<>();
scopes.add(SCOPE_LAST_NAME);
scopes.add(VerifyProfileTest.SCOPE_DEPARTMENT);
scopes.add(SCOPE_DEPARTMENT);
client_scope_default = KeycloakModelUtils.createClient(testRealm, "client-a");
client_scope_default.setDefaultClientScopes(scopes);
client_scope_default.setRedirectUris(Collections.singletonList("*"));
client_scope_optional = KeycloakModelUtils.createClient(testRealm, "client-b");
client_scope_optional.setOptionalClientScopes(scopes);
client_scope_optional.setRedirectUris(Collections.singletonList("*"));
if (testRealm.getAttributes() == null) {
testRealm.setAttributes(new HashMap<>());
}
testRealm.getAttributes().put(REALM_USER_PROFILE_ENABLED, Boolean.TRUE.toString());
client_scope_optional.setRedirectUris(Collections.singletonList("*"));
}
@Before
public void beforeTest() {
VerifyProfileTest.setUserProfileConfiguration(testRealm(),null);
}
@Test
public void testRegisterUserSuccess_lastNameOptional() {
setUserProfileConfiguration("{\"attributes\": ["
+ UP_CONFIG_BASIC_ATTRIBUTES
+ "{\"name\": \"firstName\"," + VerifyProfileTest.PERMISSIONS_ALL + ", \"required\": {}},"
+ "{\"name\": \"lastName\"," + VerifyProfileTest.PERMISSIONS_ALL + "}"
+ "{\"name\": \"firstName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + "}"
+ "]}");
loginPage.open();
@ -134,8 +106,8 @@ public class RegisterWithUserProfileTest extends AbstractTestRealmKeycloakTest {
public void testRegisterUserSuccess_lastNameRequiredForScope_notRequested() {
setUserProfileConfiguration("{\"attributes\": ["
+ UP_CONFIG_BASIC_ATTRIBUTES
+ "{\"name\": \"firstName\"," + VerifyProfileTest.PERMISSIONS_ALL + ", \"required\": {}},"
+ "{\"name\": \"lastName\"," + VerifyProfileTest.PERMISSIONS_ALL + ", \"required\":{\"scopes\":[\""+SCOPE_LAST_NAME+"\"]}}"
+ "{\"name\": \"firstName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + ", \"required\":{\"scopes\":[\""+SCOPE_LAST_NAME+"\"]}}"
+ "]}");
loginPage.open();
@ -155,8 +127,8 @@ public class RegisterWithUserProfileTest extends AbstractTestRealmKeycloakTest {
public void testRegisterUserSuccess_lastNameRequiredForScope_requested() {
setUserProfileConfiguration("{\"attributes\": ["
+ UP_CONFIG_BASIC_ATTRIBUTES
+ "{\"name\": \"firstName\"," + VerifyProfileTest.PERMISSIONS_ALL + ", \"required\": {}},"
+ "{\"name\": \"lastName\"," + VerifyProfileTest.PERMISSIONS_ALL + ", \"required\":{\"scopes\":[\""+SCOPE_LAST_NAME+"\"]}}"
+ "{\"name\": \"firstName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + ", \"required\":{\"scopes\":[\""+SCOPE_LAST_NAME+"\"]}}"
+ "]}");
oauth.scope(SCOPE_LAST_NAME).clientId(client_scope_optional.getClientId()).openLoginForm();
@ -181,8 +153,8 @@ public class RegisterWithUserProfileTest extends AbstractTestRealmKeycloakTest {
public void testRegisterUserSuccess_lastNameRequiredForScope_clientDefault() {
setUserProfileConfiguration("{\"attributes\": ["
+ UP_CONFIG_BASIC_ATTRIBUTES
+ "{\"name\": \"firstName\"," + VerifyProfileTest.PERMISSIONS_ALL + ", \"required\": {}},"
+ "{\"name\": \"lastName\"," + VerifyProfileTest.PERMISSIONS_ALL + ", \"required\":{\"scopes\":[\""+SCOPE_LAST_NAME+"\"]}}"
+ "{\"name\": \"firstName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + ", \"required\":{\"scopes\":[\""+SCOPE_LAST_NAME+"\"]}}"
+ "]}");
oauth.clientId(client_scope_default.getClientId()).openLoginForm();
@ -207,8 +179,8 @@ public class RegisterWithUserProfileTest extends AbstractTestRealmKeycloakTest {
public void testRegisterUserSuccess_lastNameLengthValidation() {
setUserProfileConfiguration("{\"attributes\": ["
+ UP_CONFIG_BASIC_ATTRIBUTES
+ "{\"name\": \"firstName\"," + VerifyProfileTest.PERMISSIONS_ALL + ", \"required\": {}},"
+ "{\"name\": \"lastName\"," + VerifyProfileTest.PERMISSIONS_ALL + ", " + VerifyProfileTest.VALIDATIONS_LENGTH + "}"
+ "{\"name\": \"firstName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + ", " + VALIDATIONS_LENGTH + "}"
+ "]}");
loginPage.open();
@ -228,8 +200,8 @@ public class RegisterWithUserProfileTest extends AbstractTestRealmKeycloakTest {
public void testRegisterUserInvalidLastNameLength() {
setUserProfileConfiguration("{\"attributes\": ["
+ UP_CONFIG_BASIC_ATTRIBUTES
+ "{\"name\": \"firstName\"," + VerifyProfileTest.PERMISSIONS_ALL + ", \"required\": {}},"
+ "{\"name\": \"lastName\"," + VerifyProfileTest.PERMISSIONS_ALL + ", " + VerifyProfileTest.VALIDATIONS_LENGTH + "}"
+ "{\"name\": \"firstName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + ", " + VALIDATIONS_LENGTH + "}"
+ "]}");
loginPage.open();
@ -248,10 +220,10 @@ public class RegisterWithUserProfileTest extends AbstractTestRealmKeycloakTest {
@Test
public void testAttributeDisplayName() {
setUserProfileConfiguration("{\"attributes\": ["
+ "{\"name\": \"firstName\",\"displayName\":\"${firstName}\"," + VerifyProfileTest.PERMISSIONS_ALL + ", \"required\": {}},"
+ "{\"name\": \"lastName\"," + VerifyProfileTest.PERMISSIONS_ALL + "},"
+ "{\"name\": \"department\", \"displayName\" : \"Department\", " + VerifyProfileTest.PERMISSIONS_ALL + ", \"required\":{}}"
setUserProfileConfiguration("{\"attributes\": ["
+ "{\"name\": \"firstName\",\"displayName\":\"${firstName}\"," + PERMISSIONS_ALL + ", \"required\": {}},"
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + "},"
+ "{\"name\": \"department\", \"displayName\" : \"Department\", " + PERMISSIONS_ALL + ", \"required\":{}}"
+ "]}");
loginPage.open();
@ -325,10 +297,10 @@ public class RegisterWithUserProfileTest extends AbstractTestRealmKeycloakTest {
@Test
public void testRegisterUserSuccess_requiredReadOnlyAttributeNotRenderedAndNotBlockingRegistration() {
setUserProfileConfiguration("{\"attributes\": ["
+ "{\"name\": \"firstName\",\"displayName\":\"${firstName}\"," + VerifyProfileTest.PERMISSIONS_ALL + ", \"required\": {}},"
+ "{\"name\": \"lastName\"," + VerifyProfileTest.PERMISSIONS_ALL + "},"
+ "{\"name\": \"department\", \"displayName\" : \"Department\", " + VerifyProfileTest.PERMISSIONS_ADMIN_EDITABLE + ", \"required\":{}}"
setUserProfileConfiguration("{\"attributes\": ["
+ "{\"name\": \"firstName\",\"displayName\":\"${firstName}\"," + PERMISSIONS_ALL + ", \"required\": {}},"
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + "},"
+ "{\"name\": \"department\", \"displayName\" : \"Department\", " + PERMISSIONS_ADMIN_EDITABLE + ", \"required\":{}}"
+ "]}");
loginPage.open();
@ -349,13 +321,13 @@ public class RegisterWithUserProfileTest extends AbstractTestRealmKeycloakTest {
@Test
public void testRegisterUserSuccess_attributeRequiredAndSelectedByScopeMustBeSet() {
setUserProfileConfiguration("{\"attributes\": ["
+ "{\"name\": \"firstName\"," + VerifyProfileTest.PERMISSIONS_ALL + ", \"required\": {}},"
+ "{\"name\": \"lastName\"," + VerifyProfileTest.PERMISSIONS_ALL + "},"
+ "{\"name\": \"department\"," + VerifyProfileTest.PERMISSIONS_ALL + ", \"required\":{}, \"selector\":{\"scopes\":[\""+VerifyProfileTest.SCOPE_DEPARTMENT+"\"]}}"
setUserProfileConfiguration("{\"attributes\": ["
+ "{\"name\": \"firstName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + "},"
+ "{\"name\": \"department\"," + PERMISSIONS_ALL + ", \"required\":{}, \"selector\":{\"scopes\":[\""+SCOPE_DEPARTMENT+"\"]}}"
+ "]}");
oauth.scope(VerifyProfileTest.SCOPE_DEPARTMENT).clientId(client_scope_optional.getClientId()).openLoginForm();
oauth.scope(SCOPE_DEPARTMENT).clientId(client_scope_optional.getClientId()).openLoginForm();
loginPage.clickRegister();
registerPage.assertCurrent();
@ -368,22 +340,22 @@ public class RegisterWithUserProfileTest extends AbstractTestRealmKeycloakTest {
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
UserRepresentation user = getUserByUsername("attributeRequiredAndSelectedByScopeMustBeSet");
UserRepresentation user = VerifyProfileTest.getUserByUsername(testRealm(),"attributeRequiredAndSelectedByScopeMustBeSet");
assertEquals("FirstAA", user.getFirstName());
assertEquals("LastAA", user.getLastName());
assertEquals("DepartmentAA", user.firstAttribute(VerifyProfileTest.ATTRIBUTE_DEPARTMENT));
assertEquals("DepartmentAA", user.firstAttribute(ATTRIBUTE_DEPARTMENT));
}
@Test
public void testRegisterUserSuccess_attributeNotRequiredAndSelectedByScopeCanBeIgnored() {
setUserProfileConfiguration("{\"attributes\": ["
+ "{\"name\": \"firstName\"," + VerifyProfileTest.PERMISSIONS_ALL + ", \"required\": {}},"
+ "{\"name\": \"lastName\"," + VerifyProfileTest.PERMISSIONS_ALL + ", \"required\": {}},"
+ "{\"name\": \"department\"," + VerifyProfileTest.PERMISSIONS_ALL + ", \"selector\":{\"scopes\":[\""+VerifyProfileTest.SCOPE_DEPARTMENT+"\"]}}"
setUserProfileConfiguration("{\"attributes\": ["
+ "{\"name\": \"firstName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
+ "{\"name\": \"department\"," + PERMISSIONS_ALL + ", \"selector\":{\"scopes\":[\""+SCOPE_DEPARTMENT+"\"]}}"
+ "]}");
oauth.scope(VerifyProfileTest.SCOPE_DEPARTMENT).clientId(client_scope_optional.getClientId()).openLoginForm();
oauth.scope(SCOPE_DEPARTMENT).clientId(client_scope_optional.getClientId()).openLoginForm();
loginPage.clickRegister();
registerPage.assertCurrent();
@ -397,16 +369,16 @@ public class RegisterWithUserProfileTest extends AbstractTestRealmKeycloakTest {
UserRepresentation user = getUser(userId);
assertEquals("FirstAA", user.getFirstName());
assertEquals("LastAA", user.getLastName());
assertEquals("", user.firstAttribute(VerifyProfileTest.ATTRIBUTE_DEPARTMENT));
assertEquals("", user.firstAttribute(ATTRIBUTE_DEPARTMENT));
}
@Test
public void testRegisterUserSuccess_attributeNotRequiredAndSelectedByScopeCanBeSet() {
setUserProfileConfiguration("{\"attributes\": ["
+ "{\"name\": \"firstName\"," + VerifyProfileTest.PERMISSIONS_ALL + ", \"required\": {}},"
+ "{\"name\": \"lastName\"," + VerifyProfileTest.PERMISSIONS_ALL + ", \"required\": {}},"
+ "{\"name\": \"department\"," + VerifyProfileTest.PERMISSIONS_ALL + ", \"selector\":{\"scopes\":[\""+VerifyProfileTest.SCOPE_DEPARTMENT+"\"]}}"
setUserProfileConfiguration("{\"attributes\": ["
+ "{\"name\": \"firstName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
+ "{\"name\": \"department\"," + PERMISSIONS_ALL + ", \"selector\":{\"scopes\":[\""+SCOPE_DEPARTMENT+"\"]}}"
+ "]}");
oauth.clientId(client_scope_default.getClientId()).openLoginForm();
@ -423,16 +395,16 @@ public class RegisterWithUserProfileTest extends AbstractTestRealmKeycloakTest {
UserRepresentation user = getUser(userId);
assertEquals("FirstAA", user.getFirstName());
assertEquals("LastAA", user.getLastName());
assertEquals("Department AA", user.firstAttribute(VerifyProfileTest.ATTRIBUTE_DEPARTMENT));
assertEquals("Department AA", user.firstAttribute(ATTRIBUTE_DEPARTMENT));
}
@Test
public void testRegisterUserSuccess_attributeRequiredButNotSelectedByScopeIsNotRenderedAndNotBlockingRegistration() {
setUserProfileConfiguration("{\"attributes\": ["
+ "{\"name\": \"firstName\"," + VerifyProfileTest.PERMISSIONS_ALL + ", \"required\": {}},"
+ "{\"name\": \"lastName\"," + VerifyProfileTest.PERMISSIONS_ALL + ", \"required\": {}},"
+ "{\"name\": \"department\"," + VerifyProfileTest.PERMISSIONS_ALL + ", \"required\":{}, \"selector\":{\"scopes\":[\""+VerifyProfileTest.SCOPE_DEPARTMENT+"\"]}}"
setUserProfileConfiguration("{\"attributes\": ["
+ "{\"name\": \"firstName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
+ "{\"name\": \"department\"," + PERMISSIONS_ALL + ", \"required\":{}, \"selector\":{\"scopes\":[\""+SCOPE_DEPARTMENT+"\"]}}"
+ "]}");
oauth.clientId(client_scope_optional.getClientId()).openLoginForm();
@ -449,7 +421,7 @@ public class RegisterWithUserProfileTest extends AbstractTestRealmKeycloakTest {
UserRepresentation user = getUser(userId);
assertEquals("FirstAA", user.getFirstName());
assertEquals("LastAA", user.getLastName());
assertEquals(null, user.firstAttribute(VerifyProfileTest.ATTRIBUTE_DEPARTMENT));
assertEquals(null, user.firstAttribute(ATTRIBUTE_DEPARTMENT));
}
@ -468,21 +440,7 @@ public class RegisterWithUserProfileTest extends AbstractTestRealmKeycloakTest {
assertEquals(lastName, user.getLastName());
}
protected UserRepresentation getUser(String userId) {
return testRealm().users().get(userId).toRepresentation();
}
protected UserRepresentation getUserByUsername(String username) {
List<UserRepresentation> users = testRealm().users().search(username);
if(users!=null && !users.isEmpty())
return users.get(0);
return null;
}
private void setUserProfileConfiguration(String configuration) {
Response r = testRealm().users().userProfile().update(configuration);
if (r.getStatus() != 200) {
Assert.fail("Configuration not set due to error: " + r.readEntity(String.class));
}
protected void setUserProfileConfiguration(String configuration) {
VerifyProfileTest.setUserProfileConfiguration(testRealm(), configuration);
}
}

View file

@ -1,24 +0,0 @@
package org.keycloak.testsuite.forms;
import static org.keycloak.userprofile.DeclarativeUserProfileProvider.REALM_USER_PROFILE_ENABLED;
import java.util.HashMap;
import org.keycloak.representations.idm.RealmRepresentation;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class UserProfileRegisterTest extends RegisterTest {
@Override
public void configureTestRealm(RealmRepresentation testRealm) {
super.configureTestRealm(testRealm);
if (testRealm.getAttributes() == null) {
testRealm.setAttributes(new HashMap<>());
}
testRealm.getAttributes().put(REALM_USER_PROFILE_ENABLED, Boolean.TRUE.toString());
}
}

View file

@ -36,6 +36,7 @@ import org.junit.Rule;
import org.junit.Test;
import org.keycloak.OAuth2Constants;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.events.Details;
import org.keycloak.events.EventType;
import org.keycloak.models.UserModel;
import org.keycloak.representations.idm.ClientRepresentation;
@ -44,7 +45,6 @@ import org.keycloak.representations.idm.RequiredActionProviderRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
import org.keycloak.testsuite.AssertEvents;
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude;
import org.keycloak.testsuite.pages.AppPage;
import org.keycloak.testsuite.pages.AppPage.RequestType;
import org.keycloak.testsuite.pages.LoginPage;
@ -54,11 +54,11 @@ import org.keycloak.testsuite.util.KeycloakModelUtils;
import org.keycloak.testsuite.util.OAuthClient;
import org.keycloak.testsuite.util.RealmBuilder;
import org.keycloak.testsuite.util.UserBuilder;
import org.openqa.selenium.By;
/**
* @author Vlastimil Elias <velias@redhat.com>
*/
@AuthServerContainerExclude(AuthServerContainerExclude.AuthServer.REMOTE)
public class VerifyProfileTest extends AbstractTestRealmKeycloakTest {
public static final String SCOPE_DEPARTMENT = "department";
@ -70,7 +70,7 @@ public class VerifyProfileTest extends AbstractTestRealmKeycloakTest {
public static String VALIDATIONS_LENGTH = "\"validations\": {\"length\": { \"min\": 3, \"max\": 255 }}";
private static final String CONFIGURATION_FOR_USER_EDIT = "{\"attributes\": ["
public static final String CONFIGURATION_FOR_USER_EDIT = "{\"attributes\": ["
+ "{\"name\": \"firstName\"," + PERMISSIONS_ALL + "},"
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + "},"
+ "{\"name\": \"department\"," + PERMISSIONS_ALL + "}"
@ -94,6 +94,9 @@ public class VerifyProfileTest extends AbstractTestRealmKeycloakTest {
@Override
public void configureTestRealm(RealmRepresentation testRealm) {
enableDynamicUserProfile(testRealm);
UserRepresentation user = UserBuilder.create().id(UUID.randomUUID().toString()).username("login-test").email("login@test.com").enabled(true).password("password").build();
userId = user.getId();
@ -134,10 +137,6 @@ public class VerifyProfileTest extends AbstractTestRealmKeycloakTest {
client_scope_optional = KeycloakModelUtils.createClient(testRealm, "client-b");
client_scope_optional.setOptionalClientScopes(Collections.singletonList(SCOPE_DEPARTMENT));
client_scope_optional.setRedirectUris(Collections.singletonList("*"));
if (testRealm.getAttributes() == null) {
testRealm.setAttributes(new HashMap<>());
}
testRealm.getAttributes().put(REALM_USER_PROFILE_ENABLED, Boolean.TRUE.toString());
}
@Rule
@ -179,9 +178,82 @@ public class VerifyProfileTest extends AbstractTestRealmKeycloakTest {
Assert.assertEquals("lastName",verifyProfilePage.getLabelForField("lastName"));
// direct value in display name
Assert.assertEquals("Department",verifyProfilePage.getLabelForField("department"));
}
@Test
public void testAttributeGuiOrder() {
setUserProfileConfiguration(CONFIGURATION_FOR_USER_EDIT);
updateUser(user5Id, "ExistingFirst", "ExistingLast", null);
setUserProfileConfiguration("{\"attributes\": ["
+ "{\"name\": \"lastName\"," + VerifyProfileTest.PERMISSIONS_ALL + "},"
+ "{\"name\": \"department\", " + VerifyProfileTest.PERMISSIONS_ALL + ", \"required\":{}},"
+ "{\"name\": \"username\", " + VerifyProfileTest.PERMISSIONS_ALL + "},"
+ "{\"name\": \"firstName\"," + VerifyProfileTest.PERMISSIONS_ALL + ", \"required\": {}},"
+ "{\"name\": \"email\", " + VerifyProfileTest.PERMISSIONS_ALL + "}"
+ "]}");
loginPage.open();
loginPage.login("login-test5", "password");
verifyProfilePage.assertCurrent();
//assert fields location in form
Assert.assertTrue(
driver.findElement(
By.cssSelector("form#kc-update-profile-form > div:nth-child(1) > div:nth-child(2) > input#lastName")
).isDisplayed()
);
Assert.assertTrue(
driver.findElement(
By.cssSelector("form#kc-update-profile-form > div:nth-child(2) > div:nth-child(2) > input#department")
).isDisplayed()
);
Assert.assertTrue(
driver.findElement(
By.cssSelector("form#kc-update-profile-form > div:nth-child(3) > div:nth-child(2) > input#username")
).isDisplayed()
);
Assert.assertTrue(
driver.findElement(
By.cssSelector("form#kc-update-profile-form > div:nth-child(4) > div:nth-child(2) > input#firstName")
).isDisplayed()
);
Assert.assertTrue(
driver.findElement(
By.cssSelector("form#kc-update-profile-form > div:nth-child(5) > div:nth-child(2) > input#email")
).isDisplayed()
);
}
@Test
public void testEvents() {
setUserProfileConfiguration(CONFIGURATION_FOR_USER_EDIT);
updateUser(user5Id, "ExistingFirst", "ExistingLast", null);
setUserProfileConfiguration("{\"attributes\": ["
+ "{\"name\": \"firstName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + "},"
+ "{\"name\": \"department\", " + PERMISSIONS_ALL + ", \"required\":{}}"
+ "]}");
loginPage.open();
loginPage.login("login-test5", "password");
verifyProfilePage.assertCurrent();
//event when form is shown
events.expectRequiredAction(EventType.VERIFY_PROFILE).user(user5Id).detail("fields_to_update", "department").assertEvent();
verifyProfilePage.update("First", "Last", "Department");
//event after profile is updated
events.expectRequiredAction(EventType.UPDATE_PROFILE).user(user5Id)
.detail(Details.PREVIOUS_FIRST_NAME, "ExistingFirst").detail(Details.UPDATED_FIRST_NAME, "First")
.detail(Details.PREVIOUS_LAST_NAME, "ExistingLast").detail(Details.UPDATED_LAST_NAME, "Last")
.assertEvent();
}
@Test
public void testDefaultProfile() {
setUserProfileConfiguration(null);
@ -203,8 +275,6 @@ public class VerifyProfileTest extends AbstractTestRealmKeycloakTest {
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
events.expectRequiredAction(EventType.VERIFY_PROFILE).user(userId).assertEvent();
UserRepresentation user = getUser(userId);
assertEquals("First", user.getFirstName());
assertEquals("Last", user.getLastName());
@ -214,6 +284,7 @@ public class VerifyProfileTest extends AbstractTestRealmKeycloakTest {
public void testUsernameOnlyIfEditAllowed() {
RealmRepresentation realm = testRealm().toRepresentation();
boolean r = realm.isEditUsernameAllowed();
try {
setUserProfileConfiguration(null);
@ -231,7 +302,7 @@ public class VerifyProfileTest extends AbstractTestRealmKeycloakTest {
driver.navigate().refresh();
assertTrue(verifyProfilePage.isUsernamePresent());
} finally {
realm.setEditUsernameAllowed(false);
realm.setEditUsernameAllowed(r);
testRealm().update(realm);
}
}
@ -252,8 +323,6 @@ public class VerifyProfileTest extends AbstractTestRealmKeycloakTest {
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
events.expectRequiredAction(EventType.VERIFY_PROFILE).user(user2Id).assertEvent();
UserRepresentation user = getUser(user2Id);
assertEquals("First", user.getFirstName());
assertEquals("", user.getLastName());
@ -285,8 +354,6 @@ public class VerifyProfileTest extends AbstractTestRealmKeycloakTest {
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
events.expectRequiredAction(EventType.VERIFY_PROFILE).user(user5Id).assertEvent();
UserRepresentation user = getUser(user5Id);
assertEquals("First", user.getFirstName());
assertEquals("Last", user.getLastName());
@ -363,8 +430,6 @@ public class VerifyProfileTest extends AbstractTestRealmKeycloakTest {
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
events.expectRequiredAction(EventType.VERIFY_PROFILE).user(user3Id).assertEvent();
UserRepresentation user = getUser(user3Id);
assertEquals("First", user.getFirstName());
assertEquals("Last", user.getLastName());
@ -392,8 +457,6 @@ public class VerifyProfileTest extends AbstractTestRealmKeycloakTest {
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
events.expectRequiredAction(EventType.VERIFY_PROFILE).user(user4Id).assertEvent();
UserRepresentation user = getUser(user4Id);
assertEquals("First", user.getFirstName());
assertEquals("Last", user.getLastName());
@ -423,12 +486,9 @@ public class VerifyProfileTest extends AbstractTestRealmKeycloakTest {
//submit OK
verifyProfilePage.update("FirstCC", "LastCC", "DepartmentCC");
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
events.expectRequiredAction(EventType.VERIFY_PROFILE).user(user5Id).assertEvent();
UserRepresentation user = getUser(user5Id);
assertEquals("FirstCC", user.getFirstName());
assertEquals("LastCC", user.getLastName());
@ -463,8 +523,6 @@ public class VerifyProfileTest extends AbstractTestRealmKeycloakTest {
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
events.expectRequiredAction(EventType.VERIFY_PROFILE).user(user5Id).assertEvent();
UserRepresentation user = getUser(user5Id);
assertEquals("FirstCC", user.getFirstName());
assertEquals("LastCC", user.getLastName());
@ -516,8 +574,6 @@ public class VerifyProfileTest extends AbstractTestRealmKeycloakTest {
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
events.expectRequiredAction(EventType.VERIFY_PROFILE).client(client_scope_optional).user(user5Id).assertEvent();
UserRepresentation user = getUser(user5Id);
assertEquals("FirstAA", user.getFirstName());
@ -552,8 +608,6 @@ public class VerifyProfileTest extends AbstractTestRealmKeycloakTest {
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
events.expectRequiredAction(EventType.VERIFY_PROFILE).client(client_scope_default).user(user5Id).assertEvent();
UserRepresentation user = getUser(user5Id);
assertEquals("FirstBB", user.getFirstName());
@ -630,8 +684,6 @@ public class VerifyProfileTest extends AbstractTestRealmKeycloakTest {
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
events.expectRequiredAction(EventType.VERIFY_PROFILE).client(client_scope_optional).user(user5Id).assertEvent();
UserRepresentation user = getUser(user5Id);
assertEquals("FirstAA", user.getFirstName());
@ -663,8 +715,6 @@ public class VerifyProfileTest extends AbstractTestRealmKeycloakTest {
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
events.expectRequiredAction(EventType.VERIFY_PROFILE).client(client_scope_optional).user(user5Id).assertEvent();
UserRepresentation user = getUser(user5Id);
assertEquals("FirstAA", user.getFirstName());
@ -696,8 +746,6 @@ public class VerifyProfileTest extends AbstractTestRealmKeycloakTest {
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
events.expectRequiredAction(EventType.VERIFY_PROFILE).client(client_scope_optional).user(user5Id).assertEvent();
UserRepresentation user = getUser(user5Id);
assertEquals("FirstAA", user.getFirstName());
@ -732,8 +780,6 @@ public class VerifyProfileTest extends AbstractTestRealmKeycloakTest {
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
events.expectRequiredAction(EventType.VERIFY_PROFILE).user(user5Id).assertEvent();
UserRepresentation user = getUser(user5Id);
assertEquals("FirstCC", user.getFirstName());
@ -761,22 +807,48 @@ public class VerifyProfileTest extends AbstractTestRealmKeycloakTest {
}
protected UserRepresentation getUser(String userId) {
return testRealm().users().get(userId).toRepresentation();
return getUser(testRealm(), userId);
}
protected void updateUser(String userId, String firstName, String lastName, String department) {
UserRepresentation ur = testRealm().users().get(userId).toRepresentation();
updateUser(testRealm(), userId, firstName, lastName, department);
}
protected void setUserProfileConfiguration(String configuration) {
setUserProfileConfiguration(testRealm(), configuration);
}
public static void enableDynamicUserProfile(RealmRepresentation testRealm) {
if (testRealm.getAttributes() == null) {
testRealm.setAttributes(new HashMap<>());
}
testRealm.getAttributes().put(REALM_USER_PROFILE_ENABLED, Boolean.TRUE.toString());
}
public static void setUserProfileConfiguration(RealmResource testRealm, String configuration) {
Response r = testRealm.users().userProfile().update(configuration);
if (r.getStatus() != 200) {
Assert.fail("UserProfile Configuration not set due to error: " + r.readEntity(String.class));
}
}
public static UserRepresentation getUser(RealmResource testRealm, String userId) {
return testRealm.users().get(userId).toRepresentation();
}
public static UserRepresentation getUserByUsername(RealmResource testRealm, String username) {
List<UserRepresentation> users = testRealm.users().search(username);
if(users!=null && !users.isEmpty())
return users.get(0);
return null;
}
public static void updateUser(RealmResource testRealm, String userId, String firstName, String lastName, String department) {
UserRepresentation ur = getUser(testRealm, userId);
ur.setFirstName(firstName);
ur.setLastName(lastName);
ur.singleAttribute(ATTRIBUTE_DEPARTMENT, department);
testRealm().users().get(userId).update(ur);
testRealm.users().get(userId).update(ur);
}
protected void setUserProfileConfiguration(String configuration) {
Response r = testRealm().users().userProfile().update(configuration);
if (r.getStatus() != 200) {
Assert.fail("Configuration not set due to error: " + r.readEntity(String.class));
}
}
}

View file

@ -1,72 +1,54 @@
<#import "template.ftl" as layout>
<#import "user-profile-commons.ftl" as userProfileCommons>
<@layout.registrationLayout displayMessage=messagesPerField.exists('global') displayRequiredFields=true; section>
<#if section = "header">
${msg("registerTitle")}
<#elseif section = "form">
<form id="kc-register-form" class="${properties.kcFormClass!}" action="${url.registrationAction}" method="post">
<#list profile.attributes as attribute>
<div class="${properties.kcFormGroupClass!}">
<div class="${properties.kcLabelWrapperClass!}">
<label for="${attribute.name}" class="${properties.kcLabelClass!}">${advancedMsg(attribute.displayName!'')}</label>
<#if attribute.required>*</#if>
</div>
<div class="${properties.kcInputWrapperClass!}">
<input type="text" id="${attribute.name}" name="${attribute.name}" value="${(attribute.value!'')}"
class="${properties.kcInputClass!}"
aria-invalid="<#if messagesPerField.existsError('${attribute.name}')>true</#if>"
<#if attribute.readOnly>disabled</#if>
<#if attribute.autocomplete??>autocomplete="${attribute.autocomplete}"</#if>
/>
<#if messagesPerField.existsError('${attribute.name}')>
<span id="input-error-${attribute.name}" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
${kcSanitize(messagesPerField.get('${attribute.name}'))?no_esc}
</span>
</#if>
</div>
</div>
<#-- render password fields just under the username or email (if used as username) -->
<#if passwordRequired?? && (attribute.name == 'username' || (attribute.name == 'email' && realm.registrationEmailAsUsername))>
<div class="${properties.kcFormGroupClass!}">
<div class="${properties.kcLabelWrapperClass!}">
<label for="password" class="${properties.kcLabelClass!}">${msg("password")}</label> *
</div>
<div class="${properties.kcInputWrapperClass!}">
<input type="password" id="password" class="${properties.kcInputClass!}" name="password"
autocomplete="new-password"
aria-invalid="<#if messagesPerField.existsError('password','password-confirm')>true</#if>"
/>
<#if messagesPerField.existsError('password')>
<span id="input-error-password" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
${kcSanitize(messagesPerField.get('password'))?no_esc}
</span>
</#if>
</div>
</div>
<div class="${properties.kcFormGroupClass!}">
<div class="${properties.kcLabelWrapperClass!}">
<label for="password-confirm"
class="${properties.kcLabelClass!}">${msg("passwordConfirm")}</label> *
</div>
<div class="${properties.kcInputWrapperClass!}">
<input type="password" id="password-confirm" class="${properties.kcInputClass!}"
name="password-confirm"
aria-invalid="<#if messagesPerField.existsError('password-confirm')>true</#if>"
/>
<#if messagesPerField.existsError('password-confirm')>
<span id="input-error-password-confirm" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
${kcSanitize(messagesPerField.get('password-confirm'))?no_esc}
</span>
</#if>
</div>
</div>
</#if>
</#list>
<@userProfileCommons.userProfileFormFields; callback, attribute>
<#if callback = "afterField">
<#-- render password fields just under the username or email (if used as username) -->
<#if passwordRequired?? && (attribute.name == 'username' || (attribute.name == 'email' && realm.registrationEmailAsUsername))>
<div class="${properties.kcFormGroupClass!}">
<div class="${properties.kcLabelWrapperClass!}">
<label for="password" class="${properties.kcLabelClass!}">${msg("password")}</label> *
</div>
<div class="${properties.kcInputWrapperClass!}">
<input type="password" id="password" class="${properties.kcInputClass!}" name="password"
autocomplete="new-password"
aria-invalid="<#if messagesPerField.existsError('password','password-confirm')>true</#if>"
/>
<#if messagesPerField.existsError('password')>
<span id="input-error-password" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
${kcSanitize(messagesPerField.get('password'))?no_esc}
</span>
</#if>
</div>
</div>
<div class="${properties.kcFormGroupClass!}">
<div class="${properties.kcLabelWrapperClass!}">
<label for="password-confirm"
class="${properties.kcLabelClass!}">${msg("passwordConfirm")}</label> *
</div>
<div class="${properties.kcInputWrapperClass!}">
<input type="password" id="password-confirm" class="${properties.kcInputClass!}"
name="password-confirm"
aria-invalid="<#if messagesPerField.existsError('password-confirm')>true</#if>"
/>
<#if messagesPerField.existsError('password-confirm')>
<span id="input-error-password-confirm" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
${kcSanitize(messagesPerField.get('password-confirm'))?no_esc}
</span>
</#if>
</div>
</div>
</#if>
</#if>
</@userProfileCommons.userProfileFormFields>
<#if recaptchaRequired??>
<div class="form-group">

View file

@ -0,0 +1,28 @@
<#import "template.ftl" as layout>
<#import "user-profile-commons.ftl" as userProfileCommons>
<@layout.registrationLayout displayMessage=messagesPerField.exists('global') displayRequiredFields=true; section>
<#if section = "header">
${msg("loginProfileTitle")}
<#elseif section = "form">
<form id="kc-update-profile-form" class="${properties.kcFormClass!}" action="${url.loginAction}" method="post">
<@userProfileCommons.userProfileFormFields/>
<div class="${properties.kcFormGroupClass!}">
<div id="kc-form-options" class="${properties.kcFormOptionsClass!}">
<div class="${properties.kcFormOptionsWrapperClass!}">
</div>
</div>
<div id="kc-form-buttons" class="${properties.kcFormButtonsClass!}">
<#if isAppInitiatedAction??>
<input class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonLargeClass!}" type="submit" value="${msg("doSubmit")}" />
<button class="${properties.kcButtonClass!} ${properties.kcButtonDefaultClass!} ${properties.kcButtonLargeClass!}" type="submit" name="cancel-aia" value="true" />${msg("doCancel")}</button>
<#else>
<input class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}" type="submit" value="${msg("doSubmit")}" />
</#if>
</div>
</div>
</form>
</#if>
</@layout.registrationLayout>

View file

@ -0,0 +1,26 @@
<#macro userProfileFormFields>
<#list profile.attributes as attribute>
<#nested "beforeField" attribute>
<div class="${properties.kcFormGroupClass!}">
<div class="${properties.kcLabelWrapperClass!}">
<label for="${attribute.name}" class="${properties.kcLabelClass!}">${advancedMsg(attribute.displayName!'')}</label>
<#if attribute.required>*</#if>
</div>
<div class="${properties.kcInputWrapperClass!}">
<input type="text" id="${attribute.name}" name="${attribute.name}" value="${(attribute.value!'')}"
class="${properties.kcInputClass!}"
aria-invalid="<#if messagesPerField.existsError('${attribute.name}')>true</#if>"
<#if attribute.readOnly>disabled</#if>
<#if attribute.autocomplete??>autocomplete="${attribute.autocomplete}"</#if>
/>
<#if messagesPerField.existsError('${attribute.name}')>
<span id="input-error-${attribute.name}" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
${kcSanitize(messagesPerField.get('${attribute.name}'))?no_esc}
</span>
</#if>
</div>
</div>
<#nested "afterField" attribute>
</#list>
</#macro>

View file

@ -1,47 +0,0 @@
<#import "template.ftl" as layout>
<@layout.registrationLayout displayMessage=!messagesPerField.existsError('username','email','firstName','lastName'); section>
<#if section = "header">
${msg("loginProfileTitle")}
<#elseif section = "form">
<form id="kc-update-profile-form" class="${properties.kcFormClass!}" action="${url.loginAction}" method="post">
<#list profile.attributes as attribute>
<div class="${properties.kcFormGroupClass!}">
<div class="${properties.kcLabelWrapperClass!}">
<label for="${attribute.name}" class="${properties.kcLabelClass!}">${advancedMsg(attribute.displayName!'')}</label>
<#if attribute.required>*</#if>
</div>
<div class="${properties.kcInputWrapperClass!}">
<input type="text" id="${attribute.name}" name="${attribute.name}" value="${(attribute.value!'')}"
class="${properties.kcInputClass!}"
aria-invalid="<#if messagesPerField.existsError('${attribute.name}')>true</#if>"
<#if attribute.readOnly>disabled</#if>
/>
<#if messagesPerField.existsError('${attribute.name}')>
<span id="input-error-${attribute.name}" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
${kcSanitize(messagesPerField.get('${attribute.name}'))?no_esc}
</span>
</#if>
</div>
</div>
</#list>
<div class="${properties.kcFormGroupClass!}">
<div id="kc-form-options" class="${properties.kcFormOptionsClass!}">
<div class="${properties.kcFormOptionsWrapperClass!}">
</div>
</div>
<div id="kc-form-buttons" class="${properties.kcFormButtonsClass!}">
<#if isAppInitiatedAction??>
<input class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonLargeClass!}" type="submit" value="${msg("doSubmit")}" />
<button class="${properties.kcButtonClass!} ${properties.kcButtonDefaultClass!} ${properties.kcButtonLargeClass!}" type="submit" name="cancel-aia" value="true" />${msg("doCancel")}</button>
<#else>
<input class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}" type="submit" value="${msg("doSubmit")}" />
</#if>
</div>
</div>
</form>
</#if>
</@layout.registrationLayout>