001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied. See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019 package org.tynamo.shiro.extension.authz.aop;
020
021 import org.apache.shiro.authz.annotation.*;
022 import org.apache.shiro.authz.aop.AuthorizingAnnotationHandler;
023 import org.tynamo.shiro.extension.authz.annotations.utils.casters.method.HandlerCreateVisitor;
024 import org.tynamo.shiro.extension.authz.annotations.utils.casters.method.MethodAnnotationCaster;
025
026 import java.lang.annotation.Annotation;
027 import java.lang.reflect.Method;
028 import java.lang.reflect.Modifier;
029 import java.util.*;
030
031
032 /**
033 * Simple util class, help work with annotations and create interceptors
034 * based on annotations.
035 *
036 */
037 public class AopHelper
038 {
039
040 /**
041 * List annotations classes which can be applied (either method or a class).
042 */
043 private final static Collection<Class<? extends Annotation>> autorizationAnnotationClasses;
044
045 /**
046 * Initialize annotations lists.
047 */
048 static
049 {
050 autorizationAnnotationClasses = new ArrayList<Class<? extends Annotation>>(5);
051 autorizationAnnotationClasses.add(RequiresPermissions.class);
052 autorizationAnnotationClasses.add(RequiresRoles.class);
053 autorizationAnnotationClasses.add(RequiresUser.class);
054 autorizationAnnotationClasses.add(RequiresGuest.class);
055 autorizationAnnotationClasses.add(RequiresAuthentication.class);
056 }
057
058 /**
059 * Create {@link org.apache.shiro.authz.aop.AuthorizingAnnotationHandler}
060 * for annotation.
061 *
062 * @param annotation
063 * @return
064 */
065 public static AuthorizingAnnotationHandler createHandler(Annotation annotation)
066 {
067 HandlerCreateVisitor visitor = new HandlerCreateVisitor();
068 MethodAnnotationCaster.getInstance().accept(visitor, annotation);
069 return visitor.getHandler();
070 }
071
072 /**
073 * Create list of {@link org.tynamo.shiro.extension.authz.aop.SecurityInterceptor}
074 * instances for method. This method search all method and class annotations and use
075 * annotation data for create interceptors.
076 * <p/>
077 * This method considers only those annotations that have been declared
078 * in the set through parameters of the method and class, regardless of the
079 * inheritance or interface implementations
080 *
081 * @param method
082 * @param clazz
083 * @return
084 */
085 public static List<SecurityInterceptor> createSecurityInterceptors(Method method, Class<?> clazz)
086 {
087 List<SecurityInterceptor> result = new ArrayList<SecurityInterceptor>();
088
089 if (isInterceptOnClassAnnotation(method.getModifiers()))
090 {
091 for (Class<? extends Annotation> ac :
092 getAutorizationAnnotationClasses())
093 {
094 Annotation annotationOnClass = clazz.getAnnotation(ac);
095 if (annotationOnClass != null)
096 {
097 result.add(new DefaultSecurityInterceptor(annotationOnClass));
098 }
099 }
100 }
101
102 for (Class<? extends Annotation> ac :
103 getAutorizationAnnotationClasses())
104 {
105 Annotation annotation = method.getAnnotation(ac);
106 if (annotation != null)
107 {
108 result.add(new DefaultSecurityInterceptor(annotation));
109 }
110 }
111
112 return result;
113 }
114
115 /**
116 * Create list of {@link org.tynamo.shiro.extension.authz.aop.SecurityInterceptor}
117 * instances for method. This method search all method and class annotations and use
118 * annotation data for create interceptors.
119 * <p/>
120 * In contrast of the {@link #createSecurityInterceptors(Method, Class)}, this method
121 * looking for the annotations in all interfaces, witch implement the targetClass.
122 * <p/>
123 * The following rules
124 * <ul>
125 * <li>If annotation on class presents, will be intercepted all methods in the class,
126 * that satisfy the {@link #isInterceptOnClassAnnotation(Method) rule}.</li>
127 * <li>Annotations on methods are <b>not</b> inherited.</li>
128 * <li>Annotations on classes are <b>not</b> inherited.</li>
129 * <li>The annotations are searched in all interfaces, witch implement the targetClass.</li>
130 * <ul>
131 *
132 * @param method
133 * @param targetClass
134 * @return
135 */
136 public static List<SecurityInterceptor> createSecurityInterceptorsSeeingInterfaces(Method method,
137 Class<?> targetClass)
138 {
139 List<SecurityInterceptor> interceptors = new ArrayList<SecurityInterceptor>();
140
141 method = findTargetMethod(method, targetClass);
142
143 interceptors.addAll(createSecurityInterceptors(method, targetClass));
144
145 Set<Class<?>> allInterfaces = new HashSet<Class<?>>();
146 getAllInterfaces(allInterfaces, targetClass);
147
148 for (Class<?> intf : allInterfaces)
149 {
150 try
151 {
152 Method candidate = intf.getMethod(method.getName(), method.getParameterTypes());
153 interceptors.addAll(createSecurityInterceptors(candidate, intf));
154 } catch (SecurityException e)
155 {
156 new RuntimeException(e);
157 } catch (NoSuchMethodException e)
158 {
159 //nothing to do
160 }
161 }
162
163 return interceptors;
164 }
165
166 /**
167 * Find the target method of interface.
168 * <p/>
169 * Ensure this: If a class have an interface, then method parameter from interface and
170 * targetClass implementation.
171 * <p/>
172 */
173 public static Method findTargetMethod(Method method, Class<?> targetClass)
174 {
175 try
176 {
177 method = targetClass.getDeclaredMethod(method.getName(), method.getParameterTypes());
178 } catch (SecurityException e)
179 {
180 throw new RuntimeException(e);
181 } catch (NoSuchMethodException e)
182 {
183 //That's ok
184 }
185 return method;
186 }
187
188 /**
189 * Recursively finds all the interfaces.
190 *
191 * @param searchInterfaces set of result interfaces
192 * @param clazz
193 */
194 private static void getAllInterfaces(Set<Class<?>> searchInterfaces, Class<?> clazz)
195 {
196 while (clazz != null)
197 {
198 searchInterfaces.addAll(Arrays.asList(clazz.getInterfaces()));
199 for (Class<?> intf : clazz.getInterfaces())
200 {
201 getAllInterfaces(searchInterfaces, intf);
202 }
203 clazz = clazz.getSuperclass();
204 }
205 }
206
207 /**
208 * Rule under which determined the fate of the class contains annotation.
209 * <p/>
210 * All public and protected methods.
211 */
212 public static boolean isInterceptOnClassAnnotation(int modifiers)
213 {
214 return Modifier.isPublic(modifiers)
215 || Modifier.isProtected(modifiers);
216 }
217
218 public static Collection<Class<? extends Annotation>> getAutorizationAnnotationClasses()
219 {
220 return autorizationAnnotationClasses;
221 }
222 }