mirror of
https://github.com/sismics/docs.git
synced 2025-01-03 00:33:48 +01:00
LDAP support, courtesy of an anonymous donator
This commit is contained in:
parent
6dc4f1b448
commit
a9719feeec
@ -131,7 +131,12 @@
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.directory.server</groupId>
|
||||
<artifactId>apacheds-all</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Only there to read old index and rebuild them -->
|
||||
<dependency>
|
||||
<groupId>org.apache.lucene</groupId>
|
||||
|
@ -44,5 +44,18 @@ public enum ConfigType {
|
||||
INBOX_PASSWORD,
|
||||
INBOX_TAG,
|
||||
INBOX_AUTOMATIC_TAGS,
|
||||
INBOX_DELETE_IMPORTED
|
||||
INBOX_DELETE_IMPORTED,
|
||||
|
||||
/**
|
||||
* LDAP connection.
|
||||
*/
|
||||
LDAP_ENABLED,
|
||||
LDAP_HOST,
|
||||
LDAP_PORT,
|
||||
LDAP_ADMIN_DN,
|
||||
LDAP_ADMIN_PASSWORD,
|
||||
LDAP_BASE_DN,
|
||||
LDAP_FILTER,
|
||||
LDAP_DEFAULT_EMAIL,
|
||||
LDAP_DEFAULT_STORAGE
|
||||
}
|
||||
|
@ -50,6 +50,19 @@ public class ConfigUtil {
|
||||
return Integer.parseInt(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the long value of a configuration parameter.
|
||||
*
|
||||
* @param configType Type of the configuration parameter
|
||||
* @return Long value of the configuration parameter
|
||||
* @throws IllegalStateException Configuration parameter undefined
|
||||
*/
|
||||
public static long getConfigLongValue(ConfigType configType) {
|
||||
String value = getConfigStringValue(configType);
|
||||
|
||||
return Long.parseLong(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the boolean value of a configuration parameter.
|
||||
*
|
||||
|
@ -0,0 +1,131 @@
|
||||
package com.sismics.docs.core.util.authentication;
|
||||
|
||||
import com.sismics.docs.core.constant.ConfigType;
|
||||
import com.sismics.docs.core.constant.Constants;
|
||||
import com.sismics.docs.core.dao.ConfigDao;
|
||||
import com.sismics.docs.core.dao.UserDao;
|
||||
import com.sismics.docs.core.model.jpa.Config;
|
||||
import com.sismics.docs.core.model.jpa.User;
|
||||
import com.sismics.docs.core.util.ConfigUtil;
|
||||
import com.sismics.util.ClasspathScanner;
|
||||
import org.apache.commons.pool.impl.GenericObjectPool;
|
||||
import org.apache.directory.api.ldap.model.cursor.EntryCursor;
|
||||
import org.apache.directory.api.ldap.model.entry.Attribute;
|
||||
import org.apache.directory.api.ldap.model.entry.Entry;
|
||||
import org.apache.directory.api.ldap.model.entry.Value;
|
||||
import org.apache.directory.api.ldap.model.message.SearchScope;
|
||||
import org.apache.directory.ldap.client.api.DefaultLdapConnectionFactory;
|
||||
import org.apache.directory.ldap.client.api.LdapConnectionConfig;
|
||||
import org.apache.directory.ldap.client.api.LdapConnectionPool;
|
||||
import org.apache.directory.ldap.client.api.PoolableLdapConnectionFactory;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* LDAP authentication handler.
|
||||
*
|
||||
* @author bgamard
|
||||
*/
|
||||
@ClasspathScanner.Priority(50) // Before the internal database
|
||||
public class LdapAuthenticationHandler implements AuthenticationHandler {
|
||||
/**
|
||||
* Logger.
|
||||
*/
|
||||
private static final Logger log = LoggerFactory.getLogger(LdapAuthenticationHandler.class);
|
||||
|
||||
/**
|
||||
* LDAP connection pool.
|
||||
*/
|
||||
private static LdapConnectionPool pool;
|
||||
|
||||
/**
|
||||
* Reset the LDAP pool.
|
||||
*/
|
||||
public static void reset() {
|
||||
if (pool != null) {
|
||||
try {
|
||||
pool.close();
|
||||
} catch (Exception e) {
|
||||
// NOP
|
||||
}
|
||||
}
|
||||
pool = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the LDAP pool.
|
||||
*/
|
||||
private static void init() {
|
||||
ConfigDao configDao = new ConfigDao();
|
||||
Config ldapEnabled = configDao.getById(ConfigType.LDAP_ENABLED);
|
||||
if (pool != null || ldapEnabled == null || !Boolean.parseBoolean(ldapEnabled.getValue())) {
|
||||
return;
|
||||
}
|
||||
|
||||
LdapConnectionConfig config = new LdapConnectionConfig();
|
||||
config.setLdapHost(ConfigUtil.getConfigStringValue(ConfigType.LDAP_HOST));
|
||||
config.setLdapPort(ConfigUtil.getConfigIntegerValue(ConfigType.LDAP_PORT));
|
||||
config.setName(ConfigUtil.getConfigStringValue(ConfigType.LDAP_ADMIN_DN));
|
||||
config.setCredentials(ConfigUtil.getConfigStringValue(ConfigType.LDAP_ADMIN_PASSWORD));
|
||||
|
||||
DefaultLdapConnectionFactory factory = new DefaultLdapConnectionFactory(config);
|
||||
GenericObjectPool.Config poolConfig = new GenericObjectPool.Config();
|
||||
poolConfig.whenExhaustedAction = GenericObjectPool.WHEN_EXHAUSTED_GROW;
|
||||
poolConfig.maxWait = 500;
|
||||
pool = new LdapConnectionPool(new PoolableLdapConnectionFactory(factory), poolConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
public User authenticate(String username, String password) {
|
||||
init();
|
||||
if (pool == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Fetch and authenticate the user
|
||||
Entry userEntry;
|
||||
try {
|
||||
EntryCursor cursor = pool.getConnection().search(ConfigUtil.getConfigStringValue(ConfigType.LDAP_BASE_DN),
|
||||
ConfigUtil.getConfigStringValue(ConfigType.LDAP_FILTER).replace("USERNAME", username), SearchScope.SUBTREE);
|
||||
if (cursor.next()) {
|
||||
userEntry = cursor.get();
|
||||
pool.getConnection().bind(userEntry.getDn(), password);
|
||||
} else {
|
||||
// User not found
|
||||
return null;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("Error authenticating \"" + username + "\" using the LDAP", e);
|
||||
return null;
|
||||
}
|
||||
|
||||
UserDao userDao = new UserDao();
|
||||
User user = userDao.getActiveByUsername(username);
|
||||
if (user == null) {
|
||||
// The user is valid but never authenticated, create the user now
|
||||
log.info("\"" + username + "\" authenticated for the first time, creating the internal user");
|
||||
user = new User();
|
||||
user.setRoleId(Constants.DEFAULT_USER_ROLE);
|
||||
user.setUsername(username);
|
||||
user.setPassword(UUID.randomUUID().toString()); // No authentication using the internal database
|
||||
Attribute mailAttribute = userEntry.get("mail");
|
||||
if (mailAttribute == null || mailAttribute.get() == null) {
|
||||
user.setEmail(ConfigUtil.getConfigStringValue(ConfigType.LDAP_DEFAULT_EMAIL));
|
||||
} else {
|
||||
Value<?> value = mailAttribute.get();
|
||||
user.setEmail(value.getString());
|
||||
}
|
||||
user.setStorageQuota(ConfigUtil.getConfigLongValue(ConfigType.LDAP_DEFAULT_STORAGE));
|
||||
try {
|
||||
userDao.create(user, "admin");
|
||||
} catch (Exception e) {
|
||||
log.error("Error while creating the internal user", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
}
|
@ -6,4 +6,5 @@ log4j.appender.MEMORY=com.sismics.util.log4j.MemoryAppender
|
||||
log4j.appender.MEMORY.size=1000
|
||||
|
||||
log4j.logger.com.sismics=INFO
|
||||
log4j.logger.org.hibernate=ERROR
|
||||
log4j.logger.org.hibernate=ERROR
|
||||
log4j.logger.org.apache.directory=ERROR
|
@ -5,6 +5,7 @@ import com.sismics.util.filter.TokenBasedSecurityFilter;
|
||||
import org.glassfish.jersey.media.multipart.FormDataMultiPart;
|
||||
import org.glassfish.jersey.media.multipart.MultiPartFeature;
|
||||
import org.glassfish.jersey.media.multipart.file.StreamDataBodyPart;
|
||||
import org.junit.Assert;
|
||||
|
||||
import javax.json.JsonObject;
|
||||
import javax.ws.rs.client.Entity;
|
||||
@ -113,7 +114,8 @@ public class ClientUtil {
|
||||
.param("username", username)
|
||||
.param("password", password)
|
||||
.param("remember", remember.toString())));
|
||||
|
||||
Assert.assertEquals(200, response.getStatus());
|
||||
|
||||
return getAuthenticationCookie(response);
|
||||
}
|
||||
|
||||
|
@ -7,4 +7,5 @@ log4j.appender.MEMORY.size=1000
|
||||
|
||||
log4j.logger.com.sismics=DEBUG
|
||||
log4j.logger.org.apache.pdfbox=ERROR
|
||||
log4j.logger.org.glassfish.jersey.servlet.WebComponent=ERROR
|
||||
log4j.logger.org.glassfish.jersey.servlet.WebComponent=ERROR
|
||||
log4j.logger.org.apache.directory=ERROR
|
@ -14,6 +14,7 @@ import com.sismics.docs.core.model.jpa.File;
|
||||
import com.sismics.docs.core.service.InboxService;
|
||||
import com.sismics.docs.core.util.ConfigUtil;
|
||||
import com.sismics.docs.core.util.DirectoryUtil;
|
||||
import com.sismics.docs.core.util.authentication.LdapAuthenticationHandler;
|
||||
import com.sismics.docs.core.util.jpa.PaginatedList;
|
||||
import com.sismics.docs.core.util.jpa.PaginatedLists;
|
||||
import com.sismics.docs.rest.constant.BaseFunction;
|
||||
@ -696,4 +697,138 @@ public class AppResource extends BaseResource {
|
||||
.add("status", "ok");
|
||||
return Response.ok().entity(response.build()).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the LDAP authentication configuration.
|
||||
*
|
||||
* @api {get} /app/config_ldap Get the LDAP authentication configuration
|
||||
* @apiName GetAppConfigLdap
|
||||
* @apiGroup App
|
||||
* @apiSuccess {Boolean} enabled LDAP authentication enabled
|
||||
* @apiSuccess {String} host LDAP server host
|
||||
* @apiSuccess {Integer} port LDAP server port
|
||||
* @apiSuccess {String} admin_dn Admin DN
|
||||
* @apiSuccess {String} admin_password Admin password
|
||||
* @apiSuccess {String} base_dn Base DN
|
||||
* @apiSuccess {String} filter LDAP filter
|
||||
* @apiSuccess {String} default_email LDAP default email
|
||||
* @apiSuccess {Integer} default_storage LDAP default storage
|
||||
* @apiError (client) ForbiddenError Access denied
|
||||
* @apiPermission admin
|
||||
* @apiVersion 1.9.0
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
@GET
|
||||
@Path("config_ldap")
|
||||
public Response getConfigLdap() {
|
||||
if (!authenticate()) {
|
||||
throw new ForbiddenClientException();
|
||||
}
|
||||
checkBaseFunction(BaseFunction.ADMIN);
|
||||
|
||||
ConfigDao configDao = new ConfigDao();
|
||||
Config enabled = configDao.getById(ConfigType.LDAP_ENABLED);
|
||||
|
||||
JsonObjectBuilder response = Json.createObjectBuilder();
|
||||
if (enabled != null && Boolean.parseBoolean(enabled.getValue())) {
|
||||
// LDAP enabled
|
||||
response.add("enabled", true)
|
||||
.add("host", ConfigUtil.getConfigStringValue(ConfigType.LDAP_HOST))
|
||||
.add("port", ConfigUtil.getConfigIntegerValue(ConfigType.LDAP_PORT))
|
||||
.add("admin_dn", ConfigUtil.getConfigStringValue(ConfigType.LDAP_ADMIN_DN))
|
||||
.add("admin_password", ConfigUtil.getConfigStringValue(ConfigType.LDAP_ADMIN_PASSWORD))
|
||||
.add("base_dn", ConfigUtil.getConfigStringValue(ConfigType.LDAP_BASE_DN))
|
||||
.add("filter", ConfigUtil.getConfigStringValue(ConfigType.LDAP_FILTER))
|
||||
.add("default_email", ConfigUtil.getConfigStringValue(ConfigType.LDAP_DEFAULT_EMAIL))
|
||||
.add("default_storage", ConfigUtil.getConfigLongValue(ConfigType.LDAP_DEFAULT_STORAGE));
|
||||
} else {
|
||||
// LDAP disabled
|
||||
response.add("enabled", false);
|
||||
}
|
||||
|
||||
return Response.ok().entity(response.build()).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the LDAP authentication.
|
||||
*
|
||||
* @api {post} /app/config_ldap Configure the LDAP authentication
|
||||
* @apiName PostAppConfigLdap
|
||||
* @apiGroup App
|
||||
* @apiParam {Boolean} enabled LDAP authentication enabled
|
||||
* @apiParam {String} host LDAP server host
|
||||
* @apiParam {Integer} port LDAP server port
|
||||
* @apiParam {String} admin_dn Admin DN
|
||||
* @apiParam {String} admin_password Admin password
|
||||
* @apiParam {String} base_dn Base DN
|
||||
* @apiParam {String} filter LDAP filter
|
||||
* @apiParam {String} default_email LDAP default email
|
||||
* @apiParam {Integer} default_storage LDAP default storage
|
||||
* @apiError (client) ForbiddenError Access denied
|
||||
* @apiError (client) ValidationError Validation error
|
||||
* @apiPermission admin
|
||||
* @apiVersion 1.9.0
|
||||
*
|
||||
* @param enabled LDAP authentication enabled
|
||||
* @param host LDAP server host
|
||||
* @param portStr LDAP server port
|
||||
* @param adminDn Admin DN
|
||||
* @param adminPassword Admin password
|
||||
* @param baseDn Base DN
|
||||
* @param filter LDAP filter
|
||||
* @param defaultEmail LDAP default email
|
||||
* @param defaultStorageStr LDAP default storage
|
||||
* @return Response
|
||||
*/
|
||||
@POST
|
||||
@Path("config_ldap")
|
||||
public Response configLdap(@FormParam("enabled") Boolean enabled,
|
||||
@FormParam("host") String host,
|
||||
@FormParam("port") String portStr,
|
||||
@FormParam("admin_dn") String adminDn,
|
||||
@FormParam("admin_password") String adminPassword,
|
||||
@FormParam("base_dn") String baseDn,
|
||||
@FormParam("filter") String filter,
|
||||
@FormParam("default_email") String defaultEmail,
|
||||
@FormParam("default_storage") String defaultStorageStr) {
|
||||
if (!authenticate()) {
|
||||
throw new ForbiddenClientException();
|
||||
}
|
||||
checkBaseFunction(BaseFunction.ADMIN);
|
||||
|
||||
ConfigDao configDao = new ConfigDao();
|
||||
|
||||
if (enabled != null && enabled) {
|
||||
// LDAP enabled, validate everything
|
||||
ValidationUtil.validateLength(host, "host", 1, 250);
|
||||
ValidationUtil.validateInteger(portStr, "port");
|
||||
ValidationUtil.validateLength(adminDn, "admin_dn", 1, 250);
|
||||
ValidationUtil.validateLength(adminPassword, "admin_password", 1, 250);
|
||||
ValidationUtil.validateLength(baseDn, "base_dn", 1, 250);
|
||||
ValidationUtil.validateLength(filter, "filter", 1, 250);
|
||||
if (!filter.contains("USERNAME")) {
|
||||
throw new ClientException("ValidationError", "'filter' must contains 'USERNAME'");
|
||||
}
|
||||
ValidationUtil.validateLength(defaultEmail, "default_email", 1, 250);
|
||||
ValidationUtil.validateLong(defaultStorageStr, "default_storage");
|
||||
configDao.update(ConfigType.LDAP_ENABLED, Boolean.TRUE.toString());
|
||||
configDao.update(ConfigType.LDAP_HOST, host);
|
||||
configDao.update(ConfigType.LDAP_PORT, portStr);
|
||||
configDao.update(ConfigType.LDAP_ADMIN_DN, adminDn);
|
||||
configDao.update(ConfigType.LDAP_ADMIN_PASSWORD, adminPassword);
|
||||
configDao.update(ConfigType.LDAP_BASE_DN, baseDn);
|
||||
configDao.update(ConfigType.LDAP_FILTER, filter);
|
||||
configDao.update(ConfigType.LDAP_DEFAULT_EMAIL, defaultEmail);
|
||||
configDao.update(ConfigType.LDAP_DEFAULT_STORAGE, defaultStorageStr);
|
||||
} else {
|
||||
// LDAP disabled
|
||||
configDao.update(ConfigType.LDAP_ENABLED, Boolean.FALSE.toString());
|
||||
}
|
||||
|
||||
// Reset the LDAP pool to reconnect with the new configuration
|
||||
LdapAuthenticationHandler.reset();
|
||||
|
||||
return Response.ok().build();
|
||||
}
|
||||
}
|
||||
|
@ -244,6 +244,15 @@ angular.module('docs',
|
||||
}
|
||||
}
|
||||
})
|
||||
.state('settings.ldap', {
|
||||
url: '/ldap',
|
||||
views: {
|
||||
'settings': {
|
||||
templateUrl: 'partial/docs/settings.ldap.html',
|
||||
controller: 'SettingsLdap'
|
||||
}
|
||||
}
|
||||
})
|
||||
.state('document', {
|
||||
url: '/document',
|
||||
abstract: true,
|
||||
|
@ -0,0 +1,33 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Settings LDAP page controller.
|
||||
*/
|
||||
angular.module('docs').controller('SettingsLdap', function($scope, Restangular, $translate, $timeout) {
|
||||
$scope.ldap = {
|
||||
enabled: false
|
||||
};
|
||||
|
||||
// Get the LDAP configuration
|
||||
Restangular.one('app/config_ldap').get().then(function (data) {
|
||||
$scope.ldap = data;
|
||||
if ($scope.ldap.default_storage) {
|
||||
$scope.ldap.default_storage /= 1000000;
|
||||
}
|
||||
});
|
||||
|
||||
// Edit SMTP config
|
||||
$scope.saveResult = undefined;
|
||||
$scope.save = function () {
|
||||
var ldap = angular.copy($scope.ldap);
|
||||
if (ldap.default_storage) {
|
||||
ldap.default_storage *= 1000000;
|
||||
}
|
||||
Restangular.one('app').post('config_ldap', ldap).then(function () {
|
||||
$scope.saveResult = $translate.instant('settings.ldap.saved');
|
||||
$timeout(function() {
|
||||
$scope.saveResult = undefined;
|
||||
}, 5000);
|
||||
});
|
||||
};
|
||||
});
|
@ -94,6 +94,7 @@
|
||||
<script src="app/docs/controller/settings/SettingsGroupEdit.js" type="text/javascript"></script>
|
||||
<script src="app/docs/controller/settings/SettingsVocabulary.js" type="text/javascript"></script>
|
||||
<script src="app/docs/controller/settings/SettingsMetadata.js" type="text/javascript"></script>
|
||||
<script src="app/docs/controller/settings/SettingsLdap.js" type="text/javascript"></script>
|
||||
<script src="app/docs/controller/usergroup/UserGroup.js" type="text/javascript"></script>
|
||||
<script src="app/docs/controller/usergroup/UserProfile.js" type="text/javascript"></script>
|
||||
<script src="app/docs/controller/usergroup/GroupProfile.js" type="text/javascript"></script>
|
||||
|
@ -278,8 +278,22 @@
|
||||
"menu_vocabularies": "Vocabularies",
|
||||
"menu_configuration": "Configuration",
|
||||
"menu_inbox": "Inbox scanning",
|
||||
"menu_ldap": "LDAP authentication",
|
||||
"menu_metadata": "Custom metadata",
|
||||
"menu_monitoring": "Monitoring",
|
||||
"ldap": {
|
||||
"title": "LDAP authentication",
|
||||
"enabled": "Enable LDAP authentication",
|
||||
"host": "LDAP hostname",
|
||||
"port": "LDAP port (389 by default)",
|
||||
"admin_dn": "Admin DN",
|
||||
"admin_password": "Admin password",
|
||||
"base_dn": "Base search DN",
|
||||
"filter": "Search filter (must contains USERNAME, eg. \"(uid=USERNAME)\")",
|
||||
"default_email": "Default email for LDAP user",
|
||||
"default_storage": "Default storage for LDAP user",
|
||||
"saved": "LDAP configuration saved successfully"
|
||||
},
|
||||
"user": {
|
||||
"title": "Users management",
|
||||
"add_user": "Add a user",
|
||||
|
@ -278,7 +278,21 @@
|
||||
"menu_vocabularies": "Vocabulaires",
|
||||
"menu_configuration": "Configuration",
|
||||
"menu_inbox": "Scanning de boîte de réception",
|
||||
"menu_ldap": "Authentification LDAP",
|
||||
"menu_monitoring": "Monitoring",
|
||||
"ldap": {
|
||||
"title": "Authentification LDAP",
|
||||
"enabled": "Activer l'authentication LDAP",
|
||||
"host": "Hôte LDAP",
|
||||
"port": "Port LDAP (389 par défaut)",
|
||||
"admin_dn": "DN administrateur",
|
||||
"admin_password": "Mot de passe administrateur",
|
||||
"base_dn": "DN de recherche",
|
||||
"filter": "Filtre de recherche (doit contenir USERNAME, cf. \"(uid=USERNAME)\")",
|
||||
"default_email": "Email par défaut pour les utilisateurs LDAP",
|
||||
"default_storage": "Stockage par défaut pour les utilisateurs LDAP",
|
||||
"saved": "Configuration IMAP sauvegardée avec succès"
|
||||
},
|
||||
"user": {
|
||||
"title": "Gestion des utilisateurs",
|
||||
"add_user": "Ajouter un utilisateur",
|
||||
|
@ -20,6 +20,7 @@
|
||||
<a class="list-group-item" ui-sref-active="{ active: 'settings.config.**' }" href="#/settings/config">{{ 'settings.menu_configuration' | translate }}</a>
|
||||
<a class="list-group-item" ui-sref-active="{ active: 'settings.metadata.**' }" href="#/settings/metadata">{{ 'settings.menu_metadata' | translate }}</a>
|
||||
<a class="list-group-item" ui-sref-active="{ active: 'settings.inbox.**' }" href="#/settings/inbox">{{ 'settings.menu_inbox' | translate }}</a>
|
||||
<a class="list-group-item" ui-sref-active="{ active: 'settings.ldap.**' }" href="#/settings/ldap">{{ 'settings.menu_ldap' | translate }}</a>
|
||||
<a class="list-group-item" ui-sref-active="{ active: 'settings.monitoring.**' }" href="#/settings/monitoring">{{ 'settings.menu_monitoring' | translate }}</a>
|
||||
</ul>
|
||||
</div>
|
||||
|
88
docs-web/src/main/webapp/src/partial/docs/settings.ldap.html
Normal file
88
docs-web/src/main/webapp/src/partial/docs/settings.ldap.html
Normal file
@ -0,0 +1,88 @@
|
||||
<h2 translate="settings.ldap.title"></h2>
|
||||
<form class="form-horizontal" name="form" novalidate>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label" for="ldapEnabled">{{ 'settings.ldap.enabled' | translate }}</label>
|
||||
<div class="col-sm-7">
|
||||
<input name="enabled" type="checkbox" id="ldapEnabled" ng-model="ldap.enabled" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-if="ldap.enabled" class="form-group" ng-class="{ 'has-error': !form.hostname.$valid && form.$dirty }">
|
||||
<label class="col-sm-2 control-label" for="ldapHost">{{ 'settings.ldap.host' | translate }}</label>
|
||||
<div class="col-sm-7">
|
||||
<input name="hostname" type="text" class="form-control" id="ldapHost" ng-model="ldap.host" ng-maxlength="250" required />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-if="ldap.enabled" class="form-group" ng-class="{ 'has-error': !form.port.$valid && form.$dirty }">
|
||||
<label class="col-sm-2 control-label" for="ldapPort">{{ 'settings.ldap.port' | translate }}</label>
|
||||
<div class="col-sm-7">
|
||||
<input name="port" type="number" class="form-control" id="ldapPort" ng-model="ldap.port" required />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-if="ldap.enabled" class="form-group" ng-class="{ 'has-error': !form.adminDn.$valid && form.$dirty }">
|
||||
<label class="col-sm-2 control-label" for="ldapAdminDn">{{ 'settings.ldap.admin_dn' | translate }}</label>
|
||||
<div class="col-sm-7">
|
||||
<input name="adminDn" type="text" class="form-control" id="ldapAdminDn" ng-model="ldap.admin_dn" ng-maxlength="250" required />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-if="ldap.enabled" class="form-group" ng-class="{ 'has-error': !form.adminPassword.$valid && form.$dirty }">
|
||||
<label class="col-sm-2 control-label" for="ldapAdminPassword">{{ 'settings.ldap.admin_password' | translate }}</label>
|
||||
<div class="col-sm-7">
|
||||
<input name="adminPassword" type="password" class="form-control" id="ldapAdminPassword" ng-model="ldap.admin_password" ng-maxlength="250" required />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-if="ldap.enabled" class="form-group" ng-class="{ 'has-error': !form.baseDn.$valid && form.$dirty }">
|
||||
<label class="col-sm-2 control-label" for="ldapBaseDn">{{ 'settings.ldap.base_dn' | translate }}</label>
|
||||
<div class="col-sm-7">
|
||||
<input name="baseDn" type="text" class="form-control" id="ldapBaseDn" ng-model="ldap.base_dn" ng-maxlength="250" required />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-if="ldap.enabled" class="form-group" ng-class="{ 'has-error': !form.filter.$valid && form.$dirty }">
|
||||
<label class="col-sm-2 control-label" for="ldapFilter">{{ 'settings.ldap.filter' | translate }}</label>
|
||||
<div class="col-sm-7">
|
||||
<input name="filter" type="text" class="form-control" id="ldapFilter"
|
||||
ng-pattern="/(USERNAME)+/"
|
||||
ng-model="ldap.filter" ng-maxlength="250" required />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-if="ldap.enabled" class="form-group" ng-class="{ 'has-error': !form.defaultEmail.$valid && form.$dirty }">
|
||||
<label class="col-sm-2 control-label" for="ldapDefaultEmail">{{ 'settings.ldap.default_email' | translate }}</label>
|
||||
<div class="col-sm-7">
|
||||
<input name="defaultEmail" type="text" class="form-control" id="ldapDefaultEmail" ng-model="ldap.default_email" ng-maxlength="250" required />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-if="ldap.enabled" class="form-group" ng-class="{ 'has-error': !form.defaultStorage.$valid && form.$dirty }">
|
||||
<label class="col-sm-2 control-label" for="ldapDefaultStorage">{{ 'settings.ldap.default_storage' | translate }}</label>
|
||||
<div class="col-sm-7">
|
||||
<div class="input-group">
|
||||
<input name="defaultStorage" type="number" id="ldapDefaultStorage" required class="form-control"
|
||||
ng-pattern="/[0-9]*/" ng-model="ldap.default_storage"/>
|
||||
<div class="input-group-addon">{{ 'filter.filesize.mb' | translate }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-3">
|
||||
<span class="help-block" ng-show="form.default_storage.$error.pattern && form.$dirty">{{ 'validation.number' | translate }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
<button type="submit" class="btn btn-primary" ng-click="save()" ng-disabled="!form.$valid">
|
||||
<span class="fas fa-pencil-alt"></span> {{ 'save' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="alert col-sm-9 alert-success"
|
||||
ng-show="saveResult">
|
||||
{{ saveResult }}
|
||||
</div>
|
@ -7,4 +7,5 @@ log4j.appender.MEMORY.size=1000
|
||||
|
||||
log4j.logger.com.sismics=INFO
|
||||
log4j.logger.org.apache.pdfbox=ERROR
|
||||
log4j.logger.org.glassfish.jersey.servlet.WebComponent=ERROR
|
||||
log4j.logger.org.glassfish.jersey.servlet.WebComponent=ERROR
|
||||
log4j.logger.org.apache.directory=ERROR
|
@ -1,10 +1,20 @@
|
||||
package com.sismics.docs.rest;
|
||||
|
||||
import com.google.common.io.Resources;
|
||||
import com.icegreen.greenmail.util.GreenMail;
|
||||
import com.icegreen.greenmail.util.GreenMailUtil;
|
||||
import com.icegreen.greenmail.util.ServerSetup;
|
||||
import com.sismics.docs.core.model.context.AppContext;
|
||||
import com.sismics.util.filter.TokenBasedSecurityFilter;
|
||||
import org.apache.directory.api.ldap.model.name.Dn;
|
||||
import org.apache.directory.server.core.api.DirectoryService;
|
||||
import org.apache.directory.server.core.api.partition.Partition;
|
||||
import org.apache.directory.server.core.factory.DefaultDirectoryServiceFactory;
|
||||
import org.apache.directory.server.core.factory.DirectoryServiceFactory;
|
||||
import org.apache.directory.server.core.partition.impl.avl.AvlPartition;
|
||||
import org.apache.directory.server.ldap.LdapServer;
|
||||
import org.apache.directory.server.protocol.shared.store.LdifFileLoader;
|
||||
import org.apache.directory.server.protocol.shared.transport.TcpTransport;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
@ -14,6 +24,7 @@ import javax.ws.rs.client.Entity;
|
||||
import javax.ws.rs.core.Form;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.Response.Status;
|
||||
import java.io.File;
|
||||
|
||||
|
||||
/**
|
||||
@ -331,4 +342,95 @@ public class TestAppResource extends BaseJerseyTest {
|
||||
|
||||
greenMail.stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the LDAP authentication.
|
||||
*/
|
||||
@Test
|
||||
public void testLdapAuthentication() throws Exception {
|
||||
// Start LDAP server
|
||||
final DirectoryServiceFactory factory = new DefaultDirectoryServiceFactory();
|
||||
factory.init("Test");
|
||||
|
||||
final DirectoryService directoryService = factory.getDirectoryService();
|
||||
directoryService.getChangeLog().setEnabled(false);
|
||||
directoryService.setShutdownHookEnabled(true);
|
||||
|
||||
final Partition partition = new AvlPartition(directoryService.getSchemaManager());
|
||||
partition.setId("Test");
|
||||
partition.setSuffixDn(new Dn(directoryService.getSchemaManager(), "o=TEST"));
|
||||
partition.initialize();
|
||||
directoryService.addPartition(partition);
|
||||
|
||||
final LdapServer ldapServer = new LdapServer();
|
||||
ldapServer.setTransports(new TcpTransport("localhost", 11389));
|
||||
ldapServer.setDirectoryService(directoryService);
|
||||
|
||||
directoryService.startup();
|
||||
ldapServer.start();
|
||||
|
||||
// Load test data in LDAP
|
||||
new LdifFileLoader(directoryService.getAdminSession(), new File(Resources.getResource("test.ldif").getFile()), null).execute();
|
||||
|
||||
// Login admin
|
||||
String adminToken = clientUtil.login("admin", "admin", false);
|
||||
|
||||
// Get the LDAP configuration
|
||||
JsonObject json = target().path("/app/config_ldap").request()
|
||||
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, adminToken)
|
||||
.get(JsonObject.class);
|
||||
Assert.assertFalse(json.getBoolean("enabled"));
|
||||
|
||||
// Change LDAP configuration
|
||||
target().path("/app/config_ldap").request()
|
||||
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, adminToken)
|
||||
.post(Entity.form(new Form()
|
||||
.param("enabled", "true")
|
||||
.param("host", "localhost")
|
||||
.param("port", "11389")
|
||||
.param("admin_dn", "uid=admin,ou=system")
|
||||
.param("admin_password", "secret")
|
||||
.param("base_dn", "o=TEST")
|
||||
.param("filter", "(&(objectclass=inetOrgPerson)(uid=USERNAME))")
|
||||
.param("default_email", "devnull@teedy.io")
|
||||
.param("default_storage", "100000000")
|
||||
), JsonObject.class);
|
||||
|
||||
// Get the LDAP configuration
|
||||
json = target().path("/app/config_ldap").request()
|
||||
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, adminToken)
|
||||
.get(JsonObject.class);
|
||||
Assert.assertTrue(json.getBoolean("enabled"));
|
||||
Assert.assertEquals("localhost", json.getString("host"));
|
||||
Assert.assertEquals(11389, json.getJsonNumber("port").intValue());
|
||||
Assert.assertEquals("uid=admin,ou=system", json.getString("admin_dn"));
|
||||
Assert.assertEquals("secret", json.getString("admin_password"));
|
||||
Assert.assertEquals("o=TEST", json.getString("base_dn"));
|
||||
Assert.assertEquals("(&(objectclass=inetOrgPerson)(uid=USERNAME))", json.getString("filter"));
|
||||
Assert.assertEquals("devnull@teedy.io", json.getString("default_email"));
|
||||
Assert.assertEquals(100000000L, json.getJsonNumber("default_storage").longValue());
|
||||
|
||||
// Login with a LDAP user
|
||||
String ldapTopen = clientUtil.login("ldap1", "secret", false);
|
||||
|
||||
// Check user informations
|
||||
json = target().path("/user").request()
|
||||
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, ldapTopen)
|
||||
.get(JsonObject.class);
|
||||
Assert.assertEquals("ldap1@teedy.io", json.getString("email"));
|
||||
|
||||
// List all documents
|
||||
json = target().path("/document/list")
|
||||
.queryParam("sort_column", 3)
|
||||
.queryParam("asc", true)
|
||||
.request()
|
||||
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, ldapTopen)
|
||||
.get(JsonObject.class);
|
||||
JsonArray documents = json.getJsonArray("documents");
|
||||
Assert.assertEquals(0, documents.size());
|
||||
|
||||
// Stop LDAP server
|
||||
ldapServer.stop();
|
||||
directoryService.shutdown();
|
||||
}
|
||||
}
|
@ -10,3 +10,4 @@ log4j.logger.com.sismics.util.jpa=ERROR
|
||||
log4j.logger.org.hibernate=ERROR
|
||||
log4j.logger.org.apache.pdfbox=INFO
|
||||
log4j.logger.com.mchange=ERROR
|
||||
log4j.logger.org.apache.directory=ERROR
|
19
docs-web/src/test/resources/test.ldif
Normal file
19
docs-web/src/test/resources/test.ldif
Normal file
@ -0,0 +1,19 @@
|
||||
version: 1
|
||||
|
||||
dn: o=TEST
|
||||
objectclass: domain
|
||||
objectclass: top
|
||||
objectclass: extensibleObject
|
||||
dc: TEST
|
||||
o: TEST
|
||||
|
||||
dn: uid=ldap1,o=TEST
|
||||
objectClass: top
|
||||
objectClass: inetOrgPerson
|
||||
objectClass: person
|
||||
objectClass: organizationalPerson
|
||||
cn: ldap1
|
||||
sn: LDAP 1
|
||||
uid: ldap1
|
||||
userPassword: secret
|
||||
mail: ldap1@teedy.io
|
7
pom.xml
7
pom.xml
@ -50,6 +50,7 @@
|
||||
<com.sun.mail.javax.mail.version>1.6.2</com.sun.mail.javax.mail.version>
|
||||
<org.jsoup.jsoup.version>1.11.3</org.jsoup.jsoup.version>
|
||||
<com.squareup.okhttp3.okhttp.version>3.11.0</com.squareup.okhttp3.okhttp.version>
|
||||
<org.apache.directory.server.apacheds-all.version>2.0.0-M17</org.apache.directory.server.apacheds-all.version>
|
||||
|
||||
<org.eclipse.jetty.jetty-server.version>9.4.17.v20190418</org.eclipse.jetty.jetty-server.version>
|
||||
<org.eclipse.jetty.jetty-webapp.version>9.4.17.v20190418</org.eclipse.jetty.jetty-webapp.version>
|
||||
@ -459,6 +460,12 @@
|
||||
<version>${org.postgresql.postgresql.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.directory.server</groupId>
|
||||
<artifactId>apacheds-all</artifactId>
|
||||
<version>${org.apache.directory.server.apacheds-all.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Servlet listener to register SPI ImageIO plugins -->
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.servlet</groupId>
|
||||
|
Loading…
Reference in New Issue
Block a user