001    package org.tynamo.security.filter;
002    
003    import org.apache.shiro.util.AntPathMatcher;
004    import org.apache.shiro.util.StringUtils;
005    import org.apache.shiro.web.filter.AccessControlFilter;
006    import org.apache.shiro.web.filter.PathMatchingFilter;
007    import org.apache.shiro.web.filter.PathMatchingFilterPatternMatcherChanger;
008    import org.apache.shiro.web.filter.authc.AuthenticationFilter;
009    import org.apache.shiro.web.filter.authz.AuthorizationFilter;
010    import org.apache.shiro.web.filter.mgt.DefaultFilterChainManager;
011    import org.apache.shiro.web.filter.mgt.FilterChainManager;
012    import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver;
013    import org.apache.shiro.web.mgt.WebSecurityManager;
014    import org.apache.shiro.web.servlet.IniShiroFilter;
015    import org.apache.tapestry5.ioc.annotations.Inject;
016    import org.apache.tapestry5.ioc.annotations.ServiceId;
017    import org.apache.tapestry5.ioc.annotations.Symbol;
018    import org.apache.tapestry5.services.ApplicationGlobals;
019    import org.apache.tapestry5.services.HttpServletRequestFilter;
020    import org.apache.tapestry5.services.HttpServletRequestHandler;
021    import org.slf4j.Logger;
022    import org.tynamo.security.FilterChainDefinition;
023    import org.tynamo.security.SecuritySymbols;
024    
025    import javax.servlet.*;
026    import javax.servlet.http.HttpServletRequest;
027    import javax.servlet.http.HttpServletResponse;
028    import java.io.IOException;
029    import java.util.Enumeration;
030    import java.util.List;
031    import java.util.Map;
032    
033    @ServiceId("SecurityRequestFilter")
034    public class SecurityRequestFilter extends IniShiroFilter implements HttpServletRequestFilter
035    {
036    
037            private IniShiroFilter shiroFilter;
038            @SuppressWarnings("unused")
039            private Logger logger;
040    
041            private String loginUrl;
042            private String unauthorizedUrl;
043            private String successUrl;
044    
045            public SecurityRequestFilter(List<FilterChainDefinition> filterChainDefinitions,
046                                         WebSecurityManager securityManager,
047                                         Logger logger,
048                                         @Inject @Symbol(SecuritySymbols.SUCCESS_URL) String successUrl,
049                                         @Inject @Symbol(SecuritySymbols.LOGIN_URL) String loginUrl,
050                                         @Inject @Symbol(SecuritySymbols.UNAUTHORIZED_URL) String unauthorizedUrl,
051                                         @Inject @Symbol(SecuritySymbols.CONFIG_PATH) String configPath,
052                                         @Inject @Symbol(SecuritySymbols.SHOULD_LOAD_INI_FROM_CONFIG_PATH) boolean shouldLoadIniFromPath,
053                                         ApplicationGlobals globals) throws Exception
054            {
055                    final ServletContext servletContext = globals.getServletContext();
056    
057                    this.logger = logger;
058                    this.loginUrl = loginUrl;
059                    this.unauthorizedUrl = unauthorizedUrl;
060                    this.successUrl = successUrl;
061    
062                    shiroFilter = new IniShiroFilter();
063                    if (shouldLoadIniFromPath)
064                    {
065                            shiroFilter.setConfigPath(configPath);
066                            shiroFilter.init(new FilterConfig()
067                            {
068    
069                                    @Override
070                                    public String getFilterName()
071                                    {
072                                            return "ShiroFilter";
073                                    }
074    
075                                    @Override
076                                    public ServletContext getServletContext()
077                                    {
078                                            return servletContext;
079                                    }
080    
081                                    @Override
082                                    public String getInitParameter(String name)
083                                    {
084                                            return null;  //To change body of implemented methods use File | Settings | File Templates.
085                                    }
086    
087                                    @SuppressWarnings("rawtypes")
088                                    @Override
089                                    public Enumeration getInitParameterNames()
090                                    {
091                                            return new Enumeration()
092                                            {
093    
094                                                    @Override
095                                                    public boolean hasMoreElements()
096                                                    {
097                                                            return false;
098                                                    }
099    
100                                                    @Override
101                                                    public Object nextElement()
102                                                    {
103                                                            return null;  //To change body of implemented methods use File | Settings | File Templates.
104                                                    }
105                                            };
106                                    }
107                            }
108                            );
109                    }
110    
111                    shiroFilter.setSecurityManager(securityManager);
112    
113                    PathMatchingFilterChainResolver chainResolver = (PathMatchingFilterChainResolver) shiroFilter.getFilterChainResolver();
114                    if (chainResolver == null)
115                    {
116                            FilterChainManager manager = new DefaultFilterChainManager();
117                            //Expose the constructed FilterChainManager by first wrapping it in a
118                            // FilterChainResolver implementation. The ShiroFilter implementations
119                            // do not know about FilterChainManagers - only resolvers:
120                            chainResolver = new PathMatchingFilterChainResolver();
121                            chainResolver.setFilterChainManager(manager);
122                            shiroFilter.setFilterChainResolver(chainResolver);
123                    }
124                    chainResolver.setPathMatcher(new AntPathMatcher() {
125                            @Override
126                            public boolean match(String pattern, String string) {
127                                    return super.match(pattern, string.toLowerCase());
128                            }
129                    });
130    
131                    Map<String, Filter> defaultFilters = chainResolver.getFilterChainManager().getFilters();
132                    //apply global settings if necessary:
133                    for (Filter filter : defaultFilters.values())
134                    {
135                            if(filter instanceof PathMatchingFilter) PathMatchingFilterPatternMatcherChanger.setLowercasingPathMatcher((PathMatchingFilter)filter);
136                            applyGlobalPropertiesIfNecessary(filter);
137                    }
138    
139    /*
140            //Apply the acquired and/or configured filters:
141            Map<String, Filter> filters = getFilters();
142            if (!CollectionUtils.isEmpty(filters)) {
143                for (Map.Entry<String, Filter> entry : filters.entrySet()) {
144                    String name = entry.getKey();
145                    Filter filter = entry.getValue();
146                    applyGlobalPropertiesIfNecessary(filter);
147                    if (filter instanceof Nameable) {
148                        ((Nameable) filter).setName(name);
149                    }
150                    //'init' argument is false, since Spring-configured filters should be initialized
151                    //in Spring (i.e. 'init-method=blah') or implement InitializingBean:
152                    manager.addFilter(name, filter, false);
153                }
154            }
155    */
156    
157                    //build up the chains:
158                    for (FilterChainDefinition filterChainDefinition : filterChainDefinitions)
159                    {
160                            logger.debug("adding filterChainDefinition: " + filterChainDefinition);
161                            chainResolver.getFilterChainManager().createChain(filterChainDefinition.getAntUrlPathExpression(), filterChainDefinition.getChainDefinition());
162                    }
163    
164            }
165    
166            @Override
167            public boolean service(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
168                                   final HttpServletRequestHandler handler) throws IOException
169            {
170                    // Assume request handled if filter chain is NOT executed
171                    final boolean[] res = new boolean[]{true};
172                    try
173                    {
174                            shiroFilter.doFilter(httpServletRequest, httpServletResponse, new FilterChain()
175                            {
176                                    public void doFilter(final ServletRequest request,
177                                                         final ServletResponse response) throws IOException, ServletException
178                                    {
179                                            res[0] = handler.service((HttpServletRequest) request, (HttpServletResponse) response);
180                                    }
181                            });
182                    } catch (ServletException e)
183                    {
184                            IOException ex = new IOException(e.getMessage());
185                            ex.initCause(e);
186                            throw ex;
187                    }
188                    return res[0];
189            }
190    
191            private void applyLoginUrlIfNecessary(Filter filter)
192            {
193                    if (StringUtils.hasText(loginUrl) && (filter instanceof AccessControlFilter))
194                    {
195                            AccessControlFilter acFilter = (AccessControlFilter) filter;
196                            //only apply the login url if they haven't explicitly configured one already:
197                            String existingLoginUrl = acFilter.getLoginUrl();
198                            if (AccessControlFilter.DEFAULT_LOGIN_URL.equals(existingLoginUrl))
199                            {
200                                    acFilter.setLoginUrl(loginUrl);
201                            }
202                    }
203            }
204    
205            private void applySuccessUrlIfNecessary(Filter filter)
206            {
207                    if (StringUtils.hasText(successUrl) && (filter instanceof AuthenticationFilter))
208                    {
209                            AuthenticationFilter authcFilter = (AuthenticationFilter) filter;
210                            //only apply the successUrl if they haven't explicitly configured one already:
211                            String existingSuccessUrl = authcFilter.getSuccessUrl();
212                            if (AuthenticationFilter.DEFAULT_SUCCESS_URL.equals(existingSuccessUrl))
213                            {
214                                    authcFilter.setSuccessUrl(successUrl);
215                            }
216                    }
217            }
218    
219            private void applyUnauthorizedUrlIfNecessary(Filter filter)
220            {
221                    if (StringUtils.hasText(unauthorizedUrl) && (filter instanceof AuthorizationFilter))
222                    {
223                            AuthorizationFilter authzFilter = (AuthorizationFilter) filter;
224                            //only apply the unauthorizedUrl if they haven't explicitly configured one already:
225                            String existingUnauthorizedUrl = authzFilter.getUnauthorizedUrl();
226                            if (existingUnauthorizedUrl == null)
227                            {
228                                    authzFilter.setUnauthorizedUrl(unauthorizedUrl);
229                            }
230                    }
231            }
232    
233            private void applyGlobalPropertiesIfNecessary(Filter filter)
234            {
235                    applyLoginUrlIfNecessary(filter);
236                    applySuccessUrlIfNecessary(filter);
237                    applyUnauthorizedUrlIfNecessary(filter);
238            }
239    }