Add LDAP authentication and enabling Spring logging
authorSasha Sherstnev <a.sherstnev@dundee.ac.uk>
Tue, 10 Dec 2013 17:07:17 +0000 (17:07 +0000)
committerSasha Sherstnev <a.sherstnev@dundee.ac.uk>
Tue, 10 Dec 2013 17:07:17 +0000 (17:07 +0000)
17 files changed:
WEB-INF/lib/spring-ldap-core-1.3.1.RELEASE.jar [new file with mode: 0644]
WEB-INF/lib/spring-security-ldap-3.1.4.RELEASE.jar [new file with mode: 0644]
WEB-INF/spring-security.xml
WEB-INF/web.xml
log/log4j.properties
server/compbio/controllers/BasicController.java
server/compbio/controllers/DocumentationController.java
server/compbio/controllers/MainController.java
server/compbio/controllers/UserController.java
server/compbio/spring/security/LDAPAuthorityMapper.java [new file with mode: 0644]
server/compbio/spring/security/PCacheLDAPAuthority.java [new file with mode: 0644]
webapp/view/fragments/mainmenu.jsp
webapp/view/help/Overview.jsp
webapp/view/login.jsp
webapp/view/support/Blocked.jsp
webapp/view/support/Denied.jsp
webapp/view/support/Notimplemented.jsp

diff --git a/WEB-INF/lib/spring-ldap-core-1.3.1.RELEASE.jar b/WEB-INF/lib/spring-ldap-core-1.3.1.RELEASE.jar
new file mode 100644 (file)
index 0000000..79058ad
Binary files /dev/null and b/WEB-INF/lib/spring-ldap-core-1.3.1.RELEASE.jar differ
diff --git a/WEB-INF/lib/spring-security-ldap-3.1.4.RELEASE.jar b/WEB-INF/lib/spring-security-ldap-3.1.4.RELEASE.jar
new file mode 100644 (file)
index 0000000..a8df167
Binary files /dev/null and b/WEB-INF/lib/spring-security-ldap-3.1.4.RELEASE.jar differ
index 4e8b52c..c0281f6 100644 (file)
@@ -1,54 +1,87 @@
-<beans:beans 
-       xmlns="http://www.springframework.org/schema/security"
-       xmlns:beans="http://www.springframework.org/schema/beans" 
+<beans:beans xmlns="http://www.springframework.org/schema/security"
+       xmlns:beans="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:mvc="http://www.springframework.org/schema/mvc"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
        http://www.springframework.org/schema/security
-       http://www.springframework.org/schema/security/spring-security-3.1.xsd">
+       http://www.springframework.org/schema/security/spring-security-3.1.xsd
+       http://www.springframework.org/schema/mvc
+       http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd">
 
-       <http auto-config="true" use-expressions="true" access-denied-page="/denied">
-               <intercept-url pattern="/stat/**" access="hasRole('ROLE_USER')" />
-               <intercept-url pattern="/sequence/**" access="hasRole('ROLE_USER')" />
+       <http auto-config="true" pattern="/**" use-expressions="true" access-denied-page="/denied">
+               <intercept-url pattern="/stat/**" access="hasAnyRole('ROLE_USER','ROLE_LDAP_USER','ROLE_ADMIN')" />
+               <intercept-url pattern="/home**" access="hasAnyRole('ROLE_USER','ROLE_LDAP_USER','ROLE_ADMIN')" />
+               <intercept-url pattern="/sequence/**" access="hasAnyRole('ROLE_USER','ROLE_LDAP_USER','ROLE_ADMIN')" />
                <intercept-url pattern="/admin/**" access="hasRole('ROLE_ADMIN')" />
                <intercept-url pattern="/database/**" access="hasRole('ROLE_ADMIN')" />
