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