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