-               <intercept-url pattern="/public*" access="permitAll"/>
-               <intercept-url pattern="/login*" access="permitAll"/>
-               <intercept-url pattern="/index*" access="permitAll"/>
-               <intercept-url pattern="/register*" access="permitAll"/>
-               <form-login 
-                       login-page="/login"
-                       default-target-url="/home"
-                       authentication-failure-url="/loginfailed"
-               />
-               <logout logout-success-url="/logout" />
+               <intercept-url pattern="/public*" access="permitAll" />
+               <intercept-url pattern="/login*" access="permitAll" />
+               <intercept-url pattern="/ldaplogin*" access="permitAll" />
+               <intercept-url pattern="/index*" access="permitAll" />
+               <intercept-url pattern="/register*" access="permitAll" />
+               <form-login login-page="/login" default-target-url="/home" authentication-failure-url="/loginfailed" />
+               <logout invalidate-session="true" logout-success-url="/logout" />
        </http>
 
-       <global-method-security secured-annotations="enabled">
-       </global-method-security>       
+       <!-- Disabling Caching -->
+       <mvc:interceptors>
+               <beans:bean id="webContentInterceptor"
+                       class="org.springframework.web.servlet.mvc.WebContentInterceptor">
+                       <beans:property name="cacheSeconds" value="0" />
+                       <beans:property name="useExpiresHeader" value="false" />
+                       <beans:property name="useCacheControlHeader" value="true" />
+                       <beans:property name="useCacheControlNoStore" value="true" />
+               </beans:bean>
+       </mvc:interceptors>
 
-<!-- 
-       <http access-denied-page="/denied.xhtml"  auto-config="true" use-expressions="false" >
-               <form-login 
-                       login-page="/login.xhtml"
-                       default-target-url="/"
-                       authentication-failure-url="/denied.xhtml"
-               login-processing-url="/static/j_spring_security_check"
-               />
-               <intercept-url pattern="/PANEL/**" access="ROLE_GENERALT"></intercept-url>
-               <logout invalidate-session="true" logout-url="/index.xhtml"/>
-       </http>
-
-       <global-method-security secured-annotations="enabled" jsr250-annotations="enabled"></global-method-security>
--->
+       <!-- enable  -->
+       <global-method-security secured-annotations="enabled"></global-method-security>
 
-       <authentication-manager>
+       <authentication-manager alias="LDAP">
+               <authentication-provider ref="ldapAuthProvider" />
                <authentication-provider>
                        <user-service>
                                <user name="sherstnev" password="sasha" authorities="ROLE_USER" />
+                               <user name="geoff" password="barton" authorities="ROLE_USER" />
+                               <user name="jim" password="procter" authorities="ROLE_USER" />
                                <user name="admin" password="admin" authorities="ROLE_USER, ROLE_ADMIN" />
                        </user-service>
                </authentication-provider>
        </authentication-manager>
 
+       <!-- LDAP and other authentication managers -->
+       <beans:bean id="MyContextSource"
+               class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
+               <beans:constructor-arg value="ldap://ldap.lifesci.dundee.ac.uk:389" />
+               <beans:property name="userDn" value="" />
+               <beans:property name="password" value="password" />
+       </beans:bean>
+       <beans:bean id="MYgrantedAuthoritiesMapper" class="compbio.spring.security.LDAPAuthorityMapper">
+       </beans:bean>
+
+       <beans:bean id="ldapAuthProvider"
+               class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider">
+               <beans:constructor-arg>
+                       <beans:bean
+                               class="org.springframework.security.ldap.authentication.BindAuthenticator">
+                               <beans:constructor-arg ref="MyContextSource" />
+                               <beans:property name="userDnPatterns">
+                                       <beans:list>
+                                               <beans:value>cn={0},ou=edir,ou=people,ou=lifesci,o=dundee</beans:value>
+                                       </beans:list>
+                               </beans:property>
+                       </beans:bean>
+               </beans:constructor-arg>
+               <beans:constructor-arg>
+                       <beans:bean
+                               class="org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator">
+                               <beans:constructor-arg ref="MyContextSource" />
+                               <beans:constructor-arg value="cn=group-gjb,ou=groups,ou=lifesci,o=dundee" /> <!-- Barton group members can log in only... -->
+                               <beans:property name="defaultRole" value="ROLE_LDAP_USER"/> <!-- temporary solution... Currently my AuthorityMapper is not working -->
+                       </beans:bean>
+               </beans:constructor-arg>
+               <!-- <beans:property name="authoritiesMapper" ref="MYgrantedAuthoritiesMapper" />--> <!-- should be used in the future -->
+       </beans:bean>
+
 </beans:beans>
