Package org.codehaus.groovy.grails.plugins.springsecurity

Source Code of org.codehaus.groovy.grails.plugins.springsecurity.AnnotationFilterInvocationDefinition

/* Copyright 2006-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.codehaus.groovy.grails.plugins.springsecurity;

import org.apache.commons.lang.WordUtils;
import org.codehaus.groovy.grails.commons.*;
import org.codehaus.groovy.grails.web.context.ServletContextHolder;
import org.codehaus.groovy.grails.web.mapping.UrlMappingInfo;
import org.codehaus.groovy.grails.web.mapping.UrlMappingsHolder;
import org.codehaus.groovy.grails.web.servlet.mvc.GrailsParameterMap;
import org.codehaus.groovy.grails.web.servlet.mvc.GrailsWebRequest;
import org.codehaus.groovy.grails.web.util.WebUtils;
import org.springframework.security.ConfigAttributeDefinition;
import org.springframework.security.intercept.web.FilterInvocation;
import org.springframework.security.intercept.web.FilterInvocationDefinitionSource;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Field;
import java.util.*;

/**
* A {@link FilterInvocationDefinitionSource} that uses rules defined with Controller annotations
* combined with static rules defined in <code>SecurityConfig.groovy</code>, e.g. for js, images, css
* or for rules that cannot be expressed in a controller like '/**'.
*
* @author <a href='mailto:beckwithb@studentsonly.com'>Burt Beckwith</a>
*/
public class AnnotationFilterInvocationDefinition extends AbstractFilterInvocationDefinition {

    private UrlMappingsHolder _urlMappingsHolder;

    @Override
    protected String determineUrl(final FilterInvocation filterInvocation) {
        HttpServletRequest request = filterInvocation.getHttpRequest();
        HttpServletResponse response = filterInvocation.getHttpResponse();
        ServletContext servletContext = ServletContextHolder.getServletContext();
        GrailsApplication application = ApplicationHolder.getApplication();

        GrailsWebRequest existingRequest = WebUtils.retrieveGrailsWebRequest();

        String requestUrl = request.getRequestURI().substring(request.getContextPath().length());

        String url = null;
        try {
            GrailsWebRequest grailsRequest = new GrailsWebRequest(request, response, servletContext);
            WebUtils.storeGrailsWebRequest(grailsRequest);

            Map<String, Object> savedParams = copyParams(grailsRequest);

            for (UrlMappingInfo mapping : _urlMappingsHolder.matchAll(requestUrl)) {
                configureMapping(mapping, grailsRequest, savedParams);

                url = findGrailsUrl(mapping, application);
                if (url != null) {
                    break;
                }
            }
        }
        finally {
            if (existingRequest == null) {
                WebUtils.clearGrailsWebRequest();
            } else {
                WebUtils.storeGrailsWebRequest(existingRequest);
            }
        }

        if (!StringUtils.hasLength(url)) {
            // probably css/js/image
            url = requestUrl;
        }

        return lowercaseAndStringQuerystring(url);
    }

    private String findGrailsUrl(final UrlMappingInfo mapping, final GrailsApplication application) {

        String actionName = mapping.getActionName();
        if (!StringUtils.hasLength(actionName)) {
            actionName = "";
        }

        String controllerName = mapping.getControllerName();

        if (isController(controllerName, actionName, application)) {
            if (!StringUtils.hasLength(actionName) || "null".equals(actionName)) {
                actionName = "index";
            }
            return ("/" + controllerName + "/" + actionName).trim();
        }

        return null;
    }

    private boolean isController(final String controllerName, final String actionName,
                                 final GrailsApplication application) {
        return application.getArtefactForFeature(ControllerArtefactHandler.TYPE,
                "/" + controllerName + "/" + actionName) != null;
    }

    private void configureMapping(final UrlMappingInfo mapping, final GrailsWebRequest grailsRequest,
                                  final Map<String, Object> savedParams) {

        // reset params since mapping.configure() sets values
        GrailsParameterMap params = grailsRequest.getParams();
        params.clear();
        params.putAll(savedParams);

        mapping.configure(grailsRequest);
    }

    @SuppressWarnings("unchecked")
    private Map<String, Object> copyParams(final GrailsWebRequest grailsRequest) {
        return new HashMap<String, Object>(grailsRequest.getParams());
    }

    /**
     * Called by the plugin to set controller role info.<br/>
     * <p/>
     * Reinitialize by calling <code>ctx.objectDefinitionSource.initialize(
     * ctx.authenticateService.securityConfig.security.annotationStaticRules,
     * ctx.grailsUrlMappingsHolder,
     * ApplicationHolder.application.controllerClasses)</code>
     *
     * @param staticRules       keys are URL patterns, values are role names for that pattern
     * @param urlMappingsHolder mapping holder
     * @param controllerClasses all controllers
     */
    public void initialize(
            final Map<String, Collection<String>> staticRules,
            final UrlMappingsHolder urlMappingsHolder,
            final GrailsClass[] controllerClasses) {

        Map<String, Map<String, Set<String>>> actionRoleMap = new HashMap<String, Map<String, Set<String>>>();
        Map<String, Set<String>> classRoleMap = new HashMap<String, Set<String>>();

        Assert.notNull(staticRules, "staticRules map is required");
        Assert.notNull(urlMappingsHolder, "urlMappingsHolder is required");

        _compiled.clear();

        _urlMappingsHolder = urlMappingsHolder;

        for (GrailsClass controllerClass : controllerClasses) {
            findControllerAnnotations((GrailsControllerClass) controllerClass, actionRoleMap, classRoleMap);
        }

        compileActionMap(actionRoleMap);
        compileClassMap(classRoleMap);
        compileStaticRules(staticRules);

        if (_log.isTraceEnabled()) {
            _log.trace("configs: " + _compiled);
        }
    }

    private void compileActionMap(final Map<String, Map<String, Set<String>>> map) {
        for (Map.Entry<String, Map<String, Set<String>>> controllerEntry : map.entrySet()) {
            String controllerName = controllerEntry.getKey();
            Map<String, Set<String>> actionRoles = controllerEntry.getValue();
            for (Map.Entry<String, Set<String>> actionEntry : actionRoles.entrySet()) {
                String actionName = actionEntry.getKey();
                Set<String> roles = actionEntry.getValue();
                storeMapping(controllerName, actionName, roles, false);
            }
        }
    }

    private void compileClassMap(final Map<String, Set<String>> classRoleMap) {
        for (Map.Entry<String, Set<String>> entry : classRoleMap.entrySet()) {
            String controllerName = entry.getKey();
            Set<String> roles = entry.getValue();
            storeMapping(controllerName, null, roles, false);
        }
    }

    private void compileStaticRules(final Map<String, Collection<String>> staticRules) {
        for (Map.Entry<String, Collection<String>> entry : staticRules.entrySet()) {
            String pattern = entry.getKey();
            Collection<String> roles = entry.getValue();
            storeMapping(pattern, null, roles, true);
        }
    }

    private void storeMapping(final String controllerNameOrPattern, final String actionName,
                              final Collection<String> roles, final boolean isPattern) {

        String fullPattern;
        if (isPattern) {
            fullPattern = controllerNameOrPattern;
        } else {
            StringBuilder sb = new StringBuilder();
            sb.append('/').append(controllerNameOrPattern);
            if (actionName != null) {
                sb.append('/').append(actionName);
            }
            sb.append("/**");
            fullPattern = sb.toString();
        }

        ConfigAttributeDefinition configAttribute = new ConfigAttributeDefinition(
                roles.toArray(new String[roles.size()]));

        Object key = getUrlMatcher().compile(fullPattern);
        ConfigAttributeDefinition replaced = _compiled.put(key, configAttribute);
        if (replaced != null) {
            _log.warn("replaced rule for '" + key + "' with roles " + replaced.getConfigAttributes()
                    + " with roles " + configAttribute.getConfigAttributes());
        }
    }

    private void findControllerAnnotations(final GrailsControllerClass controllerClass,
                                           final Map<String, Map<String, Set<String>>> actionRoleMap,
                                           final Map<String, Set<String>> classRoleMap) {

        Class<?> clazz = controllerClass.getClazz();
        String controllerName = WordUtils.uncapitalize(controllerClass.getName());

        Secured annotation = clazz.getAnnotation(Secured.class);
        if (annotation != null) {
            classRoleMap.put(controllerName, asSet(annotation.value()));
        }

        Map<String, Set<String>> annotatedClosureNames = findActionRoles(clazz);
        if (annotatedClosureNames != null) {
            actionRoleMap.put(controllerName, annotatedClosureNames);
        }
    }

    private Map<String, Set<String>> findActionRoles(final Class<?> clazz) {
        // since action closures are defined as "def foo = ..." they're
        // fields, but they end up as private
        Map<String, Set<String>> actionRoles = new HashMap<String, Set<String>>();
        for (Field field : clazz.getDeclaredFields()) {
            Secured annotation = field.getAnnotation(Secured.class);
            if (annotation != null) {
                actionRoles.put(field.getName(), asSet(annotation.value()));
            }
        }
        return actionRoles;
    }

    private Set<String> asSet(final String[] strings) {
        Set<String> set = new HashSet<String>();
        for (String string : strings) {
            set.add(string);
    }
    return set;
  }
}
TOP

Related Classes of org.codehaus.groovy.grails.plugins.springsecurity.AnnotationFilterInvocationDefinition

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.