001 package org.tynamo.security.services.impl;
002
003 import java.io.IOException;
004 import java.util.Collection;
005 import java.util.LinkedHashMap;
006 import java.util.List;
007 import java.util.Map;
008 import java.util.concurrent.Callable;
009
010 import javax.servlet.Filter;
011 import javax.servlet.FilterChain;
012 import javax.servlet.ServletContext;
013 import javax.servlet.ServletException;
014 import javax.servlet.ServletRequest;
015 import javax.servlet.ServletResponse;
016 import javax.servlet.http.HttpServletRequest;
017 import javax.servlet.http.HttpServletResponse;
018
019 import org.apache.shiro.mgt.SecurityManager;
020 import org.apache.shiro.util.AntPathMatcher;
021 import org.apache.shiro.util.PatternMatcher;
022 import org.apache.shiro.util.ThreadContext;
023 import org.apache.shiro.web.mgt.WebSecurityManager;
024 import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
025 import org.apache.shiro.web.subject.WebSubject;
026 import org.apache.shiro.web.util.WebUtils;
027 import org.apache.tapestry5.services.ApplicationGlobals;
028 import org.apache.tapestry5.services.HttpServletRequestFilter;
029 import org.apache.tapestry5.services.HttpServletRequestHandler;
030
031 public class SecurityConfiguration implements HttpServletRequestFilter {
032 private SecurityManager securityManager;
033 private ServletContext servletContext;
034
035 private Map<String, SecurityFilterChain> chainMap = new LinkedHashMap<String, SecurityFilterChain>();
036
037 // FIXME make configurable
038 // private PatternMatcher pathMatcher = new AntPathMatcher();
039 private PatternMatcher pathMatcher = new AntPathMatcher() {
040 @Override
041 public boolean matches(String pattern, String source) {
042 return super.matches(pattern, source.toLowerCase());
043 }
044 };
045
046
047 public SecurityConfiguration(ApplicationGlobals applicationGlobals, final WebSecurityManager securityManager, final Collection<SecurityFilterChain> chains) {
048 this.securityManager = securityManager;
049 servletContext = applicationGlobals.getServletContext();
050 // The order of securityFilterChains is meaningful, so we need to construct the map ourselves rather
051 // than simply use MappedConfiguration
052 for (SecurityFilterChain chain : chains) {
053 chainMap.put(chain.getPath(), chain);
054 }
055 }
056
057 private static final class HandlerFilterChain implements FilterChain {
058 private HttpServletRequestHandler handler;
059
060 private List<Filter> filters;
061
062 private int index = 0;
063
064 HandlerFilterChain(final HttpServletRequestHandler handler, final List<Filter> filters) {
065 this.handler = handler;
066 this.filters = filters;
067 this.index = 0;
068 }
069
070 public void doFilter(final ServletRequest request, final ServletResponse response) throws IOException, ServletException {
071 if (this.filters == null || this.filters.size() == this.index) handler.service((HttpServletRequest) request,
072 (HttpServletResponse) response);
073 else this.filters.get(this.index++).doFilter(request, response, this);
074 }
075
076 }
077
078 public boolean service(final HttpServletRequest originalRequest, final HttpServletResponse response, final HttpServletRequestHandler handler)
079 throws IOException {
080 // TODO consider whether this guard is necessary at all? I think possibly if container forwards the request internally
081 // or, more generically, if the same thread/container-level filter mapping handles the request twice
082 if (originalRequest instanceof ShiroHttpServletRequest) return handler.service(originalRequest, response);
083
084 final HttpServletRequest request = new ShiroHttpServletRequest(originalRequest, servletContext, false);
085
086 String requestURI = WebUtils.getPathWithinApplication(originalRequest);
087
088 SecurityFilterChain configureChain = null;
089 for (String path : chainMap.keySet()) {
090 // If the path does match, then pass on to the subclass implementation for specific checks:
091 if (pathMatcher.matches(path, requestURI)) {
092 configureChain = chainMap.get(path);
093 break;
094 }
095 }
096
097 final SecurityFilterChain chain = configureChain;
098
099 ThreadContext.bind(securityManager);
100 WebSubject subject = new WebSubject.Builder(securityManager, originalRequest, response).buildWebSubject();
101
102 boolean handled = (Boolean) subject.execute(new Callable() {
103 public Object call() throws Exception {
104 if (chain == null) return handler.service(originalRequest, response);
105 else {
106 final boolean handled = chain.getHandler().service(request, response);
107 if (!handled) return handler.service(request, response);
108 else return true;
109 }
110 }
111 });
112
113 return handled;
114 }
115 }