Merge pull request #1291 from AOEpeople/KEYCLOAK-1305
KEYCLOAK-1305 Add possibility to change username
This commit is contained in:
commit
3d3871b0e6
29 changed files with 302 additions and 22 deletions
|
@ -94,6 +94,9 @@
|
|||
<column name="ADMIN_EVENTS_DETAILS_ENABLED" type="BOOLEAN" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="EDIT_USERNAME_ALLOWED" type="BOOLEAN" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
<createTable tableName="CLIENT_SESSION_AUTH_STATUS">
|
||||
<column name="AUTHENTICATOR" type="VARCHAR(36)">
|
||||
|
|
|
@ -24,6 +24,7 @@ public class RealmRepresentation {
|
|||
protected Boolean rememberMe;
|
||||
protected Boolean verifyEmail;
|
||||
protected Boolean resetPasswordAllowed;
|
||||
protected Boolean editUsernameAllowed;
|
||||
|
||||
protected Boolean userCacheEnabled;
|
||||
protected Boolean realmCacheEnabled;
|
||||
|
@ -328,6 +329,14 @@ public class RealmRepresentation {
|
|||
this.resetPasswordAllowed = resetPassword;
|
||||
}
|
||||
|
||||
public Boolean isEditUsernameAllowed() {
|
||||
return editUsernameAllowed;
|
||||
}
|
||||
|
||||
public void setEditUsernameAllowed(Boolean editUsernameAllowed) {
|
||||
this.editUsernameAllowed = editUsernameAllowed;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public Boolean isSocial() {
|
||||
return social;
|
||||
|
|
|
@ -38,7 +38,7 @@ public class AccountBean {
|
|||
}
|
||||
|
||||
public String getUsername() {
|
||||
return user.getUsername();
|
||||
return profileFormData != null ? profileFormData.getFirst("username") : user.getUsername();
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
|
|
|
@ -46,4 +46,8 @@ public class RealmBean {
|
|||
return realm.getSupportedLocales();
|
||||
}
|
||||
|
||||
public boolean isEditUsernameAllowed() {
|
||||
return realm.isEditUsernameAllowed();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -16,11 +16,11 @@
|
|||
|
||||
<div class="form-group ${messagesPerField.printIfExists('username','has-error')}">
|
||||
<div class="col-sm-2 col-md-2">
|
||||
<label for="username" class="control-label">${msg("username")}</label>
|
||||
<label for="username" class="control-label">${msg("username")}</label> <#if realm.editUsernameAllowed><span class="required">*</span></#if>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-10 col-md-10">
|
||||
<input type="text" class="form-control" id="username" name="username" disabled="disabled" value="${(account.username!'')?html}"/>
|
||||
<input type="text" class="form-control" id="username" name="username" <#if !realm.editUsernameAllowed>disabled="disabled"</#if> value="${(account.username!'')?html}"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -80,7 +80,7 @@ totpStep1=Installieren Sie <a href="https://fedorahosted.org/freeotp/" target="_
|
|||
totpStep2=\u00D6ffnen Sie die Applikation und scannen Sie den Barcode oder geben sie den Code ein.
|
||||
totpStep3=Geben Sie den One-time Code welcher die Applikation generiert hat ein und klicken Sie auf Speichern.
|
||||
|
||||
|
||||
missingUsernameMessage=Bitte geben Sie einen Benutzernamen ein.
|
||||
missingFirstNameMessage=Bitte geben Sie einen Vornamen ein.
|
||||
missingEmailMessage=Bitte geben Sie eine E-Mail Adresse ein.
|
||||
missingLastNameMessage=Bitte geben Sie einen Nachnamen ein.
|
||||
|
|
|
@ -96,6 +96,7 @@ totpStep1=Install <a href="https://fedorahosted.org/freeotp/" target="_blank">Fr
|
|||
totpStep2=Open the application and scan the barcode or enter the key.
|
||||
totpStep3=Enter the one-time code provided by the application and click Save to finish the setup.
|
||||
|
||||
missingUsernameMessage=Please specify username.
|
||||
missingFirstNameMessage=Please specify first name.
|
||||
invalidEmailMessage=Invalid email address.
|
||||
missingLastNameMessage=Please specify last name.
|
||||
|
|
|
@ -198,6 +198,7 @@ module.controller('UserListCtrl', function($scope, realm, User) {
|
|||
module.controller('UserDetailCtrl', function($scope, realm, user, User, UserFederationInstances, $location, Dialog, Notifications) {
|
||||
$scope.realm = realm;
|
||||
$scope.create = !user.id;
|
||||
$scope.editUsername = $scope.create || $scope.realm.editUsernameAllowed;
|
||||
|
||||
if ($scope.create) {
|
||||
$scope.user = { enabled: true, attributes: {} }
|
||||
|
|
|
@ -19,6 +19,13 @@
|
|||
</div>
|
||||
<kc-tooltip>If enabled then username field is hidden from registration form and email is used as username for new user.</kc-tooltip>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="editUsernameAllowed" class="col-md-2 control-label">Edit username</label>
|
||||
<div class="col-md-6">
|
||||
<input ng-model="realm.editUsernameAllowed" name="editUsernameAllowed" id="editUsernameAllowed" onoffswitch />
|
||||
</div>
|
||||
<kc-tooltip>If enabled, the username field is editable, readonly otherwise.</kc-tooltip>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="resetPasswordAllowed" class="col-md-2 control-label">Forget password</label>
|
||||
<div class="col-md-6">
|
||||
|
|
|
@ -25,10 +25,11 @@
|
|||
<div class="col-md-6">
|
||||
<!-- Characters >,<,/,\ are forbidden in username -->
|
||||
<input class="form-control" type="text" id="username" name="username" data-ng-model="user.username" autofocus
|
||||
required ng-pattern="/^[^\<\>\\\/]*$/" data-ng-readonly="!create">
|
||||
required ng-pattern="/^[^\<\>\\\/]*$/" data-ng-readonly="!editUsername">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-md-2 control-label" for="email">Email</label>
|
||||
|
||||
|
|
|
@ -59,6 +59,10 @@ public interface RealmModel extends RoleContainerModel {
|
|||
|
||||
void setRememberMe(boolean rememberMe);
|
||||
|
||||
boolean isEditUsernameAllowed();
|
||||
|
||||
void setEditUsernameAllowed(boolean editUsernameAllowed);
|
||||
|
||||
//--- brute force settings
|
||||
boolean isBruteForceProtected();
|
||||
void setBruteForceProtected(boolean value);
|
||||
|
|
|
@ -20,6 +20,7 @@ public class RealmEntity extends AbstractIdentifiableEntity {
|
|||
private boolean passwordCredentialGrantAllowed;
|
||||
private boolean resetPasswordAllowed;
|
||||
private String passwordPolicy;
|
||||
private boolean editUsernameAllowed;
|
||||
//--- brute force settings
|
||||
private boolean bruteForceProtected;
|
||||
private int maxFailureWaitSeconds;
|
||||
|
@ -150,6 +151,14 @@ public class RealmEntity extends AbstractIdentifiableEntity {
|
|||
this.resetPasswordAllowed = resetPasswordAllowed;
|
||||
}
|
||||
|
||||
public boolean isEditUsernameAllowed() {
|
||||
return editUsernameAllowed;
|
||||
}
|
||||
|
||||
public void setEditUsernameAllowed(boolean editUsernameAllowed) {
|
||||
this.editUsernameAllowed = editUsernameAllowed;
|
||||
}
|
||||
|
||||
public String getPasswordPolicy() {
|
||||
return passwordPolicy;
|
||||
}
|
||||
|
|
|
@ -124,6 +124,7 @@ public class ModelToRepresentation {
|
|||
|
||||
rep.setVerifyEmail(realm.isVerifyEmail());
|
||||
rep.setResetPasswordAllowed(realm.isResetPasswordAllowed());
|
||||
rep.setEditUsernameAllowed(realm.isEditUsernameAllowed());
|
||||
rep.setAccessTokenLifespan(realm.getAccessTokenLifespan());
|
||||
rep.setSsoSessionIdleTimeout(realm.getSsoSessionIdleTimeout());
|
||||
rep.setSsoSessionMaxLifespan(realm.getSsoSessionMaxLifespan());
|
||||
|
|
|
@ -103,6 +103,7 @@ public class RepresentationToModel {
|
|||
if (rep.isRememberMe() != null) newRealm.setRememberMe(rep.isRememberMe());
|
||||
if (rep.isVerifyEmail() != null) newRealm.setVerifyEmail(rep.isVerifyEmail());
|
||||
if (rep.isResetPasswordAllowed() != null) newRealm.setResetPasswordAllowed(rep.isResetPasswordAllowed());
|
||||
if (rep.isEditUsernameAllowed() != null) newRealm.setEditUsernameAllowed(rep.isEditUsernameAllowed());
|
||||
if (rep.getPrivateKey() == null || rep.getPublicKey() == null) {
|
||||
KeycloakModelUtils.generateRealmKeys(newRealm);
|
||||
} else {
|
||||
|
@ -426,6 +427,7 @@ public class RepresentationToModel {
|
|||
if (rep.isRememberMe() != null) realm.setRememberMe(rep.isRememberMe());
|
||||
if (rep.isVerifyEmail() != null) realm.setVerifyEmail(rep.isVerifyEmail());
|
||||
if (rep.isResetPasswordAllowed() != null) realm.setResetPasswordAllowed(rep.isResetPasswordAllowed());
|
||||
if (rep.isEditUsernameAllowed() != null) realm.setEditUsernameAllowed(rep.isEditUsernameAllowed());
|
||||
if (rep.getSslRequired() != null) realm.setSslRequired(SslRequired.valueOf(rep.getSslRequired().toUpperCase()));
|
||||
if (rep.getAccessCodeLifespan() != null) realm.setAccessCodeLifespan(rep.getAccessCodeLifespan());
|
||||
if (rep.getAccessCodeLifespanUserAction() != null) realm.setAccessCodeLifespanUserAction(rep.getAccessCodeLifespanUserAction());
|
||||
|
|
|
@ -270,6 +270,16 @@ public class RealmAdapter implements RealmModel {
|
|||
realm.setResetPasswordAllowed(resetPassword);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEditUsernameAllowed() {
|
||||
return realm.isEditUsernameAllowed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEditUsernameAllowed(boolean editUsernameAllowed) {
|
||||
realm.setEditUsernameAllowed(editUsernameAllowed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PasswordPolicy getPasswordPolicy() {
|
||||
if (passwordPolicy == null) {
|
||||
|
|
|
@ -8,14 +8,12 @@ import org.keycloak.models.AuthenticatorModel;
|
|||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.IdentityProviderMapperModel;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.models.LDAPConstants;
|
||||
import org.keycloak.models.PasswordPolicy;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RequiredCredentialModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserFederationMapperModel;
|
||||
import org.keycloak.models.UserFederationProviderModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.cache.entities.CachedRealm;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
|
||||
|
@ -256,6 +254,18 @@ public class RealmAdapter implements RealmModel {
|
|||
updated.setResetPasswordAllowed(resetPasswordAllowed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEditUsernameAllowed() {
|
||||
if (updated != null) return updated.isEditUsernameAllowed();
|
||||
return cached.isEditUsernameAllowed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEditUsernameAllowed(boolean editUsernameAllowed) {
|
||||
getDelegateForUpdate();
|
||||
updated.setEditUsernameAllowed(editUsernameAllowed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSsoSessionIdleTimeout() {
|
||||
if (updated != null) return updated.getSsoSessionIdleTimeout();
|
||||
|
|
|
@ -42,6 +42,7 @@ public class CachedRealm {
|
|||
private boolean passwordCredentialGrantAllowed;
|
||||
private boolean resetPasswordAllowed;
|
||||
private boolean identityFederationEnabled;
|
||||
private boolean editUsernameAllowed;
|
||||
//--- brute force settings
|
||||
private boolean bruteForceProtected;
|
||||
private int maxFailureWaitSeconds;
|
||||
|
@ -114,6 +115,7 @@ public class CachedRealm {
|
|||
passwordCredentialGrantAllowed = model.isPasswordCredentialGrantAllowed();
|
||||
resetPasswordAllowed = model.isResetPasswordAllowed();
|
||||
identityFederationEnabled = model.isIdentityFederationEnabled();
|
||||
editUsernameAllowed = model.isEditUsernameAllowed();
|
||||
//--- brute force settings
|
||||
bruteForceProtected = model.isBruteForceProtected();
|
||||
maxFailureWaitSeconds = model.getMaxFailureWaitSeconds();
|
||||
|
@ -288,6 +290,10 @@ public class CachedRealm {
|
|||
return resetPasswordAllowed;
|
||||
}
|
||||
|
||||
public boolean isEditUsernameAllowed() {
|
||||
return editUsernameAllowed;
|
||||
}
|
||||
|
||||
public int getSsoSessionIdleTimeout() {
|
||||
return ssoSessionIdleTimeout;
|
||||
}
|
||||
|
|
|
@ -320,6 +320,17 @@ public class RealmAdapter implements RealmModel {
|
|||
em.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEditUsernameAllowed() {
|
||||
return realm.isEditUsernameAllowed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEditUsernameAllowed(boolean editUsernameAllowed) {
|
||||
realm.setEditUsernameAllowed(editUsernameAllowed);
|
||||
em.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNotBefore() {
|
||||
return realm.getNotBefore();
|
||||
|
|
|
@ -59,6 +59,8 @@ public class RealmEntity {
|
|||
protected boolean rememberMe;
|
||||
@Column(name="PASSWORD_POLICY")
|
||||
protected String passwordPolicy;
|
||||
@Column(name="EDIT_USERNAME_ALLOWED")
|
||||
protected boolean editUsernameAllowed;
|
||||
|
||||
@Column(name="SSO_IDLE_TIMEOUT")
|
||||
private int ssoSessionIdleTimeout;
|
||||
|
@ -254,6 +256,14 @@ public class RealmEntity {
|
|||
this.resetPasswordAllowed = resetPasswordAllowed;
|
||||
}
|
||||
|
||||
public boolean isEditUsernameAllowed() {
|
||||
return editUsernameAllowed;
|
||||
}
|
||||
|
||||
public void setEditUsernameAllowed(boolean editUsernameAllowed) {
|
||||
this.editUsernameAllowed = editUsernameAllowed;
|
||||
}
|
||||
|
||||
public int getSsoSessionIdleTimeout() {
|
||||
return ssoSessionIdleTimeout;
|
||||
}
|
||||
|
|
|
@ -254,6 +254,17 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
|
|||
updateRealm();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEditUsernameAllowed() {
|
||||
return realm.isEditUsernameAllowed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEditUsernameAllowed(boolean editUsernameAllowed) {
|
||||
realm.setEditUsernameAllowed(editUsernameAllowed);
|
||||
updateRealm();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PasswordPolicy getPasswordPolicy() {
|
||||
if (passwordPolicy == null) {
|
||||
|
|
|
@ -35,23 +35,35 @@ import org.keycloak.events.EventBuilder;
|
|||
import org.keycloak.events.EventStoreProvider;
|
||||
import org.keycloak.events.EventType;
|
||||
import org.keycloak.login.LoginFormsProvider;
|
||||
import org.keycloak.models.*;
|
||||
import org.keycloak.models.AccountRoles;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ClientSessionModel;
|
||||
import org.keycloak.models.Constants;
|
||||
import org.keycloak.models.FederatedIdentityModel;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.ModelException;
|
||||
import org.keycloak.models.ModelReadOnlyException;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserCredentialModel;
|
||||
import org.keycloak.models.UserCredentialValueModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.models.utils.FormMessage;
|
||||
import org.keycloak.models.utils.ModelToRepresentation;
|
||||
import org.keycloak.models.utils.TimeBasedOTP;
|
||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||
import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
|
||||
import org.keycloak.protocol.oidc.TokenManager;
|
||||
import org.keycloak.protocol.oidc.utils.RedirectUtils;
|
||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.services.ForbiddenException;
|
||||
import org.keycloak.services.Urls;
|
||||
import org.keycloak.services.managers.AppAuthManager;
|
||||
import org.keycloak.services.managers.Auth;
|
||||
import org.keycloak.services.managers.AuthenticationManager;
|
||||
import org.keycloak.services.managers.ClientSessionCode;
|
||||
import org.keycloak.services.messages.Messages;
|
||||
import org.keycloak.services.Urls;
|
||||
import org.keycloak.services.util.CookieHelper;
|
||||
import org.keycloak.services.util.ResolveRelative;
|
||||
import org.keycloak.services.validation.Validation;
|
||||
|
@ -73,7 +85,6 @@ import javax.ws.rs.core.Response;
|
|||
import javax.ws.rs.core.UriBuilder;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
import javax.ws.rs.core.Variant;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URI;
|
||||
import java.util.HashSet;
|
||||
|
@ -414,13 +425,16 @@ public class AccountService {
|
|||
|
||||
UserModel user = auth.getUser();
|
||||
|
||||
List<FormMessage> errors = Validation.validateUpdateProfileForm(formData);
|
||||
List<FormMessage> errors = Validation.validateUpdateProfileForm(realm, formData);
|
||||
if (errors != null && !errors.isEmpty()) {
|
||||
setReferrerOnPage();
|
||||
return account.setErrors(errors).setProfileFormData(formData).createResponse(AccountPages.ACCOUNT);
|
||||
}
|
||||
|
||||
try {
|
||||
if (realm.isEditUsernameAllowed()) {
|
||||
user.setUsername(formData.getFirst("username"));
|
||||
}
|
||||
user.setFirstName(formData.getFirst("firstName"));
|
||||
user.setLastName(formData.getFirst("lastName"));
|
||||
|
||||
|
|
|
@ -186,6 +186,9 @@ public class UsersResource {
|
|||
}
|
||||
|
||||
private void updateUserFromRep(UserModel user, UserRepresentation rep, Set<String> attrsToRemove) {
|
||||
if (realm.isEditUsernameAllowed()) {
|
||||
user.setUsername(rep.getUsername());
|
||||
}
|
||||
user.setEmail(rep.getEmail());
|
||||
user.setFirstName(rep.getFirstName());
|
||||
user.setLastName(rep.getLastName());
|
||||
|
|
|
@ -1,17 +1,16 @@
|
|||
package org.keycloak.services.validation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.ws.rs.core.MultivaluedMap;
|
||||
|
||||
import org.keycloak.models.PasswordPolicy;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.utils.FormMessage;
|
||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||
import org.keycloak.services.messages.Messages;
|
||||
|
||||
import javax.ws.rs.core.MultivaluedMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class Validation {
|
||||
|
||||
public static final String FIELD_PASSWORD_CONFIRM = "password-confirm";
|
||||
|
@ -66,10 +65,17 @@ public class Validation {
|
|||
errors.add(new FormMessage(field, message));
|
||||
}
|
||||
|
||||
|
||||
public static List<FormMessage> validateUpdateProfileForm(MultivaluedMap<String, String> formData) {
|
||||
return validateUpdateProfileForm(null, formData);
|
||||
}
|
||||
|
||||
public static List<FormMessage> validateUpdateProfileForm(RealmModel realm, MultivaluedMap<String, String> formData) {
|
||||
List<FormMessage> errors = new ArrayList<>();
|
||||
|
||||
if (realm != null && realm.isEditUsernameAllowed() && isEmpty(formData.getFirst(FIELD_USERNAME))) {
|
||||
addError(errors, FIELD_USERNAME, Messages.MISSING_USERNAME);
|
||||
}
|
||||
|
||||
if (isEmpty(formData.getFirst(FIELD_FIRST_NAME))) {
|
||||
addError(errors, FIELD_FIRST_NAME, Messages.MISSING_FIRST_NAME);
|
||||
}
|
||||
|
|
|
@ -24,11 +24,9 @@ package org.keycloak.testsuite.account;
|
|||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.account.freemarker.model.ApplicationsBean;
|
||||
import org.keycloak.events.Details;
|
||||
import org.keycloak.events.Event;
|
||||
import org.keycloak.events.EventType;
|
||||
|
@ -155,6 +153,9 @@ public class AccountTest {
|
|||
@Override
|
||||
public void config(RealmManager manager, RealmModel defaultRealm, RealmModel appRealm) {
|
||||
UserModel user = manager.getSession().users().getUserByUsername("test-user@localhost", appRealm);
|
||||
user.setFirstName("Tom");
|
||||
user.setLastName("Brady");
|
||||
user.setEmail("test-user@localhost");
|
||||
|
||||
UserCredentialModel cred = new UserCredentialModel();
|
||||
cred.setType(CredentialRepresentation.PASSWORD);
|
||||
|
@ -393,6 +394,61 @@ public class AccountTest {
|
|||
events.expectAccount(EventType.UPDATE_EMAIL).detail(Details.PREVIOUS_EMAIL, "test-user@localhost").detail(Details.UPDATED_EMAIL, "new@email.com").assertEvent();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void changeUsername() {
|
||||
// allow to edit the username in realm
|
||||
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
appRealm.setEditUsernameAllowed(true);
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
profilePage.open();
|
||||
loginPage.login("test-user@localhost", "password");
|
||||
|
||||
events.expectLogin().client("account").detail(Details.REDIRECT_URI, ACCOUNT_REDIRECT).assertEvent();
|
||||
|
||||
Assert.assertEquals("test-user@localhost", profilePage.getUsername());
|
||||
Assert.assertEquals("Tom", profilePage.getFirstName());
|
||||
Assert.assertEquals("Brady", profilePage.getLastName());
|
||||
Assert.assertEquals("test-user@localhost", profilePage.getEmail());
|
||||
|
||||
// All fields are required, so there should be an error when something is missing.
|
||||
profilePage.updateProfile("", "New first", "New last", "new@email.com");
|
||||
|
||||
Assert.assertEquals("Please specify username.", profilePage.getError());
|
||||
Assert.assertEquals("", profilePage.getUsername());
|
||||
Assert.assertEquals("New first", profilePage.getFirstName());
|
||||
Assert.assertEquals("New last", profilePage.getLastName());
|
||||
Assert.assertEquals("new@email.com", profilePage.getEmail());
|
||||
|
||||
events.assertEmpty();
|
||||
|
||||
profilePage.updateProfile("test-user-new@localhost", "New first", "New last", "new@email.com");
|
||||
|
||||
Assert.assertEquals("Your account has been updated.", profilePage.getSuccess());
|
||||
Assert.assertEquals("test-user-new@localhost", profilePage.getUsername());
|
||||
Assert.assertEquals("New first", profilePage.getFirstName());
|
||||
Assert.assertEquals("New last", profilePage.getLastName());
|
||||
Assert.assertEquals("new@email.com", profilePage.getEmail());
|
||||
|
||||
} finally {
|
||||
// reset user for other tests
|
||||
profilePage.updateProfile("test-user@localhost", "Tom", "Brady", "test-user@localhost");
|
||||
events.clear();
|
||||
|
||||
// reset realm
|
||||
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
appRealm.setEditUsernameAllowed(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setupTotp() {
|
||||
totpPage.open();
|
||||
|
|
|
@ -242,6 +242,7 @@ public class AdminAPITest {
|
|||
if (rep.isRememberMe() != null) Assert.assertEquals(rep.isRememberMe(), storedRealm.isRememberMe());
|
||||
if (rep.isVerifyEmail() != null) Assert.assertEquals(rep.isVerifyEmail(), storedRealm.isVerifyEmail());
|
||||
if (rep.isResetPasswordAllowed() != null) Assert.assertEquals(rep.isResetPasswordAllowed(), storedRealm.isResetPasswordAllowed());
|
||||
if (rep.isEditUsernameAllowed() != null) Assert.assertEquals(rep.isEditUsernameAllowed(), storedRealm.isEditUsernameAllowed());
|
||||
if (rep.getSslRequired() != null) Assert.assertEquals(rep.getSslRequired(), storedRealm.getSslRequired());
|
||||
if (rep.getAccessCodeLifespan() != null) Assert.assertEquals(rep.getAccessCodeLifespan(), storedRealm.getAccessCodeLifespan());
|
||||
if (rep.getAccessCodeLifespanUserAction() != null)
|
||||
|
|
|
@ -71,6 +71,7 @@ public class RealmTest extends AbstractClientTest {
|
|||
rep.setAccessCodeLifespanLogin(1234);
|
||||
rep.setRegistrationAllowed(true);
|
||||
rep.setRegistrationEmailAsUsername(true);
|
||||
rep.setEditUsernameAllowed(true);
|
||||
|
||||
realm.update(rep);
|
||||
|
||||
|
@ -81,16 +82,19 @@ public class RealmTest extends AbstractClientTest {
|
|||
assertEquals(1234, rep.getAccessCodeLifespanLogin().intValue());
|
||||
assertEquals(Boolean.TRUE, rep.isRegistrationAllowed());
|
||||
assertEquals(Boolean.TRUE, rep.isRegistrationEmailAsUsername());
|
||||
assertEquals(Boolean.TRUE, rep.isEditUsernameAllowed());
|
||||
|
||||
// second change
|
||||
rep.setRegistrationAllowed(false);
|
||||
rep.setRegistrationEmailAsUsername(false);
|
||||
rep.setEditUsernameAllowed(false);
|
||||
|
||||
realm.update(rep);
|
||||
|
||||
rep = realm.toRepresentation();
|
||||
assertEquals(Boolean.FALSE, rep.isRegistrationAllowed());
|
||||
assertEquals(Boolean.FALSE, rep.isRegistrationEmailAsUsername());
|
||||
assertEquals(Boolean.FALSE, rep.isEditUsernameAllowed());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
package org.keycloak.testsuite.admin;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.admin.client.resource.IdentityProviderResource;
|
||||
import org.keycloak.admin.client.resource.UserResource;
|
||||
import org.keycloak.representations.idm.ErrorRepresentation;
|
||||
import org.keycloak.representations.idm.FederatedIdentityRepresentation;
|
||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
|
||||
import javax.ws.rs.ClientErrorException;
|
||||
|
@ -410,4 +410,78 @@ public class UserTest extends AbstractClientTest {
|
|||
Assert.assertEquals("invalidClientId not enabled", error.getErrorMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateUserWithNewUsername() {
|
||||
switchEditUsernameAllowedOn();
|
||||
String id = createUser();
|
||||
|
||||
UserResource user = realm.users().get(id);
|
||||
UserRepresentation userRep = user.toRepresentation();
|
||||
userRep.setUsername("user11");
|
||||
user.update(userRep);
|
||||
|
||||
userRep = realm.users().get(id).toRepresentation();
|
||||
assertEquals("user11", userRep.getUsername());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateUserWithNewUsernameNotPossible() {
|
||||
String id = createUser();
|
||||
|
||||
UserResource user = realm.users().get(id);
|
||||
UserRepresentation userRep = user.toRepresentation();
|
||||
userRep.setUsername("user11");
|
||||
user.update(userRep);
|
||||
|
||||
userRep = realm.users().get(id).toRepresentation();
|
||||
assertEquals("user1", userRep.getUsername());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateUserWithNewUsernameAccessingViaOldUsername() {
|
||||
switchEditUsernameAllowedOn();
|
||||
createUser();
|
||||
|
||||
try {
|
||||
UserResource user = realm.users().get("user1");
|
||||
UserRepresentation userRep = user.toRepresentation();
|
||||
userRep.setUsername("user1");
|
||||
user.update(userRep);
|
||||
|
||||
realm.users().get("user11").toRepresentation();
|
||||
fail("Expected failure");
|
||||
} catch (ClientErrorException e) {
|
||||
assertEquals(404, e.getResponse().getStatus());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateUserWithExistingUsername() {
|
||||
switchEditUsernameAllowedOn();
|
||||
createUser();
|
||||
|
||||
UserRepresentation userRep = new UserRepresentation();
|
||||
userRep.setUsername("user2");
|
||||
Response response = realm.users().create(userRep);
|
||||
String createdId = ApiUtil.getCreatedId(response);
|
||||
response.close();
|
||||
|
||||
try {
|
||||
UserResource user = realm.users().get(createdId);
|
||||
userRep = user.toRepresentation();
|
||||
userRep.setUsername("user1");
|
||||
user.update(userRep);
|
||||
fail("Expected failure");
|
||||
} catch (ClientErrorException e) {
|
||||
assertEquals(409, e.getResponse().getStatus());
|
||||
}
|
||||
}
|
||||
|
||||
private void switchEditUsernameAllowedOn() {
|
||||
RealmRepresentation rep = realm.toRepresentation();
|
||||
rep.setEditUsernameAllowed(true);
|
||||
realm.update(rep);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ public class ModelTest extends AbstractModelTest {
|
|||
realm.setRegistrationAllowed(true);
|
||||
realm.setRegistrationEmailAsUsername(true);
|
||||
realm.setResetPasswordAllowed(true);
|
||||
realm.setEditUsernameAllowed(true);
|
||||
realm.setSslRequired(SslRequired.EXTERNAL);
|
||||
realm.setVerifyEmail(true);
|
||||
realm.setAccessTokenLifespan(1000);
|
||||
|
@ -55,6 +56,7 @@ public class ModelTest extends AbstractModelTest {
|
|||
Assert.assertEquals(expected.isRegistrationAllowed(), actual.isRegistrationAllowed());
|
||||
Assert.assertEquals(expected.isRegistrationEmailAsUsername(), actual.isRegistrationEmailAsUsername());
|
||||
Assert.assertEquals(expected.isResetPasswordAllowed(), actual.isResetPasswordAllowed());
|
||||
Assert.assertEquals(expected.isEditUsernameAllowed(), actual.isEditUsernameAllowed());
|
||||
Assert.assertEquals(expected.getSslRequired(), actual.getSslRequired());
|
||||
Assert.assertEquals(expected.isVerifyEmail(), actual.isVerifyEmail());
|
||||
Assert.assertEquals(expected.getAccessTokenLifespan(), actual.getAccessTokenLifespan());
|
||||
|
|
|
@ -35,6 +35,9 @@ public class AccountUpdateProfilePage extends AbstractAccountPage {
|
|||
|
||||
public static String PATH = RealmsResource.accountUrl(UriBuilder.fromUri(Constants.AUTH_SERVER_ROOT)).build("test").toString();
|
||||
|
||||
@FindBy(id = "username")
|
||||
private WebElement usernameInput;
|
||||
|
||||
@FindBy(id = "firstName")
|
||||
private WebElement firstNameInput;
|
||||
|
||||
|
@ -74,11 +77,28 @@ public class AccountUpdateProfilePage extends AbstractAccountPage {
|
|||
submitButton.click();
|
||||
}
|
||||
|
||||
public void updateProfile(String username, String firstName, String lastName, String email) {
|
||||
usernameInput.clear();
|
||||
usernameInput.sendKeys(username);
|
||||
firstNameInput.clear();
|
||||
firstNameInput.sendKeys(firstName);
|
||||
lastNameInput.clear();
|
||||
lastNameInput.sendKeys(lastName);
|
||||
emailInput.clear();
|
||||
emailInput.sendKeys(email);
|
||||
|
||||
submitButton.click();
|
||||
}
|
||||
|
||||
public void clickCancel() {
|
||||
cancelButton.click();
|
||||
}
|
||||
|
||||
|
||||
public String getUsername() {
|
||||
return usernameInput.getAttribute("value");
|
||||
}
|
||||
|
||||
public String getFirstName() {
|
||||
return firstNameInput.getAttribute("value");
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue