View Javadoc

1   package org.springframework.security.config;
2   
3   import java.util.Iterator;
4   import java.util.LinkedHashMap;
5   import java.util.List;
6   import java.util.Map;
7   
8   import org.springframework.aop.config.AopNamespaceUtils;
9   import org.springframework.beans.factory.config.BeanDefinition;
10  import org.springframework.beans.factory.config.RuntimeBeanReference;
11  import org.springframework.beans.factory.parsing.BeanComponentDefinition;
12  import org.springframework.beans.factory.support.BeanDefinitionBuilder;
13  import org.springframework.beans.factory.support.ManagedList;
14  import org.springframework.beans.factory.support.RootBeanDefinition;
15  import org.springframework.beans.factory.xml.BeanDefinitionParser;
16  import org.springframework.beans.factory.xml.ParserContext;
17  import org.springframework.security.ConfigAttributeDefinition;
18  import org.springframework.security.intercept.method.DelegatingMethodDefinitionSource;
19  import org.springframework.security.intercept.method.MapBasedMethodDefinitionSource;
20  import org.springframework.security.intercept.method.ProtectPointcutPostProcessor;
21  import org.springframework.security.intercept.method.aopalliance.MethodDefinitionSourceAdvisor;
22  import org.springframework.security.intercept.method.aopalliance.MethodSecurityInterceptor;
23  import org.springframework.util.ClassUtils;
24  import org.springframework.util.StringUtils;
25  import org.springframework.util.xml.DomUtils;
26  import org.w3c.dom.Element;
27  
28  /**
29   * Processes the top-level "global-method-security" element.
30   * 
31   * @author Ben Alex
32   * @version $Id: GlobalMethodSecurityBeanDefinitionParser.java 2999 2008-04-25 12:28:30Z luke_t $
33   */
34  class GlobalMethodSecurityBeanDefinitionParser implements BeanDefinitionParser {
35      public static final String SECURED_DEPENDENCY_CLASS = "org.springframework.security.annotation.Secured";
36      public static final String SECURED_METHOD_DEFINITION_SOURCE_CLASS = "org.springframework.security.annotation.SecuredMethodDefinitionSource";
37      public static final String JSR_250_SECURITY_METHOD_DEFINITION_SOURCE_CLASS = "org.springframework.security.annotation.Jsr250MethodDefinitionSource";
38      public static final String JSR_250_VOTER_CLASS = "org.springframework.security.annotation.Jsr250Voter";
39      private static final String ATT_ACCESS = "access";
40      private static final String ATT_EXPRESSION = "expression";
41      private static final String ATT_ACCESS_MGR = "access-decision-manager-ref";
42      private static final String ATT_USE_JSR250 = "jsr250-annotations";
43      private static final String ATT_USE_SECURED = "secured-annotations";
44  
45      private void validatePresent(String className, Element element, ParserContext parserContext) {
46          if (!ClassUtils.isPresent(className, parserContext.getReaderContext().getBeanClassLoader())) {
47              parserContext.getReaderContext().error("Cannot locate '" + className + "'", element);
48          }
49      }
50  
51      public BeanDefinition parse(Element element, ParserContext parserContext) {
52          Object source = parserContext.extractSource(element);
53          // The list of method metadata delegates
54          ManagedList delegates = new ManagedList();
55          
56          boolean jsr250Enabled = registerAnnotationBasedMethodDefinitionSources(element, parserContext, delegates);
57          
58          MapBasedMethodDefinitionSource mapBasedMethodDefinitionSource = new MapBasedMethodDefinitionSource();
59          delegates.add(mapBasedMethodDefinitionSource);
60          
61          // Now create a Map<String, ConfigAttribute> for each <protect-pointcut> sub-element        
62          Map pointcutMap = parseProtectPointcuts(parserContext, 
63                  DomUtils.getChildElementsByTagName(element, Elements.PROTECT_POINTCUT));
64          
65          if (pointcutMap.size() > 0) {
66              registerProtectPointcutPostProcessor(parserContext, pointcutMap, mapBasedMethodDefinitionSource, source);
67          }
68          
69          registerDelegatingMethodDefinitionSource(parserContext, delegates, source);
70          
71          // Register the applicable AccessDecisionManager, handling the special JSR 250 voter if being used
72          String accessManagerId = element.getAttribute(ATT_ACCESS_MGR);
73  
74          if (!StringUtils.hasText(accessManagerId)) {
75              ConfigUtils.registerDefaultAccessManagerIfNecessary(parserContext);
76  
77              if (jsr250Enabled) {
78                  ConfigUtils.addVoter(new RootBeanDefinition(JSR_250_VOTER_CLASS, null, null), parserContext);                
79              }
80  
81              accessManagerId = BeanIds.ACCESS_MANAGER;
82          }
83          
84          registerMethodSecurityInterceptor(parserContext, accessManagerId, source);
85          
86          registerAdvisor(parserContext, source);
87  
88          AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);
89          
90          return null;
91      }
92      
93      /**
94       * Checks whether JSR-250 and/or Secured annotations are enabled and adds the appropriate 
95       * MethodDefinitionSource delegates if required. 
96       */
97      private boolean registerAnnotationBasedMethodDefinitionSources(Element element, ParserContext pc, ManagedList delegates) {
98          boolean useJsr250 = "enabled".equals(element.getAttribute(ATT_USE_JSR250));
99          boolean useSecured = "enabled".equals(element.getAttribute(ATT_USE_SECURED));
100         
101         // Check the required classes are present
102         if (useSecured) {
103             validatePresent(SECURED_METHOD_DEFINITION_SOURCE_CLASS, element, pc);
104             validatePresent(SECURED_DEPENDENCY_CLASS, element, pc);
105             delegates.add(BeanDefinitionBuilder.rootBeanDefinition(SECURED_METHOD_DEFINITION_SOURCE_CLASS).getBeanDefinition());
106         }
107 
108         if (useJsr250) {
109             validatePresent(JSR_250_SECURITY_METHOD_DEFINITION_SOURCE_CLASS, element, pc);
110             validatePresent(JSR_250_VOTER_CLASS, element, pc);
111             delegates.add(BeanDefinitionBuilder.rootBeanDefinition(JSR_250_SECURITY_METHOD_DEFINITION_SOURCE_CLASS).getBeanDefinition());           
112         }
113         
114         return useJsr250;
115     }
116     
117     private void registerDelegatingMethodDefinitionSource(ParserContext parserContext, ManagedList delegates, Object source) {
118         if (parserContext.getRegistry().containsBeanDefinition(BeanIds.DELEGATING_METHOD_DEFINITION_SOURCE)) {
119             parserContext.getReaderContext().error("Duplicate <global-method-security> detected.", source);
120         }
121         RootBeanDefinition delegatingMethodDefinitionSource = new RootBeanDefinition(DelegatingMethodDefinitionSource.class);
122         delegatingMethodDefinitionSource.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
123         delegatingMethodDefinitionSource.setSource(source);
124         delegatingMethodDefinitionSource.getPropertyValues().addPropertyValue("methodDefinitionSources", delegates);
125         parserContext.getRegistry().registerBeanDefinition(BeanIds.DELEGATING_METHOD_DEFINITION_SOURCE, delegatingMethodDefinitionSource);        
126     }
127     
128     private void registerProtectPointcutPostProcessor(ParserContext parserContext, Map pointcutMap,
129             MapBasedMethodDefinitionSource mapBasedMethodDefinitionSource, Object source) {
130         RootBeanDefinition ppbp = new RootBeanDefinition(ProtectPointcutPostProcessor.class);
131         ppbp.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
132         ppbp.setSource(source);
133         ppbp.getConstructorArgumentValues().addGenericArgumentValue(mapBasedMethodDefinitionSource);
134         ppbp.getPropertyValues().addPropertyValue("pointcutMap", pointcutMap);
135         parserContext.getRegistry().registerBeanDefinition(BeanIds.PROTECT_POINTCUT_POST_PROCESSOR, ppbp);
136     }
137 
138     private Map parseProtectPointcuts(ParserContext parserContext, List protectPointcutElts) {
139         Map pointcutMap = new LinkedHashMap();
140 
141         for (Iterator i = protectPointcutElts.iterator(); i.hasNext();) {
142             Element childElt = (Element) i.next();
143             String accessConfig = childElt.getAttribute(ATT_ACCESS);
144             String expression = childElt.getAttribute(ATT_EXPRESSION);
145 
146             if (!StringUtils.hasText(accessConfig)) {
147                 parserContext.getReaderContext().error("Access configuration required", parserContext.extractSource(childElt));
148             }
149 
150             if (!StringUtils.hasText(expression)) {
151                 parserContext.getReaderContext().error("Pointcut expression required", parserContext.extractSource(childElt));
152             }
153 
154             ConfigAttributeDefinition def = new ConfigAttributeDefinition(StringUtils.commaDelimitedListToStringArray(accessConfig));
155             pointcutMap.put(expression, def);
156         }
157 
158         return pointcutMap;
159     }
160 
161     private void registerMethodSecurityInterceptor(ParserContext parserContext, String accessManagerId, Object source) {
162         RootBeanDefinition interceptor = new RootBeanDefinition(MethodSecurityInterceptor.class);
163         interceptor.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
164         interceptor.setSource(source);
165         
166         interceptor.getPropertyValues().addPropertyValue("accessDecisionManager", new RuntimeBeanReference(accessManagerId));
167         interceptor.getPropertyValues().addPropertyValue("authenticationManager", new RuntimeBeanReference(BeanIds.AUTHENTICATION_MANAGER));
168         interceptor.getPropertyValues().addPropertyValue("objectDefinitionSource", new RuntimeBeanReference(BeanIds.DELEGATING_METHOD_DEFINITION_SOURCE));
169         parserContext.getRegistry().registerBeanDefinition(BeanIds.METHOD_SECURITY_INTERCEPTOR, interceptor);
170         parserContext.registerComponent(new BeanComponentDefinition(interceptor, BeanIds.METHOD_SECURITY_INTERCEPTOR));
171         
172         parserContext.getRegistry().registerBeanDefinition(BeanIds.METHOD_SECURITY_INTERCEPTOR_POST_PROCESSOR,
173                 new RootBeanDefinition(MethodSecurityInterceptorPostProcessor.class));
174     }
175 
176     private void registerAdvisor(ParserContext parserContext, Object source) {
177         RootBeanDefinition advisor = new RootBeanDefinition(MethodDefinitionSourceAdvisor.class);
178         advisor.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
179         advisor.setSource(source);
180         advisor.getConstructorArgumentValues().addGenericArgumentValue(BeanIds.METHOD_SECURITY_INTERCEPTOR);
181         advisor.getConstructorArgumentValues().addGenericArgumentValue(new RuntimeBeanReference(BeanIds.DELEGATING_METHOD_DEFINITION_SOURCE));
182 
183         parserContext.getRegistry().registerBeanDefinition(BeanIds.METHOD_DEFINITION_SOURCE_ADVISOR, advisor);        
184     }    
185 }