diff --git a/pom.xml b/pom.xml index e5e408c5..04fe105a 100644 --- a/pom.xml +++ b/pom.xml @@ -70,6 +70,21 @@ spring-security-config ${org.springframework-version} + + org.springframework.security + spring-security-ldap + ${org.springframework-version} + + + + + org.slf4j + slf4j-log4j12 + 1.5.6 + jar + compile + + log4j log4j @@ -204,6 +226,12 @@ org.zkoss.zk zul ${zk.version} + + + org.slf4j + slf4j-jdk14 + + org.zkoss.zk diff --git a/src/main/java/info/bukova/isspst/DbInitListener.java b/src/main/java/info/bukova/isspst/DbInitListener.java index a72da4dd..75f6fd23 100644 --- a/src/main/java/info/bukova/isspst/DbInitListener.java +++ b/src/main/java/info/bukova/isspst/DbInitListener.java @@ -12,8 +12,6 @@ import javax.servlet.ServletContextListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.support.WebApplicationContextUtils; @@ -34,23 +32,16 @@ public class DbInitListener implements ServletContextListener { Logger logger = LoggerFactory.getLogger(DbInitListener.class); logger.info("Initializing database"); - User tmpAdmin = new User(); - Role tmpRole = new Role(); - tmpRole.setAuthority(Constants.ROLE_ADMIN); - tmpAdmin.setUsername(Constants.DEF_ADMIN); - tmpAdmin.addAuthority(tmpRole); - SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(tmpAdmin, null, tmpAdmin.getAuthorities())); - WebApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(evt.getServletContext()); roleService = ctx.getBean(RoleService.class); userService = ctx.getBean(UserService.class); permService = ctx.getBean(PermissionService.class); + userService.grantAdmin(); checkRoles(); checkUsers(); checkPermissions(); - - SecurityContextHolder.getContext().setAuthentication(null); + userService.removeAccess(); } private void checkRoles() { diff --git a/src/main/java/info/bukova/isspst/data/User.java b/src/main/java/info/bukova/isspst/data/User.java index 680c668f..51adb0c0 100644 --- a/src/main/java/info/bukova/isspst/data/User.java +++ b/src/main/java/info/bukova/isspst/data/User.java @@ -49,12 +49,15 @@ public class User extends BaseSimpleData implements UserDetails, DataModel { @Override public List getAuthorities() { List roles = new ArrayList(); + int i = 10000000; for (Role r : authorities) { roles.add(r); for (Permission p : r.getPermissions()) { Role role = new Role(); boolean addRole = true; role.setAuthority(p.getAuthority() + "_" + p.getModule()); + role.setId(i); + ++i; for (Role chRole : roles) { if (chRole.getAuthority().equals(role.getAuthority())) { diff --git a/src/main/java/info/bukova/isspst/security/AuthPopulator.java b/src/main/java/info/bukova/isspst/security/AuthPopulator.java new file mode 100644 index 00000000..5701a609 --- /dev/null +++ b/src/main/java/info/bukova/isspst/security/AuthPopulator.java @@ -0,0 +1,76 @@ +package info.bukova.isspst.security; + +import info.bukova.isspst.Constants; +import info.bukova.isspst.data.Role; +import info.bukova.isspst.data.User; +import info.bukova.isspst.services.users.RoleService; +import info.bukova.isspst.services.users.UserService; + +import java.util.Collection; + +import javax.naming.NamingException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.ldap.core.DirContextOperations; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator; + +public class AuthPopulator implements LdapAuthoritiesPopulator { + + private UserService userService; + private RoleService roleService; + + public AuthPopulator(UserService userService, RoleService roleService) { + this.userService = userService; + this.roleService = roleService; + } + + @Override + public Collection getGrantedAuthorities( + DirContextOperations userData, String login) { + + User user = null; + try { + user = (User) userService.loadUserByUsername(login); + } catch (UsernameNotFoundException e) { + Logger logger = LoggerFactory.getLogger(AuthPopulator.class); + logger.info("Importing user from LDAP"); + + user = new User(); + user.setUsername(login); + Role role = roleService.getRoleByAuthority(Constants.ROLE_USER); + user.addAuthority(role); + + if (userData.attributeExists("givenName")) { + try { + user.setFirstName(userData.getAttributes().get("givenName").get().toString()); + } catch (NamingException e1) { + logger.info("LDAP object has no 'givenName' attribute"); + } + } + if (userData.attributeExists("sn")) { + try { + user.setLastName(userData.getAttributes().get("sn").get().toString()); + } catch (NamingException e1) { + logger.info("LDAP object has no 'sn' attribute"); + } + } + if (userData.attributeExists("mail")) { + try { + user.setEmail(userData.getAttributes().get("mail").get().toString()); + } catch (NamingException e1) { + logger.info("LDAP object has no 'mail' attribute"); + } + } + + userService.grantAdmin(); + userService.add(user); + userService.removeAccess(); + } + + return user != null ? user.getAuthorities() : null; + } + +} diff --git a/src/main/java/info/bukova/isspst/IsspstPermissionEvaluator.java b/src/main/java/info/bukova/isspst/security/IsspstPermissionEvaluator.java similarity index 92% rename from src/main/java/info/bukova/isspst/IsspstPermissionEvaluator.java rename to src/main/java/info/bukova/isspst/security/IsspstPermissionEvaluator.java index 5aed5aa6..574e30c1 100644 --- a/src/main/java/info/bukova/isspst/IsspstPermissionEvaluator.java +++ b/src/main/java/info/bukova/isspst/security/IsspstPermissionEvaluator.java @@ -1,5 +1,7 @@ -package info.bukova.isspst; +package info.bukova.isspst.security; +import info.bukova.isspst.Constants; +import info.bukova.isspst.Module; import info.bukova.isspst.data.Role; import info.bukova.isspst.services.Service; diff --git a/src/main/java/info/bukova/isspst/LoginFailHandler.java b/src/main/java/info/bukova/isspst/security/LoginFailHandler.java similarity index 95% rename from src/main/java/info/bukova/isspst/LoginFailHandler.java rename to src/main/java/info/bukova/isspst/security/LoginFailHandler.java index 8d2bb71e..10e2313d 100644 --- a/src/main/java/info/bukova/isspst/LoginFailHandler.java +++ b/src/main/java/info/bukova/isspst/security/LoginFailHandler.java @@ -1,4 +1,4 @@ -package info.bukova.isspst; +package info.bukova.isspst.security; import java.io.IOException; diff --git a/src/main/java/info/bukova/isspst/services/AbstractOwnedService.java b/src/main/java/info/bukova/isspst/services/AbstractOwnedService.java index 2236fc04..af1051d0 100644 --- a/src/main/java/info/bukova/isspst/services/AbstractOwnedService.java +++ b/src/main/java/info/bukova/isspst/services/AbstractOwnedService.java @@ -6,6 +6,7 @@ import org.hibernate.NonUniqueResultException; import org.hibernate.Query; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; import org.springframework.transaction.annotation.Transactional; import info.bukova.isspst.data.OwnedDataModel; @@ -36,7 +37,7 @@ public class AbstractOwnedService extends AbstractServ @Transactional protected User getLoggedInUser() { try { - String query = "from User where ID = " + ((User)SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getId(); + String query = "from User where USERNAME = '" + ((UserDetails)SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUsername() + "'"; Query q = dao.getQuery(query); return (User) q.uniqueResult(); } catch (NonUniqueResultException e) { diff --git a/src/main/java/info/bukova/isspst/services/users/UserService.java b/src/main/java/info/bukova/isspst/services/users/UserService.java index 429b472b..d97e3620 100644 --- a/src/main/java/info/bukova/isspst/services/users/UserService.java +++ b/src/main/java/info/bukova/isspst/services/users/UserService.java @@ -12,5 +12,7 @@ public interface UserService extends UserDetailsService, Service { public void saveWithPwd(User user, String password); public User getCurrent(); public String encodePassword(User user, String plain); + public void grantAdmin(); + public void removeAccess(); } diff --git a/src/main/java/info/bukova/isspst/services/users/UserServiceImpl.java b/src/main/java/info/bukova/isspst/services/users/UserServiceImpl.java index ae4ee230..ae3f5683 100644 --- a/src/main/java/info/bukova/isspst/services/users/UserServiceImpl.java +++ b/src/main/java/info/bukova/isspst/services/users/UserServiceImpl.java @@ -1,6 +1,7 @@ package info.bukova.isspst.services.users; import org.hibernate.Query; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.authentication.encoding.PasswordEncoder; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; @@ -8,6 +9,7 @@ import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.transaction.annotation.Transactional; +import info.bukova.isspst.Constants; import info.bukova.isspst.data.Role; import info.bukova.isspst.data.User; import info.bukova.isspst.services.AbstractService; @@ -58,11 +60,16 @@ public class UserServiceImpl extends AbstractService implements UserServic } @Override + @Transactional public User getCurrent() { Authentication auth = SecurityContextHolder.getContext().getAuthentication(); if (auth != null && auth.getPrincipal() != null) { - return (User)auth.getPrincipal(); + try { + return (User)loadUserByUsername(((UserDetails)auth.getPrincipal()).getUsername()); + } catch(UsernameNotFoundException e) { + return null; + } } return null; @@ -73,5 +80,20 @@ public class UserServiceImpl extends AbstractService implements UserServic return encoder.encodePassword(plain, user.getUsername()); } + @Override + public void grantAdmin() { + User tmpAdmin = new User(); + Role tmpRole = new Role(); + tmpRole.setAuthority(Constants.ROLE_ADMIN); + tmpAdmin.setUsername(Constants.DEF_ADMIN); + tmpAdmin.addAuthority(tmpRole); + SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(tmpAdmin, null, tmpAdmin.getAuthorities())); + } + + @Override + public void removeAccess() { + SecurityContextHolder.getContext().setAuthentication(null); + } + } diff --git a/src/main/java/info/bukova/isspst/ui/FormViewModel.java b/src/main/java/info/bukova/isspst/ui/FormViewModel.java index a5dab5a5..f0dc3b4e 100644 --- a/src/main/java/info/bukova/isspst/ui/FormViewModel.java +++ b/src/main/java/info/bukova/isspst/ui/FormViewModel.java @@ -21,11 +21,15 @@ public class FormViewModel { private Map errMessages; private Service service; private boolean newRec; + private ServiceConstraint constraint; @Init public void init(@ExecutionArgParam("selected") T selected, @ExecutionArgParam("service") Service service) { this.dataBean = selected; this.service = service; + constraint = new ServiceConstraint(); + constraint.setDataBean(selected); + constraint.setService(service); if (selected.getId() == 0 && selected.getCreated() == null) { newRec = true; } else { @@ -33,6 +37,10 @@ public class FormViewModel { } } + public ServiceConstraint getConstriant() { + return constraint; + } + public T getDataBean() { return dataBean; } diff --git a/src/main/java/info/bukova/isspst/ui/ListViewModel.java b/src/main/java/info/bukova/isspst/ui/ListViewModel.java index 0537287a..ca42ea03 100644 --- a/src/main/java/info/bukova/isspst/ui/ListViewModel.java +++ b/src/main/java/info/bukova/isspst/ui/ListViewModel.java @@ -131,7 +131,7 @@ public class ListViewModel { try { newRecMode(); editBean = service.create(); - if (dataBean == null) { + if (editBean == null) { editBean = dataClass.newInstance(); } showForm(); diff --git a/src/main/java/info/bukova/isspst/ui/ServiceConstraint.java b/src/main/java/info/bukova/isspst/ui/ServiceConstraint.java new file mode 100644 index 00000000..dd0e4283 --- /dev/null +++ b/src/main/java/info/bukova/isspst/ui/ServiceConstraint.java @@ -0,0 +1,54 @@ +package info.bukova.isspst.ui; + +import info.bukova.isspst.data.DataModel; +import info.bukova.isspst.services.Service; +import info.bukova.isspst.services.ValidationException; + +import java.lang.reflect.InvocationTargetException; +import java.util.Map; + +import org.apache.commons.beanutils.BeanUtils; +import org.zkoss.zk.ui.Component; +import org.zkoss.zk.ui.WrongValueException; +import org.zkoss.zul.Constraint; + +public class ServiceConstraint implements Constraint { + + private Service service; + private T dataBean; + + @Override + public void validate(Component component, Object value) + throws WrongValueException { + + String id = component.getId(); + if (id == null || id.isEmpty()) { + return; + } + + try { + BeanUtils.setProperty(dataBean, id, value); + service.validate(dataBean); + } catch (ValidationException e) { + Map errMessages = e.getMessages(); + + if (errMessages != null && errMessages.get(id) != null && !errMessages.get(id).isEmpty()) { + WrongValueException ex = new WrongValueException(component, errMessages.get(id)); + throw ex; + } + } catch (IllegalAccessException e) { + + } catch (InvocationTargetException e) { + + } + } + + public void setService(Service service) { + this.service = service; + } + + public void setDataBean(T dataBean) { + this.dataBean = dataBean; + } + +} diff --git a/src/main/resources/users.ldif b/src/main/resources/users.ldif new file mode 100644 index 00000000..e6bc13b2 --- /dev/null +++ b/src/main/resources/users.ldif @@ -0,0 +1,71 @@ +dn: ou=groups,dc=bukova,dc=info +objectclass: top +objectclass: organizationalUnit +ou: groups + +dn: ou=people,dc=bukova,dc=info +objectclass: top +objectclass: organizationalUnit +ou: people + +dn: uid=kadel,ou=people,dc=bukova,dc=info +objectclass: top +objectclass: person +objectclass: organizationalPerson +objectclass: inetOrgPerson +cn: Kadel Pohlavnik +sn: Pohlavnik +givenName: Kadel +uid: kadel +userPassword: pokus + +dn: uid=admin,ou=people,dc=bukova,dc=info +objectclass: top +objectclass: person +objectclass: organizationalPerson +objectclass: inetOrgPerson +cn: Administrator +sn: admin +uid: admin +userPassword: xsacfgd + +dn: uid=dianne,ou=people,dc=bukova,dc=info +objectclass: top +objectclass: person +objectclass: organizationalPerson +objectclass: inetOrgPerson +cn: Dianne Emu +sn: Emu +uid: dianne +userPassword: emu + +dn: uid=scott,ou=people,dc=bukova,dc=info +objectclass: top +objectclass: person +objectclass: organizationalPerson +objectclass: inetOrgPerson +cn: Scott +sn: Wombat +uid: scott +userPassword: wombat + +dn: cn=user,ou=groups,dc=bukova,dc=info +objectclass: top +objectclass: groupOfNames +cn: user +member: uid=rod,ou=people,dc=bukova,dc=info +member: uid=dianne,ou=people,dc=bukova,dc=info +member: uid=scott,ou=people,dc=bukova,dc=info + +dn: cn=teller,ou=groups,dc=bukova,dc=info +objectclass: top +objectclass: groupOfNames +cn: teller +member: uid=rod,ou=people,dc=bukova,dc=info +member: dianne=rod,ou=people,dc=bukova,dc=info + +dn: cn=supervisor,ou=groups,dc=bukova,dc=info +objectclass: top +objectclass: groupOfNames +cn: supervisor +member: uid=rod,ou=people,dc=bukova,dc=info diff --git a/src/main/webapp/WEB-INF/ldap.properties b/src/main/webapp/WEB-INF/ldap.properties new file mode 100644 index 00000000..4060b43d --- /dev/null +++ b/src/main/webapp/WEB-INF/ldap.properties @@ -0,0 +1,2 @@ +ldap.server=ldap://localhost:3089 +ldap.userDNPattern=uid=\{0\},OU=people,DC=bukova,DC=info \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/spring/database-auth.xml b/src/main/webapp/WEB-INF/spring/database-auth.xml new file mode 100644 index 00000000..275a3c1d --- /dev/null +++ b/src/main/webapp/WEB-INF/spring/database-auth.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + diff --git a/src/main/webapp/WEB-INF/spring/ldap-auth.xml b/src/main/webapp/WEB-INF/spring/ldap-auth.xml new file mode 100644 index 00000000..5670d09f --- /dev/null +++ b/src/main/webapp/WEB-INF/spring/ldap-auth.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + ${ldap.userDNPattern} + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/webapp/WEB-INF/spring/root-context.xml b/src/main/webapp/WEB-INF/spring/root-context.xml index 13f46192..fe183541 100644 --- a/src/main/webapp/WEB-INF/spring/root-context.xml +++ b/src/main/webapp/WEB-INF/spring/root-context.xml @@ -11,7 +11,14 @@ - + + + + /WEB-INF/jdbc.properties + /WEB-INF/ldap.properties + + + - + @@ -65,13 +72,8 @@ - - - - - - - + + @@ -81,7 +83,7 @@ - + diff --git a/src/main/webapp/admin/addressbook/address.zul b/src/main/webapp/admin/addressbook/address.zul index 2d54e43c..9fe94a87 100644 --- a/src/main/webapp/admin/addressbook/address.zul +++ b/src/main/webapp/admin/addressbook/address.zul @@ -12,12 +12,10 @@ - -