index 2032623..9f2caa5 100644 (file)
        <!-- Spring Security -->\r
 \r
        <listener>\r
+               <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>\r
+       </listener>\r
+\r
+       <listener>\r
                <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>\r
        </listener>\r
 \r
                        /WEB-INF/spring-security.xml\r
                </param-value>\r
        </context-param>\r
+       \r
+       <context-param>\r
+               <param-name>log4jConfigLocation</param-name>\r
+               <param-value>/WEB-INF/classes/log4j.properties</param-value>\r
+       </context-param>\r
 \r
        <filter>\r
                <filter-name>springSecurityFilterChain</filter-name>\r
index d186883..45cdbf0 100644 (file)
@@ -57,6 +57,8 @@ log4j.appender.B.File=${catalina.base}/logs/debugging.log
 log4j.appender.B.layout=org.apache.log4j.PatternLayout\r
 log4j.appender.B.layout.ConversionPattern=%m%n %d{MM-dd@HH:mm:ss} %-5p (%13F:%L) %3x - \r
 \r
+log4j.category.org.springframework=ALL\r
+\r
 # %d{ABSOLUTE} %5p %c{1}:%L -\r
 #log4j.logger.compbio.engine.local.LocalExecutorService=INFO, C\r
 #log4j.appender.C=org.apache.log4j.FileAppender\r
index d4be834..389a0f7 100644 (file)
@@ -1,21 +1,44 @@
 package compbio.controllers;
 
+import java.util.Collection;
+
+import org.springframework.security.core.GrantedAuthority;
 import org.springframework.security.core.context.SecurityContextHolder;
 import org.springframework.security.core.userdetails.UserDetails;
-/*
-import org.springframework.stereotype.Controller;
-import org.springframework.ui.ModelMap;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestMethod;
-*/
 
 public class BasicController {
 
        protected String getPrincipalName() {
                Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
                if (principal instanceof UserDetails) {
-                       return ((UserDetails) principal).getUsername();
-               } 
+                       UserDetails details = (UserDetails) principal;
+                       String ldapprefix = "";
+                       String role = details.getUsername();
+                       Collection<? extends GrantedAuthority> au = details.getAuthorities();
+                       for (GrantedAuthority ga : au) {
+                               System.out.println("role -> " + ga.getAuthority());
+                               if (ga.getAuthority().equals("ROLE_LDAP_USER")) {
+                                       ldapprefix = "LDAP:";
+                               }
+                       }
+                       return ldapprefix + role;
+               }
                return principal.toString();
        }
+
+       protected boolean isUserRole() {
+               Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
+               if (principal instanceof UserDetails) {
+                       UserDetails details = (UserDetails) principal;
+                       Collection<? extends GrantedAuthority> au = details.getAuthorities();
+                       for (GrantedAuthority ga : au) {
+                               if (ga.getAuthority().equals("ROLE_USER") || ga.getAuthority().equals("ROLE_LDAP_USER")) {
+                                       return true;
+                               }
+                       }
+                       return false;
+               }
+               return false;
+       }
+
 }
