source: branches/TaskRewrite/src/plugins/acegi-0.5.1/src/java/org/codehaus/groovy/grails/plugins/springsecurity/AnnotationFilterInvocationDefinition.java @ 58

Last change on this file since 58 was 58, checked in by gav, 15 years ago

Configure BootStrap? with latest concepts.
Install and setup Acegi plugin with custom views.
Test Fixture plugin in a test app but couldn't get it to work with Acegi encodePassword() so gave up.

File size: 9.7 KB
Line 
1/* Copyright 2006-2009 the original author or authors.
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *      http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15package org.codehaus.groovy.grails.plugins.springsecurity;
16
17import java.lang.reflect.Field;
18import java.util.Collection;
19import java.util.HashMap;
20import java.util.HashSet;
21import java.util.Map;
22import java.util.Set;
23
24import javax.servlet.ServletContext;
25import javax.servlet.http.HttpServletRequest;
26import javax.servlet.http.HttpServletResponse;
27
28import org.apache.commons.lang.WordUtils;
29import org.codehaus.groovy.grails.commons.ApplicationHolder;
30import org.codehaus.groovy.grails.commons.ControllerArtefactHandler;
31import org.codehaus.groovy.grails.commons.GrailsApplication;
32import org.codehaus.groovy.grails.commons.GrailsClass;
33import org.codehaus.groovy.grails.commons.GrailsControllerClass;
34import org.codehaus.groovy.grails.web.context.ServletContextHolder;
35import org.codehaus.groovy.grails.web.mapping.UrlMappingInfo;
36import org.codehaus.groovy.grails.web.mapping.UrlMappingsHolder;
37import org.codehaus.groovy.grails.web.servlet.mvc.GrailsParameterMap;
38import org.codehaus.groovy.grails.web.servlet.mvc.GrailsWebRequest;
39import org.codehaus.groovy.grails.web.util.WebUtils;
40import org.springframework.security.ConfigAttributeDefinition;
41import org.springframework.security.intercept.web.FilterInvocation;
42import org.springframework.security.intercept.web.FilterInvocationDefinitionSource;
43import org.springframework.util.Assert;
44import org.springframework.util.StringUtils;
45
46/**
47 * A {@link FilterInvocationDefinitionSource} that uses rules defined with Controller annotations
48 * combined with static rules defined in <code>SecurityConfig.groovy</code>, e.g. for js, images, css
49 * or for rules that cannot be expressed in a controller like '/**'.
50 *
51 * @author <a href='mailto:beckwithb@studentsonly.com'>Burt Beckwith</a>
52 */
53public class AnnotationFilterInvocationDefinition extends AbstractFilterInvocationDefinition {
54
55        private UrlMappingsHolder _urlMappingsHolder;
56
57        @Override
58        protected String determineUrl(final FilterInvocation filterInvocation) {
59                HttpServletRequest request = filterInvocation.getHttpRequest();
60                HttpServletResponse response = filterInvocation.getHttpResponse();
61                ServletContext servletContext = ServletContextHolder.getServletContext();
62                GrailsApplication application = ApplicationHolder.getApplication();
63
64                GrailsWebRequest existingRequest = WebUtils.retrieveGrailsWebRequest();
65
66                String requestUrl = request.getRequestURI().substring(request.getContextPath().length());
67
68                String url = null;
69                try {
70                        GrailsWebRequest grailsRequest = new GrailsWebRequest(request, response, servletContext);
71                        WebUtils.storeGrailsWebRequest(grailsRequest);
72
73                        Map<String, Object> savedParams = copyParams(grailsRequest);
74
75                        for (UrlMappingInfo mapping : _urlMappingsHolder.matchAll(requestUrl)) {
76                                configureMapping(mapping, grailsRequest, savedParams);
77
78                                url = findGrailsUrl(mapping, application);
79                                if (url != null) {
80                                        break;
81                                }
82                        }
83                }
84                finally {
85                        if (existingRequest == null) {
86                                WebUtils.clearGrailsWebRequest();
87                        }
88                        else {
89                                WebUtils.storeGrailsWebRequest(existingRequest);
90                        }
91                }
92
93                if (!StringUtils.hasLength(url)) {
94                        // probably css/js/image
95                        url = requestUrl;
96                }
97
98                return lowercaseAndStringQuerystring(url);
99        }
100
101        private String findGrailsUrl(final UrlMappingInfo mapping, final GrailsApplication application) {
102
103                String actionName = mapping.getActionName();
104                if (!StringUtils.hasLength(actionName)) {
105                        actionName = "";
106                }
107
108                String controllerName = mapping.getControllerName();
109
110                if (isController(controllerName, actionName, application)) {
111                        if (!StringUtils.hasLength(actionName) || "null".equals(actionName)) {
112                                actionName = "index";
113                        }
114                        return ("/" + controllerName + "/" + actionName).trim();
115                }
116
117                return null;
118        }
119
120        private boolean isController(final String controllerName, final String actionName,
121                        final GrailsApplication application) {
122                return application.getArtefactForFeature(ControllerArtefactHandler.TYPE,
123                                "/" + controllerName + "/" + actionName) != null;
124        }
125
126        private void configureMapping(final UrlMappingInfo mapping, final GrailsWebRequest grailsRequest,
127                        final Map<String, Object> savedParams) {
128
129                // reset params since mapping.configure() sets values
130                GrailsParameterMap params = grailsRequest.getParams();
131                params.clear();
132                params.putAll(savedParams);
133
134                mapping.configure(grailsRequest);
135        }
136
137        @SuppressWarnings("unchecked")
138        private Map<String, Object> copyParams(final GrailsWebRequest grailsRequest) {
139                return new HashMap<String, Object>(grailsRequest.getParams());
140        }
141
142        /**
143         * Called by the plugin to set controller role info.<br/>
144         *
145         * Reinitialize by calling <code>ctx.objectDefinitionSource.initialize(
146         *      ctx.authenticateService.securityConfig.security.annotationStaticRules,
147         *      ctx.grailsUrlMappingsHolder,
148         *      ApplicationHolder.application.controllerClasses)</code>
149         *
150         * @param staticRules  keys are URL patterns, values are role names for that pattern
151         * @param urlMappingsHolder  mapping holder
152         * @param controllerClasses  all controllers
153         */
154        public void initialize(
155                        final Map<String, Collection<String>> staticRules,
156                        final UrlMappingsHolder urlMappingsHolder,
157                        final GrailsClass[] controllerClasses) {
158
159                Map<String, Map<String, Set<String>>> actionRoleMap = new HashMap<String, Map<String,Set<String>>>();
160                Map<String, Set<String>> classRoleMap = new HashMap<String, Set<String>>();
161
162                Assert.notNull(staticRules, "staticRules map is required");
163                Assert.notNull(urlMappingsHolder, "urlMappingsHolder is required");
164
165                _compiled.clear();
166
167                _urlMappingsHolder = urlMappingsHolder;
168
169                for (GrailsClass controllerClass : controllerClasses) {
170                        findControllerAnnotations((GrailsControllerClass)controllerClass, actionRoleMap, classRoleMap);
171                }
172
173                compileActionMap(actionRoleMap);
174                compileClassMap(classRoleMap);
175                compileStaticRules(staticRules);
176
177                if (_log.isTraceEnabled()) {
178                        _log.trace("configs: " + _compiled);
179                }
180        }
181
182        private void compileActionMap(final Map<String, Map<String, Set<String>>> map) {
183                for (Map.Entry<String, Map<String, Set<String>>> controllerEntry : map.entrySet()) {
184                        String controllerName = controllerEntry.getKey();
185                        Map<String, Set<String>> actionRoles = controllerEntry.getValue();
186                        for (Map.Entry<String, Set<String>> actionEntry : actionRoles.entrySet()) {
187                                String actionName = actionEntry.getKey();
188                                Set<String> roles = actionEntry.getValue();
189                                storeMapping(controllerName, actionName, roles, false);
190                        }
191                }
192        }
193
194        private void compileClassMap(final Map<String, Set<String>> classRoleMap) {
195                for (Map.Entry<String, Set<String>> entry : classRoleMap.entrySet()) {
196                        String controllerName = entry.getKey();
197                        Set<String> roles = entry.getValue();
198                        storeMapping(controllerName, null, roles, false);
199                }
200        }
201
202        private void compileStaticRules(final Map<String, Collection<String>> staticRules) {
203                for (Map.Entry<String, Collection<String>> entry : staticRules.entrySet()) {
204                        String pattern = entry.getKey();
205                        Collection<String> roles = entry.getValue();
206                        storeMapping(pattern, null, roles, true);
207                }
208        }
209
210        private void storeMapping(final String controllerNameOrPattern, final String actionName,
211                        final Collection<String> roles, final boolean isPattern) {
212
213                String fullPattern;
214                if (isPattern) {
215                        fullPattern = controllerNameOrPattern;
216                }
217                else {
218                        StringBuilder sb = new StringBuilder();
219                        sb.append('/').append(controllerNameOrPattern);
220                        if (actionName != null) {
221                                sb.append('/').append(actionName);
222                        }
223                        sb.append("/**");
224                        fullPattern = sb.toString();
225                }
226
227                ConfigAttributeDefinition configAttribute = new ConfigAttributeDefinition(
228                                roles.toArray(new String[roles.size()]));
229
230                Object key = getUrlMatcher().compile(fullPattern);
231                ConfigAttributeDefinition replaced = _compiled.put(key, configAttribute);
232                if (replaced != null) {
233                        _log.warn("replaced rule for '" + key + "' with roles " + replaced.getConfigAttributes()
234                                        + " with roles " + configAttribute.getConfigAttributes());
235                }
236        }
237
238        private void findControllerAnnotations(final GrailsControllerClass controllerClass,
239                        final Map<String, Map<String, Set<String>>> actionRoleMap,
240                        final Map<String, Set<String>> classRoleMap) {
241
242                Class<?> clazz = controllerClass.getClazz();
243                String controllerName = WordUtils.uncapitalize(controllerClass.getName());
244
245                Secured annotation = clazz.getAnnotation(Secured.class);
246                if (annotation != null) {
247                        classRoleMap.put(controllerName, asSet(annotation.value()));
248                }
249
250                Map<String, Set<String>> annotatedClosureNames = findActionRoles(clazz);
251                if (annotatedClosureNames != null) {
252                        actionRoleMap.put(controllerName, annotatedClosureNames);
253                }
254        }
255
256        private Map<String, Set<String>> findActionRoles(final Class<?> clazz) {
257                // since action closures are defined as "def foo = ..." they're
258                // fields, but they end up as private
259                Map<String, Set<String>> actionRoles = new HashMap<String, Set<String>>();
260                for (Field field : clazz.getDeclaredFields()) {
261                        Secured annotation = field.getAnnotation(Secured.class);
262                        if (annotation != null) {
263                                actionRoles.put(field.getName(), asSet(annotation.value()));
264                        }
265                }
266                return actionRoles;
267        }
268
269        private Set<String> asSet(final String[] strings) {
270                Set<String> set = new HashSet<String>();
271                for (String string : strings) {
272                        set.add(string);
273                }
274                return set;
275        }
276}
Note: See TracBrowser for help on using the repository browser.