From fd5b6407e68c6a98e6d3659da55369d5cb0d3ee2 Mon Sep 17 00:00:00 2001 From: Piotr Gawron <piotr.gawron@uni.lu> Date: Wed, 18 Jul 2018 11:53:48 +0200 Subject: [PATCH] user is automatically created when local user doesn't exist and authentication goes via ldap --- .../model/user/ConfigurationElementType.java | 2 +- .../java/lcsb/mapviewer/model/user/User.java | 17 +- .../src/db/12.1.0~alpha.0/fix_db_20180718.sql | 2 + .../mapviewer/persist/dao/user/UserDao.java | 165 +-- .../persist/dao/user/UserDaoTest.java | 43 - .../mapviewer/services/impl/LdapService.java | 14 +- .../services/impl/ProjectService.java | 17 +- .../mapviewer/services/impl/UserService.java | 1130 +++++++++-------- .../services/interfaces/ILdapService.java | 2 +- .../services/interfaces/IUserService.java | 7 + .../services/impl/AllImplServiceTests.java | 15 +- .../services/impl/LdapServiceTest.java | 4 +- .../services/impl/UserServiceTest.java | 62 + .../testFiles/ldap/john-doe-test-example.ldif | 92 ++ .../CustomAuthenticationProvider.java | 53 + .../security/MvSecurityServiceImpl.java | 72 -- .../main/webapp/WEB-INF/security-context.xml | 9 +- 17 files changed, 923 insertions(+), 783 deletions(-) create mode 100644 persist/src/db/12.1.0~alpha.0/fix_db_20180718.sql create mode 100644 service/testFiles/ldap/john-doe-test-example.ldif create mode 100644 web/src/main/java/lcsb/mapviewer/security/CustomAuthenticationProvider.java delete mode 100644 web/src/main/java/lcsb/mapviewer/security/MvSecurityServiceImpl.java diff --git a/model/src/main/java/lcsb/mapviewer/model/user/ConfigurationElementType.java b/model/src/main/java/lcsb/mapviewer/model/user/ConfigurationElementType.java index dae902490a..d5aba762cb 100644 --- a/model/src/main/java/lcsb/mapviewer/model/user/ConfigurationElementType.java +++ b/model/src/main/java/lcsb/mapviewer/model/user/ConfigurationElementType.java @@ -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), // ; diff --git a/model/src/main/java/lcsb/mapviewer/model/user/User.java b/model/src/main/java/lcsb/mapviewer/model/user/User.java index fdb5e080a1..1add1fd25f 100644 --- a/model/src/main/java/lcsb/mapviewer/model/user/User.java +++ b/model/src/main/java/lcsb/mapviewer/model/user/User.java @@ -118,8 +118,13 @@ public class User implements Serializable { */ private boolean removed = false; - @Column(name="terms_of_use_consent") - private boolean termsOfUseConsent = false; + /** + * User is connected to LDAP directory. + */ + private boolean connectedToLdap = false; + + @Column(name = "terms_of_use_consent") + private boolean termsOfUseConsent = false; /** * Set of user privileges. @@ -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; + } + } diff --git a/persist/src/db/12.1.0~alpha.0/fix_db_20180718.sql b/persist/src/db/12.1.0~alpha.0/fix_db_20180718.sql new file mode 100644 index 0000000000..6872876ec7 --- /dev/null +++ b/persist/src/db/12.1.0~alpha.0/fix_db_20180718.sql @@ -0,0 +1,2 @@ +-- user account can be connected to LDAP directory +alter table user_table add column connectedtoldap boolean default false; diff --git a/persist/src/main/java/lcsb/mapviewer/persist/dao/user/UserDao.java b/persist/src/main/java/lcsb/mapviewer/persist/dao/user/UserDao.java index 84af311b9b..bedec091dd 100644 --- a/persist/src/main/java/lcsb/mapviewer/persist/dao/user/UserDao.java +++ b/persist/src/main/java/lcsb/mapviewer/persist/dao/user/UserDao.java @@ -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,102 +13,80 @@ import lcsb.mapviewer.persist.dao.BaseDao; */ public class UserDao extends BaseDao<User> { - /** - * Service that provides password encoding. - */ - @Autowired - private PasswordEncoder passwordEncoder; - - /** - * Default constructor. - */ - public UserDao() { - 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)); - } + /** + * Default constructor. + */ + public UserDao() { + super(User.class, "removed"); + } - /** - * Returns user with a given login and password. - * - * @param login - * user login - * @param password - * - user password (encrypted) - * @return user for given login and password - */ - 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(); - if (list.size() == 0) { - return null; - } else { - User user = (User) list.get(0); - return user; - } - } + /** + * Returns user with a given login and password. + * + * @param login + * user login + * @param password + * - user password (encrypted) + * @return user for given login and password + */ + 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(); + if (list.size() == 0) { + return null; + } else { + User user = (User) list.get(0); + return user; + } + } - /** - * Returns user with a given login. - * - * @param login - * user login - * @return user for a given login - */ - public User getUserByLogin(String login) { - return getByParameter("login", login); - } + /** + * Returns user with a given login. + * + * @param login + * user login + * @return user for a given login + */ + public User getUserByLogin(String login) { + return getByParameter("login", login); + } - /** - * Returns user with a given email. - * - * @param email - * user email - * @return user for a given email - */ - public User getUserByEmail(String email) { - return getByParameter("email", email); - } + /** + * Returns user with a given email. + * + * @param email + * user email + * @return user for a given email + */ + public User getUserByEmail(String email) { + return getByParameter("email", email); + } - @Override - public void delete(User object) { - object.setRemoved(true); - object.setLogin("[REMOVED]_" + object.getId() + "_" + object.getLogin()); - update(object); - } + @Override + public void delete(User object) { + object.setRemoved(true); + object.setLogin("[REMOVED]_" + object.getId() + "_" + object.getLogin()); + update(object); + } - /** - * Returns {@link User} for given "name surname" string. - * - * @param nameSurnameString - * string identifing user with name and surname separated by single - * space - * @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()) - .setParameter("param_val", nameSurnameString).list(); - if (list.size() == 0) { - return null; - } else { - return (User) list.get(0); - } - } + /** + * Returns {@link User} for given "name surname" string. + * + * @param nameSurnameString + * string identifing user with name and surname separated by single + * space + * @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()) + .setParameter("param_val", nameSurnameString).list(); + if (list.size() == 0) { + return null; + } else { + return (User) list.get(0); + } + } } diff --git a/persist/src/test/java/lcsb/mapviewer/persist/dao/user/UserDaoTest.java b/persist/src/test/java/lcsb/mapviewer/persist/dao/user/UserDaoTest.java index 0192989f6d..373262230d 100644 --- a/persist/src/test/java/lcsb/mapviewer/persist/dao/user/UserDaoTest.java +++ b/persist/src/test/java/lcsb/mapviewer/persist/dao/user/UserDaoTest.java @@ -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 { diff --git a/service/src/main/java/lcsb/mapviewer/services/impl/LdapService.java b/service/src/main/java/lcsb/mapviewer/services/impl/LdapService.java index 8a2a96dbe3..8e40c11e36 100644 --- a/service/src/main/java/lcsb/mapviewer/services/impl/LdapService.java +++ b/service/src/main/java/lcsb/mapviewer/services/impl/LdapService.java @@ -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()) { diff --git a/service/src/main/java/lcsb/mapviewer/services/impl/ProjectService.java b/service/src/main/java/lcsb/mapviewer/services/impl/ProjectService.java index b79e0ef372..fe26c10cbf 100644 --- a/service/src/main/java/lcsb/mapviewer/services/impl/ProjectService.java +++ b/service/src/main/java/lcsb/mapviewer/services/impl/ProjectService.java @@ -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); } } diff --git a/service/src/main/java/lcsb/mapviewer/services/impl/UserService.java b/service/src/main/java/lcsb/mapviewer/services/impl/UserService.java index 6eb486fdb5..2baf2dee79 100644 --- a/service/src/main/java/lcsb/mapviewer/services/impl/UserService.java +++ b/service/src/main/java/lcsb/mapviewer/services/impl/UserService.java @@ -1,530 +1,600 @@ -package lcsb.mapviewer.services.impl; - -import java.awt.Color; -import java.math.BigInteger; -import java.security.SecureRandom; -import java.util.ArrayList; -import java.util.List; -import java.util.Random; - -import org.apache.log4j.Logger; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.core.authority.AuthorityUtils; -import org.springframework.security.core.session.SessionInformation; -import org.springframework.security.core.session.SessionRegistry; -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.transaction.annotation.Transactional; - -import lcsb.mapviewer.commands.ColorExtractor; -import lcsb.mapviewer.common.Configuration; -import lcsb.mapviewer.common.ObjectUtils; -import lcsb.mapviewer.common.comparator.IntegerComparator; -import lcsb.mapviewer.common.exception.InvalidArgumentException; -import lcsb.mapviewer.common.geometry.ColorParser; -import lcsb.mapviewer.model.Project; -import lcsb.mapviewer.model.log.LogType; -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.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.interfaces.IConfigurationService; -import lcsb.mapviewer.services.interfaces.ILogService; -import lcsb.mapviewer.services.interfaces.ILogService.LogParams; -import lcsb.mapviewer.services.interfaces.IUserService; - -/** - * Implementation of the service that manages users. - * - * @author Piotr Gawron - * - */ -@Transactional(value = "txManager") -public class UserService implements IUserService { - - /** - * Default class logger. - */ - private static Logger logger = Logger.getLogger(UserService.class); - - /** - * Data access object for users. - */ - @Autowired - private UserDao userDao; - - /** - * Data access object for projects. - */ - @Autowired - private ProjectDao projectDao; - - /** - * Data access object for privileges. - */ - @Autowired - private PrivilegeDao privilegeDao; - - @Autowired - private SessionRegistry sessionRegistry; - - /** - * Service that provides password encoding. - */ - @Autowired - private PasswordEncoder passwordEncoder; - - /** - * Service used for logging. - */ - @Autowired - private ILogService logService; - - /** - * Service used for accessing configuration parameters. - */ - @Autowired - private IConfigurationService configurationService; - - @Override - public String login(String login, String password) { - Random random = new SecureRandom(); - String id = new BigInteger(130, random).toString(32); - return this.login(login, password, id); - } - - @Override - public boolean userHasPrivilege(User user, PrivilegeType type) { - return getUserPrivilegeLevel(user, type) > 0; - } - - @Override - public boolean userHasPrivilege(User user, PrivilegeType type, Object object) { - return getUserPrivilegeLevel(user, type, object) > 0; - } - - @Override - public void setUserPrivilege(User user, BasicPrivilege privilege) { - updateUserPrivilegesWithoutDbModification(user, privilege); - updateUser(user); - userDao.flush(); - } - - private void updateUserPrivilegesWithoutDbModification(User user, BasicPrivilege privilege) { - BasicPrivilege oldPrivilege = null; - for (BasicPrivilege privilegeIter : user.getPrivileges()) { - if (privilegeIter.equalsPrivilege(privilege)) { - oldPrivilege = privilegeIter; - } - } - if (oldPrivilege != null) { - privilege.setUser(null); - oldPrivilege.setLevel(privilege.getLevel()); - } else { - privilege.setUser(user); - user.getPrivileges().add(privilege); - } - } - - @Override - public void addUser(User user) { - userDao.add(user); - LogParams params = new LogParams().description("User " + user.getLogin() + " created.").type(LogType.USER_CREATED) - .object(user); - logService.log(params); - } - - @Override - public void updateUser(User user) { - userDao.update(user); - } - - @Override - public void deleteUser(User user) { - userDao.delete(user); - LogParams params = new LogParams().description("User " + user.getLogin() + " removed.").type(LogType.USER_CREATED) - .object(user); - logService.log(params); - } - - @Override - public User getUserById(int id) { - User result = userDao.getById(id); - if (result != null) { - userDao.refresh(result); - } - return result; - } - - @Override - public User getUserByLogin(String login) { - User result = userDao.getUserByLogin(login); - if (result != null) { - userDao.refresh(result); - } - return result; - } - - @Override - public void dropPrivilegesForObjectType(PrivilegeType type, int id) { - IntegerComparator integerComparator = new IntegerComparator(); - // this will be slow when number of user will increase (we fetch all - // users and drop privileges one by one) - List<User> users = userDao.getAll(); - for (User user : users) { - List<BasicPrivilege> toRemove = new ArrayList<BasicPrivilege>(); - for (BasicPrivilege privilege : user.getPrivileges()) { - if (privilege.getType().equals(type) && privilege instanceof ObjectPrivilege - && integerComparator.compare(((ObjectPrivilege) privilege).getIdObject(), id) == 0) { - toRemove.add(privilege); - } - } - if (toRemove.size() > 0) { - user.getPrivileges().removeAll(toRemove); - userDao.update(user); - } - } - } - - @Override - public int getUserPrivilegeLevel(User user, PrivilegeType type) { - if (type.getPrivilegeClassType() != BasicPrivilege.class) { - throw new InvalidArgumentException("This privilege requires additional information"); - } - for (BasicPrivilege privilege : user.getPrivileges()) { - if (privilege.getType().equals(type)) { - return privilege.getLevel(); - } - } - return 0; - } - - @Override - public int getUserPrivilegeLevel(User user, PrivilegeType type, Object object) { - Integer id = null; - if (object != null) { - try { - id = ObjectUtils.getIdOfObject(object); - } catch (Exception e) { - logger.error(e.getMessage(), e); - throw new InvalidArgumentException("Internal server error. Problem with accessing id of the parameter object"); - } - if (!type.getPrivilegeObjectType().isAssignableFrom(object.getClass())) { - throw new InvalidArgumentException("This privilege accept only " + type.getPrivilegeObjectType() - + " objects parameter, but " + object.getClass() + " class found."); - } - } - return getUserPrivilegeLevel(user, type, id); - } - - private int getUserPrivilegeLevel(User user, PrivilegeType type, Integer id) { - if (type.getPrivilegeClassType() != ObjectPrivilege.class) { - throw new InvalidArgumentException("This privilege doesn't accept object parameter"); - } - if (user == null) { - throw new InvalidArgumentException("User cannot be null"); - } - - // refresh user from db - if (user.getId() != null) { - user = userDao.getById(user.getId()); - } - IntegerComparator integerComparator = new IntegerComparator(); - for (BasicPrivilege privilege : user.getPrivileges()) { - if (privilege.getClass() == ObjectPrivilege.class) { - ObjectPrivilege oPrivilege = (ObjectPrivilege) privilege; - if (oPrivilege.getType().equals(type) && integerComparator.compare(oPrivilege.getIdObject(), id) == 0) { - return privilege.getLevel(); - } - } - } - return -1; - } - - @Override - public void setUserPrivilege(User user, PrivilegeType type, Integer value) { - BasicPrivilege privilege = new BasicPrivilege(value, type, user); - - BasicPrivilege oldPrivilege = null; - for (BasicPrivilege privilegeIter : user.getPrivileges()) { - if (privilegeIter.getType().equals(type)) { - oldPrivilege = privilegeIter; - } - } - if (oldPrivilege != null) { - user.getPrivileges().remove(oldPrivilege); - oldPrivilege.setUser(null); - } - user.getPrivileges().add(privilege); - updateUser(user); - userDao.flush(); - - } - - /** - * @return the userDao - * @see #userDao - */ - public UserDao getUserDao() { - return userDao; - } - - /** - * @param userDao - * the userDao to set - * @see #userDao - */ - public void setUserDao(UserDao userDao) { - 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 - */ - public PrivilegeDao getPrivilegeDao() { - return privilegeDao; - } - - /** - * @param privilegeDao - * the privilegeDao to set - * @see #privilegeDao - */ - public void setPrivilegeDao(PrivilegeDao privilegeDao) { - this.privilegeDao = privilegeDao; - } - - /** - * @return the passwordEncoder - * @see #passwordEncoder - */ - public PasswordEncoder getPasswordEncoder() { - return passwordEncoder; - } - - /** - * @param passwordEncoder - * the passwordEncoder to set - * @see #passwordEncoder - */ - public void setPasswordEncoder(PasswordEncoder passwordEncoder) { - this.passwordEncoder = passwordEncoder; - } - - /** - * @param password - * input password - * @return encoded password - */ - @Override - public String encodePassword(String password) { - return passwordEncoder.encode(password); - } - - @Override - public User getUserByNameSurname(String nameSurnameString) { - return userDao.getUserByNameSurname(nameSurnameString); - } - - @Override - public ColorExtractor getColorExtractorForUser(User loggedUser) { - Color colorMin = null; - Color colorMax = null; - Color colorSimple = null; - if (loggedUser != null) { - User dbUser = getUserById(loggedUser.getId()); - if (dbUser != null) { - colorMin = dbUser.getMinColor(); - colorMax = dbUser.getMaxColor(); - colorSimple = dbUser.getSimpleColor(); - } - } - ColorParser parser = new ColorParser(); - - if (colorMin == null) { - colorMin = parser.parse(configurationService.getConfigurationValue(ConfigurationElementType.MIN_COLOR_VAL)); - } - if (colorMax == null) { - colorMax = parser.parse(configurationService.getConfigurationValue(ConfigurationElementType.MAX_COLOR_VAL)); - } - if (colorSimple == null) { - colorSimple = parser.parse(configurationService.getConfigurationValue(ConfigurationElementType.SIMPLE_COLOR_VAL)); - } - return new ColorExtractor(colorMin, colorMax, colorSimple); - } - - /** - * @return the configurationService - * @see #configurationService - */ - public IConfigurationService getConfigurationService() { - return configurationService; - } - - /** - * @param configurationService - * the configurationService to set - * @see #configurationService - */ - public void setConfigurationService(IConfigurationService configurationService) { - this.configurationService = configurationService; - } - - @Override - public User getUserByToken(String token) throws SecurityException { - if (!isSessionExpired(token)) { - String login = ((org.springframework.security.core.userdetails.User) (sessionRegistry.getSessionInformation(token) - .getPrincipal())).getUsername(); - return userDao.getUserByLogin(login); - } else { - throw new SecurityException("Invalid token"); - } - } - - private boolean isSessionExpired(String token) { - SessionInformation sessionData = sessionRegistry.getSessionInformation(token); - if (sessionData == null) { - logger.debug("No session data for token id: " + token); - return true; - } - return sessionData.isExpired(); - } - - @Override - public boolean userHasPrivilege(String token, PrivilegeType type, Object object) throws SecurityException { - return userHasPrivilege(getUserByToken(token), type, object); - } - - @Override - public void logout(String tokenString) { - if (!isSessionExpired(tokenString)) { - sessionRegistry.removeSessionInformation(tokenString); - } - } - - @Override - public User getUserById(String creatorId, String authenticationToken) throws SecurityException { - User user = getUserByToken(authenticationToken); - Integer id = Integer.parseInt(creatorId); - if (user.getId().equals(id)) { - return user; - } else if (userHasPrivilege(authenticationToken, PrivilegeType.USER_MANAGEMENT)) { - return getUserById(id); - } else { - throw new SecurityException("You cannot access data of other users"); - } - } - - @Override - public List<User> getUsers(String token) throws SecurityException { - if (userHasPrivilege(token, PrivilegeType.USER_MANAGEMENT)) { - return userDao.getAll(); - } else { - throw new SecurityException("You have no access to users data"); - } - } - - @Override - public void setUserPrivilege(User user, PrivilegeType type, Object value, String token) throws SecurityException { - if (!userHasPrivilege(token, PrivilegeType.USER_MANAGEMENT)) { - throw new SecurityException("You cannot modify user privileges"); - } - if (value instanceof Integer) { - setUserPrivilege(user, type, (Integer) value); - } else if (value instanceof Boolean) { - if ((Boolean) value) { - setUserPrivilege(user, type, 1); - } else { - setUserPrivilege(user, type, 0); - } - } else { - throw new InvalidArgumentException("Invalid privilege value: " + value); - } - } - - @Override - public void setUserPrivilege(User user, PrivilegeType type, Object value, Integer objectId, String token) - throws SecurityException { - boolean canModify = userHasPrivilege(token, PrivilegeType.USER_MANAGEMENT); - if (!canModify) { - if (type.getPrivilegeObjectType().isAssignableFrom(Project.class)) { - canModify = getUserPrivilegeLevel(getUserByToken(token), type, objectId) > 0; - } - } - if (!canModify) { - throw new SecurityException("You cannot modify user privileges"); - } - Project projectIdWrapper = new Project(); - if (objectId == null) { - projectIdWrapper = null; - } else { - projectIdWrapper.setId(objectId); - } - if (value instanceof Integer) { - setUserPrivilege(user, new ObjectPrivilege(projectIdWrapper, (Integer) value, type, user)); - } else if (value instanceof Boolean) { - if ((Boolean) value) { - setUserPrivilege(user, new ObjectPrivilege(projectIdWrapper, 1, type, user)); - } else { - setUserPrivilege(user, new ObjectPrivilege(projectIdWrapper, 0, type, user)); - } - } else { - throw new InvalidArgumentException("Invalid privilege value: " + value); - } - - } - - @Override - public void updateUser(User modifiedUser, String token) throws SecurityException { - User user = getUserByToken(token); - if (user.getLogin().equals(modifiedUser.getLogin()) || userHasPrivilege(token, PrivilegeType.USER_MANAGEMENT)) { - updateUser(modifiedUser); - } else { - throw new SecurityException("You cannot modify user"); - } - - } - - @Override - public boolean userHasPrivilege(String token, PrivilegeType type) throws SecurityException { - return userHasPrivilege(getUserByToken(token), type); - } - - @Override - public String login(String login, String password, String token) { - User user = userDao.getUserByLoginAndPassword(login, password); - if (user == null && Configuration.ANONYMOUS_LOGIN.equals(login) && "".equals(password)) { - user = getUserByLogin(Configuration.ANONYMOUS_LOGIN); - } - if (user != null) { - sessionRegistry.registerNewSession(token, new org.springframework.security.core.userdetails.User(login, - passwordEncoder.encode(password), AuthorityUtils.commaSeparatedStringToAuthorityList(""))); - return token; - } else { - return null; - } - } - -} +package lcsb.mapviewer.services.impl; + +import java.awt.Color; +import java.math.BigInteger; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import org.apache.log4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.authority.AuthorityUtils; +import org.springframework.security.core.session.SessionInformation; +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; +import lcsb.mapviewer.common.comparator.IntegerComparator; +import lcsb.mapviewer.common.exception.InvalidArgumentException; +import lcsb.mapviewer.common.geometry.ColorParser; +import lcsb.mapviewer.model.Project; +import lcsb.mapviewer.model.log.LogType; +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.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; + +/** + * Implementation of the service that manages users. + * + * @author Piotr Gawron + * + */ +@Transactional(value = "txManager") +public class UserService implements IUserService { + + /** + * Default class logger. + */ + private static Logger logger = Logger.getLogger(UserService.class); + + /** + * Data access object for users. + */ + @Autowired + private UserDao userDao; + + /** + * Data access object for privileges. + */ + @Autowired + private PrivilegeDao privilegeDao; + + @Autowired + private SessionRegistry sessionRegistry; + + /** + * Service that provides password encoding. + */ + @Autowired + private PasswordEncoder passwordEncoder; + + /** + * Service used for logging. + */ + @Autowired + private ILogService logService; + + @Autowired + private ILdapService ldapService; + + @Autowired + private ProjectDao projectDao; + + /** + * Service used for accessing configuration parameters. + */ + @Autowired + private IConfigurationService configurationService; + + @Override + public String login(String login, String password) { + Random random = new SecureRandom(); + String id = new BigInteger(130, random).toString(32); + return this.login(login, password, id); + } + + @Override + public boolean userHasPrivilege(User user, PrivilegeType type) { + return getUserPrivilegeLevel(user, type) > 0; + } + + @Override + public boolean userHasPrivilege(User user, PrivilegeType type, Object object) { + return getUserPrivilegeLevel(user, type, object) > 0; + } + + @Override + public void setUserPrivilege(User user, BasicPrivilege privilege) { + updateUserPrivilegesWithoutDbModification(user, privilege); + updateUser(user); + userDao.flush(); + } + + private void updateUserPrivilegesWithoutDbModification(User user, BasicPrivilege privilege) { + BasicPrivilege oldPrivilege = null; + for (BasicPrivilege privilegeIter : user.getPrivileges()) { + if (privilegeIter.equalsPrivilege(privilege)) { + oldPrivilege = privilegeIter; + } + } + if (oldPrivilege != null) { + privilege.setUser(null); + oldPrivilege.setLevel(privilege.getLevel()); + } else { + privilege.setUser(user); + user.getPrivileges().add(privilege); + } + } + + @Override + public void addUser(User user) { + userDao.add(user); + LogParams params = new LogParams().description("User " + user.getLogin() + " created.").type(LogType.USER_CREATED) + .object(user); + logService.log(params); + } + + @Override + public void updateUser(User user) { + userDao.update(user); + } + + @Override + public void deleteUser(User user) { + userDao.delete(user); + LogParams params = new LogParams().description("User " + user.getLogin() + " removed.").type(LogType.USER_CREATED) + .object(user); + logService.log(params); + } + + @Override + public User getUserById(int id) { + User result = userDao.getById(id); + if (result != null) { + userDao.refresh(result); + } + return result; + } + + @Override + public User getUserByLogin(String login) { + User result = userDao.getUserByLogin(login); + if (result != null) { + userDao.refresh(result); + } + return result; + } + + @Override + public void dropPrivilegesForObjectType(PrivilegeType type, int id) { + IntegerComparator integerComparator = new IntegerComparator(); + // this will be slow when number of user will increase (we fetch all + // users and drop privileges one by one) + List<User> users = userDao.getAll(); + for (User user : users) { + 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) { + toRemove.add(privilege); + } + } + if (toRemove.size() > 0) { + user.getPrivileges().removeAll(toRemove); + userDao.update(user); + } + } + } + + @Override + public int getUserPrivilegeLevel(User user, PrivilegeType type) { + if (type.getPrivilegeClassType() != BasicPrivilege.class) { + throw new InvalidArgumentException("This privilege requires additional information"); + } + for (BasicPrivilege privilege : user.getPrivileges()) { + if (privilege.getType().equals(type)) { + return privilege.getLevel(); + } + } + return 0; + } + + @Override + public int getUserPrivilegeLevel(User user, PrivilegeType type, Object object) { + Integer id = null; + if (object != null) { + try { + id = ObjectUtils.getIdOfObject(object); + } catch (Exception e) { + logger.error(e.getMessage(), e); + throw new InvalidArgumentException("Internal server error. Problem with accessing id of the parameter object"); + } + if (!type.getPrivilegeObjectType().isAssignableFrom(object.getClass())) { + throw new InvalidArgumentException("This privilege accept only " + type.getPrivilegeObjectType() + + " objects parameter, but " + object.getClass() + " class found."); + } + } + return getUserPrivilegeLevel(user, type, id); + } + + private int getUserPrivilegeLevel(User user, PrivilegeType type, Integer id) { + if (type.getPrivilegeClassType() != ObjectPrivilege.class) { + throw new InvalidArgumentException("This privilege doesn't accept object parameter"); + } + if (user == null) { + throw new InvalidArgumentException("User cannot be null"); + } + + // refresh user from db + if (user.getId() != null) { + user = userDao.getById(user.getId()); + } + IntegerComparator integerComparator = new IntegerComparator(); + for (BasicPrivilege privilege : user.getPrivileges()) { + if (privilege.getClass() == ObjectPrivilege.class) { + ObjectPrivilege oPrivilege = (ObjectPrivilege) privilege; + if (oPrivilege.getType().equals(type) && integerComparator.compare(oPrivilege.getIdObject(), id) == 0) { + return privilege.getLevel(); + } + } + } + return -1; + } + + @Override + public void setUserPrivilege(User user, PrivilegeType type, Integer value) { + BasicPrivilege privilege = new BasicPrivilege(value, type, user); + + BasicPrivilege oldPrivilege = null; + for (BasicPrivilege privilegeIter : user.getPrivileges()) { + if (privilegeIter.getType().equals(type)) { + oldPrivilege = privilegeIter; + } + } + if (oldPrivilege != null) { + user.getPrivileges().remove(oldPrivilege); + oldPrivilege.setUser(null); + } + user.getPrivileges().add(privilege); + updateUser(user); + userDao.flush(); + + } + + /** + * @return the userDao + * @see #userDao + */ + public UserDao getUserDao() { + return userDao; + } + + /** + * @param userDao + * the userDao to set + * @see #userDao + */ + public void setUserDao(UserDao userDao) { + this.userDao = userDao; + } + + /** + * @return the privilegeDao + * @see #privilegeDao + */ + public PrivilegeDao getPrivilegeDao() { + return privilegeDao; + } + + /** + * @param privilegeDao + * the privilegeDao to set + * @see #privilegeDao + */ + public void setPrivilegeDao(PrivilegeDao privilegeDao) { + this.privilegeDao = privilegeDao; + } + + /** + * @return the passwordEncoder + * @see #passwordEncoder + */ + public PasswordEncoder getPasswordEncoder() { + return passwordEncoder; + } + + /** + * @param passwordEncoder + * the passwordEncoder to set + * @see #passwordEncoder + */ + public void setPasswordEncoder(PasswordEncoder passwordEncoder) { + this.passwordEncoder = passwordEncoder; + } + + /** + * @param password + * input password + * @return encoded password + */ + @Override + public String encodePassword(String password) { + return passwordEncoder.encode(password); + } + + @Override + public User getUserByNameSurname(String nameSurnameString) { + return userDao.getUserByNameSurname(nameSurnameString); + } + + @Override + public ColorExtractor getColorExtractorForUser(User loggedUser) { + Color colorMin = null; + Color colorMax = null; + Color colorSimple = null; + if (loggedUser != null) { + User dbUser = getUserById(loggedUser.getId()); + if (dbUser != null) { + colorMin = dbUser.getMinColor(); + colorMax = dbUser.getMaxColor(); + colorSimple = dbUser.getSimpleColor(); + } + } + ColorParser parser = new ColorParser(); + + if (colorMin == null) { + colorMin = parser.parse(configurationService.getConfigurationValue(ConfigurationElementType.MIN_COLOR_VAL)); + } + if (colorMax == null) { + colorMax = parser.parse(configurationService.getConfigurationValue(ConfigurationElementType.MAX_COLOR_VAL)); + } + if (colorSimple == null) { + colorSimple = parser.parse(configurationService.getConfigurationValue(ConfigurationElementType.SIMPLE_COLOR_VAL)); + } + return new ColorExtractor(colorMin, colorMax, colorSimple); + } + + /** + * @return the configurationService + * @see #configurationService + */ + public IConfigurationService getConfigurationService() { + return configurationService; + } + + /** + * @param configurationService + * the configurationService to set + * @see #configurationService + */ + public void setConfigurationService(IConfigurationService configurationService) { + this.configurationService = configurationService; + } + + @Override + public User getUserByToken(String token) throws SecurityException { + if (!isSessionExpired(token)) { + String login = ((org.springframework.security.core.userdetails.User) (sessionRegistry.getSessionInformation(token) + .getPrincipal())).getUsername(); + return userDao.getUserByLogin(login); + } else { + throw new SecurityException("Invalid token"); + } + } + + private boolean isSessionExpired(String token) { + SessionInformation sessionData = sessionRegistry.getSessionInformation(token); + if (sessionData == null) { + logger.debug("No session data for token id: " + token); + return true; + } + return sessionData.isExpired(); + } + + @Override + public boolean userHasPrivilege(String token, PrivilegeType type, Object object) throws SecurityException { + return userHasPrivilege(getUserByToken(token), type, object); + } + + @Override + public void logout(String tokenString) { + if (!isSessionExpired(tokenString)) { + sessionRegistry.removeSessionInformation(tokenString); + } + } + + @Override + public User getUserById(String creatorId, String authenticationToken) throws SecurityException { + User user = getUserByToken(authenticationToken); + Integer id = Integer.parseInt(creatorId); + if (user.getId().equals(id)) { + return user; + } else if (userHasPrivilege(authenticationToken, PrivilegeType.USER_MANAGEMENT)) { + return getUserById(id); + } else { + throw new SecurityException("You cannot access data of other users"); + } + } + + @Override + public List<User> getUsers(String token) throws SecurityException { + if (userHasPrivilege(token, PrivilegeType.USER_MANAGEMENT)) { + return userDao.getAll(); + } else { + throw new SecurityException("You have no access to users data"); + } + } + + @Override + public void setUserPrivilege(User user, PrivilegeType type, Object value, String token) throws SecurityException { + if (!userHasPrivilege(token, PrivilegeType.USER_MANAGEMENT)) { + throw new SecurityException("You cannot modify user privileges"); + } + if (value instanceof Integer) { + setUserPrivilege(user, type, (Integer) value); + } else if (value instanceof Boolean) { + if ((Boolean) value) { + setUserPrivilege(user, type, 1); + } else { + setUserPrivilege(user, type, 0); + } + } else { + throw new InvalidArgumentException("Invalid privilege value: " + value); + } + } + + @Override + public void setUserPrivilege(User user, PrivilegeType type, Object value, Integer objectId, String token) + throws SecurityException { + boolean canModify = userHasPrivilege(token, PrivilegeType.USER_MANAGEMENT); + if (!canModify) { + if (type.getPrivilegeObjectType().isAssignableFrom(Project.class)) { + canModify = getUserPrivilegeLevel(getUserByToken(token), type, objectId) > 0; + } + } + if (!canModify) { + throw new SecurityException("You cannot modify user privileges"); + } + Project projectIdWrapper = new Project(); + if (objectId == null) { + projectIdWrapper = null; + } else { + projectIdWrapper.setId(objectId); + } + if (value instanceof Integer) { + setUserPrivilege(user, new ObjectPrivilege(projectIdWrapper, (Integer) value, type, user)); + } else if (value instanceof Boolean) { + if ((Boolean) value) { + setUserPrivilege(user, new ObjectPrivilege(projectIdWrapper, 1, type, user)); + } else { + setUserPrivilege(user, new ObjectPrivilege(projectIdWrapper, 0, type, user)); + } + } else { + throw new InvalidArgumentException("Invalid privilege value: " + value); + } + + } + + @Override + public void updateUser(User modifiedUser, String token) throws SecurityException { + User user = getUserByToken(token); + if (user.getLogin().equals(modifiedUser.getLogin()) || userHasPrivilege(token, PrivilegeType.USER_MANAGEMENT)) { + updateUser(modifiedUser); + } else { + throw new SecurityException("You cannot modify user"); + } + + } + + @Override + public boolean userHasPrivilege(String token, PrivilegeType type) throws SecurityException { + return userHasPrivilege(getUserByToken(token), type); + } + + @Override + public String login(String login, String password, String token) { + 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); + } + if (user != null) { + sessionRegistry.registerNewSession(token, new org.springframework.security.core.userdetails.User(login, + passwordEncoder.encode(password), AuthorityUtils.commaSeparatedStringToAuthorityList(""))); + return token; + } else { + return null; + } + } + + 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); + } + } + } + +} diff --git a/service/src/main/java/lcsb/mapviewer/services/interfaces/ILdapService.java b/service/src/main/java/lcsb/mapviewer/services/interfaces/ILdapService.java index d449879390..8a56e5c37b 100644 --- a/service/src/main/java/lcsb/mapviewer/services/interfaces/ILdapService.java +++ b/service/src/main/java/lcsb/mapviewer/services/interfaces/ILdapService.java @@ -56,6 +56,6 @@ public interface ILdapService { * @return true if LDAP configuration * ({@link ConfigurationElementTypeGroup#LDAP_CONFIGURATION}) is valid */ - boolean isValidConfiguratio(); + boolean isValidConfiguration(); } diff --git a/service/src/main/java/lcsb/mapviewer/services/interfaces/IUserService.java b/service/src/main/java/lcsb/mapviewer/services/interfaces/IUserService.java index d11ffec752..f1e752ff2b 100644 --- a/service/src/main/java/lcsb/mapviewer/services/interfaces/IUserService.java +++ b/service/src/main/java/lcsb/mapviewer/services/interfaces/IUserService.java @@ -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); + } diff --git a/service/src/test/java/lcsb/mapviewer/services/impl/AllImplServiceTests.java b/service/src/test/java/lcsb/mapviewer/services/impl/AllImplServiceTests.java index f08941eeb3..3a5bb0badd 100644 --- a/service/src/test/java/lcsb/mapviewer/services/impl/AllImplServiceTests.java +++ b/service/src/test/java/lcsb/mapviewer/services/impl/AllImplServiceTests.java @@ -6,13 +6,14 @@ import org.junit.runners.Suite.SuiteClasses; @RunWith(Suite.class) @SuiteClasses({ CommentServiceTest.class, // - ConfigurationServiceTest.class, // - ExternalServicesServiceTest.class, // - LayoutServiceTest.class, // - Md5PasswordEncoderTest.class, // - ProjectServiceTest.class, // - SearchServiceTest.class, // - UserServiceTest.class,// + ConfigurationServiceTest.class, // + ExternalServicesServiceTest.class, // + LayoutServiceTest.class, // + LdapServiceTest.class, // + Md5PasswordEncoderTest.class, // + ProjectServiceTest.class, // + SearchServiceTest.class, // + UserServiceTest.class,// }) public class AllImplServiceTests { diff --git a/service/src/test/java/lcsb/mapviewer/services/impl/LdapServiceTest.java b/service/src/test/java/lcsb/mapviewer/services/impl/LdapServiceTest.java index f1bb417c2a..7f3cf3c58a 100644 --- a/service/src/test/java/lcsb/mapviewer/services/impl/LdapServiceTest.java +++ b/service/src/test/java/lcsb/mapviewer/services/impl/LdapServiceTest.java @@ -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; diff --git a/service/src/test/java/lcsb/mapviewer/services/impl/UserServiceTest.java b/service/src/test/java/lcsb/mapviewer/services/impl/UserServiceTest.java index 57ae4d75f0..fb7ea42cdd 100644 --- a/service/src/test/java/lcsb/mapviewer/services/impl/UserServiceTest.java +++ b/service/src/test/java/lcsb/mapviewer/services/impl/UserServiceTest.java @@ -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 { diff --git a/service/testFiles/ldap/john-doe-test-example.ldif b/service/testFiles/ldap/john-doe-test-example.ldif new file mode 100644 index 0000000000..4d89df27e3 --- /dev/null +++ b/service/testFiles/ldap/john-doe-test-example.ldif @@ -0,0 +1,92 @@ +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 +displayName: John Doe +gecos: John Doe +givenName: John +initials: JD +krbLastPwdChange: 20180608111630Z +krbPasswordExpiration: 20190223111630Z +krbPrincipalName: john.doe.test@UNI.LU +loginShell: /bin/bash +mail: john.doe.test@uni.lu +memberOf: cn=lcsb,cn=groups,cn=accounts,dc=uni,dc=lu +memberOf: ipaUniqueID=d7549fa2-03a2-11e5-95f9-00163e0a4f7b,cn=hbac,dc=uni,dc + =lu +memberOf: ipaUniqueID=eff7677e-03a2-11e5-add5-00163e0a4f7b,cn=sudorules,cn=s + udo,dc=uni,dc=lu +memberOf: ipaUniqueID=f10ec0f6-7ef6-11e5-957b-001a4ae5121e,cn=hbac,dc=uni,dc + =lu +memberOf: ipaUniqueID=2cf9b59e-7ef7-11e5-89c0-001a4ae5121e,cn=sudorules,cn=s + udo,dc=uni,dc=lu +memberOf: ipaUniqueID=eeb5f68e-9775-11e5-81fb-00163e0a4f7b,cn=hbac,dc=uni,dc + =lu +memberOf: ipaUniqueID=176f7fb4-9776-11e5-a097-00163e0a4f7b,cn=sudorules,cn=s + udo,dc=uni,dc=lu +memberOf: ipaUniqueID=7dda82ae-99e1-11e5-834b-001a4ae5121e,cn=hbac,dc=uni,dc + =lu +memberOf: ipaUniqueID=a44d842c-99e1-11e5-9c2f-001a4ae5121e,cn=sudorules,cn=s + udo,dc=uni,dc=lu +memberOf: cn=webdav-public-minerva,cn=groups,cn=accounts,dc=uni,dc=lu +memberOf: ipaUniqueID=1d1f58b8-a247-11e5-ac5d-00163e0a4f7b,cn=hbac,dc=uni,dc + =lu +memberOf: ipaUniqueID=41f879a8-a247-11e5-9d34-00163e0a4f7b,cn=sudorules,cn=s + udo,dc=uni,dc=lu +memberOf: ipaUniqueID=33e9e6a2-c8d1-11e5-b578-001a4ae5121e,cn=hbac,dc=uni,dc + =lu +memberOf: ipaUniqueID=572fea30-c8d1-11e5-b770-001a4ae5121e,cn=sudorules,cn=s + udo,dc=uni,dc=lu +memberOf: cn=lcsb-biocore,cn=groups,cn=accounts,dc=uni,dc=lu +memberOf: cn=gitlab,cn=groups,cn=accounts,dc=uni,dc=lu +memberOf: cn=xwiki,cn=groups,cn=accounts,dc=uni,dc=lu +memberOf: cn=owncloud,cn=groups,cn=accounts,dc=uni,dc=lu +memberOf: cn=xwiki-biocore,cn=groups,cn=accounts,dc=uni,dc=lu +memberOf: cn=webdav-public-biocore,cn=groups,cn=accounts,dc=uni,dc=lu +memberOf: cn=grafana-biocore-gitlab-viewers,cn=groups,cn=accounts,dc=uni,dc= + lu +memberOf: cn=grafana-biocore-icinga-viewers,cn=groups,cn=accounts,dc=uni,dc= + lu +memberOf: ipaUniqueID=634b5286-3781-11e6-93c5-001a4ae5121e,cn=hbac,dc=uni,dc + =lu +memberOf: ipaUniqueID=7fe6a210-3781-11e6-b515-001a4ae5121e,cn=sudorules,cn=s + udo,dc=uni,dc=lu +memberOf: cn=ncer-pd-ada,cn=groups,cn=accounts,dc=uni,dc=lu +memberOf: cn=r3lab-docker,cn=groups,cn=accounts,dc=uni,dc=lu +memberOf: ipaUniqueID=390a3ce0-854b-11e6-85c3-001a4ae5127c,cn=hbac,dc=uni,dc + =lu +memberOf: ipaUniqueID=6d558608-854b-11e6-9aa7-001a4ae5127c,cn=sudorules,cn=s + udo,dc=uni,dc=lu +telephoneNumber: +3524666445526 + diff --git a/web/src/main/java/lcsb/mapviewer/security/CustomAuthenticationProvider.java b/web/src/main/java/lcsb/mapviewer/security/CustomAuthenticationProvider.java new file mode 100644 index 0000000000..6346901dd2 --- /dev/null +++ b/web/src/main/java/lcsb/mapviewer/security/CustomAuthenticationProvider.java @@ -0,0 +1,53 @@ +package lcsb.mapviewer.security; + +import java.util.ArrayList; + +import org.apache.log4j.Logger; +import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.transaction.annotation.Transactional; + +import lcsb.mapviewer.services.interfaces.IUserService; + +@Transactional(readOnly = false) +public class CustomAuthenticationProvider implements AuthenticationProvider { + Logger logger = Logger.getLogger(CustomAuthenticationProvider.class); + + private IUserService userService; + + @Override + public Authentication authenticate(Authentication authentication) throws AuthenticationException { + // login + String name = authentication.getName(); + + // password + String password = null; + if (authentication.getCredentials() != null) { + password = authentication.getCredentials().toString(); + } + + // Your custom authentication logic here + if (userService.login(name, password) != null) { + Authentication auth = new UsernamePasswordAuthenticationToken(name, password, new ArrayList<>()); + return auth; + } + throw new BadCredentialsException("Invalid credentials"); + } + + @Override + public boolean supports(Class<?> authentication) { + return authentication.equals(UsernamePasswordAuthenticationToken.class); + } + + public IUserService getUserService() { + return userService; + } + + public void setUserService(IUserService userService) { + this.userService = userService; + } + +} diff --git a/web/src/main/java/lcsb/mapviewer/security/MvSecurityServiceImpl.java b/web/src/main/java/lcsb/mapviewer/security/MvSecurityServiceImpl.java deleted file mode 100644 index 109a881dfa..0000000000 --- a/web/src/main/java/lcsb/mapviewer/security/MvSecurityServiceImpl.java +++ /dev/null @@ -1,72 +0,0 @@ -package lcsb.mapviewer.security; - -import org.apache.log4j.Logger; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.core.authority.AuthorityUtils; -import org.springframework.security.core.userdetails.User; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.security.core.userdetails.UsernameNotFoundException; -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.transaction.annotation.Transactional; - -import lcsb.mapviewer.common.Configuration; -import lcsb.mapviewer.services.interfaces.IUserService; - -/** - * Spring implementation of class accessing user details. - * - * @author Piotr Gawron - * - */ -@Transactional(readOnly = false) -public class MvSecurityServiceImpl implements UserDetailsService { - /** - * Default class logger. - */ - @SuppressWarnings("unused") - private static Logger logger = Logger.getLogger(MvSecurityServiceImpl.class); - - /** - * Service used for accessing user data. - */ - @Autowired - private IUserService userService; - - @Autowired - private PasswordEncoder passwordEncoder; - - @Override - public UserDetails loadUserByUsername(String login) { - if (login == null || login.trim().isEmpty() || login.equals(Configuration.ANONYMOUS_LOGIN)) { - return new User(login, passwordEncoder.encode(""), AuthorityUtils.commaSeparatedStringToAuthorityList("")); - } - - lcsb.mapviewer.model.user.User user = userService.getUserByLogin(login); - if (user == null) { - throw new UsernameNotFoundException("Invalid username or password."); - } - StringBuilder credentials = new StringBuilder(); - - return new User(user.getLogin(), user.getCryptedPassword(), - AuthorityUtils.commaSeparatedStringToAuthorityList(credentials.toString())); - } - - /** - * @return the userService - * @see #userService - */ - public IUserService getUserService() { - return userService; - } - - /** - * @param userService - * the userService to set - * @see #userService - */ - public void setUserService(IUserService userService) { - this.userService = userService; - } - -} diff --git a/web/src/main/webapp/WEB-INF/security-context.xml b/web/src/main/webapp/WEB-INF/security-context.xml index 41c1b8c834..783c154e31 100644 --- a/web/src/main/webapp/WEB-INF/security-context.xml +++ b/web/src/main/webapp/WEB-INF/security-context.xml @@ -72,10 +72,6 @@ <constructor-arg value="/login.xhtml"></constructor-arg> </bean> - <bean id="userDetailsService" class="lcsb.mapviewer.security.MvSecurityServiceImpl"> - <property name="userService" ref="UserService" /> - </bean> - <bean id="sas" class="org.springframework.security.web.authentication.session.CompositeSessionAuthenticationStrategy"> <constructor-arg> <list> @@ -98,9 +94,8 @@ <bean id="httpSessionSecurityContextRepository" class="org.springframework.security.web.context.HttpSessionSecurityContextRepository"/> - <bean id="authenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider"> - <property name="userDetailsService" ref="userDetailsService" /> - <property name="passwordEncoder" ref="PasswordEncoder" /> + <bean id="authenticationProvider" class="lcsb.mapviewer.security.CustomAuthenticationProvider"> + <property name="userService" ref="UserService" /> </bean> <security:authentication-manager alias="authenticationManager"> -- GitLab