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