diff --git a/.classpath b/.classpath
new file mode 100644
index 0000000..e43402f
--- /dev/null
+++ b/.classpath
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.project b/.project
new file mode 100644
index 0000000..9b31a84
--- /dev/null
+++ b/.project
@@ -0,0 +1,23 @@
+
+
+ authorization-common-library
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.m2e.core.maven2Builder
+
+
+
+
+
+ org.eclipse.jdt.core.javanature
+ org.eclipse.m2e.core.maven2Nature
+
+
diff --git a/pom.xml b/pom.xml
index effb698..f731812 100644
--- a/pom.xml
+++ b/pom.xml
@@ -2,7 +2,7 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
org.gcube.common
- authorization-library
+ common-authorization
1.0.0-SNAPSHOT
authorization service common library
@@ -16,6 +16,19 @@
distro
+
+
+ org.gcube.core
+ common-scope
+ [1.0.0-SNAPSHOT, 2.0.0-SNAPSHOT)
+
+
+ org.slf4j
+ slf4j-api
+ 1.7.5
+
+
+
diff --git a/src/main/java/org/gcube/common/authorization/library/AuthorizationEntry.java b/src/main/java/org/gcube/common/authorization/library/AuthorizationEntry.java
index 4dda367..602074e 100644
--- a/src/main/java/org/gcube/common/authorization/library/AuthorizationEntry.java
+++ b/src/main/java/org/gcube/common/authorization/library/AuthorizationEntry.java
@@ -1,5 +1,7 @@
package org.gcube.common.authorization.library;
+import java.util.List;
+
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
@@ -9,15 +11,15 @@ import javax.xml.bind.annotation.XmlRootElement;
public class AuthorizationEntry {
private String userName;
- private String role;
+ private List roles;
private String scope;
protected AuthorizationEntry(){}
- public AuthorizationEntry(String userName, String role, String scope) {
+ public AuthorizationEntry(String userName, List roles, String scope) {
super();
this.userName = userName;
- this.role = role;
+ this.roles = roles;
this.scope = scope;
}
@@ -25,8 +27,8 @@ public class AuthorizationEntry {
return userName;
}
- public String getRole() {
- return role;
+ public List getRoles() {
+ return roles;
}
public String getScope() {
@@ -35,7 +37,7 @@ public class AuthorizationEntry {
@Override
public String toString() {
- return "AuthorizationEntry [userName=" + userName + ", role=" + role
+ return "AuthorizationEntry [userName=" + userName + ", roles=" + roles
+ ", scope=" + scope + "]";
}
diff --git a/src/main/java/org/gcube/common/authorization/library/AuthorizationInvocationHandler.java b/src/main/java/org/gcube/common/authorization/library/AuthorizationInvocationHandler.java
new file mode 100644
index 0000000..70b01f2
--- /dev/null
+++ b/src/main/java/org/gcube/common/authorization/library/AuthorizationInvocationHandler.java
@@ -0,0 +1,62 @@
+package org.gcube.common.authorization.library;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.List;
+
+import org.gcube.common.authorization.library.annotations.IsAllowedFor;
+import org.gcube.common.authorization.library.annotations.SubjectToQuota;
+import org.gcube.common.authorization.library.provider.AuthorizationProvider;
+import org.gcube.common.authorization.library.provider.Service;
+import org.gcube.common.authorization.library.provider.UserInfo;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class AuthorizationInvocationHandler implements InvocationHandler{
+
+ public static Logger log = LoggerFactory.getLogger(AuthorizationInvocationHandler.class);
+
+ private String handledClass;
+
+ private Object obj;
+
+ ResourceAuthorizationProxy resourceAuthorizationProxy;
+
+ protected AuthorizationInvocationHandler(I obj, String className, ResourceAuthorizationProxy resourceAuthorizationProxy) {
+ handledClass = className;
+ this.obj = obj;
+ this.resourceAuthorizationProxy = resourceAuthorizationProxy;
+ }
+
+ public Object invoke(Object proxy, Method method,
+ Object[] args) throws Throwable {
+ log.trace("calling proxed method "+method.getName()+" on "+handledClass);
+ UserInfo info = AuthorizationProvider.instance.get();
+ if(method.isAnnotationPresent(IsAllowedFor.class)){
+ IsAllowedFor allowed = method.getAnnotation(IsAllowedFor.class);
+ if (allowed.roles().length>0 && isOneElementContainedinRoles(info.getRoles(), allowed.roles())){
+ String message = "blocking method "+method.getName()+" for user "+info.getUserName()+": only roles "+Arrays.toString(allowed.roles()) +" can access";
+ log.warn(message);
+ throw new SecurityException(message);
+ }
+ }
+ if(method.isAnnotationPresent(SubjectToQuota.class)){
+ Service service = new Service(resourceAuthorizationProxy.getServiceClass(), resourceAuthorizationProxy.getServiceName());
+ if (info.getBannedServices().contains(service)){
+ String message = "blocking method "+method.getName()+" for user "+info.getUserName()+": overquota reached";
+ log.warn(message);
+ throw new SecurityException(message);
+ }
+ }
+ return method.invoke(obj, args);
+ }
+
+ private static boolean isOneElementContainedinRoles(List elements, String[] allowedRoles){
+ for (String role: allowedRoles )
+ if (elements.contains(role))
+ return true;
+ return false;
+ }
+
+}
diff --git a/src/main/java/org/gcube/common/authorization/library/AuthorizedTasks.java b/src/main/java/org/gcube/common/authorization/library/AuthorizedTasks.java
new file mode 100644
index 0000000..494aaf2
--- /dev/null
+++ b/src/main/java/org/gcube/common/authorization/library/AuthorizedTasks.java
@@ -0,0 +1,75 @@
+package org.gcube.common.authorization.library;
+
+import java.util.concurrent.Callable;
+
+import org.gcube.common.authorization.library.provider.AuthorizationProvider;
+import org.gcube.common.authorization.library.provider.UserInfo;
+import org.gcube.common.scope.api.ScopeProvider;
+
+
+public class AuthorizedTasks {
+
+ /**
+ * Binds a {@link Callable} task to the current scope and user.
+ * @param task the task
+ * @return an equivalent {@link Callable} task bound to the current scope and user
+ */
+ static public Callable bind(final Callable task) {
+
+ final String callScope = ScopeProvider.instance.get();
+
+ final UserInfo userCall = AuthorizationProvider.instance.get();
+
+ return new Callable() {
+ @Override
+ public V call() throws Exception {
+
+ //bind underlying thread to callscope
+ ScopeProvider.instance.set(callScope);
+ //bind underlying thread to call user
+ AuthorizationProvider.instance.set(userCall);
+ try {
+ return task.call();
+ }
+ finally {
+ ScopeProvider.instance.reset();
+ AuthorizationProvider.instance.reset();
+ }
+
+ }
+ };
+ }
+
+ /**
+ * Binds a {@link Runnable} task to the current scope and user.
+ * @param task the task
+ * @return an equivalent {@link Runnable} task bound to the current scope and user
+ */
+ static public Runnable bind(final Runnable task) {
+
+ final String callScope = ScopeProvider.instance.get();
+
+ final UserInfo userCall = AuthorizationProvider.instance.get();
+
+ return new Runnable() {
+ @Override
+ public void run() {
+
+ //bind underlying thread to callscope
+ ScopeProvider.instance.set(callScope);
+ //bind underlying thread to call user
+ AuthorizationProvider.instance.set(userCall);
+
+ try {
+ task.run();
+ }
+ finally {
+ ScopeProvider.instance.reset();
+ AuthorizationProvider.instance.reset();
+ }
+
+ }
+ };
+ }
+
+}
diff --git a/src/main/java/org/gcube/common/authorization/library/GenericProxyFactory.java b/src/main/java/org/gcube/common/authorization/library/GenericProxyFactory.java
new file mode 100644
index 0000000..2b80274
--- /dev/null
+++ b/src/main/java/org/gcube/common/authorization/library/GenericProxyFactory.java
@@ -0,0 +1,16 @@
+package org.gcube.common.authorization.library;
+
+import java.lang.reflect.Proxy;
+
+public class GenericProxyFactory {
+
+
+ @SuppressWarnings("unchecked")
+ public static T getProxy(Class intf,
+ final I obj, ResourceAuthorizationProxy resourceAuthorizationProxy) {
+ return (T)
+ Proxy.newProxyInstance(obj.getClass().getClassLoader(),
+ new Class[] { intf },
+ new AuthorizationInvocationHandler(obj, intf.getSimpleName(), resourceAuthorizationProxy));
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/org/gcube/common/authorization/library/ResourceAuthorizationProxy.java b/src/main/java/org/gcube/common/authorization/library/ResourceAuthorizationProxy.java
new file mode 100644
index 0000000..07d958e
--- /dev/null
+++ b/src/main/java/org/gcube/common/authorization/library/ResourceAuthorizationProxy.java
@@ -0,0 +1,19 @@
+package org.gcube.common.authorization.library;
+
+public abstract class ResourceAuthorizationProxy {
+
+ private I delegate;
+
+ public ResourceAuthorizationProxy(Class classIntrface, T wrapped){
+ delegate = GenericProxyFactory.getProxy(classIntrface,wrapped, this );
+ }
+
+ public I getDelegate() {
+ return delegate;
+ }
+
+ public abstract String getServiceClass();
+
+ public abstract String getServiceName();
+
+}
diff --git a/src/main/java/org/gcube/common/authorization/library/annotations/IsAllowedFor.java b/src/main/java/org/gcube/common/authorization/library/annotations/IsAllowedFor.java
new file mode 100644
index 0000000..242192c
--- /dev/null
+++ b/src/main/java/org/gcube/common/authorization/library/annotations/IsAllowedFor.java
@@ -0,0 +1,15 @@
+package org.gcube.common.authorization.library.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Inherited
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface IsAllowedFor {
+
+ String[] roles();
+}
diff --git a/src/main/java/org/gcube/common/authorization/library/annotations/SubjectToQuota.java b/src/main/java/org/gcube/common/authorization/library/annotations/SubjectToQuota.java
new file mode 100644
index 0000000..02f12f7
--- /dev/null
+++ b/src/main/java/org/gcube/common/authorization/library/annotations/SubjectToQuota.java
@@ -0,0 +1,14 @@
+package org.gcube.common.authorization.library.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Inherited
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface SubjectToQuota {
+
+}
diff --git a/src/main/java/org/gcube/common/authorization/library/provider/AuthorizationProvider.java b/src/main/java/org/gcube/common/authorization/library/provider/AuthorizationProvider.java
index 35e4eb3..d24e1b4 100644
--- a/src/main/java/org/gcube/common/authorization/library/provider/AuthorizationProvider.java
+++ b/src/main/java/org/gcube/common/authorization/library/provider/AuthorizationProvider.java
@@ -1,12 +1,18 @@
package org.gcube.common.authorization.library.provider;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
public class AuthorizationProvider {
public static AuthorizationProvider instance = new AuthorizationProvider();
+ private static Logger logger = LoggerFactory.getLogger(AuthorizationProvider.class);
+
// Thread local variable containing each thread's ID
- private static final ThreadLocal threadAuth =
- new ThreadLocal() {
+ private static final InheritableThreadLocal threadAuth =
+ new InheritableThreadLocal() {
@Override protected UserInfo initialValue() {
return null;
@@ -17,11 +23,18 @@ public class AuthorizationProvider {
private AuthorizationProvider(){}
public UserInfo get(){
- return threadAuth.get();
+ UserInfo info = threadAuth.get();
+ logger.info("getting "+info+" in thread "+Thread.currentThread().getId() );
+ return info;
}
public void set(UserInfo authorizationToken){
threadAuth.set(authorizationToken);
+ logger.info("setting "+authorizationToken+" in thread "+Thread.currentThread().getId() );
+ }
+
+ public void reset(){
+ threadAuth.remove();
}
}
diff --git a/src/main/java/org/gcube/common/authorization/library/provider/Service.java b/src/main/java/org/gcube/common/authorization/library/provider/Service.java
new file mode 100644
index 0000000..cc2209b
--- /dev/null
+++ b/src/main/java/org/gcube/common/authorization/library/provider/Service.java
@@ -0,0 +1,61 @@
+package org.gcube.common.authorization.library.provider;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlRootElement;
+
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.FIELD)
+public class Service {
+
+ private String serviceClass;
+ private String serviceName;
+
+ public Service(String serviceClass, String serviceName) {
+ super();
+ this.serviceClass = serviceClass;
+ this.serviceName = serviceName;
+ }
+
+ public String getServiceClass() {
+ return serviceClass;
+ }
+ public String getServiceName() {
+ return serviceName;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result
+ + ((serviceClass == null) ? 0 : serviceClass.hashCode());
+ result = prime * result
+ + ((serviceName == null) ? 0 : serviceName.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ Service other = (Service) obj;
+ if (serviceClass == null) {
+ if (other.serviceClass != null)
+ return false;
+ } else if (!serviceClass.equals(other.serviceClass))
+ return false;
+ if (serviceName == null) {
+ if (other.serviceName != null)
+ return false;
+ } else if (!serviceName.equals(other.serviceName))
+ return false;
+ return true;
+ }
+
+
+}
diff --git a/src/main/java/org/gcube/common/authorization/library/provider/UserInfo.java b/src/main/java/org/gcube/common/authorization/library/provider/UserInfo.java
index 8d39216..7c251fc 100644
--- a/src/main/java/org/gcube/common/authorization/library/provider/UserInfo.java
+++ b/src/main/java/org/gcube/common/authorization/library/provider/UserInfo.java
@@ -1,5 +1,7 @@
package org.gcube.common.authorization.library.provider;
+import java.util.List;
+
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
@@ -10,30 +12,34 @@ import javax.xml.bind.annotation.XmlRootElement;
public class UserInfo {
private String userName;
- private String role;
-
+ private List roles;
+ private List bannedServices;
+
protected UserInfo(){}
- public UserInfo(String userName, String role) {
+ public UserInfo(String userName, List roles, List bannedServices) {
super();
this.userName = userName;
- this.role = role;
+ this.roles = roles;
+ this.bannedServices = bannedServices;
}
public String getUserName() {
return userName;
}
- public String getRole() {
- return role;
+ public List getRoles() {
+ return roles;
+ }
+
+ public List getBannedServices() {
+ return bannedServices;
}
-
-
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
- result = prime * result + ((role == null) ? 0 : role.hashCode());
+ result = prime * result + ((roles == null) ? 0 : roles.hashCode());
result = prime * result
+ ((userName == null) ? 0 : userName.hashCode());
return result;
@@ -48,10 +54,10 @@ public class UserInfo {
if (getClass() != obj.getClass())
return false;
UserInfo other = (UserInfo) obj;
- if (role == null) {
- if (other.role != null)
+ if (roles == null) {
+ if (other.roles != null)
return false;
- } else if (!role.equals(other.role))
+ } else if (!roles.equals(other.roles))
return false;
if (userName == null) {
if (other.userName != null)
@@ -63,7 +69,7 @@ public class UserInfo {
@Override
public String toString() {
- return "UserInfo [userName=" + userName + ", role=" + role + "]";
+ return "UserInfo [userName=" + userName + ", roles=" + roles + "]";
}