Merge pull request #1295 from mposolda/ldap2

Ldap fixes + added authType to UI to allow users specify LDAP auth type
This commit is contained in:
Marek Posolda 2015-05-28 20:44:31 +02:00
commit eaf5034ec7
9 changed files with 57 additions and 20 deletions

View file

@ -12,7 +12,6 @@ import javax.naming.directory.SearchControls;
import org.keycloak.models.LDAPConstants; import org.keycloak.models.LDAPConstants;
import org.keycloak.models.UserFederationProvider; import org.keycloak.models.UserFederationProvider;
import org.keycloak.models.UserFederationProviderModel;
/** /**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a> * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
@ -37,8 +36,12 @@ public class LDAPConfig {
} }
public String getAuthType() { public String getAuthType() {
// hardcoded for now String value = config.get(LDAPConstants.AUTH_TYPE);
return "simple"; if (value == null) {
return LDAPConstants.AUTH_TYPE_SIMPLE;
} else {
return value;
}
} }
public String getSecurityProtocol() { public String getSecurityProtocol() {
@ -70,7 +73,7 @@ public class LDAPConfig {
String[] objectClasses = objClassesStr.split(","); String[] objectClasses = objClassesStr.split(",");
// Trim them // Trim them
Set<String> userObjClasses = new HashSet<String>(); Set<String> userObjClasses = new HashSet<>();
for (int i=0 ; i<objectClasses.length ; i++) { for (int i=0 ; i<objectClasses.length ; i++) {
userObjClasses.add(objectClasses[i].trim()); userObjClasses.add(objectClasses[i].trim());
} }

View file

@ -106,7 +106,7 @@ public class LDAPFederationProvider implements UserFederationProvider {
proxied = new UnsyncedLDAPUserModelDelegate(local, this); proxied = new UnsyncedLDAPUserModelDelegate(local, this);
} }
Set<UserFederationMapperModel> federationMappers = realm.getUserFederationMappers(); Set<UserFederationMapperModel> federationMappers = realm.getUserFederationMappersByFederationProvider(model.getId());
for (UserFederationMapperModel mapperModel : federationMappers) { for (UserFederationMapperModel mapperModel : federationMappers) {
LDAPFederationMapper ldapMapper = getMapper(mapperModel); LDAPFederationMapper ldapMapper = getMapper(mapperModel);
proxied = ldapMapper.proxy(mapperModel, this, ldapObject, proxied, realm); proxied = ldapMapper.proxy(mapperModel, this, ldapObject, proxied, realm);
@ -263,7 +263,7 @@ public class LDAPFederationProvider implements UserFederationProvider {
UserModel imported = session.userStorage().addUser(realm, ldapUsername); UserModel imported = session.userStorage().addUser(realm, ldapUsername);
imported.setEnabled(true); imported.setEnabled(true);
Set<UserFederationMapperModel> federationMappers = realm.getUserFederationMappers(); Set<UserFederationMapperModel> federationMappers = realm.getUserFederationMappersByFederationProvider(getModel().getId());
for (UserFederationMapperModel mapperModel : federationMappers) { for (UserFederationMapperModel mapperModel : federationMappers) {
LDAPFederationMapper ldapMapper = getMapper(mapperModel); LDAPFederationMapper ldapMapper = getMapper(mapperModel);
ldapMapper.onImportUserFromLDAP(mapperModel, this, ldapUser, imported, realm, true); ldapMapper.onImportUserFromLDAP(mapperModel, this, ldapUser, imported, realm, true);
@ -399,7 +399,7 @@ public class LDAPFederationProvider implements UserFederationProvider {
if ((fedModel.getId().equals(currentUser.getFederationLink())) && (ldapUser.getUuid().equals(currentUser.getAttribute(LDAPConstants.LDAP_ID)))) { if ((fedModel.getId().equals(currentUser.getFederationLink())) && (ldapUser.getUuid().equals(currentUser.getAttribute(LDAPConstants.LDAP_ID)))) {
// Update keycloak user // Update keycloak user
Set<UserFederationMapperModel> federationMappers = realm.getUserFederationMappers(); Set<UserFederationMapperModel> federationMappers = realm.getUserFederationMappersByFederationProvider(model.getId());
for (UserFederationMapperModel mapperModel : federationMappers) { for (UserFederationMapperModel mapperModel : federationMappers) {
LDAPFederationMapper ldapMapper = getMapper(mapperModel); LDAPFederationMapper ldapMapper = getMapper(mapperModel);
ldapMapper.onImportUserFromLDAP(mapperModel, this, ldapUser, currentUser, realm, false); ldapMapper.onImportUserFromLDAP(mapperModel, this, ldapUser, currentUser, realm, false);

View file

@ -62,7 +62,7 @@ public class LDAPUtils {
ldapQuery.addSearchDns(config.getUserDns()); ldapQuery.addSearchDns(config.getUserDns());
ldapQuery.addObjectClasses(config.getUserObjectClasses()); ldapQuery.addObjectClasses(config.getUserObjectClasses());
Set<UserFederationMapperModel> mapperModels = realm.getUserFederationMappers(); Set<UserFederationMapperModel> mapperModels = realm.getUserFederationMappersByFederationProvider(ldapProvider.getModel().getId());
ldapQuery.addMappers(mapperModels); ldapQuery.addMappers(mapperModels);
return ldapQuery; return ldapQuery;

View file

@ -108,7 +108,7 @@ public class LDAPIdentityStore implements IdentityStore {
@Override @Override
public List<LDAPObject> fetchQueryResults(LDAPIdentityQuery identityQuery) { public List<LDAPObject> fetchQueryResults(LDAPIdentityQuery identityQuery) {
List<LDAPObject> results = new ArrayList<LDAPObject>(); List<LDAPObject> results = new ArrayList<>();
try { try {
if (identityQuery.getSorting() != null && !identityQuery.getSorting().isEmpty()) { if (identityQuery.getSorting() != null && !identityQuery.getSorting().isEmpty()) {
@ -153,7 +153,7 @@ public class LDAPIdentityStore implements IdentityStore {
} }
} }
} catch (Exception e) { } catch (Exception e) {
throw new ModelException("Querying of identity type failed " + identityQuery, e); throw new ModelException("Querying of LDAP failed " + identityQuery, e);
} }
return results; return results;
@ -382,7 +382,7 @@ public class LDAPIdentityStore implements IdentityStore {
NamingEnumeration<? extends Attribute> ldapAttributes = attributes.getAll(); NamingEnumeration<? extends Attribute> ldapAttributes = attributes.getAll();
// Exact name of attributes might be different // Exact name of attributes might be different
List<String> uppercasedReadOnlyAttrNames = new ArrayList<String>(); List<String> uppercasedReadOnlyAttrNames = new ArrayList<>();
for (String readonlyAttr : readOnlyAttrNames) { for (String readonlyAttr : readOnlyAttrNames) {
uppercasedReadOnlyAttrNames.add(readonlyAttr.toUpperCase()); uppercasedReadOnlyAttrNames.add(readonlyAttr.toUpperCase());
} }
@ -402,11 +402,11 @@ public class LDAPIdentityStore implements IdentityStore {
Object uuidValue = ldapAttribute.get(); Object uuidValue = ldapAttribute.get();
ldapObject.setUuid(this.operationManager.decodeEntryUUID(uuidValue)); ldapObject.setUuid(this.operationManager.decodeEntryUUID(uuidValue));
} else { } else {
Set<String> attrValues = new TreeSet<String>(); Set<String> attrValues = new TreeSet<>();
NamingEnumeration<?> enumm = ldapAttribute.getAll(); NamingEnumeration<?> enumm = ldapAttribute.getAll();
while (enumm.hasMoreElements()) { while (enumm.hasMoreElements()) {
String objectClass = enumm.next().toString(); String attrVal = enumm.next().toString();
attrValues.add(objectClass); attrValues.add(attrVal);
} }
if (ldapAttributeName.toLowerCase().equals(LDAPConstants.OBJECT_CLASS)) { if (ldapAttributeName.toLowerCase().equals(LDAPConstants.OBJECT_CLASS)) {

View file

@ -451,8 +451,9 @@ public class LDAPOperationManager {
private Map<String, Object> createConnectionProperties() { private Map<String, Object> createConnectionProperties() {
HashMap<String, Object> env = new HashMap<String, Object>(); HashMap<String, Object> env = new HashMap<String, Object>();
String authType = this.config.getAuthType();
env.put(Context.INITIAL_CONTEXT_FACTORY, this.config.getFactoryName()); env.put(Context.INITIAL_CONTEXT_FACTORY, this.config.getFactoryName());
env.put(Context.SECURITY_AUTHENTICATION, this.config.getAuthType()); env.put(Context.SECURITY_AUTHENTICATION, authType);
String protocol = this.config.getSecurityProtocol(); String protocol = this.config.getSecurityProtocol();
@ -468,7 +469,7 @@ public class LDAPOperationManager {
bindCredential = this.config.getBindCredential().toCharArray(); bindCredential = this.config.getBindCredential().toCharArray();
} }
if (bindDN != null) { if (!LDAPConstants.AUTH_TYPE_NONE.equals(authType)) {
env.put(Context.SECURITY_PRINCIPAL, bindDN); env.put(Context.SECURITY_PRINCIPAL, bindDN);
env.put(Context.SECURITY_CREDENTIALS, bindCredential); env.put(Context.SECURITY_CREDENTIALS, bindCredential);
} }

View file

@ -541,6 +541,7 @@ module.controller('LDAPCtrl', function($scope, $location, $route, Notifications,
instance.config.debug = false; instance.config.debug = false;
instance.config.useKerberosForPasswordAuthentication = false; instance.config.useKerberosForPasswordAuthentication = false;
instance.config.authType = 'simple';
instance.config.batchSizeForSync = DEFAULT_BATCH_SIZE; instance.config.batchSizeForSync = DEFAULT_BATCH_SIZE;
instance.config.searchScope = "1"; instance.config.searchScope = "1";
@ -556,12 +557,16 @@ module.controller('LDAPCtrl', function($scope, $location, $route, Notifications,
instance.config.debug = (instance.config.debug === 'true' || instance.config.debug === true); instance.config.debug = (instance.config.debug === 'true' || instance.config.debug === true);
instance.config.useKerberosForPasswordAuthentication = (instance.config.useKerberosForPasswordAuthentication === 'true' || instance.config.useKerberosForPasswordAuthentication === true); instance.config.useKerberosForPasswordAuthentication = (instance.config.useKerberosForPasswordAuthentication === 'true' || instance.config.useKerberosForPasswordAuthentication === true);
if (!instance.config.authType) {
instance.config.authType = 'simple';
}
if (!instance.config.batchSizeForSync) { if (!instance.config.batchSizeForSync) {
instance.config.batchSizeForSync = DEFAULT_BATCH_SIZE; instance.config.batchSizeForSync = DEFAULT_BATCH_SIZE;
} }
if (!instance.config.searchScope) { if (!instance.config.searchScope) {
instance.config.searchScope = "1"; instance.config.searchScope = '1';
} }
$scope.fullSyncEnabled = (instance.fullSyncPeriod && instance.fullSyncPeriod > 0); $scope.fullSyncEnabled = (instance.fullSyncPeriod && instance.fullSyncPeriod > 0);
$scope.changedSyncEnabled = (instance.changedSyncPeriod && instance.changedSyncPeriod > 0); $scope.changedSyncEnabled = (instance.changedSyncPeriod && instance.changedSyncPeriod > 0);
} }
@ -581,6 +586,11 @@ module.controller('LDAPCtrl', function($scope, $location, $route, Notifications,
{ "id": "other", "name": "Other" } { "id": "other", "name": "Other" }
]; ];
$scope.authTypes = [
{ "id": "none", "name": "none" },
{ "id": "simple", "name": "simple" }
];
$scope.searchScopes = [ $scope.searchScopes = [
{ "id": "1", "name": "One Level" }, { "id": "1", "name": "One Level" },
{ "id": "2", "name": "Subtree" } { "id": "2", "name": "Subtree" }

View file

@ -119,16 +119,29 @@
</kc-tooltip> </kc-tooltip>
</div> </div>
<div class="form-group clearfix"> <div class="form-group clearfix">
<label class="col-md-2 control-label" for="authType"><span class="required">*</span> Authentication Type</label>
<div class="col-md-6">
<div>
<select class="form-control" id="authType"
ng-model="instance.config.authType"
ng-options="authType.id as authType.name for authType in authTypes"
required>
</select>
</div>
</div>
<kc-tooltip>LDAP Authentication type. Right now just 'none' (anonymous LDAP authentication) or 'simple' (Bind credential + Bind password authentication) mechanisms are available</kc-tooltip>
</div>
<div class="form-group clearfix" data-ng-hide="instance.config.authType == 'none'">
<label class="col-md-2 control-label" for="ldapBindDn"><span class="required">*</span> Bind DN</label> <label class="col-md-2 control-label" for="ldapBindDn"><span class="required">*</span> Bind DN</label>
<div class="col-md-6"> <div class="col-md-6">
<input class="form-control" id="ldapBindDn" type="text" ng-model="instance.config.bindDn" placeholder="LDAP Bind DN" required> <input class="form-control" id="ldapBindDn" type="text" ng-model="instance.config.bindDn" placeholder="LDAP Bind DN" data-ng-required="instance.config.authType != 'none'">
</div> </div>
<kc-tooltip>DN of LDAP admin, which will be used by Keycloak to access LDAP server</kc-tooltip> <kc-tooltip>DN of LDAP admin, which will be used by Keycloak to access LDAP server</kc-tooltip>
</div> </div>
<div class="form-group clearfix"> <div class="form-group clearfix" data-ng-hide="instance.config.authType == 'none'">
<label class="col-md-2 control-label" for="ldapBindCredential"><span class="required">*</span> Bind Credential</label> <label class="col-md-2 control-label" for="ldapBindCredential"><span class="required">*</span> Bind Credential</label>
<div class="col-md-6"> <div class="col-md-6">
<input class="form-control" id="ldapBindCredential" type="password" ng-model="instance.config.bindCredential" placeholder="LDAP Bind Credentials" required> <input class="form-control" id="ldapBindCredential" type="password" ng-model="instance.config.bindCredential" placeholder="LDAP Bind Credentials" data-ng-required="instance.config.authType != 'none'">
</div> </div>
<kc-tooltip>Password of LDAP admin</kc-tooltip> <kc-tooltip>Password of LDAP admin</kc-tooltip>
<div class="col-sm-4" data-ng-show="access.manageRealm"> <div class="col-sm-4" data-ng-show="access.manageRealm">

View file

@ -23,6 +23,10 @@ public class LDAPConstants {
public static final String BIND_DN = "bindDn"; public static final String BIND_DN = "bindDn";
public static final String BIND_CREDENTIAL = "bindCredential"; public static final String BIND_CREDENTIAL = "bindCredential";
public static final String AUTH_TYPE = "authType";
public static final String AUTH_TYPE_NONE = "none";
public static final String AUTH_TYPE_SIMPLE = "simple";
public static final String SEARCH_SCOPE = "searchScope"; public static final String SEARCH_SCOPE = "searchScope";
public static final String CONNECTION_POOLING = "connectionPooling"; public static final String CONNECTION_POOLING = "connectionPooling";
public static final String PAGINATION = "pagination"; public static final String PAGINATION = "pagination";

View file

@ -4,6 +4,8 @@ import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper; import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider; import javax.ws.rs.ext.Provider;
import org.jboss.logging.Logger;
import org.keycloak.messages.MessagesProvider; import org.keycloak.messages.MessagesProvider;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ModelException; import org.keycloak.models.ModelException;
@ -15,6 +17,8 @@ import org.keycloak.services.ErrorResponse;
@Provider @Provider
public class ModelExceptionMapper implements ExceptionMapper<ModelException> { public class ModelExceptionMapper implements ExceptionMapper<ModelException> {
private static final Logger logger = Logger.getLogger(ModelExceptionMapper.class);
@Context @Context
private KeycloakSession session; private KeycloakSession session;
@ -22,6 +26,8 @@ public class ModelExceptionMapper implements ExceptionMapper<ModelException> {
public Response toResponse(ModelException ex) { public Response toResponse(ModelException ex) {
String message = session.getProvider(MessagesProvider.class, "admin") String message = session.getProvider(MessagesProvider.class, "admin")
.getMessage(ex.getMessage(), ex.getParameters()); .getMessage(ex.getMessage(), ex.getParameters());
logger.error(message, ex);
return ErrorResponse.error(message, Response.Status.BAD_REQUEST); return ErrorResponse.error(message, Response.Status.BAD_REQUEST);
} }
} }