001package org.tynamo.security.services.impl; 002 003import java.io.IOException; 004import java.util.Collection; 005import java.util.concurrent.Callable; 006 007import javax.servlet.ServletContext; 008import javax.servlet.http.HttpServletRequest; 009import javax.servlet.http.HttpServletResponse; 010 011import org.apache.shiro.mgt.SecurityManager; 012import org.apache.shiro.util.ThreadContext; 013import org.apache.shiro.web.mgt.WebSecurityManager; 014import org.apache.shiro.web.servlet.ShiroHttpServletRequest; 015import org.apache.shiro.web.subject.WebSubject; 016import org.apache.tapestry5.services.ApplicationGlobals; 017import org.apache.tapestry5.services.HttpServletRequestFilter; 018import org.apache.tapestry5.services.HttpServletRequestHandler; 019import org.tynamo.security.internal.services.LoginContextService; 020 021public class SecurityConfiguration implements HttpServletRequestFilter { 022 023 private final SecurityManager securityManager; 024 private final ServletContext servletContext; 025 private final LoginContextService loginContextService; 026 027 private final Collection<SecurityFilterChain> chains; 028 029 public SecurityConfiguration(ApplicationGlobals applicationGlobals, final WebSecurityManager securityManager, LoginContextService loginContextService, final Collection<SecurityFilterChain> chains) { 030 031 this.securityManager = securityManager; 032 this.loginContextService = loginContextService; 033 this.servletContext = applicationGlobals.getServletContext(); 034 this.chains = chains; 035 036 } 037 038 public boolean service(final HttpServletRequest originalRequest, final HttpServletResponse response, final HttpServletRequestHandler handler) 039 throws IOException { 040 // TODO consider whether this guard is necessary at all? I think possibly if container forwards the request internally 041 // or, more generically, if the same thread/container-level filter mapping handles the request twice 042 if (originalRequest instanceof ShiroHttpServletRequest) return handler.service(originalRequest, response); 043 044 final HttpServletRequest request = new ShiroHttpServletRequest(originalRequest, servletContext, true); 045 046 final String requestURI = loginContextService.getLocalelessPathWithinApplication(); 047 048 final SecurityFilterChain chain = getMatchingChain(requestURI); 049 050 ThreadContext.bind(securityManager); 051 WebSubject subject = new WebSubject.Builder(securityManager, originalRequest, response).buildWebSubject(); 052 053 try { 054 return subject.execute(new Callable<Boolean>() { 055 public Boolean call() throws Exception { 056 if (chain == null) return handler.service(request, response); 057 else { 058 boolean handled = chain.getHandler().service(request, response); 059 return handled || handler.service(request, response); 060 } 061 } 062 }); 063 } 064 finally { 065 /** 066 * final 'clean up' operation that removes the underlying {@link ThreadLocal ThreadLocal} from the thread 067 * at the end of execution to prevent leaks in pooled thread environments. 068 */ 069 ThreadContext.remove(); 070 } 071 } 072 073 private SecurityFilterChain getMatchingChain(final String requestURI) { 074 for (SecurityFilterChain chain : chains) { 075 // If the path does match, then pass on to the subclass implementation for specific checks: 076 if (chain.matches(requestURI)) { 077 return chain; 078 } 079 } 080 return null; 081 } 082}