Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
net.e6tech.elements.web.cxf.SecurityAnnotationEngine Maven / Gradle / Ivy
/*
Copyright 2015-2019 Futeh Kao
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 net.e6tech.elements.web.cxf;
import net.e6tech.elements.common.logging.Logger;
import net.e6tech.elements.common.reflection.MethodSignature;
import org.apache.cxf.common.util.ClassHelper;
import javax.annotation.security.DenyAll;
import javax.annotation.security.PermitAll;
import javax.annotation.security.RolesAllowed;
import javax.ws.rs.GET;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.*;
/**
* Created by barry.
*/
public class SecurityAnnotationEngine {
private static Logger logger = Logger.getLogger();
private static final Set SKIP_METHODS;
static {
SKIP_METHODS = new HashSet<>();
for (Method method : Object.class.getDeclaredMethods()) {
SKIP_METHODS.add(new MethodSignature(method));
}
}
private Map securityProviders = new HashMap<>();
private Map>> scannedClassMap = new HashMap<>();
public Map getSecurityProviders() {
return securityProviders;
}
public void setSecurityProviders(Map securityProviders) {
this.securityProviders = securityProviders;
}
@SuppressWarnings("unchecked")
public Class extends T> getSecurityProvider(Class cls) {
Class roleProvider = securityProviders.get(cls);
if (roleProvider == null)
roleProvider = securityProviders.get(cls.getName());
if (roleProvider == null)
roleProvider = cls;
return roleProvider;
}
@SuppressWarnings("unchecked")
public SecurityAnnotationEngine register(Class cls) {
Class roleProvider = getSecurityProvider(cls);
// populate from most specific to least specific. Once a method signature
// acquires a role set, it won't be re-populated.
Map> methodMap = scannedClassMap.computeIfAbsent(cls.getName(), key -> new HashMap<>());
scanRoles(roleProvider, methodMap, RolesAllowed.class);
scanRoles(roleProvider, methodMap, DenyAll.class);
scanRoles(roleProvider, methodMap, PermitAll.class);
if (cls != roleProvider) {
scanRoles(cls, methodMap, RolesAllowed.class);
scanRoles(cls, methodMap, DenyAll.class);
scanRoles(cls, methodMap, PermitAll.class);
}
if (methodMap.isEmpty()) {
logger.warn("The roles map is empty, the service object is not protected: {}", cls.getName());
}
scannedClassMap.put(cls.getName(), methodMap);
return this;
}
public boolean hasAccess(Object instance, Method method, Object[] args, String userRole) {
if (userRole == null) {
return hasAccess(instance, method, args, Collections.emptySet());
}
Set roles = new HashSet<>();
roles.add(userRole);
return hasAccess(instance, method, args, roles);
}
public boolean hasAccess(Object instance, Method method, Object[] args, Set userRoles) {
Set value = lookupRoles(instance, method, args);
if (value == null) {
if (logger.isWarnEnabled())
logger.warn("no security map entry found: class: {} method:{}",
instance.getClass().getName(), createMethodSig(method));
return true;
}
if (userRoles.contains("ReadOnly")) {
// not a GET method but potentially with ReadOnly annotation
if (method.getAnnotation(GET.class) == null) {
return method.getAnnotation(ReadOnly.class) != null;
} else if (method.getAnnotation(GET.class) != null) { // a GET method
if (method.getAnnotation(ReadOnly.class) != null)
return true;
else if (method.getAnnotation(NotReadOnly.class) == null
&& method.getDeclaringClass().getAnnotation(NotReadOnly.class) == null) {
return true;
}
}
}
if (userRoles.contains("PermitAll")) {
return true;
} else if (value.contains(DenyAll.class.getSimpleName())) {
return false;
} else if (value.contains(PermitAll.class.getSimpleName())) {
return true;
} else {
for (String userRole : userRoles) {
if (value.contains(userRole))
return true;
}
return false;
}
}
@SuppressWarnings("squid:S1172")
public Set lookupRoles(Object instance, Method method, Object[] args) {
Class> cls = ClassHelper.getRealClass(instance);
return lookupRoles(cls, method);
}
public Set lookupRoles(Class cls, Method method) {
MethodSignature methodSig = createMethodSig(method);
logger.trace("lookupRole: class: {} method:{}", cls.getName(), methodSig);
Map> methodMap = scannedClassMap.get(cls.getName());
if (methodMap == null)
return Collections.emptySet();
Set roles = methodMap.get(methodSig);
logger.trace("==> cls:{} m:{} roles:{}", cls, methodSig, roles);
if (roles == null)
roles = Collections.emptySet();
return roles;
}
public void logMethodMap() {
if (logger.isTraceEnabled()) {
List clsNameList = new ArrayList<>(scannedClassMap.keySet());
Collections.sort(clsNameList);
for (String clsName : clsNameList) {
logger.trace("registered class: {}", clsName);
Map> methodMap = scannedClassMap.get(clsName);
List methodNameList = new ArrayList<>(methodMap.keySet());
for (MethodSignature signature : methodNameList) {
Set roles = methodMap.get(signature);
logger.trace(" method:{} roles:{}", signature, roles);
}
}
}
}
private void scanRoles(Class> cls, Map> rolesMap, Class extends Annotation> annotationClass) {
if (cls == null || cls == Object.class)
return;
Set classRoles = getRoles(cls, annotationClass);
for (Method m : cls.getDeclaredMethods()) {
MethodSignature signature = createMethodSig(m);
if (SKIP_METHODS.contains(signature)
|| rolesMap.get(signature) != null) { // already populated
continue;
}
Set methodRoles = getRoles(m, annotationClass);
Set resultRoles = !methodRoles.isEmpty() ? methodRoles : classRoles;
if (!resultRoles.isEmpty()) {
rolesMap.put(signature, resultRoles);
}
}
scanRoles(cls.getSuperclass(), rolesMap, annotationClass);
}
@SuppressWarnings({"squid:S134", "squid:S3776", "squid:S3878"})
private Set getRoles(AnnotatedElement element, Class extends Annotation> annotationClass) {
if (annotationClass.equals(RolesAllowed.class)) {
RolesAllowed rolesAllowed = element.getAnnotation(RolesAllowed.class);
if (rolesAllowed != null) {
Set set = new HashSet<>();
Collections.addAll(set, rolesAllowed.value());
return set;
} else {
return Collections.emptySet();
}
} else {
Set set = new HashSet<>();
Annotation annotation = element.getAnnotation(annotationClass);
if (annotation != null) {
set.add(annotationClass.getSimpleName());
return set;
} else {
return Collections.emptySet();
}
}
}
private MethodSignature createMethodSig(Method method) {
return new MethodSignature(method);
}
}