1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.myfaces.tobago.internal.util;
21
22 import org.slf4j.Logger;
23 import org.slf4j.LoggerFactory;
24
25 import javax.annotation.security.DenyAll;
26 import javax.annotation.security.PermitAll;
27 import javax.annotation.security.RolesAllowed;
28 import javax.enterprise.context.ApplicationScoped;
29 import javax.enterprise.inject.spi.Bean;
30 import javax.enterprise.inject.spi.BeanManager;
31 import javax.enterprise.inject.spi.CDI;
32 import javax.faces.context.FacesContext;
33 import javax.inject.Named;
34 import java.lang.annotation.Annotation;
35 import java.lang.invoke.MethodHandles;
36 import java.lang.reflect.AnnotatedElement;
37 import java.lang.reflect.Method;
38 import java.util.ArrayList;
39 import java.util.Arrays;
40 import java.util.List;
41 import java.util.Map;
42 import java.util.concurrent.ConcurrentHashMap;
43 import java.util.regex.Matcher;
44 import java.util.regex.Pattern;
45
46 @Named
47 @ApplicationScoped
48 public class AuthorizationHelper {
49
50 private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
51
52 public static final String AUTHORIZATION_HELPER = "authorizationHelper";
53
54 private static final Pattern PATTERN = Pattern.compile("#\\{(\\w+(?:\\.\\w+)*)\\.(\\w+)(?:\\(.*\\))?}");
55
56 private static final Annotation NULL_VALUE = new Annotation() {
57 @Override
58 public Class<? extends Annotation> annotationType() {
59 return null;
60 }
61
62 @Override
63 public String toString() {
64 return "(NULL)";
65 }
66 };
67
68 private final Map<String, Object> cache = new ConcurrentHashMap<>();
69
70 private final BeanManager beanManager;
71
72 public AuthorizationHelper() {
73 beanManager = CDI.current().getBeanManager();
74 LOG.info("Using bean manager: '{}'", beanManager);
75 }
76
77 public static AuthorizationHelper getInstance(final FacesContext facesContext) {
78 return (AuthorizationHelper)
79 facesContext.getELContext().getELResolver().getValue(facesContext.getELContext(), null, AUTHORIZATION_HELPER);
80 }
81
82 Object getObject(String beanString) {
83 Object bean = null;
84 for (final Bean<?> entry : beanManager.getBeans(beanString)) {
85 if (bean == null) {
86 bean = entry;
87 } else {
88 LOG.warn("Bean name ambiguous: '{}'", beanString);
89 }
90 }
91 return bean;
92 }
93
94 public boolean isAuthorized(final FacesContext facesContext, final String expression) {
95
96 final Annotation securityAnnotation = getSecurityAnnotation(expression);
97 if (securityAnnotation == null) {
98 return true;
99 }
100
101 if (securityAnnotation instanceof DenyAll) {
102 if (LOG.isDebugEnabled()) {
103 LOG.debug("DenyAll");
104 }
105 return false;
106 }
107 if (securityAnnotation instanceof RolesAllowed) {
108 final String[] roles = ((RolesAllowed) securityAnnotation).value();
109 if (LOG.isDebugEnabled()) {
110 LOG.debug("RolesAllowed " + Arrays.asList(((RolesAllowed) securityAnnotation).value()));
111 }
112 for (final String role : roles) {
113 final boolean authorised = facesContext.getExternalContext().isUserInRole(role);
114 if (authorised) {
115 return true;
116 }
117 }
118 return false;
119 }
120 if (securityAnnotation instanceof PermitAll) {
121 if (LOG.isDebugEnabled()) {
122 LOG.debug("PermitAll");
123 }
124 return true;
125 }
126 return true;
127 }
128
129 private Annotation getSecurityAnnotation(final String expression) {
130 if (cache.containsKey(expression)) {
131 final Object obj = cache.get(expression);
132 if (obj instanceof Annotation) {
133 return (Annotation) obj;
134 }
135 return null;
136 } else {
137 Annotation securityAnnotation = null;
138 final Matcher matcher = PATTERN.matcher(expression);
139 if (matcher.matches()) {
140 final String beanString = matcher.group(1);
141 final String methodString = matcher.group(2);
142
143 final Object bean = getObject(beanString);
144 if (bean != null) {
145
146 final List<Method> methods = findMethods(bean, methodString);
147 switch (methods.size()) {
148 case 0:
149 LOG.error("No Method '" + methodString + "' in class " + bean.getClass());
150 break;
151 case 1:
152 securityAnnotation = getSecurityAnnotations(methods.get(0));
153 break;
154 default:
155 LOG.warn("Method name ambiguous '" + methodString + "' in class " + bean.getClass()
156 + ". Found " + methods.size() + " but only 1 is supported, yet.");
157 }
158
159 if (securityAnnotation == null) {
160 securityAnnotation = getSecurityAnnotations(bean.getClass());
161 }
162 }
163 }
164 if (securityAnnotation == null) {
165 securityAnnotation = NULL_VALUE;
166 }
167
168 cache.put(expression, securityAnnotation);
169 if (LOG.isInfoEnabled()) {
170 LOG.info("Security annotation '{}' saved for expression '{}'", securityAnnotation, expression);
171 }
172
173 return securityAnnotation;
174 }
175 }
176
177 private Annotation getSecurityAnnotations(final AnnotatedElement annotatedElement) {
178 Annotation annotation = annotatedElement.getAnnotation(RolesAllowed.class);
179 if (annotation != null) {
180 return annotation;
181 }
182 annotation = annotatedElement.getAnnotation(DenyAll.class);
183 if (annotation != null) {
184 return annotation;
185 }
186 annotation = annotatedElement.getAnnotation(PermitAll.class);
187 return annotation;
188 }
189
190 private List<Method> findMethods(final Object bean, final String name) {
191 final Class clazz = ((Bean) bean).getBeanClass();
192 final Method[] methods = clazz.getMethods();
193 final List<Method> result = new ArrayList<>();
194 for (final Method method : methods) {
195 if (method.getName().equals(name)) {
196 result.add(method);
197 }
198 }
199 return result;
200 }
201
202 }