Commit fd5b6407 authored by Piotr Gawron's avatar Piotr Gawron
Browse files

user is automatically created when local user doesn't exist and authentication goes via ldap

parent 8d0dc7af
......@@ -221,7 +221,7 @@ public enum ConfigurationElementType {
ConfigurationElementTypeGroup.LDAP_CONFIGURATION), //
LDAP_EMAIL_ATTRIBUTE("LDAP email attribute", "mail", ConfigurationElementEditType.STRING, true,
ConfigurationElementTypeGroup.LDAP_CONFIGURATION),//
LDAP_FILTER("LDAP filter ", "(memberof=cn=gitlab,cn=groups,cn=accounts,dc=uni,dc=lu)", ConfigurationElementEditType.STRING, true,
LDAP_FILTER("LDAP filter ", "(memberof=cn=minerva,cn=groups,cn=accounts,dc=uni,dc=lu)", ConfigurationElementEditType.STRING, true,
ConfigurationElementTypeGroup.LDAP_CONFIGURATION), //
;
......
......@@ -118,7 +118,12 @@ public class User implements Serializable {
*/
private boolean removed = false;
@Column(name="terms_of_use_consent")
/**
* User is connected to LDAP directory.
*/
private boolean connectedToLdap = false;
@Column(name = "terms_of_use_consent")
private boolean termsOfUseConsent = false;
/**
......@@ -378,4 +383,12 @@ public class User implements Serializable {
this.termsOfUseConsent = termsOfUseConsent;
}
public boolean isConnectedToLdap() {
return connectedToLdap;
}
public void setConnectedToLdap(boolean connectedToLdap) {
this.connectedToLdap = connectedToLdap;
}
}
-- user account can be connected to LDAP directory
alter table user_table add column connectedtoldap boolean default false;
......@@ -2,9 +2,6 @@ package lcsb.mapviewer.persist.dao.user;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import lcsb.mapviewer.model.user.User;
import lcsb.mapviewer.persist.dao.BaseDao;
......@@ -16,12 +13,6 @@ import lcsb.mapviewer.persist.dao.BaseDao;
*/
public class UserDao extends BaseDao<User> {
/**
* Service that provides password encoding.
*/
@Autowired
private PasswordEncoder passwordEncoder;
/**
* Default constructor.
*/
......@@ -29,22 +20,6 @@ public class UserDao extends BaseDao<User> {
super(User.class, "removed");
}
/**
* Returns user with a given login and password.
*
* @param login
* user login
* @param password
* - user password (plain text)
* @return user for given login and password
*/
public User getUserByLoginAndPassword(String login, String password) {
if (password == null) {
return getUserByLoginAndCryptedPassword(login, null);
}
return getUserByLoginAndCryptedPassword(login, passwordEncoder.encode(password));
}
/**
* Returns user with a given login and password.
*
......@@ -56,8 +31,8 @@ public class UserDao extends BaseDao<User> {
*/
public User getUserByLoginAndCryptedPassword(String login, String password) {
List<?> list = getSession()
.createQuery(" from User where login=:login and cryptedPassword =:passwd " + removableAndStatemant()).setParameter("login", login)
.setParameter("passwd", password).list();
.createQuery(" from User where login=:login and cryptedPassword =:passwd " + removableAndStatemant())
.setParameter("login", login).setParameter("passwd", password).list();
if (list.size() == 0) {
return null;
} else {
......@@ -104,8 +79,8 @@ public class UserDao extends BaseDao<User> {
* @return {@link User} for given "name surname" string
*/
public User getUserByNameSurname(String nameSurnameString) {
List<?> list = getSession()
.createQuery(" from " + this.getClazz().getSimpleName() + " where concat(name, ' ', surname) " + " = :param_val " + removableAndStatemant())
List<?> list = getSession().createQuery(" from " + this.getClazz().getSimpleName()
+ " where concat(name, ' ', surname) " + " = :param_val " + removableAndStatemant())
.setParameter("param_val", nameSurnameString).list();
if (list.size() == 0) {
return null;
......
......@@ -122,49 +122,6 @@ public class UserDaoTest extends PersistTestFunctions {
}
}
@Test
public void testGetUserByLoginAndPassword() throws Exception {
try {
User user = new User();
user.setCryptedPassword(passwordEncoder.encode(testPasswd));
user.setLogin(testLogin);
userDao.add(user);
User user2 = userDao.getUserByLoginAndPassword(testLogin, testPasswd);
assertNotNull(user2);
assertEquals(user2.getId(), user.getId());
assertEquals(user2.getLogin(), user.getLogin());
assertEquals(user2.getCryptedPassword(), user.getCryptedPassword());
userDao.delete(user);
// after we remove it we shouldn't be able to get it
User user3 = userDao.getUserByLoginAndPassword(testLogin, testPasswd);
assertNull(user3);
// after we remove it we shouldn't be able to get the removed and modified
// object
User user4 = userDao.getUserByLoginAndPassword(user2.getLogin(), testPasswd);
assertNull(user4);
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
@Test
public void testGetUserByLoginAndEmptyPassword() throws Exception {
try {
User user = new User();
user.setCryptedPassword(passwordEncoder.encode(testPasswd));
user.setLogin(testLogin);
userDao.add(user);
User user2 = userDao.getUserByLoginAndPassword(testLogin, null);
assertNull(user2);
userDao.delete(user);
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
@Test
public void testGetUserByLogin() throws Exception {
try {
......
......@@ -78,7 +78,7 @@ public class LdapService implements ILdapService {
@Override
public boolean login(String login, String password) throws LDAPException {
if (!isValidConfiguratio()) {
if (!isValidConfiguration()) {
logger.warn("Invalid LDAP configuration");
return false;
}
......@@ -98,7 +98,7 @@ public class LdapService implements ILdapService {
@Override
public List<String> getUsernames() throws LDAPException {
if (!isValidConfiguratio()) {
if (!isValidConfiguration()) {
logger.warn("Invalid LDAP configuration");
return new ArrayList<>();
}
......@@ -128,7 +128,7 @@ public class LdapService implements ILdapService {
@Override
public UserDTO getUserByLogin(String login) throws LDAPException {
if (!isValidConfiguratio()) {
if (!isValidConfiguration()) {
logger.warn("Invalid LDAP configuration");
return null;
}
......@@ -187,11 +187,11 @@ public class LdapService implements ILdapService {
}
}
private Filter createObjectClassFilter() {
private Filter createObjectClassFilter() throws LDAPException {
String objectClass = configurationService.getConfigurationValue(ConfigurationElementType.LDAP_OBJECT_CLASS);
if (objectClass == null || objectClass.trim().isEmpty()) {
objectClass = "*";
if (objectClass == null || objectClass.trim().isEmpty() || objectClass .equals( "*")) {
return Filter.create("objectClass=*");
}
return Filter.createEqualityFilter("objectClass", objectClass);
......@@ -220,7 +220,7 @@ public class LdapService implements ILdapService {
}
@Override
public boolean isValidConfiguratio() {
public boolean isValidConfiguration() {
try {
String baseDn = configurationService.getConfigurationValue(ConfigurationElementType.LDAP_BASE_DN);
if (baseDn == null || baseDn.trim().isEmpty()) {
......
......@@ -5,7 +5,6 @@ import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
......@@ -60,7 +59,6 @@ import lcsb.mapviewer.converter.zip.ZipEntryFile;
import lcsb.mapviewer.model.Project;
import lcsb.mapviewer.model.ProjectStatus;
import lcsb.mapviewer.model.cache.UploadedFileEntry;
import lcsb.mapviewer.model.graphics.MapCanvasType;
import lcsb.mapviewer.model.log.LogType;
import lcsb.mapviewer.model.map.BioEntity;
import lcsb.mapviewer.model.map.MiriamData;
......@@ -477,20 +475,7 @@ public class ProjectService implements IProjectService {
for (User user : userDao.getAll()) {
if (!processedUser.contains(user)) {
processedUser.add(user);
for (PrivilegeType type : PrivilegeType.values()) {
if (Project.class.equals(type.getPrivilegeObjectType())) {
int level = userService.getUserPrivilegeLevel(user, type, (Integer) null);
if (level < 0) {
if (configurationService.getValue(type).getValue().equalsIgnoreCase("true")) {
level = 1;
} else {
level = 0;
}
}
ObjectPrivilege privilege = new ObjectPrivilege(project, level, type, user);
userService.setUserPrivilege(user, privilege);
}
}
userService.createDefaultProjectPrivilegesForUser(project, user);
}
}
......
......@@ -15,6 +15,8 @@ import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.transaction.annotation.Transactional;
import com.unboundid.ldap.sdk.LDAPException;
import lcsb.mapviewer.commands.ColorExtractor;
import lcsb.mapviewer.common.Configuration;
import lcsb.mapviewer.common.ObjectUtils;
......@@ -32,7 +34,9 @@ import lcsb.mapviewer.persist.dao.ProjectDao;
import lcsb.mapviewer.persist.dao.user.PrivilegeDao;
import lcsb.mapviewer.persist.dao.user.UserDao;
import lcsb.mapviewer.services.SecurityException;
import lcsb.mapviewer.services.UserDTO;
import lcsb.mapviewer.services.interfaces.IConfigurationService;
import lcsb.mapviewer.services.interfaces.ILdapService;
import lcsb.mapviewer.services.interfaces.ILogService;
import lcsb.mapviewer.services.interfaces.ILogService.LogParams;
import lcsb.mapviewer.services.interfaces.IUserService;
......@@ -57,12 +61,6 @@ public class UserService implements IUserService {
@Autowired
private UserDao userDao;
/**
* Data access object for projects.
*/
@Autowired
private ProjectDao projectDao;
/**
* Data access object for privileges.
*/
......@@ -84,6 +82,12 @@ public class UserService implements IUserService {
@Autowired
private ILogService logService;
@Autowired
private ILdapService ldapService;
@Autowired
private ProjectDao projectDao;
/**
* Service used for accessing configuration parameters.
*/
......@@ -176,7 +180,7 @@ public class UserService implements IUserService {
// users and drop privileges one by one)
List<User> users = userDao.getAll();
for (User user : users) {
List<BasicPrivilege> toRemove = new ArrayList<BasicPrivilege>();
List<BasicPrivilege> toRemove = new ArrayList<>();
for (BasicPrivilege privilege : user.getPrivileges()) {
if (privilege.getType().equals(type) && privilege instanceof ObjectPrivilege
&& integerComparator.compare(((ObjectPrivilege) privilege).getIdObject(), id) == 0) {
......@@ -282,23 +286,6 @@ public class UserService implements IUserService {
this.userDao = userDao;
}
/**
* @return the projectDao
* @see #projectDao
*/
public ProjectDao getProjectDao() {
return projectDao;
}
/**
* @param projectDao
* the projectDao to set
* @see #projectDao
*/
public void setProjectDao(ProjectDao projectDao) {
this.projectDao = projectDao;
}
/**
* @return the privilegeDao
* @see #privilegeDao
......@@ -514,7 +501,23 @@ public class UserService implements IUserService {
@Override
public String login(String login, String password, String token) {
User user = userDao.getUserByLoginAndPassword(login, password);
User user = userDao.getUserByLogin(login);
if (user == null) {
user = createUserFromLdap(login, password, user);
} else if (!user.isConnectedToLdap()) {
String cryptedPassword;
if (password != null) {
cryptedPassword = passwordEncoder.encode(password);
} else {
cryptedPassword = "";
}
user = userDao.getUserByLoginAndCryptedPassword(login, cryptedPassword);
} else {
if (!authenticateOverLdap(login, password)) {
user = null;
}
}
if (user == null && Configuration.ANONYMOUS_LOGIN.equals(login) && "".equals(password)) {
user = getUserByLogin(Configuration.ANONYMOUS_LOGIN);
}
......@@ -527,4 +530,71 @@ public class UserService implements IUserService {
}
}
private boolean authenticateOverLdap(String login, String password) {
if (!ldapService.isValidConfiguration()) {
return false;
}
try {
return ldapService.login(login, password);
} catch (LDAPException e) {
logger.warn("Problem with accessing LDAP directory", e);
return false;
}
}
private User createUserFromLdap(String login, String password, User user) {
if (!ldapService.isValidConfiguration()) {
return null;
}
try {
boolean authenticatedOverLdap = ldapService.login(login, password);
if (authenticatedOverLdap) {
UserDTO ldapUserData = ldapService.getUserByLogin(login);
user = new User();
user.setLogin(login);
user.setCryptedPassword(passwordEncoder.encode(password));
user.setName(ldapUserData.getFirstName());
user.setSurname(ldapUserData.getLastName());
user.setEmail(ldapUserData.getEmail());
user.setConnectedToLdap(true);
addUser(user);
for (Project project : projectDao.getAll()) {
createDefaultProjectPrivilegesForUser(project, user);
}
}
return user;
} catch (LDAPException e) {
logger.warn("Problem with accessing LDAP directory", e);
return null;
}
}
@Override
public ILdapService getLdapService() {
return ldapService;
}
@Override
public void setLdapService(ILdapService ldapService) {
this.ldapService = ldapService;
}
@Override
public void createDefaultProjectPrivilegesForUser(Project project, User user) {
for (PrivilegeType type : PrivilegeType.values()) {
if (Project.class.equals(type.getPrivilegeObjectType())) {
int level = getUserPrivilegeLevel(user, type, (Integer) null);
if (level < 0) {
if (configurationService.getValue(type).getValue().equalsIgnoreCase("true")) {
level = 1;
} else {
level = 0;
}
}
ObjectPrivilege privilege = new ObjectPrivilege(project, level, type, user);
setUserPrivilege(user, privilege);
}
}
}
}
......@@ -56,6 +56,6 @@ public interface ILdapService {
* @return true if LDAP configuration
* ({@link ConfigurationElementTypeGroup#LDAP_CONFIGURATION}) is valid
*/
boolean isValidConfiguratio();
boolean isValidConfiguration();
}
......@@ -3,6 +3,7 @@ package lcsb.mapviewer.services.interfaces;
import java.util.List;
import lcsb.mapviewer.commands.ColorExtractor;
import lcsb.mapviewer.model.Project;
import lcsb.mapviewer.model.user.BasicPrivilege;
import lcsb.mapviewer.model.user.PrivilegeType;
import lcsb.mapviewer.model.user.User;
......@@ -198,4 +199,10 @@ public interface IUserService {
String login(String login, String password, String id);
ILdapService getLdapService();
void setLdapService(ILdapService ldapService);
void createDefaultProjectPrivilegesForUser(Project project, User user);
}
......@@ -9,6 +9,7 @@ import org.junit.runners.Suite.SuiteClasses;
ConfigurationServiceTest.class, //
ExternalServicesServiceTest.class, //
LayoutServiceTest.class, //
LdapServiceTest.class, //
Md5PasswordEncoderTest.class, //
ProjectServiceTest.class, //
SearchServiceTest.class, //
......
......@@ -69,9 +69,9 @@ public class LdapServiceTest extends ServiceTestFunctions {
@Test
public void testIsValidConfiguration() throws Exception {
try {
assertTrue(ldapService.isValidConfiguratio());
assertTrue(ldapService.isValidConfiguration());
configurationService.setConfigurationValue(ConfigurationElementType.LDAP_BASE_DN, "");
assertFalse(ldapService.isValidConfiguratio());
assertFalse(ldapService.isValidConfiguration());
} catch (Exception e) {
e.printStackTrace();
throw e;
......
......@@ -11,14 +11,24 @@ import org.apache.log4j.Logger;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.springframework.test.annotation.Rollback;
import com.unboundid.ldap.listener.InMemoryDirectoryServer;
import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig;
import com.unboundid.ldap.sdk.LDAPConnection;
import com.unboundid.ldap.sdk.LDAPException;
import lcsb.mapviewer.model.Project;
import lcsb.mapviewer.model.user.BasicPrivilege;
import lcsb.mapviewer.model.user.ConfigurationElementType;
import lcsb.mapviewer.model.user.ObjectPrivilege;
import lcsb.mapviewer.model.user.PrivilegeType;
import lcsb.mapviewer.model.user.User;
import lcsb.mapviewer.services.ServiceTestFunctions;
import lcsb.mapviewer.services.interfaces.ILdapService;
@Rollback(true)
public class UserServiceTest extends ServiceTestFunctions {
......@@ -46,6 +56,58 @@ public class UserServiceTest extends ServiceTestFunctions {
}
}
@Test
public void testLoginFromLdap() throws Exception {
ILdapService originalLdapService = userService.getLdapService();
try {
String login = "john.doe.test";
String passwd = "test_passwd";
assertNull(userService.getUserByLogin(login));
LdapService ldapService = createMockLdapService("testFiles/ldap/john-doe-test-example.ldif", login, passwd);
userService.setLdapService(ldapService);
assertNull(userService.login(login, "incorrect password"));
assertNotNull("User from LDAP wasn't authenticated", userService.login(login, passwd));
User user = userService.getUserByLogin(login);
assertNotNull("After authentication from LDAP user is not present in the system", user);
assertTrue(user.isConnectedToLdap());
} catch (Exception e) {
e.printStackTrace();
throw e;
} finally {
userService.setLdapService(originalLdapService);
}
}
private LdapService createMockLdapService(String filename, String login, String passwd) throws LDAPException {
configurationService.setConfigurationValue(ConfigurationElementType.LDAP_BASE_DN, "dc=uni,dc=lu");
configurationService.setConfigurationValue(ConfigurationElementType.LDAP_OBJECT_CLASS, "person");
LdapService ldapService = Mockito.spy(LdapService.class);
ldapService.setConfigurationService(configurationService);
Mockito.when(ldapService.getConnection()).thenAnswer(new Answer<LDAPConnection>() {
@Override
public LDAPConnection answer(InvocationOnMock invocation) throws Throwable {
// Create the configuration to use for the server.
InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig("dc=uni,dc=lu");
config.addAdditionalBindCredentials("uid=" + login + ",cn=users,cn=accounts,dc=uni,dc=lu", passwd);
config.setSchema(null);
// Create the directory server instance, populate it with data from the
// "test-data.ldif" file, and start listening for client connections.
InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config);
ds.importFromLDIF(true, filename);
ds.startListening();
return ds.getConnection();
}
});
return ldapService;
}
@Test
public void testLoginWithNull() {
try {
......
version: 1
dn: dc=uni,dc=lu
objectClass: top
objectClass: domain
dc: uni
dn: cn=accounts,dc=uni,dc=lu
objectClass: extensibleObject
cn: accounts
dn: cn=users,cn=accounts,dc=uni,dc=lu
objectClass: extensibleObject
cn: users
dn: uid=john.doe.test,cn=users,cn=accounts,dc=uni,dc=lu
objectClass: mepOriginEntry
objectClass: ipaSshGroupOfPubKeys
objectClass: posixaccount
objectClass: inetuser
objectClass: krbprincipalaux
objectClass: krbticketpolicyaux
objectClass: organizationalperson
objectClass: inetorgperson
objectClass: ipasshuser
objectClass: top
objectClass: person
objectClass: ipaobject
cn: John Doe
gidNumber: 369550501
homeDirectory: /home/john.doe.test
ipaUniqueID: adf723e6-20e4-11e5-8907-001a4ae51219
sn: Doe
uid: john.doe.test
uidNumber: 369550501