Merge pull request #458 from mposolda/master

Support for Active Directory and RHDS
This commit is contained in:
Bill Burke 2014-06-09 15:15:15 -04:00
commit 9a3abf6a6a
16 changed files with 226 additions and 70 deletions

View file

@ -67,6 +67,10 @@ public class PicketlinkAuthenticationProvider implements AuthenticationProvider
try {
User picketlinkUser = new User(username);
identityManager.add(picketlinkUser);
// Hack needed due to ActiveDirectory bug in Picketlink TODO: Remove once https://issues.jboss.org/browse/PLINK-485 fixed and updated in keycloak master
picketlinkUser = BasicModel.getUser(identityManager, picketlinkUser.getLoginName());
return picketlinkUser.getId();
} catch (IdentityManagementException ie) {
throw convertIDMException(ie);

View file

@ -902,6 +902,12 @@ module.controller('RealmSMTPSettingsCtrl', function($scope, Current, Realm, real
module.controller('RealmLdapSettingsCtrl', function($scope, $location, Notifications, Realm, realm) {
console.log('RealmLdapSettingsCtrl');
$scope.ldapVendors = [
{ "id": "ad", "name": "Active Directory" },
{ "id": "rhds", "name": "Red Hat Directory Server" },
{ "id": "other", "name": "Other" }
];
$scope.realm = realm;
var oldCopy = angular.copy($scope.realm);

View file

@ -13,6 +13,17 @@
<fieldset>
<legend><span class="text">Required Settings</span></legend>
<div class="form-group clearfix">
<label class="col-sm-2 control-label" for="vendor">Vendor <span class="required">*</span></label>
<div class="col-sm-4">
<div class="select-kc">
<select id="vendor"
ng-model="realm.ldapServer.vendor"
ng-options="vendor.id as vendor.name for vendor in ldapVendors">
</select>
</div>
</div>
</div>
<div class="form-group clearfix">
<label class="col-sm-2 control-label" for="ldapConnectionUrl">Connection URL <span class="required">*</span></label>
<div class="col-sm-4">

View file

@ -49,6 +49,7 @@ public class LDAPEmbeddedServer extends AbstractLDAPTest {
protected boolean startEmbeddedLdapLerver = true;
protected String bindDn = "uid=admin,ou=system";
protected String bindCredential = "secret";
protected String vendor;
public static String IDM_TEST_LDAP_CONNECTION_URL = "idm.test.ldap.connection.url";
public static String IDM_TEST_LDAP_BASE_DN = "idm.test.ldap.base.dn";
@ -59,6 +60,7 @@ public class LDAPEmbeddedServer extends AbstractLDAPTest {
public static String IDM_TEST_LDAP_START_EMBEDDED_LDAP_SERVER = "idm.test.ldap.start.embedded.ldap.server";
public static String IDM_TEST_LDAP_BIND_DN = "idm.test.ldap.bind.dn";
public static String IDM_TEST_LDAP_BIND_CREDENTIAL = "idm.test.ldap.bind.credential";
public static String IDM_TEST_LDAP_VENDOR = "idm.test.ldap.vendor";
public LDAPEmbeddedServer() {
@ -85,6 +87,7 @@ public class LDAPEmbeddedServer extends AbstractLDAPTest {
startEmbeddedLdapLerver = Boolean.parseBoolean(p.getProperty(IDM_TEST_LDAP_START_EMBEDDED_LDAP_SERVER, "true"));
bindDn = p.getProperty(IDM_TEST_LDAP_BIND_DN, bindDn);
bindCredential = p.getProperty(IDM_TEST_LDAP_BIND_CREDENTIAL, bindCredential);
vendor = p.getProperty(IDM_TEST_LDAP_VENDOR);
}
@Override
@ -132,6 +135,7 @@ public class LDAPEmbeddedServer extends AbstractLDAPTest {
ldapConfig.put(LdapConstants.BIND_DN, getBindDn());
ldapConfig.put(LdapConstants.BIND_CREDENTIAL, getBindCredential());
ldapConfig.put(LdapConstants.USER_DN_SUFFIX, getUserDnSuffix());
ldapConfig.put(LdapConstants.VENDOR, getVendor());
realm.setLdapServerConfig(ldapConfig);
}
@ -202,6 +206,10 @@ public class LDAPEmbeddedServer extends AbstractLDAPTest {
return bindCredential;
}
public String getVendor() {
return vendor;
}
@Override
public void importLDIF(String fileName) throws Exception {
// import LDIF only in case we are running against embedded LDAP server

View file

@ -75,23 +75,23 @@ public class AuthProvidersLDAPTest extends AbstractModelTest {
@Test
public void testLdapAuthentication() {
MultivaluedMap<String, String> formData = AuthProvidersExternalModelTest.createFormData("john", "password");
MultivaluedMap<String, String> formData = AuthProvidersExternalModelTest.createFormData("johnkeycloak", "password");
// Set password of user in LDAP
LdapTestUtils.setLdapPassword(providerSession, realm, "john", "password");
LdapTestUtils.setLdapPassword(providerSession, realm, "johnkeycloak", "password");
// Verify that user doesn't exists in realm2 and can't authenticate here
Assert.assertEquals(AuthenticationManager.AuthenticationStatus.INVALID_USER, am.authenticateForm(null, realm, formData));
Assert.assertNull(realm.getUser("john"));
Assert.assertNull(realm.getUser("johnkeycloak"));
// Add ldap authenticationProvider
setupAuthenticationProviders();
// Authenticate john and verify that now he exists in realm
Assert.assertEquals(AuthenticationManager.AuthenticationStatus.SUCCESS, am.authenticateForm(null, realm, formData));
UserModel john = realm.getUser("john");
UserModel john = realm.getUser("johnkeycloak");
Assert.assertNotNull(john);
Assert.assertEquals("john", john.getLoginName());
Assert.assertEquals("johnkeycloak", john.getLoginName());
Assert.assertEquals("John", john.getFirstName());
Assert.assertEquals("Doe", john.getLastName());
Assert.assertEquals("john@email.org", john.getEmail());
@ -119,7 +119,7 @@ public class AuthProvidersLDAPTest extends AbstractModelTest {
Assert.assertEquals(AuthenticationManager.AuthenticationStatus.INVALID_USER, am.authenticateForm(null, realm, formData));
// User exists in ldap
formData = AuthProvidersExternalModelTest.createFormData("john", "invalid");
formData = AuthProvidersExternalModelTest.createFormData("johnkeycloak", "invalid");
Assert.assertEquals(AuthenticationManager.AuthenticationStatus.INVALID_CREDENTIALS, am.authenticateForm(null, realm, formData));
// User exists in realm
@ -142,23 +142,23 @@ public class AuthProvidersLDAPTest extends AbstractModelTest {
// Add ldap
setupAuthenticationProviders();
LdapTestUtils.setLdapPassword(providerSession, realm, "john", "password");
LdapTestUtils.setLdapPassword(providerSession, realm, "johnkeycloak", "password");
// First authenticate successfully to sync john into realm
MultivaluedMap<String, String> formData = AuthProvidersExternalModelTest.createFormData("john", "password");
MultivaluedMap<String, String> formData = AuthProvidersExternalModelTest.createFormData("johnkeycloak", "password");
Assert.assertEquals(AuthenticationManager.AuthenticationStatus.SUCCESS, am.authenticateForm(null, realm, formData));
// Change credential and validate that user can authenticate
AuthenticationProviderManager authProviderManager = AuthenticationProviderManager.getManager(realm, providerSession);
UserModel john = realm.getUser("john");
UserModel john = realm.getUser("johnkeycloak");
try {
Assert.assertTrue(authProviderManager.updatePassword(john, "password-updated"));
} catch (AuthenticationProviderException ape) {
ape.printStackTrace();
Assert.fail("Error not expected");
}
formData = AuthProvidersExternalModelTest.createFormData("john", "password-updated");
formData = AuthProvidersExternalModelTest.createFormData("johnkeycloak", "password-updated");
Assert.assertEquals(AuthenticationManager.AuthenticationStatus.SUCCESS, am.authenticateForm(null, realm, formData));
// Password updated just in LDAP, so validating directly in realm should fail
@ -174,7 +174,7 @@ public class AuthProvidersLDAPTest extends AbstractModelTest {
ape.printStackTrace();
Assert.fail("Error not expected");
}
formData = AuthProvidersExternalModelTest.createFormData("john", "password-updated2");
formData = AuthProvidersExternalModelTest.createFormData("johnkeycloak", "password-updated2");
Assert.assertEquals(AuthenticationManager.AuthenticationStatus.INVALID_CREDENTIALS, am.authenticateForm(null, realm, formData));
}

View file

@ -201,10 +201,11 @@ public class ImportTest extends AbstractModelTest {
// Test ldap config
Map<String, String> ldapConfig = realm.getLdapServerConfig();
Assert.assertTrue(ldapConfig.size() == 5);
Assert.assertTrue(ldapConfig.size() == 6);
Assert.assertEquals("ldap://localhost:10389", ldapConfig.get("connectionUrl"));
Assert.assertEquals("dc=keycloak,dc=org", ldapConfig.get("baseDn"));
Assert.assertEquals("ou=People,dc=keycloak,dc=org", ldapConfig.get("userDnSuffix"));
Assert.assertEquals("other", ldapConfig.get("vendor"));
// Test authentication providers
List<AuthenticationProviderModel> authProviderModels = realm.getAuthenticationProviders();

View file

@ -9,12 +9,12 @@ objectclass: top
objectclass: organizationalUnit
ou: People
dn: uid=john,ou=People,dc=keycloak,dc=org
dn: uid=johnkeycloak,ou=People,dc=keycloak,dc=org
objectclass: top
objectclass: uidObject
objectclass: person
objectclass: inetOrgPerson
uid: john
uid: johnkeycloak
cn: John
sn: Doe
mail: john@email.org

View file

@ -17,7 +17,8 @@
"baseDn": "dc=keycloak,dc=org",
"userDnSuffix": "ou=People,dc=keycloak,dc=org",
"bindDn": "uid=admin,ou=system",
"bindCredential": "secret"
"bindCredential": "secret",
"vendor": "other"
},
"socialProviders": {
"google.key": "abc",

View file

@ -0,0 +1,32 @@
package org.keycloak.picketlink;
import org.keycloak.models.RealmModel;
import org.picketlink.idm.IdentityManager;
import org.picketlink.idm.PartitionManager;
/**
* Per-request IdentityManager caching . Not thread-safe
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public abstract class AbstractIdentityManagerProvider implements IdentityManagerProvider {
private IdentityManager identityManager;
@Override
public IdentityManager getIdentityManager(RealmModel realm) {
if (identityManager == null) {
PartitionManager partitionManager = getPartitionManager(realm);
identityManager = partitionManager.createIdentityManager();
}
return identityManager;
}
protected abstract PartitionManager getPartitionManager(RealmModel realm);
@Override
public void close() {
identityManager = null;
}
}

View file

@ -1,27 +0,0 @@
package org.keycloak.picketlink.idm;
import org.picketlink.idm.IdentityManager;
import org.picketlink.idm.ldap.internal.LDAPPlainTextPasswordCredentialHandler;
import org.picketlink.idm.model.Account;
import org.picketlink.idm.model.basic.User;
import org.picketlink.idm.spi.IdentityContext;
import static org.picketlink.idm.IDMLog.CREDENTIAL_LOGGER;
import static org.picketlink.idm.model.basic.BasicModel.getUser;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class LDAPAgentIgnoreCredentialHandler extends LDAPPlainTextPasswordCredentialHandler {
@Override
protected Account getAccount(IdentityContext context, String loginName) {
IdentityManager identityManager = getIdentityManager(context);
if (CREDENTIAL_LOGGER.isDebugEnabled()) {
CREDENTIAL_LOGGER.debugf("Trying to find account [%s] using default account type [%s]", loginName, User.class);
}
return getUser(identityManager, loginName);
}
}

View file

@ -0,0 +1,93 @@
package org.keycloak.picketlink.idm;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Date;
import javax.naming.directory.BasicAttribute;
import javax.naming.directory.DirContext;
import javax.naming.directory.ModificationItem;
import org.picketlink.idm.IdentityManager;
import org.picketlink.idm.credential.Password;
import org.picketlink.idm.ldap.internal.LDAPIdentityStore;
import org.picketlink.idm.ldap.internal.LDAPOperationManager;
import org.picketlink.idm.ldap.internal.LDAPPlainTextPasswordCredentialHandler;
import org.picketlink.idm.model.Account;
import org.picketlink.idm.model.AttributedType;
import org.picketlink.idm.model.basic.User;
import org.picketlink.idm.spi.IdentityContext;
import static org.picketlink.idm.IDMLog.CREDENTIAL_LOGGER;
import static org.picketlink.idm.model.basic.BasicModel.getUser;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class LDAPKeycloakCredentialHandler extends LDAPPlainTextPasswordCredentialHandler {
private static Method GET_BINDING_DN_METHOD;
private static Method GET_OPERATION_MANAGER_METHOD;
static {
GET_BINDING_DN_METHOD = getMethodOnLDAPStore("getBindingDN", AttributedType.class);
GET_OPERATION_MANAGER_METHOD = getMethodOnLDAPStore("getOperationManager");
}
// Used just in ActiveDirectory to put account into "enabled" state (aka userAccountControl=512, see http://support.microsoft.com/kb/305144/en ) after password update. If value is -1, it's ignored
private String userAccountControlAfterPasswordUpdate;
@Override
public void setup(LDAPIdentityStore store) {
if (store.getConfig().isActiveDirectory() || Boolean.getBoolean("keycloak.ldap.ad.skipUserAccountControlAfterPasswordUpdate")) {
String userAccountControlProp = System.getProperty("keycloak.ldap.ad.userAccountControlAfterPasswordUpdate");
this.userAccountControlAfterPasswordUpdate = userAccountControlProp!=null ? userAccountControlProp : "512";
CREDENTIAL_LOGGER.info("Will use userAccountControl=" + userAccountControlAfterPasswordUpdate + " after password update of user in Active Directory");
}
}
// Overridden as in Keycloak, we don't have Agents
@Override
protected Account getAccount(IdentityContext context, String loginName) {
IdentityManager identityManager = getIdentityManager(context);
if (CREDENTIAL_LOGGER.isDebugEnabled()) {
CREDENTIAL_LOGGER.debugf("Trying to find account [%s] using default account type [%s]", loginName, User.class);
}
return getUser(identityManager, loginName);
}
@Override
public void update(IdentityContext context, Account account, Password password, LDAPIdentityStore store, Date effectiveDate, Date expiryDate) {
super.update(context, account, password, store, effectiveDate, expiryDate);
if (userAccountControlAfterPasswordUpdate != null) {
ModificationItem[] mods = new ModificationItem[1];
BasicAttribute mod0 = new BasicAttribute("userAccountControl", userAccountControlAfterPasswordUpdate);
mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, mod0);
try {
String bindingDN = (String) GET_BINDING_DN_METHOD.invoke(store, account);
LDAPOperationManager operationManager = (LDAPOperationManager) GET_OPERATION_MANAGER_METHOD.invoke(store);
operationManager.modifyAttribute(bindingDN, mod0);
} catch (IllegalAccessException iae) {
throw new RuntimeException(iae);
} catch (InvocationTargetException ite) {
Throwable cause = ite.getTargetException() != null ? ite.getTargetException() : ite;
throw new RuntimeException(cause);
}
}
}
// Hack as methods are protected on LDAPIdentityStore :/
private static Method getMethodOnLDAPStore(String methodName, Class... classes) {
try {
Method m = LDAPIdentityStore.class.getDeclaredMethod(methodName, classes);
m.setAccessible(true);
return m;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

View file

@ -5,6 +5,11 @@ package org.keycloak.picketlink.idm;
*/
public class LdapConstants {
public static final String VENDOR = "vendor";
public static final String VENDOR_RHDS = "rhds";
public static final String VENDOR_ACTIVE_DIRECTORY = "ad";
public static final String VENDOR_OTHER = "other";
public static final String CONNECTION_URL = "connectionUrl";
public static final String BASE_DN = "baseDn";
public static final String USER_DN_SUFFIX = "userDnSuffix";

View file

@ -6,15 +6,15 @@ import java.util.concurrent.ConcurrentHashMap;
import org.jboss.logging.Logger;
import org.keycloak.models.RealmModel;
import org.keycloak.picketlink.idm.LDAPAgentIgnoreCredentialHandler;
import org.keycloak.picketlink.idm.LDAPKeycloakCredentialHandler;
import org.keycloak.picketlink.idm.LdapConstants;
import org.picketlink.idm.PartitionManager;
import org.picketlink.idm.config.IdentityConfigurationBuilder;
import org.picketlink.idm.config.LDAPIdentityStoreConfiguration;
import org.picketlink.idm.internal.DefaultPartitionManager;
import org.picketlink.idm.model.basic.User;
import static org.picketlink.common.constants.LDAPConstants.CN;
import static org.picketlink.common.constants.LDAPConstants.CREATE_TIMESTAMP;
import static org.picketlink.common.constants.LDAPConstants.EMAIL;
import static org.picketlink.common.constants.LDAPConstants.SN;
import static org.picketlink.common.constants.LDAPConstants.UID;
@ -39,8 +39,8 @@ public class PartitionManagerRegistry {
// Ldap config might have changed for the realm. In this case, we must re-initialize
if (context == null || !ldapConfig.equals(context.config)) {
logger.infof("Creating new partition manager for the realm: %s, LDAP Connection URL: %s, LDAP Base DN: %s", realm.getId(),
ldapConfig.get(LdapConstants.CONNECTION_URL), ldapConfig.get(LdapConstants.BASE_DN));
logger.infof("Creating new partition manager for the realm: %s, LDAP Connection URL: %s, LDAP Base DN: %s, LDAP Vendor: %s", realm.getId(),
ldapConfig.get(LdapConstants.CONNECTION_URL), ldapConfig.get(LdapConstants.BASE_DN), ldapConfig.get(LdapConstants.VENDOR));
PartitionManager manager = createPartitionManager(ldapConfig);
context = new PartitionManagerContext(ldapConfig, manager);
partitionManagers.put(realm.getId(), context);
@ -66,26 +66,43 @@ public class PartitionManagerRegistry {
checkSystemProperty("com.sun.jndi.ldap.connect.pool.protocol", "plain");
checkSystemProperty("com.sun.jndi.ldap.connect.pool.debug", "off");
String vendor = ldapConfig.get(LdapConstants.VENDOR);
// RHDS is using "nsuniqueid" as unique identifier instead of "entryUUID"
if (vendor != null && vendor.equals(LdapConstants.VENDOR_RHDS)) {
checkSystemProperty(LDAPIdentityStoreConfiguration.ENTRY_IDENTIFIER_ATTRIBUTE_NAME, "nsuniqueid");
}
boolean activeDirectory = vendor != null && vendor.equals(LdapConstants.VENDOR_ACTIVE_DIRECTORY);
// Try to compute properties based on LDAP server type, but still allow to override them through System properties TODO: Should allow better way than overriding from System properties. Perhaps init from XML?
String ldapLoginName = getNameOfLDAPAttribute("keycloak.ldap.idm.loginName", UID, CN, activeDirectory);
String ldapFirstName = getNameOfLDAPAttribute("keycloak.ldap.idm.firstName", CN, "givenName", activeDirectory);
String ldapLastName = getNameOfLDAPAttribute("keycloak.ldap.idm.lastName", SN, SN, activeDirectory);
String ldapEmail = getNameOfLDAPAttribute("keycloak.ldap.idm.email", EMAIL, EMAIL, activeDirectory);
logger.infof("LDAP Attributes mapping: loginName: %s, firstName: %s, lastName: %s, email: %s", ldapLoginName, ldapFirstName, ldapLastName, ldapEmail);
// Use same mapping for User and Agent for now
builder
.named("SIMPLE_LDAP_STORE_CONFIG")
.stores()
.ldap()
.connectionProperties(connectionProps)
.addCredentialHandler(LDAPAgentIgnoreCredentialHandler.class)
.addCredentialHandler(LDAPKeycloakCredentialHandler.class)
.baseDN(ldapConfig.get(LdapConstants.BASE_DN))
.bindDN(ldapConfig.get(LdapConstants.BIND_DN))
.bindCredential(ldapConfig.get(LdapConstants.BIND_CREDENTIAL))
.url(ldapConfig.get(LdapConstants.CONNECTION_URL))
.activeDirectory(activeDirectory)
.supportAllFeatures()
.mapping(User.class)
.baseDN(ldapConfig.get(LdapConstants.USER_DN_SUFFIX))
.objectClasses("inetOrgPerson", "organizationalPerson")
.attribute("loginName", UID, true)
.attribute("firstName", CN)
.attribute("lastName", SN)
.attribute("email", EMAIL)
.readOnlyAttribute("createdDate", CREATE_TIMESTAMP);
.attribute("loginName", ldapLoginName, true)
.attribute("firstName", ldapFirstName)
.attribute("lastName", ldapLastName)
.attribute("email", ldapEmail);
return new DefaultPartitionManager(builder.buildAll());
}
@ -96,6 +113,16 @@ public class PartitionManagerRegistry {
}
}
private String getNameOfLDAPAttribute(String systemPropertyName, String defaultAttrName, String defaultAttrNameInActiveDirectory, boolean activeDirectory) {
// System property has biggest priority if available
String sysProperty = System.getProperty(systemPropertyName);
if (sysProperty != null) {
return sysProperty;
}
return activeDirectory ? defaultAttrNameInActiveDirectory : defaultAttrName;
}
private class PartitionManagerContext {
private PartitionManagerContext(Map<String,String> config, PartitionManager manager) {

View file

@ -1,6 +1,7 @@
package org.keycloak.picketlink.realm;
import org.keycloak.models.RealmModel;
import org.keycloak.picketlink.AbstractIdentityManagerProvider;
import org.keycloak.picketlink.IdentityManagerProvider;
import org.picketlink.idm.IdentityManager;
import org.picketlink.idm.PartitionManager;
@ -8,7 +9,7 @@ import org.picketlink.idm.PartitionManager;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class RealmIdentityManagerProvider implements IdentityManagerProvider {
public class RealmIdentityManagerProvider extends AbstractIdentityManagerProvider {
private final PartitionManagerRegistry partitionManagerRegistry;
@ -17,12 +18,7 @@ public class RealmIdentityManagerProvider implements IdentityManagerProvider {
}
@Override
public IdentityManager getIdentityManager(RealmModel realm) {
PartitionManager partitionManager = partitionManagerRegistry.getPartitionManager(realm);
return partitionManager.createIdentityManager();
}
@Override
public void close() {
protected PartitionManager getPartitionManager(RealmModel realm) {
return partitionManagerRegistry.getPartitionManager(realm);
}
}

View file

@ -62,7 +62,7 @@ public class AuthProvidersIntegrationTest {
// Configure LDAP
ldapRule.getEmbeddedServer().setupLdapInRealm(appRealm);
LdapTestUtils.setLdapPassword(providerSession, appRealm, "john", "password");
LdapTestUtils.setLdapPassword(providerSession, appRealm, "johnkeycloak", "password");
}
});
@ -135,7 +135,7 @@ public class AuthProvidersIntegrationTest {
@Test
public void loginLdap() {
loginPage.open();
loginPage.login("john", "password");
loginPage.login("johnkeycloak", "password");
Assert.assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType());
Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
@ -186,20 +186,19 @@ public class AuthProvidersIntegrationTest {
@Test
public void passwordChangeLdap() throws Exception {
changePasswordPage.open();
loginPage.login("john", "password");
loginPage.login("johnkeycloak", "password");
changePasswordPage.changePassword("password", "new-password", "new-password");
Assert.assertEquals("Your password has been updated", profilePage.getSuccess());
changePasswordPage.logout();
// TODO: Disabled until https://issues.jboss.org/browse/PLINK-384 is released and updated
// loginPage.open();
// loginPage.login("john", "password");
// Assert.assertEquals("Invalid username or password.", loginPage.getError());
loginPage.open();
loginPage.login("johnkeycloak", "password");
Assert.assertEquals("Invalid username or password.", loginPage.getError());
loginPage.open();
loginPage.login("john", "new-password");
loginPage.login("johnkeycloak", "new-password");
Assert.assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType());
}

View file

@ -19,12 +19,12 @@ objectclass: top
objectclass: organizationalUnit
ou: Groups
dn: uid=john,ou=People,dc=keycloak,dc=org
dn: uid=johnkeycloak,ou=People,dc=keycloak,dc=org
objectclass: top
objectclass: uidObject
objectclass: person
objectclass: inetOrgPerson
uid: john
uid: johnkeycloak
cn: John
sn: Doe
mail: john@email.org