index 07c72b9..ed95d23 100644 (file)
@@ -18,24 +18,32 @@ public class DocumentationController extends BasicController {
        @RequestMapping(value = "/help/overview", method = RequestMethod.GET)
        public String formOverviewPage(Map<String, Object> model) {
                model.put("username", getPrincipalName());
+               if (isUserRole())
+                       model.put("permissions", "user_role");
                return "help/Overview";
        }
 
        @RequestMapping(value = "/help/howto", method = RequestMethod.GET)
        public String formHowtoPage(Map<String, Object> model) {
                model.put("username", getPrincipalName());
+               if (isUserRole())
+                       model.put("permissions", "user_role");
                return "support/Notimplemented";
        }
 
        @RequestMapping(value = "/help/doc", method = RequestMethod.GET)
        public String formDocPage(Map<String, Object> model) {
                model.put("username", getPrincipalName());
+               if (isUserRole())
+                       model.put("permissions", "user_role");
                return "support/Notimplemented";
        }
 
        @RequestMapping(value = "/help/javadoc", method = RequestMethod.GET)
        public String formJavadoc(Map<String, Object> model) {
                model.put("username", getPrincipalName());
+               if (isUserRole())
+                       model.put("permissions", "user_role");
                return "support/Notimplemented";
        }
 
index 4aeb1cd..a945e25 100644 (file)
@@ -39,7 +39,6 @@ public class MainController extends BasicController {
        @RequestMapping(value = "/home", method = RequestMethod.GET)
        public String printHome(ModelMap model ) {
                model.addAttribute("username", getPrincipalName());
-               model.addAttribute("message", "Spring Security Custom Form example");
                return "home";
        }
 
index 7dbfdf4..a00bd15 100644 (file)
@@ -3,9 +3,15 @@ package compbio.controllers;
 import java.util.Date;
 import java.util.regex.Pattern;
 
+import javax.naming.directory.DirContext;
+
+import org.springframework.ldap.core.LdapTemplate;
+import org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.dao.DataIntegrityViolationException;
 import org.springframework.mail.SimpleMailMessage;
-import org.springframework.mail.javamail.JavaMailSender;
+import org.springframework.security.ldap.LdapUtils;
+import org.springframework.ldap.core.ContextSource;
 import org.springframework.stereotype.Controller;
 import org.springframework.ui.Model;
 import org.springframework.ui.ModelMap;
@@ -23,7 +29,9 @@ import compbio.cassandra.CassandraUserManager;
 @Controller
 public class UserController {
 
-       // @Inject
+       @Autowired
+       ContextSource contextSource;
+
        // JavaMailSender mailSender;
        private final Pattern EMAIL = Pattern.compile("[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}");
 
@@ -131,4 +139,29 @@ public class UserController {
                // mailSender.send(message);
        }
 
+       private boolean authenticate(String userDn, String credentials) {
+               DirContext ctx = null;
+               try {
+                       ctx = contextSource.getContext(userDn, credentials);
+                       return true;
+               } catch (Exception e) {
+                       // Context creation failed - authentication did not succeed
+                       System.out.println("LDAP Login failed");
+                       return false;
+               } finally {
+                       // It is imperative that the created DirContext instance is always
+                       // closed
+                       LdapUtils.closeContext(ctx);
+               }
+       }
+
+       @RequestMapping(value = "/ldaplogindo", method = RequestMethod.POST)
+       public String LDAPlogin(Model model, @RequestParam("j_username") String username, @RequestParam("j_password") String credentials) {
+               System.out.println("Try to authenticate with LDAP: username: " + username + ", credentials: " + credentials);
+               if (authenticate(username, credentials)) {
+                       return "/home";
+               }
+               return "/public";
+       }
+
 }
diff --git a/server/compbio/spring/security/LDAPAuthorityMapper.java b/server/compbio/spring/security/LDAPAuthorityMapper.java
new file mode 100644 (file)
index 0000000..061a3c6
--- /dev/null
@@ -0,0 +1,51 @@
+package compbio.spring.security;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.annotation.Resource;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
+import org.springframework.security.ldap.authentication.LdapAuthenticationProvider;
+
+/**
+ * Maps groups defined in LDAP to roles for a specific user.
+ */
+@Resource
+public class LDAPAuthorityMapper implements GrantedAuthoritiesMapper {
+
+       private static final Log logger = LogFactory.getLog(LDAPAuthorityMapper.class);
+
+       public LDAPAuthorityMapper() {
+       }
+/*
+       public Collection<? extends GrantedAuthority> mapAuthorities(final Collection<? extends GrantedAuthority> authorities) {
+
+               Set<PCacheLDAPAuthority> roles = EnumSet.noneOf(PCacheLDAPAuthority.class);
+               roles.add(PCacheLDAPAuthority.ROLE_LDAP_USER);
+               logger.info("LDAPAuthorityMapper: set new role ROLE_LDAP_USER");
+               *
+                * for (GrantedAuthority authority : authorities) { if
+                * (ROLE_CUSTOMER_SERVICE_OFFICER.equals(authority.getAuthority())) {
+                * roles.add(PCacheLDAPAuthority.ROLE_USER); } else if
+                * (ROLE_ADMIN.equals(authority.getAuthority())) {
+                * roles.add(PCacheLDAPAuthority.ROLE_ADMIN); } }
+                *
+               return roles;
+       }*/
+       
+       
+       public Collection<? extends GrantedAuthority> mapAuthorities(final Collection<? extends GrantedAuthority> authorities) {
+               SimpleGrantedAuthority sa = new SimpleGrantedAuthority("ROLE_LDAP_USER");
+               Set<GrantedAuthority> roles = new HashSet<GrantedAuthority>();
+               roles.add(sa);
+               logger.info("LDAPAuthorityMapper: set new role ROLE_LDAP_USER");
+
+               return roles;
+       }
+}
diff --git a/server/compbio/spring/security/PCacheLDAPAuthority.java b/server/compbio/spring/security/PCacheLDAPAuthority.java
new file mode 100644 (file)
index 0000000..dde6d87
--- /dev/null
@@ -0,0 +1,45 @@
+package compbio.spring.security;
+
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.util.Assert;
+
+/**
+ * Maps groups defined in LDAP to roles for a specific user.
+ */
+/*
+ * public enum PCacheLDAPAuthority implements GrantedAuthority { ROLE_LDAP_USER;
+ * public String getAuthority() { return name(); } }
+ */
+public final class PCacheLDAPAuthority implements GrantedAuthority {
+
+       private static final long serialVersionUID = 1;
+
+       private String role = "ROLE_LDAP_USER";
+
+       public PCacheLDAPAuthority(String role) {
+               Assert.hasText(role, "A granted authority textual representation is required");
+               this.role = role;
+       }
+
+       public String getAuthority() {
+               return role;
+       }
+
+       public boolean equals(Object obj) {
+               if (this == obj) {
+                       return true;
+               }
+               if (obj instanceof PCacheLDAPAuthority) {
+                       return role.equals(((PCacheLDAPAuthority) obj).role);
+               }
+               return false;
+       }
+
+       public int hashCode() {
+               return this.role.hashCode();
+       }
+
+       public String toString() {
+               return this.role;
+       }
+}
index 9b3316c..f310fbf 100644 (file)
                <ul class="nav navbar-nav navbar-right">
                        <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown"><span class="glyphicon glyphicon-user"></span> ${username}<b class="caret"></b></a>
                                <ul class="dropdown-menu">
-                                       <li><a href="<spring:url value="/logout" htmlEscape="true" />">Logout</a></li>
-                                       <li><a href="<spring:url value="/register/edit/query" htmlEscape="true" />">User account</a></li>
+                                       <li><a href="<spring:url value="/j_spring_security_logout" />">Logout</a></li>
+                                       <sec:authorize access="hasRole('ROLE_USER')">
+                                               <li><a href="<spring:url value="/register/edit/query" htmlEscape="true" />">User account</a></li>
+                                       </sec:authorize>
                                </ul>
                        </li>
                        <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown"><span class="glyphicon glyphicon-question-sign"></span> Help<b class="caret"></b></a>
index 37b3eb4..1be9ef3 100644 (file)
@@ -6,13 +6,18 @@
 <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
 <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn"%>
 <%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
-<%@page import="java.util.ArrayList"%>
+<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags"%>
 
 <html>
 <jsp:include page="../fragments/header.jsp" />
 <body>
        <div class="container">
-       <jsp:include page="../fragments/mainmenu.jsp" />
+       <sec:authorize access="!isAuthenticated()">
+               <jsp:include page="../fragments/publicmenu.jsp" />
+       </sec:authorize>
+               <sec:authorize access="isAuthenticated()">
+               <jsp:include page="../fragments/mainmenu.jsp" />
+       </sec:authorize>
 
        <div class="panel panel-default">
                <div class="panel-heading">
index 37e1b3e..38e9ec8 100644 (file)
@@ -6,21 +6,20 @@
 <%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%>
 <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
 
-<spring:url value="/j_spring_security_check" var="scheck" />
+<spring:url value="/j_spring_security_check" var="usercheck" />
 
 <html>
 <jsp:include page="fragments/header.jsp" />
 <body onload='document.f.j_username.focus();'>
        <div class="container">
                <jsp:include page="fragments/publicmenu.jsp" />
-               <spring:url value="/ip/query" var="query" />
 
                <div class="panel panel-default">
                        <div class="panel-heading">
-                               <div style="font-weight: bold;">Enter your username (email used during registration) and password</div>
+                               <div style="font-weight: bold;">Enter your username (email) and password</div>
                        </div>
                        <div class="panel-body">
-                               <form name='f' action="${scheck}" method='POST'>
+                               <form name='f' action="${usercheck}" method='POST'>
                                        <div class="col-xs-3"><!-- make the field shorter -->
                                                <c:choose>
                                                <c:when test="${error == null}">
index f08f915..405c921 100644 (file)
 <jsp:include page="../fragments/header.jsp" />
 <body>
        <div class="container">
-               <jsp:include page="../fragments/mainmenu.jsp" />
+               <c:choose>
+                       <c:when test="${permissions == 'user_role'}">
+                               <jsp:include page="../fragments/mainmenu.jsp" />
+                       </c:when>
+                       <c:otherwise>
+                               <jsp:include page="../fragments/publicmenu.jsp" />
+                       </c:otherwise>
+               </c:choose>
 
                <div class="panel panel-default">
                        <div class="panel-heading">
index 928cf35..bcb306c 100644 (file)
 <jsp:include page="../fragments/header.jsp" />
 <body>
        <div class="container">
-               <jsp:include page="../fragments/mainmenu.jsp" />
+               <c:choose>
+                       <c:when test="${permissions == 'user_role'}">
+                               <jsp:include page="../fragments/mainmenu.jsp" />
+                       </c:when>
+                       <c:otherwise>
+                               <jsp:include page="../fragments/publicmenu.jsp" />
+                       </c:otherwise>
+               </c:choose>
 
                <div class="panel panel-default">
                        <div class="panel-heading">
index c409b96..1eb2bd0 100644 (file)
 <jsp:include page="../fragments/header.jsp" />
 <body>
        <div class="container">
-               <jsp:include page="../fragments/mainmenu.jsp" />
+               <c:choose>
+                       <c:when test="${permissions == 'user_role'}">
+                               <jsp:include page="../fragments/mainmenu.jsp" />
+                       </c:when>
+                       <c:otherwise>
+                               <jsp:include page="../fragments/publicmenu.jsp" />
+                       </c:otherwise>
+               </c:choose>
 
                <div class="panel panel-default">
                        <div class="panel-heading">