keycloak-aplcache
Changes
forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/FreeMarkerAccount.java 6(+4 -2)
forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/FeaturesBean.java 7(+6 -1)
spi/authentication-model/src/main/java/org/keycloak/spi/authentication/model/AbstractModelAuthenticationProvider.java 8(+8 -0)
spi/authentication-picketlink/src/main/java/org/keycloak/spi/authentication/picketlink/PicketlinkAuthenticationProvider.java 77(+57 -20)
spi/authentication-spi/src/main/java/org/keycloak/spi/authentication/AuthenticationProvider.java 11(+11 -0)
spi/authentication-spi/src/main/java/org/keycloak/spi/authentication/AuthenticationProviderManager.java 43(+39 -4)
testsuite/integration/src/test/java/org/keycloak/testsuite/forms/AuthProvidersIntegrationTest.java 33(+33 -0)
Details
diff --git a/forms/account-api/src/main/java/org/keycloak/account/Account.java b/forms/account-api/src/main/java/org/keycloak/account/Account.java
index f92b161..5a62fec 100644
--- a/forms/account-api/src/main/java/org/keycloak/account/Account.java
+++ b/forms/account-api/src/main/java/org/keycloak/account/Account.java
@@ -31,5 +31,5 @@ public interface Account {
Account setEvents(List<Event> events);
- Account setFeatures(boolean social, boolean audit);
+ Account setFeatures(boolean social, boolean audit, boolean passwordUpdateSupported);
}
diff --git a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/FreeMarkerAccount.java b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/FreeMarkerAccount.java
index 53f43d4..9b6f0d8 100644
--- a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/FreeMarkerAccount.java
+++ b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/FreeMarkerAccount.java
@@ -44,6 +44,7 @@ public class FreeMarkerAccount implements Account {
private List<Event> events;
private boolean social;
private boolean audit;
+ private boolean passwordUpdateSupported;
public static enum MessageType {SUCCESS, WARNING, ERROR}
@@ -95,7 +96,7 @@ public class FreeMarkerAccount implements Account {
attributes.put("url", new UrlBean(realm, theme, baseUri));
- attributes.put("features", new FeaturesBean(social, audit));
+ attributes.put("features", new FeaturesBean(social, audit, passwordUpdateSupported));
switch (page) {
case ACCOUNT:
@@ -172,9 +173,10 @@ public class FreeMarkerAccount implements Account {
}
@Override
- public Account setFeatures(boolean social, boolean audit) {
+ public Account setFeatures(boolean social, boolean audit, boolean passwordUpdateSupported) {
this.social = social;
this.audit = audit;
+ this.passwordUpdateSupported = passwordUpdateSupported;
return this;
}
}
diff --git a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/FeaturesBean.java b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/FeaturesBean.java
index 6f8158b..06e99eb 100644
--- a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/FeaturesBean.java
+++ b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/FeaturesBean.java
@@ -7,10 +7,12 @@ public class FeaturesBean {
private final boolean social;
private final boolean log;
+ private final boolean passwordUpdateSupported;
- public FeaturesBean(boolean social, boolean log) {
+ public FeaturesBean(boolean social, boolean log, boolean passwordUpdateSupported) {
this.social = social;
this.log = log;
+ this.passwordUpdateSupported = passwordUpdateSupported;
}
public boolean isSocial() {
@@ -21,4 +23,7 @@ public class FeaturesBean {
return log;
}
+ public boolean isPasswordUpdateSupported() {
+ return passwordUpdateSupported;
+ }
}
diff --git a/forms/common-themes/src/main/resources/theme/account/base/template.ftl b/forms/common-themes/src/main/resources/theme/account/base/template.ftl
index 6a39817..883d8fb 100644
--- a/forms/common-themes/src/main/resources/theme/account/base/template.ftl
+++ b/forms/common-themes/src/main/resources/theme/account/base/template.ftl
@@ -40,7 +40,7 @@
<div class="bs-sidebar col-sm-3 ng-scope">
<ul>
<li class="<#if active=='account'>active</#if>"><a href="${url.accountUrl}">Account</a></li>
- <li class="<#if active=='password'>active</#if>"><a href="${url.passwordUrl}">Password</a></li>
+ <#if features.passwordUpdateSupported><li class="<#if active=='password'>active</#if>"><a href="${url.passwordUrl}">Password</a></li></#if>
<li class="<#if active=='totp'>active</#if>"><a href="${url.totpUrl}">Authenticator</a></li>
<#if features.social><li class="<#if active=='social'>active</#if>"><a href="${url.socialUrl}">Social</a></li></#if>
<#if features.log><li class="<#if active=='log'>active</#if>"><a href="${url.logUrl}">Log</a></li></#if>
diff --git a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
index 8bb9e53..2d942d9 100755
--- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
@@ -231,6 +231,7 @@ public class AuthenticationManager {
user.setLastName(authUser.getLastName());
user.setEmail(authUser.getEmail());
realm.setAuthenticationLink(user, new AuthenticationLinkModel(authUser.getProviderName(), authUser.getId()));
+ logger.info("User " + authUser.getUsername() + " created and linked with provider " + authUser.getProviderName());
} else {
logger.warn("User " + username + " not found");
return AuthenticationStatus.INVALID_USER;
diff --git a/services/src/main/java/org/keycloak/services/resources/AccountService.java b/services/src/main/java/org/keycloak/services/resources/AccountService.java
index a789660..bdc97fe 100755
--- a/services/src/main/java/org/keycloak/services/resources/AccountService.java
+++ b/services/src/main/java/org/keycloak/services/resources/AccountService.java
@@ -35,6 +35,8 @@ import org.keycloak.audit.Events;
import org.keycloak.jaxrs.JaxrsOAuthClient;
import org.keycloak.models.AccountRoles;
import org.keycloak.models.ApplicationModel;
+import org.keycloak.models.AuthenticationLinkModel;
+import org.keycloak.models.AuthenticationProviderModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.Constants;
import org.keycloak.models.RealmModel;
@@ -143,12 +145,21 @@ public class AccountService {
public void init() {
auditProvider = providers.getProvider(AuditProvider.class);
- account = AccountLoader.load().createAccount(uriInfo).setRealm(realm).setFeatures(realm.isSocial(), auditProvider != null);
+ account = AccountLoader.load().createAccount(uriInfo).setRealm(realm);
+ boolean passwordUpdateSupported = false;
auth = authManager.authenticate(realm, headers);
if (auth != null) {
account.setUser(auth.getUser());
+
+ AuthenticationLinkModel authLinkModel = realm.getAuthenticationLink(auth.getUser());
+ if (authLinkModel != null) {
+ AuthenticationProviderModel authProviderModel = AuthenticationProviderManager.getConfiguredProviderModel(realm, authLinkModel.getAuthProvider());
+ passwordUpdateSupported = authProviderModel.isPasswordUpdateSupported();
+ }
}
+
+ account.setFeatures(realm.isSocial(), auditProvider != null, passwordUpdateSupported);
}
public static UriBuilder accountServiceBaseUrl(UriInfo uriInfo) {
diff --git a/services/src/main/java/org/keycloak/services/resources/TokenService.java b/services/src/main/java/org/keycloak/services/resources/TokenService.java
index b326216..a18e141 100755
--- a/services/src/main/java/org/keycloak/services/resources/TokenService.java
+++ b/services/src/main/java/org/keycloak/services/resources/TokenService.java
@@ -383,13 +383,15 @@ public class TokenService {
return Flows.forms(realm, request, uriInfo).setError(error).setFormData(formData).createRegistration();
}
- UserModel user = realm.getUser(username);
- if (user != null) {
+ AuthenticationProviderManager authenticationProviderManager = AuthenticationProviderManager.getManager(realm);
+
+ // Validate that user with this username doesn't exist in realm or any authentication provider
+ if (realm.getUser(username) != null || authenticationProviderManager.getUser(username) != null) {
audit.error(Errors.USERNAME_IN_USE);
return Flows.forms(realm, request, uriInfo).setError(Messages.USERNAME_EXISTS).setFormData(formData).createRegistration();
}
- user = realm.addUser(username);
+ UserModel user = realm.addUser(username);
user.setEnabled(true);
user.setFirstName(formData.getFirst("firstName"));
user.setLastName(formData.getFirst("lastName"));
diff --git a/spi/authentication-model/src/main/java/org/keycloak/spi/authentication/model/AbstractModelAuthenticationProvider.java b/spi/authentication-model/src/main/java/org/keycloak/spi/authentication/model/AbstractModelAuthenticationProvider.java
index 66166ab..d69c428 100644
--- a/spi/authentication-model/src/main/java/org/keycloak/spi/authentication/model/AbstractModelAuthenticationProvider.java
+++ b/spi/authentication-model/src/main/java/org/keycloak/spi/authentication/model/AbstractModelAuthenticationProvider.java
@@ -30,6 +30,14 @@ public abstract class AbstractModelAuthenticationProvider implements Authenticat
}
@Override
+ public String registerUser(RealmModel currentRealm, Map<String, String> config, String username) throws AuthenticationProviderException {
+ RealmModel realm = getRealm(currentRealm, config);
+ UserModel user = currentRealm.addUser(username);
+ user.setEnabled(true);
+ return user.getId();
+ }
+
+ @Override
public AuthProviderStatus validatePassword(RealmModel currentRealm, Map<String, String> config, String username, String password) throws AuthenticationProviderException {
RealmModel realm = getRealm(currentRealm, config);
UserModel user = KeycloakModelUtils.findUserByNameOrEmail(realm, username);
diff --git a/spi/authentication-picketlink/src/main/java/org/keycloak/spi/authentication/picketlink/PicketlinkAuthenticationProvider.java b/spi/authentication-picketlink/src/main/java/org/keycloak/spi/authentication/picketlink/PicketlinkAuthenticationProvider.java
index 4111806..8344425 100755
--- a/spi/authentication-picketlink/src/main/java/org/keycloak/spi/authentication/picketlink/PicketlinkAuthenticationProvider.java
+++ b/spi/authentication-picketlink/src/main/java/org/keycloak/spi/authentication/picketlink/PicketlinkAuthenticationProvider.java
@@ -14,6 +14,7 @@ import org.keycloak.spi.authentication.AuthenticationProvider;
import org.keycloak.spi.authentication.AuthenticationProviderException;
import org.keycloak.spi.picketlink.PartitionManagerProvider;
import org.keycloak.util.ProviderLoader;
+import org.picketlink.idm.IdentityManagementException;
import org.picketlink.idm.IdentityManager;
import org.picketlink.idm.PartitionManager;
import org.picketlink.idm.credential.Credentials;
@@ -44,25 +45,47 @@ public class PicketlinkAuthenticationProvider implements AuthenticationProvider
@Override
public AuthUser getUser(RealmModel realm, Map<String, String> configuration, String username) throws AuthenticationProviderException {
IdentityManager identityManager = getIdentityManager(realm);
- User picketlinkUser = BasicModel.getUser(identityManager, username);
- return picketlinkUser == null ? null : new AuthUser(picketlinkUser.getId(), picketlinkUser.getLoginName(), getName())
- .setName(picketlinkUser.getFirstName(), picketlinkUser.getLastName())
- .setEmail(picketlinkUser.getEmail())
- .setProviderName(getName());
+
+ try {
+ User picketlinkUser = BasicModel.getUser(identityManager, username);
+ return picketlinkUser == null ? null : new AuthUser(picketlinkUser.getId(), picketlinkUser.getLoginName(), getName())
+ .setName(picketlinkUser.getFirstName(), picketlinkUser.getLastName())
+ .setEmail(picketlinkUser.getEmail())
+ .setProviderName(getName());
+ } catch (IdentityManagementException ie) {
+ throw convertIDMException(ie);
+ }
+ }
+
+ @Override
+ public String registerUser(RealmModel realm, Map<String, String> configuration, String username) throws AuthenticationProviderException {
+ IdentityManager identityManager = getIdentityManager(realm);
+
+ try {
+ User picketlinkUser = new User(username);
+ identityManager.add(picketlinkUser);
+ return picketlinkUser.getId();
+ } catch (IdentityManagementException ie) {
+ throw convertIDMException(ie);
+ }
}
@Override
public AuthProviderStatus validatePassword(RealmModel realm, Map<String, String> configuration, String username, String password) throws AuthenticationProviderException {
IdentityManager identityManager = getIdentityManager(realm);
- UsernamePasswordCredentials credential = new UsernamePasswordCredentials();
- credential.setUsername(username);
- credential.setPassword(new Password(password.toCharArray()));
- identityManager.validateCredentials(credential);
- if (credential.getStatus() == Credentials.Status.VALID) {
- return AuthProviderStatus.SUCCESS;
- } else {
- return AuthProviderStatus.INVALID_CREDENTIALS;
+ try {
+ UsernamePasswordCredentials credential = new UsernamePasswordCredentials();
+ credential.setUsername(username);
+ credential.setPassword(new Password(password.toCharArray()));
+ identityManager.validateCredentials(credential);
+ if (credential.getStatus() == Credentials.Status.VALID) {
+ return AuthProviderStatus.SUCCESS;
+ } else {
+ return AuthProviderStatus.INVALID_CREDENTIALS;
+ }
+ } catch (IdentityManagementException ie) {
+ throw convertIDMException(ie);
}
}
@@ -70,14 +93,18 @@ public class PicketlinkAuthenticationProvider implements AuthenticationProvider
public boolean updateCredential(RealmModel realm, Map<String, String> configuration, String username, String password) throws AuthenticationProviderException {
IdentityManager identityManager = getIdentityManager(realm);
- User picketlinkUser = BasicModel.getUser(identityManager, username);
- if (picketlinkUser == null) {
- logger.debugf("User '%s' doesn't exists. Skip password update", username);
- return false;
- }
+ try {
+ User picketlinkUser = BasicModel.getUser(identityManager, username);
+ if (picketlinkUser == null) {
+ logger.debugf("User '%s' doesn't exists. Skip password update", username);
+ return false;
+ }
- identityManager.updateCredential(picketlinkUser, new Password(password.toCharArray()));
- return true;
+ identityManager.updateCredential(picketlinkUser, new Password(password.toCharArray()));
+ return true;
+ } catch (IdentityManagementException ie) {
+ throw convertIDMException(ie);
+ }
}
public IdentityManager getIdentityManager(RealmModel realm) throws AuthenticationProviderException {
@@ -103,4 +130,14 @@ public class PicketlinkAuthenticationProvider implements AuthenticationProvider
}
return identityManager;
}
+
+ private AuthenticationProviderException convertIDMException(IdentityManagementException ie) {
+ Throwable realCause = ie;
+ while (realCause.getCause() != null) {
+ realCause = realCause.getCause();
+ }
+
+ // Use the message from the realCause
+ return new AuthenticationProviderException(realCause.getMessage(), ie);
+ }
}
diff --git a/spi/authentication-spi/src/main/java/org/keycloak/spi/authentication/AuthenticationProvider.java b/spi/authentication-spi/src/main/java/org/keycloak/spi/authentication/AuthenticationProvider.java
index 550da90..898bc1c 100644
--- a/spi/authentication-spi/src/main/java/org/keycloak/spi/authentication/AuthenticationProvider.java
+++ b/spi/authentication-spi/src/main/java/org/keycloak/spi/authentication/AuthenticationProvider.java
@@ -31,6 +31,17 @@ public interface AuthenticationProvider {
AuthUser getUser(RealmModel realm, Map<String, String> configuration, String username) throws AuthenticationProviderException;
/**
+ * Try to register user with this authentication provider
+ *
+ * @param realm
+ * @param configuration
+ * @param username
+ * @return ID of newly created user (For example ID from LDAP)
+ * @throws AuthenticationProviderException if user creation couldn't happen
+ */
+ String registerUser(RealmModel realm, Map<String, String> configuration, String username) throws AuthenticationProviderException;
+
+ /**
* Standard Authentication flow
*
* @param username
diff --git a/spi/authentication-spi/src/main/java/org/keycloak/spi/authentication/AuthenticationProviderManager.java b/spi/authentication-spi/src/main/java/org/keycloak/spi/authentication/AuthenticationProviderManager.java
index 3319cff..823a14b 100644
--- a/spi/authentication-spi/src/main/java/org/keycloak/spi/authentication/AuthenticationProviderManager.java
+++ b/spi/authentication-spi/src/main/java/org/keycloak/spi/authentication/AuthenticationProviderManager.java
@@ -73,7 +73,11 @@ public class AuthenticationProviderManager {
public AuthProviderStatus validatePassword(UserModel user, String password) {
AuthenticationLinkModel authLink = realm.getAuthenticationLink(user);
if (authLink == null) {
- authLink = new AuthenticationLinkModel(AuthenticationProviderModel.DEFAULT_PROVIDER.getProviderName(), user.getId());
+ // User not yet linked with any authenticationProvider. Find provider with biggest priority where he is and link
+ AuthUser authUser = getUser(user.getLoginName());
+ authLink = new AuthenticationLinkModel(authUser.getProviderName(), authUser.getId());
+ realm.setAuthenticationLink(user, authLink);
+ logger.infof("User '%s' linked with provider '%s'", authUser.getUsername(), authUser.getProviderName());
}
String providerName = authLink.getAuthProvider();
@@ -99,7 +103,38 @@ public class AuthenticationProviderManager {
public boolean updatePassword(UserModel user, String password) throws AuthenticationProviderException {
AuthenticationLinkModel authLink = realm.getAuthenticationLink(user);
if (authLink == null) {
- authLink = new AuthenticationLinkModel(AuthenticationProviderModel.DEFAULT_PROVIDER.getProviderName(), user.getId());
+ // Find provider with biggest priority where password update is supported. Then register user here and link him
+ List<AuthenticationProviderModel> configuredProviders = getConfiguredProviderModels(realm);
+ for (AuthenticationProviderModel providerModel : configuredProviders) {
+ if (providerModel.isPasswordUpdateSupported()) {
+ AuthenticationProvider delegate = getProvider(providerModel.getProviderName());
+ if (delegate != null) {
+ AuthUser authUser = delegate.getUser(realm, providerModel.getConfig(), user.getLoginName());
+ if (authUser != null) {
+ // Linking existing user supported just for "model" provider. In other cases throw exception
+ if (providerModel.getProviderName().equals(AuthenticationProviderModel.DEFAULT_PROVIDER.getProviderName())) {
+ authLink = new AuthenticationLinkModel(providerModel.getProviderName(), authUser.getId());
+ realm.setAuthenticationLink(user, authLink);
+ logger.infof("User '%s' linked with provider '%s'", authUser.getUsername(), authUser.getProviderName());
+ } else {
+ throw new AuthenticationProviderException("User " + authUser.getUsername() + " exists in provider "
+ + authUser.getProviderName() + " but is not linked with model user");
+ }
+ } else {
+ String userIdInProvider = delegate.registerUser(realm, providerModel.getConfig(), user.getLoginName());
+ authLink = new AuthenticationLinkModel(providerModel.getProviderName(), userIdInProvider);
+ realm.setAuthenticationLink(user, authLink);
+ logger.infof("User '%s' registered in provider '%s' and linked", user.getLoginName(), providerModel.getProviderName());
+ }
+ break;
+ }
+ }
+ }
+
+ if (authLink == null) {
+ logger.warnf("No providers found where password update is supported for user '%s'", user.getLoginName());
+ return false;
+ }
}
String providerName = authLink.getAuthProvider();
@@ -147,7 +182,7 @@ public class AuthenticationProviderManager {
return delegate;
}
- private List<AuthenticationProviderModel> getConfiguredProviderModels(RealmModel realm) {
+ private static List<AuthenticationProviderModel> getConfiguredProviderModels(RealmModel realm) {
List<AuthenticationProviderModel> configuredProviders = realm.getAuthenticationProviders();
// Use model based authentication of current realm by default
@@ -159,7 +194,7 @@ public class AuthenticationProviderManager {
return configuredProviders;
}
- private AuthenticationProviderModel getConfiguredProviderModel(RealmModel realm, String providerName) {
+ public static AuthenticationProviderModel getConfiguredProviderModel(RealmModel realm, String providerName) {
List<AuthenticationProviderModel> providers = getConfiguredProviderModels(realm);
for (AuthenticationProviderModel provider : providers) {
if (providerName.equals(provider.getProviderName())) {
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/AuthProvidersIntegrationTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/AuthProvidersIntegrationTest.java
index 37cde85..7bf2e3a 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/AuthProvidersIntegrationTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/AuthProvidersIntegrationTest.java
@@ -23,6 +23,7 @@ import org.keycloak.testsuite.pages.AccountPasswordPage;
import org.keycloak.testsuite.pages.AccountUpdateProfilePage;
import org.keycloak.testsuite.pages.AppPage;
import org.keycloak.testsuite.pages.LoginPage;
+import org.keycloak.testsuite.pages.RegisterPage;
import org.keycloak.testsuite.rule.KeycloakRule;
import org.keycloak.testsuite.rule.LDAPRule;
import org.keycloak.testsuite.rule.WebResource;
@@ -83,6 +84,9 @@ public class AuthProvidersIntegrationTest {
protected AppPage appPage;
@WebResource
+ protected RegisterPage registerPage;
+
+ @WebResource
protected LoginPage loginPage;
@WebResource
@@ -111,6 +115,9 @@ public class AuthProvidersIntegrationTest {
Assert.assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType());
Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
+
+ profilePage.open();
+ Assert.assertFalse(profilePage.isPasswordUpdateSupported());
}
@Test
@@ -120,6 +127,9 @@ public class AuthProvidersIntegrationTest {
Assert.assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType());
Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
+
+ profilePage.open();
+ Assert.assertTrue(profilePage.isPasswordUpdateSupported());
}
@Test
@@ -131,6 +141,7 @@ public class AuthProvidersIntegrationTest {
Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
profilePage.open();
+ Assert.assertTrue(profilePage.isPasswordUpdateSupported());
Assert.assertEquals("John", profilePage.getFirstName());
Assert.assertEquals("Doe", profilePage.getLastName());
Assert.assertEquals("john@email.org", profilePage.getEmail());
@@ -191,4 +202,26 @@ public class AuthProvidersIntegrationTest {
loginPage.login("john", "new-password");
Assert.assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType());
}
+
+ @Test
+ public void registerExistingLdapUser() {
+ loginPage.open();
+ loginPage.clickRegister();
+ registerPage.assertCurrent();
+
+ registerPage.register("firstName", "lastName", "email", "existing", "password", "password");
+
+ registerPage.assertCurrent();
+ Assert.assertEquals("Username already exists", registerPage.getError());
+ }
+
+ @Test
+ public void registerUserLdapSuccess() {
+ loginPage.open();
+ loginPage.clickRegister();
+ registerPage.assertCurrent();
+
+ registerPage.register("firstName", "lastName", "email", "registerUserSuccess", "password", "password");
+ Assert.assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType());
+ }
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AccountUpdateProfilePage.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AccountUpdateProfilePage.java
index 582c112..04f1c60 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AccountUpdateProfilePage.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AccountUpdateProfilePage.java
@@ -96,4 +96,8 @@ public class AccountUpdateProfilePage extends AbstractAccountPage {
public String getError() {
return errorMessage.getText();
}
+
+ public boolean isPasswordUpdateSupported() {
+ return driver.getPageSource().contains(PATH + "/password");
+ }
}
diff --git a/testsuite/integration/src/test/resources/ldap/users.ldif b/testsuite/integration/src/test/resources/ldap/users.ldif
index 76295d3..0debe0b 100644
--- a/testsuite/integration/src/test/resources/ldap/users.ldif
+++ b/testsuite/integration/src/test/resources/ldap/users.ldif
@@ -28,3 +28,13 @@ uid: john
cn: John
sn: Doe
mail: john@email.org
+
+dn: uid=existing,ou=People,dc=keycloak,dc=org
+objectclass: top
+objectclass: uidObject
+objectclass: person
+objectclass: inetOrgPerson
+uid: existing
+cn: Existing
+sn: Foo
+mail: existing@email.org