diff --git a/CHANGELOG.md b/CHANGELOG.md index 03c3015..5a7a06f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm # Changelog for Common Smartgears +## [v3.1.2-SNAPSHOT] - 2022-01-19 + +- enabled policy check on smartgears +- container configuration for test added + ## [v3.1.1] - 2021-09-29 - minimal privilege granted also on empty resource_access in JWT token diff --git a/pom.xml b/pom.xml index 481e439..895cdd9 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ org.gcube.core common-smartgears - 3.1.1 + 3.1.2-SNAPSHOT SmartGears diff --git a/src/main/java/org/gcube/smartgears/Bootstrap.java b/src/main/java/org/gcube/smartgears/Bootstrap.java index 1428436..088f496 100644 --- a/src/main/java/org/gcube/smartgears/Bootstrap.java +++ b/src/main/java/org/gcube/smartgears/Bootstrap.java @@ -90,7 +90,7 @@ public class Bootstrap implements ServletContainerInitializer { * using gcube facilities annotation based * ( i.e org.gcube.common.validator.annotations) */ - context.configuration().validate(); + //context.configuration().validate(); } catch (RuntimeException e) { diff --git a/src/main/java/org/gcube/smartgears/handlers/OfflineProfilePublisher.java b/src/main/java/org/gcube/smartgears/handlers/OfflineProfilePublisher.java new file mode 100644 index 0000000..3622c89 --- /dev/null +++ b/src/main/java/org/gcube/smartgears/handlers/OfflineProfilePublisher.java @@ -0,0 +1,24 @@ +package org.gcube.smartgears.handlers; + +import java.util.Collection; + +public class OfflineProfilePublisher implements ProfilePublisher { + + @Override + public void addTo(Collection tokens) { + } + + @Override + public void addToAll() { + } + + @Override + public void update() { + } + + @Override + public void removeFrom(Collection tokens) { + } + + +} diff --git a/src/main/java/org/gcube/smartgears/handlers/ProfilePublisher.java b/src/main/java/org/gcube/smartgears/handlers/ProfilePublisher.java new file mode 100644 index 0000000..6c3e374 --- /dev/null +++ b/src/main/java/org/gcube/smartgears/handlers/ProfilePublisher.java @@ -0,0 +1,23 @@ +package org.gcube.smartgears.handlers; + +import java.util.Collection; + +public interface ProfilePublisher { + + /** + * Adds for the first time the current resource profile of the application in one or more scopes. + * @param scopes the scopes + */ + void addTo(Collection tokens); + + void addToAll(); + + void update(); + + /** + * Removes the application from one or more scopes. + * @param scopes the scopes + */ + void removeFrom(Collection tokens); + +} \ No newline at end of file diff --git a/src/main/java/org/gcube/smartgears/handlers/application/lifecycle/ProfileManager.java b/src/main/java/org/gcube/smartgears/handlers/application/lifecycle/ProfileManager.java index a897070..5859cb8 100644 --- a/src/main/java/org/gcube/smartgears/handlers/application/lifecycle/ProfileManager.java +++ b/src/main/java/org/gcube/smartgears/handlers/application/lifecycle/ProfileManager.java @@ -22,8 +22,11 @@ import org.gcube.common.events.Observes; import org.gcube.common.events.Observes.Kind; import org.gcube.common.resources.gcore.GCoreEndpoint; import org.gcube.smartgears.Constants; +import org.gcube.smartgears.configuration.Mode; import org.gcube.smartgears.context.Property; import org.gcube.smartgears.context.application.ApplicationContext; +import org.gcube.smartgears.handlers.OfflineProfilePublisher; +import org.gcube.smartgears.handlers.ProfilePublisher; import org.gcube.smartgears.handlers.application.ApplicationLifecycleEvent; import org.gcube.smartgears.handlers.application.ApplicationLifecycleHandler; import org.gcube.smartgears.lifecycle.application.ApplicationLifecycle; @@ -50,7 +53,7 @@ import org.slf4j.LoggerFactory; * * @author Fabio Simeoni * @see ProfileBuilder - * @see ProfilePublisher + * @see ProfilePublisherImpl */ @XmlRootElement(name = profile_management) public class ProfileManager extends ApplicationLifecycleHandler { @@ -90,8 +93,11 @@ public class ProfileManager extends ApplicationLifecycleHandler { share(profile); - publisher = new ProfilePublisher(context); - + publisher = context.container().configuration().mode()!=Mode.offline? + new ProfilePublisherImpl(context): + new OfflineProfilePublisher(); + + registerObservers(); } diff --git a/src/main/java/org/gcube/smartgears/handlers/application/lifecycle/ProfilePublisher.java b/src/main/java/org/gcube/smartgears/handlers/application/lifecycle/ProfilePublisherImpl.java similarity index 94% rename from src/main/java/org/gcube/smartgears/handlers/application/lifecycle/ProfilePublisher.java rename to src/main/java/org/gcube/smartgears/handlers/application/lifecycle/ProfilePublisherImpl.java index 24521ea..54f9930 100644 --- a/src/main/java/org/gcube/smartgears/handlers/application/lifecycle/ProfilePublisher.java +++ b/src/main/java/org/gcube/smartgears/handlers/application/lifecycle/ProfilePublisherImpl.java @@ -14,6 +14,7 @@ import org.gcube.common.resources.gcore.GCoreEndpoint; import org.gcube.informationsystem.publisher.ScopedPublisher; import org.gcube.smartgears.configuration.Mode; import org.gcube.smartgears.context.application.ApplicationContext; +import org.gcube.smartgears.handlers.ProfilePublisher; import org.gcube.smartgears.provider.ProviderFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -27,9 +28,9 @@ import org.slf4j.LoggerFactory; * @author Fabio Simeoni * */ -public class ProfilePublisher { +public class ProfilePublisherImpl implements ProfilePublisher { - private static final Logger log = LoggerFactory.getLogger(ProfilePublisher.class); + private static final Logger log = LoggerFactory.getLogger(ProfilePublisherImpl.class); //the underlying IS publisher private final ScopedPublisher publisher; @@ -42,7 +43,7 @@ public class ProfilePublisher { * Creates an instance for a given application. * @param context the context of the application */ - public ProfilePublisher(ApplicationContext context) { + public ProfilePublisherImpl(ApplicationContext context) { this.context = context; this.publisher=ProviderFactory.provider().publisherFor(context); this.authProxy = ProviderFactory.provider().authorizationProxy(); @@ -52,6 +53,7 @@ public class ProfilePublisher { * Adds for the first time the current resource profile of the application in one or more scopes. * @param scopes the scopes */ + @Override public void addTo(Collection tokens) { notEmpty("tokens",tokens); @@ -88,7 +90,7 @@ public class ProfilePublisher { try{//This classloader set is needed for the jaxb context if (previousToken==null) SecurityTokenProvider.instance.set((String)tokens.toArray()[0]); - if (context.container().configuration().mode()!=Mode.root) Thread.currentThread().setContextClassLoader(ProfilePublisher.class.getClassLoader()); + if (context.container().configuration().mode()!=Mode.root) Thread.currentThread().setContextClassLoader(ProfilePublisherImpl.class.getClassLoader()); profile = publisher.create(profile, resolveScopesFromTokens(tokens)); } catch (Exception e) { @@ -102,11 +104,13 @@ public class ProfilePublisher { log.debug("shared profile with scopes {}", profile.scopes().asCollection()); } + @Override public void addToAll() { this.addTo(context.configuration().startTokens()); } + @Override public void update() { @@ -140,7 +144,7 @@ public class ProfilePublisher { SecurityTokenProvider.instance.set((String)context.configuration().startTokens().toArray()[0]); if (context.container().configuration().mode()!=Mode.root) - Thread.currentThread().setContextClassLoader(ProfilePublisher.class.getClassLoader()); + Thread.currentThread().setContextClassLoader(ProfilePublisherImpl.class.getClassLoader()); profile = publisher.update(profile); } catch (Exception e) { @@ -159,6 +163,7 @@ public class ProfilePublisher { * Removes the application from one or more scopes. * @param scopes the scopes */ + @Override public void removeFrom(Collection tokens) { GCoreEndpoint profile = context.profile(GCoreEndpoint.class); @@ -192,7 +197,7 @@ public class ProfilePublisher { if (previousToken==null) SecurityTokenProvider.instance.set((String)tokens.toArray()[0]); if (context.container().configuration().mode()!=Mode.root) - Thread.currentThread().setContextClassLoader(ProfilePublisher.class.getClassLoader()); + Thread.currentThread().setContextClassLoader(ProfilePublisherImpl.class.getClassLoader()); profile = publisher.remove(profile, resolveScopesFromTokens(tokens)); } catch (Exception e) { diff --git a/src/main/java/org/gcube/smartgears/handlers/application/request/RequestAccounting.java b/src/main/java/org/gcube/smartgears/handlers/application/request/RequestAccounting.java index c7b3e9c..61f7b33 100644 --- a/src/main/java/org/gcube/smartgears/handlers/application/request/RequestAccounting.java +++ b/src/main/java/org/gcube/smartgears/handlers/application/request/RequestAccounting.java @@ -12,6 +12,7 @@ import org.gcube.common.authorization.library.provider.AuthorizationProvider; import org.gcube.common.authorization.library.provider.SecurityTokenProvider; import org.gcube.common.scope.api.ScopeProvider; import org.gcube.smartgears.Constants; +import org.gcube.smartgears.configuration.Mode; import org.gcube.smartgears.context.application.ApplicationContext; import org.gcube.smartgears.handlers.application.RequestEvent; import org.gcube.smartgears.handlers.application.RequestHandler; @@ -72,8 +73,9 @@ public class RequestAccounting extends RequestHandler { callerIp=e.request().getRemoteHost(); boolean success = e.response().getStatus()<400; - - generateAccounting(caller,callerQualifier,callerIp==null?"UNKNOWN":callerIp , success, context); + + if (context.container().configuration().mode()!=Mode.offline) + generateAccounting(caller,callerQualifier,callerIp==null?"UNKNOWN":callerIp , success, context); log.info("REQUEST SERVED ON {}:{}({}) CALLED FROM {}@{} IN SCOPE {} {}(CODE {}) IN {} millis", context.configuration().name(),context.configuration().serviceClass(), InnerMethodName.instance.get(), diff --git a/src/main/java/org/gcube/smartgears/handlers/application/request/RequestValidator.java b/src/main/java/org/gcube/smartgears/handlers/application/request/RequestValidator.java index 4466967..423eb54 100644 --- a/src/main/java/org/gcube/smartgears/handlers/application/request/RequestValidator.java +++ b/src/main/java/org/gcube/smartgears/handlers/application/request/RequestValidator.java @@ -1,21 +1,31 @@ package org.gcube.smartgears.handlers.application.request; +import static org.gcube.common.authorization.client.Constants.authorizationService; import static org.gcube.smartgears.handlers.application.request.RequestError.application_failed_error; import static org.gcube.smartgears.handlers.application.request.RequestError.application_unavailable_error; import static org.gcube.smartgears.handlers.application.request.RequestError.invalid_request_error; +import java.util.List; + import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlRootElement; +import org.gcube.common.authorization.library.PolicyUtils; +import org.gcube.common.authorization.library.policies.Policy; +import org.gcube.common.authorization.library.provider.AuthorizationProvider; import org.gcube.common.authorization.library.provider.SecurityTokenProvider; +import org.gcube.common.authorization.library.provider.ServiceIdentifier; +import org.gcube.common.authorization.library.utils.Caller; import org.gcube.common.scope.api.ScopeProvider; import org.gcube.common.scope.impl.ScopeBean; import org.gcube.common.scope.impl.ScopeBean.Type; import org.gcube.smartgears.Constants; +import org.gcube.smartgears.configuration.Mode; import org.gcube.smartgears.configuration.container.ContainerConfiguration; import org.gcube.smartgears.context.application.ApplicationContext; import org.gcube.smartgears.handlers.application.RequestEvent; import org.gcube.smartgears.handlers.application.RequestHandler; +import org.gcube.smartgears.utils.Utils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -25,7 +35,7 @@ public class RequestValidator extends RequestHandler { @XmlAttribute(required=false, name="oauth") @Deprecated boolean oauthCompatibility = false; - + private static Logger log = LoggerFactory.getLogger(RequestValidator.class); private ApplicationContext context; @@ -34,7 +44,7 @@ public class RequestValidator extends RequestHandler { public String getName() { return Constants.request_validation; } - + @Override public void handleRequest(RequestEvent call) { @@ -43,16 +53,16 @@ public class RequestValidator extends RequestHandler { context = call.context(); validateAgainstLifecycle(call); - + rejectUnauthorizedCalls(call); - - validateScopeCall(); - - /*if (SecurityTokenProvider.instance.get()!=null) - validatePolicy(SecurityTokenProvider.instance.get(), call);*/ + + if (context.container().configuration().mode()!=Mode.offline) { + validateScopeCall(); + validatePolicy(ScopeProvider.instance.get(), call); + } } - + private void validateAgainstLifecycle(RequestEvent call) { switch(context.lifecycle().state()) { @@ -71,16 +81,16 @@ public class RequestValidator extends RequestHandler { } private void validateScopeCall() { - + String scope = ScopeProvider.instance.get(); - + if (scope == null) { log.warn("rejecting unscoped call to {}",context.name()); invalid_request_error.fire("call is unscoped"); } - + ScopeBean bean = new ScopeBean(scope); - + ContainerConfiguration conf = context.container().configuration(); if (!conf.allowedContexts().contains(scope) && !(conf.authorizeChildrenContext() && bean.is(Type.VRE) && conf.allowedContexts().contains(bean.enclosingScope().toString()) ) ) { @@ -90,25 +100,13 @@ public class RequestValidator extends RequestHandler { } private void rejectUnauthorizedCalls(RequestEvent call){ - + String token = SecurityTokenProvider.instance.get(); String scope = ScopeProvider.instance.get(); - + if (token == null && scope==null){ log.warn("rejecting call to {}, authorization required",context.name(),token); - //if (call.context().container().configuration().authenticationEnpoint()==null){ RequestError.request_not_authorized_error.fire(context.name()+": authorization required"); - /*}else { - log.info("authorization enpoint found on configuration, redirecting the call"); - String recallLocation = String.format("http://%s:%d%s", call.context().container().configuration().hostname(), call.context().container().configuration().port(), call.uri()); - //call.response().setHeader("Allowed-Contexts", call.context().container().configuration().allowedContexts().toString()); - try { - call.response().sendRedirect(context.container().configuration().authenticationEnpoint()+"?Recall-Location="+recallLocation); - } catch (IOException e) { - log.error("errror redirecting call",e ); - } - }*/ - } } @@ -117,30 +115,23 @@ public class RequestValidator extends RequestHandler { return getName(); } - /* - private void validatePolicy(String token, RequestEvent call){ - log.info("accessing policy validator with token {} ", token); - AuthorizationEntry authEntry = null; - try{ - authEntry = authorizationService().get(token); - }catch(ObjectNotFound onf){ - log.warn("rejecting call to {}, invalid token {}",context.name(),token); - invalid_request_error.fire(context.name()+" invalid token : "+token); - }catch(Exception e){ - log.error("error contacting authorization service",e); - internal_server_error.fire("error contacting authorization service"); - } + private void validatePolicy(String scope, RequestEvent call){ + log.info("accessing policy validator in scope {} ", scope); ServiceIdentifier serviceIdentifier = Utils.getServiceInfo(call.context()).getServiceIdentifier(); - for (Policy policy: authEntry.getPolicies()) - if (PolicyUtils.isPolicyValidForClient(policy.getServiceAccess(), serviceIdentifier)){ - log.error("rejecting call to {} : {} is not allowed to contact the service ",context.name(),authEntry.getClientInfo().getId()); - invalid_request_error.fire("rejecting call to "+context.name()+": "+authEntry.getClientInfo().getId()+" is not allowed to contact the service"); - } + Caller caller = AuthorizationProvider.instance.get(); + try { + List policies = authorizationService().getPolicies(scope); + for (Policy policy: policies) + if (PolicyUtils.isPolicyValidForClient(policy.getServiceAccess(), serviceIdentifier)){ + log.error("rejecting call to {} : {} is not allowed to contact the service ",context.name(), caller.getClient().getId()); + invalid_request_error.fire("rejecting call to "+context.name()+": "+caller.getClient().getId()+" is not allowed to contact the service"); + } + }catch (Exception e) { + log.warn("error getting policies from context {}", scope, e); + } + } - }*/ - - } diff --git a/src/main/java/org/gcube/smartgears/handlers/container/lifecycle/ProfileManager.java b/src/main/java/org/gcube/smartgears/handlers/container/lifecycle/ProfileManager.java index 2bcfce1..ad9a450 100644 --- a/src/main/java/org/gcube/smartgears/handlers/container/lifecycle/ProfileManager.java +++ b/src/main/java/org/gcube/smartgears/handlers/container/lifecycle/ProfileManager.java @@ -23,8 +23,11 @@ import javax.xml.bind.annotation.XmlRootElement; import org.gcube.common.events.Observes; import org.gcube.common.resources.gcore.HostingNode; +import org.gcube.smartgears.configuration.Mode; import org.gcube.smartgears.context.Property; import org.gcube.smartgears.context.container.ContainerContext; +import org.gcube.smartgears.handlers.OfflineProfilePublisher; +import org.gcube.smartgears.handlers.ProfilePublisher; import org.gcube.smartgears.handlers.container.ContainerHandler; import org.gcube.smartgears.handlers.container.ContainerLifecycleEvent.Start; import org.gcube.smartgears.lifecycle.container.ContainerLifecycle; @@ -83,7 +86,9 @@ public class ProfileManager extends ContainerHandler { share(profile); - publisher = new ProfilePublisher(context); + publisher = context.configuration().mode()!=Mode.offline? + new ProfilePublisherImpl(context): + new OfflineProfilePublisher(); registerObservers(); diff --git a/src/main/java/org/gcube/smartgears/handlers/container/lifecycle/ProfilePublisher.java b/src/main/java/org/gcube/smartgears/handlers/container/lifecycle/ProfilePublisherImpl.java similarity index 96% rename from src/main/java/org/gcube/smartgears/handlers/container/lifecycle/ProfilePublisher.java rename to src/main/java/org/gcube/smartgears/handlers/container/lifecycle/ProfilePublisherImpl.java index 3c7eabf..f40a4a5 100644 --- a/src/main/java/org/gcube/smartgears/handlers/container/lifecycle/ProfilePublisher.java +++ b/src/main/java/org/gcube/smartgears/handlers/container/lifecycle/ProfilePublisherImpl.java @@ -14,6 +14,7 @@ import org.gcube.informationsystem.publisher.ScopedPublisher; import org.gcube.smartgears.configuration.Mode; import org.gcube.smartgears.context.container.ContainerContext; import org.gcube.smartgears.handlers.ProfileEvents; +import org.gcube.smartgears.handlers.ProfilePublisher; import org.gcube.smartgears.provider.ProviderFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -26,9 +27,9 @@ import org.slf4j.LoggerFactory; * @author Fabio Simeoni * */ -public class ProfilePublisher { +public class ProfilePublisherImpl implements ProfilePublisher { - private static final Logger log = LoggerFactory.getLogger(ProfilePublisher.class); + private static final Logger log = LoggerFactory.getLogger(ProfilePublisherImpl.class); //the underlying IS publisher private final ScopedPublisher publisher; @@ -41,7 +42,7 @@ public class ProfilePublisher { * Creates an instance for the container. * @param context the context of the application */ - public ProfilePublisher(ContainerContext context) { + public ProfilePublisherImpl(ContainerContext context) { this.context = context; this.publisher=ProviderFactory.provider().publisherFor(context); this.authProxy = ProviderFactory.provider().authorizationProxy(); @@ -95,7 +96,7 @@ public class ProfilePublisher { if (previousToken==null) SecurityTokenProvider.instance.set((String)tokens.toArray()[0]); if (context.configuration().mode()!=Mode.root) - Thread.currentThread().setContextClassLoader(ProfilePublisher.class.getClassLoader()); + Thread.currentThread().setContextClassLoader(ProfilePublisherImpl.class.getClassLoader()); profile = publisher.create(profile, resolveScopesFromTokens(tokens)); } catch (Exception e) { rethrowUnchecked(e); @@ -157,7 +158,7 @@ public class ProfilePublisher { SecurityTokenProvider.instance.set((String)context.configuration().startTokens().toArray()[0]); if (context.configuration().mode()!=Mode.root) - Thread.currentThread().setContextClassLoader(ProfilePublisher.class.getClassLoader()); + Thread.currentThread().setContextClassLoader(ProfilePublisherImpl.class.getClassLoader()); profile = publisher.update(profile); } catch (Exception e) { rethrowUnchecked(e); @@ -209,7 +210,7 @@ public class ProfilePublisher { if (previousToken==null) SecurityTokenProvider.instance.set((String)tokens.toArray()[0]); if (context.configuration().mode()!=Mode.root) - Thread.currentThread().setContextClassLoader(ProfilePublisher.class.getClassLoader()); + Thread.currentThread().setContextClassLoader(ProfilePublisherImpl.class.getClassLoader()); profile = publisher.remove(profile, resolveScopesFromTokens(tokens)); } catch (Exception e) { rethrowUnchecked(e); diff --git a/src/main/java/org/gcube/smartgears/managers/ApplicationManager.java b/src/main/java/org/gcube/smartgears/managers/ApplicationManager.java index 38fbe42..7f3e735 100644 --- a/src/main/java/org/gcube/smartgears/managers/ApplicationManager.java +++ b/src/main/java/org/gcube/smartgears/managers/ApplicationManager.java @@ -27,6 +27,7 @@ import org.gcube.common.authorization.client.proxy.AuthorizationProxy; import org.gcube.common.authorization.library.provider.SecurityTokenProvider; import org.gcube.common.events.Observes; import org.gcube.smartgears.Constants; +import org.gcube.smartgears.configuration.Mode; import org.gcube.smartgears.configuration.application.ApplicationExtensions; import org.gcube.smartgears.configuration.application.ApplicationHandlers; import org.gcube.smartgears.context.application.ApplicationContext; @@ -74,17 +75,18 @@ public class ApplicationManager { for (Entry servlet : application.getServletRegistrations().entrySet()) log.trace("servlet {} : {} {} ", application.getServletContextName(),servlet.getKey(), servlet.getValue().getMappings()); - - context.configuration().validate(); - + + /* if (context.configuration().secure() && container.configuration().securePort()==null) throw new IllegalStateException( String.format("Application %s cannot be managed because is declared as secure without a secure connector port declared in the container", context.application().getContextPath())); */ - - context.configuration().startTokens(generateTokensForApplication(container).stream().collect(Collectors.toSet())); - + + if (context.container().configuration().mode()!=Mode.offline) { + context.configuration().startTokens(generateTokensForApplication(container).stream().collect(Collectors.toSet())); + context.configuration().validate(); + } saveApplicationState(); // make context available to application in case it is gcube-aware diff --git a/src/main/java/org/gcube/smartgears/managers/ContainerManager.java b/src/main/java/org/gcube/smartgears/managers/ContainerManager.java index 9d3d327..3d7a9d2 100644 --- a/src/main/java/org/gcube/smartgears/managers/ContainerManager.java +++ b/src/main/java/org/gcube/smartgears/managers/ContainerManager.java @@ -21,6 +21,7 @@ import org.gcube.common.authorization.library.provider.ClientInfo; import org.gcube.common.authorization.library.provider.ContainerInfo; import org.gcube.common.events.Observes; import org.gcube.common.events.Observes.Kind; +import org.gcube.smartgears.configuration.Mode; import org.gcube.smartgears.configuration.container.ContainerHandlers; import org.gcube.smartgears.context.application.ApplicationContext; import org.gcube.smartgears.context.container.ContainerContext; @@ -62,12 +63,10 @@ public class ContainerManager { this.context = context; try { + - // TODO Ask if is not enough that is already done in - // Bootstrap.initialiseContainer() function; - context.configuration().validate(); - - validateContainer(context); + if (context.configuration().mode()!=Mode.offline) + validateContainer(context); saveContainerState(); @@ -112,6 +111,7 @@ public class ContainerManager { private void validateContainer(ContainerContext context) { //List tokensToRemove = new ArrayList(); + context.configuration().validate(); Set foundContexts= new HashSet(); try {