commit
2eae9c17a4
10 changed files with 484 additions and 201 deletions
|
@ -1,6 +1,7 @@
|
||||||
package org.keycloak.federation.ldap;
|
package org.keycloak.federation.ldap;
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
|
import org.keycloak.models.UserCredentialValueModel;
|
||||||
import org.keycloak.models.UserFederationProvider;
|
import org.keycloak.models.UserFederationProvider;
|
||||||
import org.keycloak.models.UserFederationProviderModel;
|
import org.keycloak.models.UserFederationProviderModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
@ -19,6 +20,7 @@ import org.picketlink.idm.model.basic.BasicModel;
|
||||||
import org.picketlink.idm.model.basic.User;
|
import org.picketlink.idm.model.basic.User;
|
||||||
import org.picketlink.idm.query.IdentityQuery;
|
import org.picketlink.idm.query.IdentityQuery;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
|
@ -35,10 +37,12 @@ public class LDAPFederationProvider implements UserFederationProvider {
|
||||||
private static final Logger logger = Logger.getLogger(LDAPFederationProvider.class);
|
private static final Logger logger = Logger.getLogger(LDAPFederationProvider.class);
|
||||||
public static final String LDAP_ID = "LDAP_ID";
|
public static final String LDAP_ID = "LDAP_ID";
|
||||||
public static final String SYNC_REGISTRATIONS = "syncRegistrations";
|
public static final String SYNC_REGISTRATIONS = "syncRegistrations";
|
||||||
|
public static final String EDIT_MODE = "editMode";
|
||||||
|
|
||||||
protected KeycloakSession session;
|
protected KeycloakSession session;
|
||||||
protected UserFederationProviderModel model;
|
protected UserFederationProviderModel model;
|
||||||
protected PartitionManager partitionManager;
|
protected PartitionManager partitionManager;
|
||||||
|
protected EditMode editMode;
|
||||||
|
|
||||||
protected static final Set<String> supportedCredentialTypes = new HashSet<String>();
|
protected static final Set<String> supportedCredentialTypes = new HashSet<String>();
|
||||||
|
|
||||||
|
@ -51,6 +55,9 @@ public class LDAPFederationProvider implements UserFederationProvider {
|
||||||
this.session = session;
|
this.session = session;
|
||||||
this.model = model;
|
this.model = model;
|
||||||
this.partitionManager = partitionManager;
|
this.partitionManager = partitionManager;
|
||||||
|
String editModeString = model.getConfig().get(EDIT_MODE);
|
||||||
|
if (editModeString == null) editMode = EditMode.READ_ONLY;
|
||||||
|
editMode = EditMode.valueOf(editModeString);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ModelException convertIDMException(IdentityManagementException ie) {
|
private ModelException convertIDMException(IdentityManagementException ie) {
|
||||||
|
@ -77,22 +84,37 @@ public class LDAPFederationProvider implements UserFederationProvider {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UserModel proxy(UserModel local) {
|
public UserModel proxy(UserModel local) {
|
||||||
// todo
|
switch (editMode) {
|
||||||
return new LDAPUserModelDelegate(local, this);
|
case READ_ONLY:
|
||||||
|
return new ReadonlyLDAPUserModelDelegate(local, this);
|
||||||
|
case WRITABLE:
|
||||||
|
return new WritableLDAPUserModelDelegate(local, this);
|
||||||
|
case UNSYNCED:
|
||||||
|
return new UnsyncedLDAPUserModelDelegate(local, this);
|
||||||
|
}
|
||||||
|
return local;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<String> getSupportedCredentialTypes() {
|
public Set<String> getSupportedCredentialTypes(UserModel local) {
|
||||||
|
if (editMode == EditMode.UNSYNCED ) {
|
||||||
|
for (UserCredentialValueModel cred : local.getCredentialsDirectly()) {
|
||||||
|
if (cred.getType().equals(UserCredentialModel.PASSWORD)) {
|
||||||
|
return Collections.emptySet();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return supportedCredentialTypes;
|
return supportedCredentialTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean synchronizeRegistrations() {
|
public boolean synchronizeRegistrations() {
|
||||||
return "true".equalsIgnoreCase(model.getConfig().get(SYNC_REGISTRATIONS));
|
return "true".equalsIgnoreCase(model.getConfig().get(SYNC_REGISTRATIONS)) && editMode == EditMode.WRITABLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UserModel register(RealmModel realm, UserModel user) {
|
public UserModel register(RealmModel realm, UserModel user) {
|
||||||
|
if (editMode == EditMode.READ_ONLY || editMode == EditMode.UNSYNCED) throw new IllegalStateException("Registration is not supported by this ldap server");;
|
||||||
if (!synchronizeRegistrations()) throw new IllegalStateException("Registration is not supported by this ldap server");
|
if (!synchronizeRegistrations()) throw new IllegalStateException("Registration is not supported by this ldap server");
|
||||||
IdentityManager identityManager = getIdentityManager();
|
IdentityManager identityManager = getIdentityManager();
|
||||||
|
|
||||||
|
@ -112,6 +134,8 @@ public class LDAPFederationProvider implements UserFederationProvider {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean removeUser(RealmModel realm, UserModel user) {
|
public boolean removeUser(RealmModel realm, UserModel user) {
|
||||||
|
if (editMode == EditMode.READ_ONLY || editMode == EditMode.UNSYNCED) return false;
|
||||||
|
|
||||||
IdentityManager identityManager = getIdentityManager();
|
IdentityManager identityManager = getIdentityManager();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
package org.keycloak.federation.ldap;
|
||||||
|
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
|
import org.keycloak.models.ModelException;
|
||||||
|
import org.keycloak.models.UserCredentialModel;
|
||||||
|
import org.keycloak.models.UserModel;
|
||||||
|
import org.keycloak.models.utils.UserModelDelegate;
|
||||||
|
import org.picketlink.idm.IdentityManagementException;
|
||||||
|
import org.picketlink.idm.IdentityManager;
|
||||||
|
import org.picketlink.idm.credential.Password;
|
||||||
|
import org.picketlink.idm.credential.TOTPCredential;
|
||||||
|
import org.picketlink.idm.model.basic.BasicModel;
|
||||||
|
import org.picketlink.idm.model.basic.User;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class ReadonlyLDAPUserModelDelegate extends UserModelDelegate implements UserModel {
|
||||||
|
private static final Logger logger = Logger.getLogger(ReadonlyLDAPUserModelDelegate.class);
|
||||||
|
|
||||||
|
protected LDAPFederationProvider provider;
|
||||||
|
|
||||||
|
public ReadonlyLDAPUserModelDelegate(UserModel delegate, LDAPFederationProvider provider) {
|
||||||
|
super(delegate);
|
||||||
|
this.provider = provider;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setUsername(String username) {
|
||||||
|
throw new IllegalStateException("Federated storage is not writable");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setLastName(String lastName) {
|
||||||
|
throw new IllegalStateException("Federated storage is not writable");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setFirstName(String first) {
|
||||||
|
throw new IllegalStateException("Federated storage is not writable");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateCredential(UserCredentialModel cred) {
|
||||||
|
if (provider.getSupportedCredentialTypes(delegate).contains(cred.getType())) {
|
||||||
|
throw new IllegalStateException("Federated storage is not writable");
|
||||||
|
}
|
||||||
|
delegate.updateCredential(cred);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setEmail(String email) {
|
||||||
|
throw new IllegalStateException("Federated storage is not writable");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
package org.keycloak.federation.ldap;
|
||||||
|
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
|
import org.keycloak.models.ModelException;
|
||||||
|
import org.keycloak.models.UserCredentialModel;
|
||||||
|
import org.keycloak.models.UserModel;
|
||||||
|
import org.keycloak.models.utils.UserModelDelegate;
|
||||||
|
import org.picketlink.idm.IdentityManagementException;
|
||||||
|
import org.picketlink.idm.IdentityManager;
|
||||||
|
import org.picketlink.idm.credential.Password;
|
||||||
|
import org.picketlink.idm.credential.TOTPCredential;
|
||||||
|
import org.picketlink.idm.model.basic.BasicModel;
|
||||||
|
import org.picketlink.idm.model.basic.User;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class UnsyncedLDAPUserModelDelegate extends UserModelDelegate implements UserModel {
|
||||||
|
private static final Logger logger = Logger.getLogger(UnsyncedLDAPUserModelDelegate.class);
|
||||||
|
|
||||||
|
protected LDAPFederationProvider provider;
|
||||||
|
|
||||||
|
public UnsyncedLDAPUserModelDelegate(UserModel delegate, LDAPFederationProvider provider) {
|
||||||
|
super(delegate);
|
||||||
|
this.provider = provider;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,132 @@
|
||||||
|
package org.keycloak.federation.ldap;
|
||||||
|
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
|
import org.keycloak.models.ApplicationModel;
|
||||||
|
import org.keycloak.models.AuthenticationLinkModel;
|
||||||
|
import org.keycloak.models.ModelException;
|
||||||
|
import org.keycloak.models.RoleModel;
|
||||||
|
import org.keycloak.models.UserCredentialModel;
|
||||||
|
import org.keycloak.models.UserCredentialValueModel;
|
||||||
|
import org.keycloak.models.UserModel;
|
||||||
|
import org.keycloak.models.utils.UserModelDelegate;
|
||||||
|
import org.picketlink.idm.IdentityManagementException;
|
||||||
|
import org.picketlink.idm.IdentityManager;
|
||||||
|
import org.picketlink.idm.credential.Password;
|
||||||
|
import org.picketlink.idm.credential.TOTPCredential;
|
||||||
|
import org.picketlink.idm.model.basic.BasicModel;
|
||||||
|
import org.picketlink.idm.model.basic.User;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class WritableLDAPUserModelDelegate extends UserModelDelegate implements UserModel {
|
||||||
|
private static final Logger logger = Logger.getLogger(WritableLDAPUserModelDelegate.class);
|
||||||
|
|
||||||
|
protected LDAPFederationProvider provider;
|
||||||
|
|
||||||
|
public WritableLDAPUserModelDelegate(UserModel delegate, LDAPFederationProvider provider) {
|
||||||
|
super(delegate);
|
||||||
|
this.provider = provider;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setUsername(String username) {
|
||||||
|
IdentityManager identityManager = provider.getIdentityManager();
|
||||||
|
|
||||||
|
try {
|
||||||
|
User picketlinkUser = BasicModel.getUser(identityManager, delegate.getUsername());
|
||||||
|
if (picketlinkUser == null) {
|
||||||
|
throw new IllegalStateException("User not found in LDAP storage!");
|
||||||
|
}
|
||||||
|
picketlinkUser.setLoginName(username);
|
||||||
|
identityManager.update(picketlinkUser);
|
||||||
|
} catch (IdentityManagementException ie) {
|
||||||
|
throw new ModelException(ie);
|
||||||
|
}
|
||||||
|
delegate.setUsername(username);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setLastName(String lastName) {
|
||||||
|
IdentityManager identityManager = provider.getIdentityManager();
|
||||||
|
|
||||||
|
try {
|
||||||
|
User picketlinkUser = BasicModel.getUser(identityManager, delegate.getUsername());
|
||||||
|
if (picketlinkUser == null) {
|
||||||
|
throw new IllegalStateException("User not found in LDAP storage!");
|
||||||
|
}
|
||||||
|
picketlinkUser.setLastName(lastName);
|
||||||
|
identityManager.update(picketlinkUser);
|
||||||
|
} catch (IdentityManagementException ie) {
|
||||||
|
throw new ModelException(ie);
|
||||||
|
}
|
||||||
|
delegate.setLastName(lastName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setFirstName(String first) {
|
||||||
|
IdentityManager identityManager = provider.getIdentityManager();
|
||||||
|
|
||||||
|
try {
|
||||||
|
User picketlinkUser = BasicModel.getUser(identityManager, delegate.getUsername());
|
||||||
|
if (picketlinkUser == null) {
|
||||||
|
throw new IllegalStateException("User not found in LDAP storage!");
|
||||||
|
}
|
||||||
|
picketlinkUser.setFirstName(first);
|
||||||
|
identityManager.update(picketlinkUser);
|
||||||
|
} catch (IdentityManagementException ie) {
|
||||||
|
throw new ModelException(ie);
|
||||||
|
}
|
||||||
|
delegate.setFirstName(first);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateCredential(UserCredentialModel cred) {
|
||||||
|
if (!provider.getSupportedCredentialTypes(delegate).contains(cred.getType())) {
|
||||||
|
delegate.updateCredential(cred);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
IdentityManager identityManager = provider.getIdentityManager();
|
||||||
|
|
||||||
|
try {
|
||||||
|
User picketlinkUser = BasicModel.getUser(identityManager, getUsername());
|
||||||
|
if (picketlinkUser == null) {
|
||||||
|
logger.debugf("User '%s' doesn't exists. Skip password update", getUsername());
|
||||||
|
throw new IllegalStateException("User doesn't exist in LDAP storage");
|
||||||
|
}
|
||||||
|
if (cred.getType().equals(UserCredentialModel.PASSWORD)) {
|
||||||
|
identityManager.updateCredential(picketlinkUser, new Password(cred.getValue().toCharArray()));
|
||||||
|
} else if (cred.getType().equals(UserCredentialModel.TOTP)) {
|
||||||
|
TOTPCredential credential = new TOTPCredential(cred.getValue());
|
||||||
|
credential.setDevice(cred.getDevice());
|
||||||
|
identityManager.updateCredential(picketlinkUser, credential);
|
||||||
|
}
|
||||||
|
} catch (IdentityManagementException ie) {
|
||||||
|
throw new ModelException(ie);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setEmail(String email) {
|
||||||
|
IdentityManager identityManager = provider.getIdentityManager();
|
||||||
|
|
||||||
|
try {
|
||||||
|
User picketlinkUser = BasicModel.getUser(identityManager, delegate.getUsername());
|
||||||
|
if (picketlinkUser == null) {
|
||||||
|
throw new IllegalStateException("User not found in LDAP storage!");
|
||||||
|
}
|
||||||
|
picketlinkUser.setEmail(email);
|
||||||
|
identityManager.update(picketlinkUser);
|
||||||
|
} catch (IdentityManagementException ie) {
|
||||||
|
throw new ModelException(ie);
|
||||||
|
}
|
||||||
|
delegate.setEmail(email);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -33,6 +33,19 @@
|
||||||
<input class="form-control" id="priority" type="text" ng-model="instance.priority">
|
<input class="form-control" id="priority" type="text" ng-model="instance.priority">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-sm-2 control-label" for="editMode">Edit mode</label>
|
||||||
|
<div class="col-sm-4">
|
||||||
|
<div class="select-kc">
|
||||||
|
<select id="editMode"
|
||||||
|
ng-model="instance.config.editMode">
|
||||||
|
<option>READ_ONLY</option>
|
||||||
|
<option>WRITABLE</option>
|
||||||
|
<option>UNSYNCED</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="form-group clearfix block">
|
<div class="form-group clearfix block">
|
||||||
<label class="col-sm-2 control-label" for="syncRegistrations">Sync Registrations</label>
|
<label class="col-sm-2 control-label" for="syncRegistrations">Sync Registrations</label>
|
||||||
<div class="col-sm-4">
|
<div class="col-sm-4">
|
||||||
|
|
|
@ -307,7 +307,7 @@ public class UserFederationManager implements UserProvider {
|
||||||
UserFederationProvider link = getFederationLink(realm, user);
|
UserFederationProvider link = getFederationLink(realm, user);
|
||||||
if (link != null) {
|
if (link != null) {
|
||||||
validateUser(realm, user);
|
validateUser(realm, user);
|
||||||
if (link.getSupportedCredentialTypes().size() > 0) {
|
if (link.getSupportedCredentialTypes(user).size() > 0) {
|
||||||
List<UserCredentialModel> fedCreds = new ArrayList<UserCredentialModel>();
|
List<UserCredentialModel> fedCreds = new ArrayList<UserCredentialModel>();
|
||||||
List<UserCredentialModel> localCreds = new ArrayList<UserCredentialModel>();
|
List<UserCredentialModel> localCreds = new ArrayList<UserCredentialModel>();
|
||||||
for (UserCredentialModel cred : input) {
|
for (UserCredentialModel cred : input) {
|
||||||
|
@ -331,7 +331,7 @@ public class UserFederationManager implements UserProvider {
|
||||||
UserFederationProvider link = getFederationLink(realm, user);
|
UserFederationProvider link = getFederationLink(realm, user);
|
||||||
if (link != null) {
|
if (link != null) {
|
||||||
validateUser(realm, user);
|
validateUser(realm, user);
|
||||||
Set<String> supportedCredentialTypes = link.getSupportedCredentialTypes();
|
Set<String> supportedCredentialTypes = link.getSupportedCredentialTypes(user);
|
||||||
if (supportedCredentialTypes.size() > 0) {
|
if (supportedCredentialTypes.size() > 0) {
|
||||||
List<UserCredentialModel> fedCreds = new ArrayList<UserCredentialModel>();
|
List<UserCredentialModel> fedCreds = new ArrayList<UserCredentialModel>();
|
||||||
List<UserCredentialModel> localCreds = new ArrayList<UserCredentialModel>();
|
List<UserCredentialModel> localCreds = new ArrayList<UserCredentialModel>();
|
||||||
|
|
|
@ -19,6 +19,28 @@ public interface UserFederationProvider extends Provider {
|
||||||
public static final String FIRST_NAME = UserModel.EMAIL;
|
public static final String FIRST_NAME = UserModel.EMAIL;
|
||||||
public static final String LAST_NAME = UserModel.LAST_NAME;
|
public static final String LAST_NAME = UserModel.LAST_NAME;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optional type that can be by implementations to describe edit mode of federation storage
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
enum EditMode {
|
||||||
|
/**
|
||||||
|
* federation storage is read-only
|
||||||
|
*/
|
||||||
|
READ_ONLY,
|
||||||
|
/**
|
||||||
|
* federation storage is writable
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
WRITABLE,
|
||||||
|
/**
|
||||||
|
* updates to user are stored locally and not synced with federation storage.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
UNSYNCED
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gives the provider an option to proxy UserModels loaded from local storage.
|
* Gives the provider an option to proxy UserModels loaded from local storage.
|
||||||
* This method is called whenever a UserModel is pulled from local storage.
|
* This method is called whenever a UserModel is pulled from local storage.
|
||||||
|
@ -81,12 +103,12 @@ public interface UserFederationProvider extends Provider {
|
||||||
boolean isValid(UserModel local);
|
boolean isValid(UserModel local);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* What UserCredentialModel types are supported by this provider. Keycloak will only call
|
* What UserCredentialModel types should be handled by this provider for this user? Keycloak will only call
|
||||||
* validCredentials() with the credential types specified in this method.
|
* validCredentials() with the credential types specified in this method.
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
Set<String> getSupportedCredentialTypes();
|
Set<String> getSupportedCredentialTypes(UserModel user);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate credentials for this user.
|
* Validate credentials for this user.
|
||||||
|
@ -98,4 +120,6 @@ public interface UserFederationProvider extends Provider {
|
||||||
*/
|
*/
|
||||||
boolean validCredentials(RealmModel realm, UserModel user, List<UserCredentialModel> input);
|
boolean validCredentials(RealmModel realm, UserModel user, List<UserCredentialModel> input);
|
||||||
boolean validCredentials(RealmModel realm, UserModel user, UserCredentialModel... input);
|
boolean validCredentials(RealmModel realm, UserModel user, UserCredentialModel... input);
|
||||||
void close();}
|
void close();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -1,19 +1,11 @@
|
||||||
package org.keycloak.federation.ldap;
|
package org.keycloak.models.utils;
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
|
||||||
import org.keycloak.models.ApplicationModel;
|
import org.keycloak.models.ApplicationModel;
|
||||||
import org.keycloak.models.AuthenticationLinkModel;
|
import org.keycloak.models.AuthenticationLinkModel;
|
||||||
import org.keycloak.models.ModelException;
|
|
||||||
import org.keycloak.models.RoleModel;
|
import org.keycloak.models.RoleModel;
|
||||||
import org.keycloak.models.UserCredentialModel;
|
import org.keycloak.models.UserCredentialModel;
|
||||||
import org.keycloak.models.UserCredentialValueModel;
|
import org.keycloak.models.UserCredentialValueModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.picketlink.idm.IdentityManagementException;
|
|
||||||
import org.picketlink.idm.IdentityManager;
|
|
||||||
import org.picketlink.idm.credential.Password;
|
|
||||||
import org.picketlink.idm.credential.TOTPCredential;
|
|
||||||
import org.picketlink.idm.model.basic.BasicModel;
|
|
||||||
import org.picketlink.idm.model.basic.User;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -23,15 +15,11 @@ import java.util.Set;
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
public class LDAPUserModelDelegate implements UserModel {
|
public class UserModelDelegate implements UserModel {
|
||||||
private static final Logger logger = Logger.getLogger(LDAPUserModelDelegate.class);
|
|
||||||
|
|
||||||
protected UserModel delegate;
|
protected UserModel delegate;
|
||||||
protected LDAPFederationProvider provider;
|
|
||||||
|
|
||||||
public LDAPUserModelDelegate(UserModel delegate, LDAPFederationProvider provider) {
|
public UserModelDelegate(UserModel delegate) {
|
||||||
this.delegate = delegate;
|
this.delegate = delegate;
|
||||||
this.provider = provider;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -40,13 +28,33 @@ public class LDAPUserModelDelegate implements UserModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setAttribute(String name, String value) {
|
public String getUsername() {
|
||||||
delegate.setAttribute(name, value);
|
return delegate.getUsername();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEmailVerified() {
|
public void setUsername(String username) {
|
||||||
return delegate.isEmailVerified();
|
delegate.setUsername(username);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return delegate.isEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isTotp() {
|
||||||
|
return delegate.isTotp();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setEnabled(boolean enabled) {
|
||||||
|
delegate.setEnabled(enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAttribute(String name, String value) {
|
||||||
|
delegate.setAttribute(name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -54,14 +62,89 @@ public class LDAPUserModelDelegate implements UserModel {
|
||||||
delegate.removeAttribute(name);
|
delegate.removeAttribute(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAttribute(String name) {
|
||||||
|
return delegate.getAttribute(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, String> getAttributes() {
|
||||||
|
return delegate.getAttributes();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<RequiredAction> getRequiredActions() {
|
||||||
|
return delegate.getRequiredActions();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addRequiredAction(RequiredAction action) {
|
||||||
|
delegate.addRequiredAction(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeRequiredAction(RequiredAction action) {
|
||||||
|
delegate.removeRequiredAction(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFirstName() {
|
||||||
|
return delegate.getFirstName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setFirstName(String firstName) {
|
||||||
|
delegate.setFirstName(firstName);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getLastName() {
|
public String getLastName() {
|
||||||
return delegate.getLastName();
|
return delegate.getLastName();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setFederationLink(String link) {
|
public void setLastName(String lastName) {
|
||||||
delegate.setFederationLink(link);
|
delegate.setLastName(lastName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getEmail() {
|
||||||
|
return delegate.getEmail();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setEmail(String email) {
|
||||||
|
delegate.setEmail(email);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmailVerified() {
|
||||||
|
return delegate.isEmailVerified();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setEmailVerified(boolean verified) {
|
||||||
|
delegate.setEmailVerified(verified);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setTotp(boolean totp) {
|
||||||
|
delegate.setTotp(totp);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateCredential(UserCredentialModel cred) {
|
||||||
|
delegate.updateCredential(cred);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<UserCredentialValueModel> getCredentialsDirectly() {
|
||||||
|
return delegate.getCredentialsDirectly();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateCredentialDirectly(UserCredentialValueModel cred) {
|
||||||
|
delegate.updateCredentialDirectly(cred);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -70,8 +153,18 @@ public class LDAPUserModelDelegate implements UserModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, String> getAttributes() {
|
public void setAuthenticationLink(AuthenticationLinkModel authenticationLink) {
|
||||||
return delegate.getAttributes();
|
delegate.setAuthenticationLink(authenticationLink);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<RoleModel> getRealmRoleMappings() {
|
||||||
|
return delegate.getRealmRoleMappings();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<RoleModel> getApplicationRoleMappings(ApplicationModel app) {
|
||||||
|
return delegate.getApplicationRoleMappings(app);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -85,13 +178,8 @@ public class LDAPUserModelDelegate implements UserModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setEnabled(boolean enabled) {
|
public Set<RoleModel> getRoleMappings() {
|
||||||
delegate.setEnabled(enabled);
|
return delegate.getRoleMappings();
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void removeRequiredAction(UserModel.RequiredAction action) {
|
|
||||||
delegate.removeRequiredAction(action);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -99,171 +187,13 @@ public class LDAPUserModelDelegate implements UserModel {
|
||||||
delegate.deleteRoleMapping(role);
|
delegate.deleteRoleMapping(role);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setUsername(String username) {
|
|
||||||
IdentityManager identityManager = provider.getIdentityManager();
|
|
||||||
|
|
||||||
try {
|
|
||||||
User picketlinkUser = BasicModel.getUser(identityManager, delegate.getUsername());
|
|
||||||
if (picketlinkUser == null) {
|
|
||||||
throw new IllegalStateException("User not found in LDAP storage!");
|
|
||||||
}
|
|
||||||
picketlinkUser.setLoginName(username);
|
|
||||||
identityManager.update(picketlinkUser);
|
|
||||||
} catch (IdentityManagementException ie) {
|
|
||||||
throw new ModelException(ie);
|
|
||||||
}
|
|
||||||
delegate.setUsername(username);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEnabled() {
|
|
||||||
return delegate.isEnabled();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getFirstName() {
|
|
||||||
return delegate.getFirstName();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setLastName(String lastName) {
|
|
||||||
IdentityManager identityManager = provider.getIdentityManager();
|
|
||||||
|
|
||||||
try {
|
|
||||||
User picketlinkUser = BasicModel.getUser(identityManager, delegate.getUsername());
|
|
||||||
if (picketlinkUser == null) {
|
|
||||||
throw new IllegalStateException("User not found in LDAP storage!");
|
|
||||||
}
|
|
||||||
picketlinkUser.setLastName(lastName);
|
|
||||||
identityManager.update(picketlinkUser);
|
|
||||||
} catch (IdentityManagementException ie) {
|
|
||||||
throw new ModelException(ie);
|
|
||||||
}
|
|
||||||
delegate.setLastName(lastName);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setEmailVerified(boolean verified) {
|
|
||||||
delegate.setEmailVerified(verified);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void updateCredential(UserCredentialModel cred) {
|
|
||||||
if (!provider.getSupportedCredentialTypes().contains(cred.getType())) {
|
|
||||||
delegate.updateCredential(cred);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
IdentityManager identityManager = provider.getIdentityManager();
|
|
||||||
|
|
||||||
try {
|
|
||||||
User picketlinkUser = BasicModel.getUser(identityManager, getUsername());
|
|
||||||
if (picketlinkUser == null) {
|
|
||||||
logger.debugf("User '%s' doesn't exists. Skip password update", getUsername());
|
|
||||||
throw new IllegalStateException("User doesn't exist in LDAP storage");
|
|
||||||
}
|
|
||||||
if (cred.getType().equals(UserCredentialModel.PASSWORD)) {
|
|
||||||
identityManager.updateCredential(picketlinkUser, new Password(cred.getValue().toCharArray()));
|
|
||||||
} else if (cred.getType().equals(UserCredentialModel.TOTP)) {
|
|
||||||
TOTPCredential credential = new TOTPCredential(cred.getValue());
|
|
||||||
credential.setDevice(cred.getDevice());
|
|
||||||
identityManager.updateCredential(picketlinkUser, credential);
|
|
||||||
}
|
|
||||||
} catch (IdentityManagementException ie) {
|
|
||||||
throw new ModelException(ie);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setEmail(String email) {
|
|
||||||
IdentityManager identityManager = provider.getIdentityManager();
|
|
||||||
|
|
||||||
try {
|
|
||||||
User picketlinkUser = BasicModel.getUser(identityManager, delegate.getUsername());
|
|
||||||
if (picketlinkUser == null) {
|
|
||||||
throw new IllegalStateException("User not found in LDAP storage!");
|
|
||||||
}
|
|
||||||
picketlinkUser.setEmail(email);
|
|
||||||
identityManager.update(picketlinkUser);
|
|
||||||
} catch (IdentityManagementException ie) {
|
|
||||||
throw new ModelException(ie);
|
|
||||||
}
|
|
||||||
delegate.setEmail(email);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addRequiredAction(UserModel.RequiredAction action) {
|
|
||||||
delegate.addRequiredAction(action);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<UserCredentialValueModel> getCredentialsDirectly() {
|
|
||||||
return delegate.getCredentialsDirectly();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isTotp() {
|
|
||||||
return delegate.isTotp();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setFirstName(String firstName) {
|
|
||||||
delegate.setFirstName(firstName);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Set<UserModel.RequiredAction> getRequiredActions() {
|
|
||||||
return delegate.getRequiredActions();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getEmail() {
|
|
||||||
return delegate.getEmail();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setTotp(boolean totp) {
|
|
||||||
delegate.setTotp(totp);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setAuthenticationLink(AuthenticationLinkModel authenticationLink) {
|
|
||||||
delegate.setAuthenticationLink(authenticationLink);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getUsername() {
|
|
||||||
return delegate.getUsername();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getFederationLink() {
|
public String getFederationLink() {
|
||||||
return delegate.getFederationLink();
|
return delegate.getFederationLink();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<RoleModel> getRealmRoleMappings() {
|
public void setFederationLink(String link) {
|
||||||
return delegate.getRealmRoleMappings();
|
delegate.setFederationLink(link);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Set<RoleModel> getRoleMappings() {
|
|
||||||
return delegate.getRoleMappings();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Set<RoleModel> getApplicationRoleMappings(ApplicationModel app) {
|
|
||||||
return delegate.getApplicationRoleMappings(app);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getAttribute(String name) {
|
|
||||||
return delegate.getAttribute(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void updateCredentialDirectly(UserCredentialValueModel cred) {
|
|
||||||
delegate.updateCredentialDirectly(cred);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -67,7 +67,7 @@ public class DummyUserFederationProvider implements UserFederationProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<String> getSupportedCredentialTypes() {
|
public Set<String> getSupportedCredentialTypes(UserModel user) {
|
||||||
return Collections.emptySet();
|
return Collections.emptySet();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,8 @@ import org.junit.runners.MethodSorters;
|
||||||
import org.keycloak.OAuth2Constants;
|
import org.keycloak.OAuth2Constants;
|
||||||
import org.keycloak.federation.ldap.LDAPFederationProvider;
|
import org.keycloak.federation.ldap.LDAPFederationProvider;
|
||||||
import org.keycloak.federation.ldap.LDAPFederationProviderFactory;
|
import org.keycloak.federation.ldap.LDAPFederationProviderFactory;
|
||||||
|
import org.keycloak.models.UserCredentialValueModel;
|
||||||
|
import org.keycloak.models.UserFederationProvider;
|
||||||
import org.keycloak.models.UserFederationProviderModel;
|
import org.keycloak.models.UserFederationProviderModel;
|
||||||
import org.keycloak.testutils.LDAPEmbeddedServer;
|
import org.keycloak.testutils.LDAPEmbeddedServer;
|
||||||
import org.keycloak.testsuite.LDAPTestUtils;
|
import org.keycloak.testsuite.LDAPTestUtils;
|
||||||
|
@ -66,10 +68,11 @@ public class FederationProvidersIntegrationTest {
|
||||||
String vendor = ldapServer.getVendor();
|
String vendor = ldapServer.getVendor();
|
||||||
ldapConfig.put(LDAPConstants.VENDOR, vendor);
|
ldapConfig.put(LDAPConstants.VENDOR, vendor);
|
||||||
ldapConfig.put(LDAPFederationProvider.SYNC_REGISTRATIONS, "true");
|
ldapConfig.put(LDAPFederationProvider.SYNC_REGISTRATIONS, "true");
|
||||||
|
ldapConfig.put(LDAPFederationProvider.EDIT_MODE, UserFederationProvider.EditMode.WRITABLE.toString());
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
ldapModel = appRealm.addUserFederationProvider(LDAPFederationProviderFactory.PROVIDER_NAME, ldapConfig, 0, null);
|
ldapModel = appRealm.addUserFederationProvider(LDAPFederationProviderFactory.PROVIDER_NAME, ldapConfig, 0, "test-ldap");
|
||||||
|
|
||||||
// Configure LDAP
|
// Configure LDAP
|
||||||
ldapRule.getEmbeddedServer().setupLdapInRealm(appRealm);
|
ldapRule.getEmbeddedServer().setupLdapInRealm(appRealm);
|
||||||
|
@ -201,4 +204,76 @@ public class FederationProvidersIntegrationTest {
|
||||||
Assert.assertEquals(user.getFederationLink(), ldapModel.getId());
|
Assert.assertEquals(user.getFederationLink(), ldapModel.getId());
|
||||||
keycloakRule.stopSession(session, false);
|
keycloakRule.stopSession(session, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReadonly() {
|
||||||
|
KeycloakSession session = keycloakRule.startSession();
|
||||||
|
RealmModel appRealm = session.realms().getRealmByName("test");
|
||||||
|
|
||||||
|
UserFederationProviderModel model = new UserFederationProviderModel(ldapModel.getId(), ldapModel.getProviderName(), ldapModel.getConfig(), ldapModel.getPriority(), ldapModel.getDisplayName());
|
||||||
|
model.getConfig().put(LDAPFederationProvider.EDIT_MODE, UserFederationProvider.EditMode.READ_ONLY.toString());
|
||||||
|
appRealm.updateUserFederationProvider(model);
|
||||||
|
UserModel user = session.users().getUserByUsername("johnkeycloak", appRealm);
|
||||||
|
Assert.assertNotNull(user);
|
||||||
|
Assert.assertNotNull(user.getFederationLink());
|
||||||
|
Assert.assertEquals(user.getFederationLink(), ldapModel.getId());
|
||||||
|
try {
|
||||||
|
user.setEmail("error@error.com");
|
||||||
|
Assert.fail("should fail");
|
||||||
|
} catch (Exception e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
user.setLastName("Berk");
|
||||||
|
Assert.fail("should fail");
|
||||||
|
} catch (Exception e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
user.setFirstName("Bilbo");
|
||||||
|
Assert.fail("should fail");
|
||||||
|
} catch (Exception e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
UserCredentialModel cred = UserCredentialModel.password("poop");
|
||||||
|
user.updateCredential(cred);
|
||||||
|
Assert.fail("should fail");
|
||||||
|
} catch (Exception e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
session.getTransaction().rollback();
|
||||||
|
session.close();
|
||||||
|
session = keycloakRule.startSession();
|
||||||
|
appRealm = session.realms().getRealmByName("test");
|
||||||
|
Assert.assertEquals(UserFederationProvider.EditMode.WRITABLE.toString(), appRealm.getUserFederationProviders().get(0).getConfig().get(LDAPFederationProvider.EDIT_MODE));
|
||||||
|
keycloakRule.stopSession(session, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUnsynced() {
|
||||||
|
KeycloakSession session = keycloakRule.startSession();
|
||||||
|
RealmModel appRealm = session.realms().getRealmByName("test");
|
||||||
|
|
||||||
|
UserFederationProviderModel model = new UserFederationProviderModel(ldapModel.getId(), ldapModel.getProviderName(), ldapModel.getConfig(), ldapModel.getPriority(), ldapModel.getDisplayName());
|
||||||
|
model.getConfig().put(LDAPFederationProvider.EDIT_MODE, UserFederationProvider.EditMode.UNSYNCED.toString());
|
||||||
|
appRealm.updateUserFederationProvider(model);
|
||||||
|
UserModel user = session.users().getUserByUsername("johnkeycloak", appRealm);
|
||||||
|
Assert.assertNotNull(user);
|
||||||
|
Assert.assertNotNull(user.getFederationLink());
|
||||||
|
Assert.assertEquals(user.getFederationLink(), ldapModel.getId());
|
||||||
|
|
||||||
|
UserCredentialModel cred = UserCredentialModel.password("candy");
|
||||||
|
user.updateCredential(cred);
|
||||||
|
UserCredentialValueModel userCredentialValueModel = user.getCredentialsDirectly().get(0);
|
||||||
|
Assert.assertEquals(UserCredentialModel.PASSWORD, userCredentialValueModel.getType());
|
||||||
|
Assert.assertTrue(session.users().validCredentials(appRealm, user, cred));
|
||||||
|
session.getTransaction().rollback();
|
||||||
|
session.close();
|
||||||
|
session = keycloakRule.startSession();
|
||||||
|
appRealm = session.realms().getRealmByName("test");
|
||||||
|
Assert.assertEquals(UserFederationProvider.EditMode.WRITABLE.toString(), appRealm.getUserFederationProviders().get(0).getConfig().get(LDAPFederationProvider.EDIT_MODE));
|
||||||
|
keycloakRule.stopSession(session, false);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue