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:
commit
eaf5034ec7
9 changed files with 57 additions and 20 deletions
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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)) {
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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" }
|
||||||
|
|
|
@ -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">
|
||||||
|
|
|
@ -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";
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue