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 }