diff --git a/.classpath b/.classpath index 63c80a6..11def0e 100644 --- a/.classpath +++ b/.classpath @@ -24,16 +24,16 @@ - - - - - + + + + + diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs index 8db228c..4e4a3ad 100644 --- a/.settings/org.eclipse.jdt.core.prefs +++ b/.settings/org.eclipse.jdt.core.prefs @@ -1,9 +1,9 @@ eclipse.preferences.version=1 org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 -org.eclipse.jdt.core.compiler.compliance=1.7 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.compliance=1.8 org.eclipse.jdt.core.compiler.problem.assertIdentifier=error org.eclipse.jdt.core.compiler.problem.enumIdentifier=error org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning org.eclipse.jdt.core.compiler.release=disabled -org.eclipse.jdt.core.compiler.source=1.7 +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/README.md b/README.md index b8eefc8..ec907f1 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Storage Hub Webapp +# Authorization service StorageHub implements the gCube Workspace feature @@ -13,11 +13,11 @@ StorageHub implements the gCube Workspace feature ## Documentation -* Use of this theme is described in the [Wiki](https://wiki.gcube-system.org/gcube/StorageHub_REST_API). +* Use of this theme is described in the [Wiki](https://wiki.gcube-system.org/gcube/Authorization_Framework). ## Change log -See [Releases](https://code-repo.d4science.org/gCubeSystem/storagehub/releases). +See [Releases](https://code-repo.d4science.org/gCubeSystem/authorization-service/releases). ## Authors diff --git a/pom.xml b/pom.xml index ac99d20..46bb61b 100644 --- a/pom.xml +++ b/pom.xml @@ -1,4 +1,5 @@ - 4.0.0 @@ -19,48 +20,60 @@ distro + + + + org.gcube.distribution + gcube-bom + 1.3.1 + pom + import + + + + org.gcube.common common-authorization - [2.0.0-SNAPSHOT, 3.0.0-SNAPSHOT) org.gcube.core common-encryption - [1.0.0-SNAPSHOT, 2.0.0-SNAPSHOT) - + + org.gcube.data.publishing + document-store-lib + + + org.gcube.accounting + accounting-lib + org.gcube.core common-scope-maps - [1.0.0-SNAPSHOT,2.0.0-SNAPSHOT) org.gcube.core common-scope - [1.0.0-SNAPSHOT,2.0.0-SNAPSHOT) org.gcube.core common-encryption - [1.0.0-SNAPSHOT, 2.0.0-SNAPSHOT) javax.ws.rs javax.ws.rs-api - 2.0 org.glassfish.jersey.containers jersey-container-servlet - 2.13 org.glassfish.jersey.containers.glassfish @@ -165,11 +178,6 @@ maven-compiler-plugin - 2.3.2 - - 1.7 - 1.7 - org.apache.maven.plugins diff --git a/src/main/java/org/gcube/common/authorizationservice/ApiKeyManager.java b/src/main/java/org/gcube/common/authorizationservice/ApiKeyManager.java index 405acf4..64b09cb 100644 --- a/src/main/java/org/gcube/common/authorizationservice/ApiKeyManager.java +++ b/src/main/java/org/gcube/common/authorizationservice/ApiKeyManager.java @@ -21,6 +21,7 @@ import lombok.extern.slf4j.Slf4j; import org.gcube.common.authorization.library.AuthorizationEntry; import org.gcube.common.authorization.library.QualifiersList; +import org.gcube.common.authorization.library.provider.CalledMethodProvider; import org.gcube.common.authorizationservice.filters.AuthorizedCallFilter; import org.gcube.common.authorizationservice.util.Constants; import org.gcube.common.authorizationservice.util.TokenPersistence; @@ -43,6 +44,7 @@ public class ApiKeyManager { @PUT @Consumes(MediaType.APPLICATION_XML) public String generateApiKey(@QueryParam(value="qualifier")String qualifier, @Context HttpServletRequest req) { + CalledMethodProvider.instance.set("generate(APIKEY)"); try{ AuthorizationEntry info = (AuthorizationEntry)req.getAttribute(AuthorizedCallFilter.AUTH_ATTRIBUTE); @@ -79,6 +81,7 @@ public class ApiKeyManager { @GET @Produces(MediaType.APPLICATION_XML) public QualifiersList retrieveApiKeys(@Context HttpServletRequest req) { + CalledMethodProvider.instance.set("retrieve(APIKEY)"); log.info("calling getApiKey"); try{ AuthorizationEntry info = (AuthorizationEntry)req.getAttribute(AuthorizedCallFilter.AUTH_ATTRIBUTE); diff --git a/src/main/java/org/gcube/common/authorizationservice/TokenManager.java b/src/main/java/org/gcube/common/authorizationservice/TokenManager.java index d0d497b..57327de 100644 --- a/src/main/java/org/gcube/common/authorizationservice/TokenManager.java +++ b/src/main/java/org/gcube/common/authorizationservice/TokenManager.java @@ -1,12 +1,10 @@ package org.gcube.common.authorizationservice; -import java.util.HashMap; +import java.util.ArrayList; import java.util.List; -import java.util.Map; import java.util.UUID; import javax.inject.Inject; -import javax.print.attribute.HashAttributeSet; import javax.servlet.http.HttpServletRequest; import javax.validation.constraints.NotNull; import javax.ws.rs.Consumes; @@ -22,20 +20,22 @@ import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; -import lombok.extern.slf4j.Slf4j; - import org.gcube.common.authorization.library.AuthorizationEntry; import org.gcube.common.authorization.library.ExternalServiceList; +import org.gcube.common.authorization.library.provider.CalledMethodProvider; import org.gcube.common.authorization.library.provider.ContainerInfo; import org.gcube.common.authorization.library.provider.ExternalServiceInfo; import org.gcube.common.authorization.library.provider.ServiceInfo; import org.gcube.common.authorization.library.provider.UserInfo; +import org.gcube.common.authorization.library.utils.AuthorizationEntryList; import org.gcube.common.authorization.library.utils.ListMapper; +import org.gcube.common.authorization.library.utils.MapAdapter; +import org.gcube.common.authorization.library.utils.MultiServiceTokenRequest; import org.gcube.common.authorizationservice.filters.AuthorizedCallFilter; import org.gcube.common.authorizationservice.util.Constants; import org.gcube.common.authorizationservice.util.TokenPersistence; -import org.gcube.common.scope.api.ScopeProvider; -import org.gcube.common.scope.api.ServiceMap; + +import lombok.extern.slf4j.Slf4j; @Path("token") @Slf4j @@ -56,7 +56,7 @@ public class TokenManager { @Path("{token}") @Produces(MediaType.APPLICATION_XML) public AuthorizationEntry retrieveToken(@NotNull @PathParam("token") String token ) { - + CalledMethodProvider.instance.set("retieve"); log.info("token retreiver called with token {}",token); AuthorizationEntry info = persistence.getAuthorizationEntry(token); @@ -87,6 +87,36 @@ public class TokenManager { } + /** + * + * retrieves the AuthorzationEntry connected to the specified token + * + * @param token + * @return the authorization entry + */ + @GET + @Path("resolve") + @Produces(MediaType.APPLICATION_XML) + public AuthorizationEntryList retrieveTokenBunch(@NotNull @QueryParam("token") List tokens ) { + CalledMethodProvider.instance.set("retrieve"); + log.info("token retreiver called with token {}",tokens); + + List toReturn = new ArrayList(); + for (String token : tokens ) { + AuthorizationEntry info = persistence.getAuthorizationEntry(token); + if (token!=null) toReturn.add(info); + } + log.info("info retrieved {}",toReturn); + + if (toReturn.isEmpty()){ + log.error("no tokens poassed have been found "); + throw new WebApplicationException(Response.status(Response.Status.NOT_FOUND) + .entity("no tokens poassed have been found ").type(MediaType.TEXT_PLAIN).build()); + } + + return new AuthorizationEntryList(toReturn); + + } @@ -100,7 +130,7 @@ public class TokenManager { @GET @Path("resolve/{user}") public String getTokenByUserAndContext(@NotNull @PathParam("user") String user, @QueryParam("context") String context ) { - + CalledMethodProvider.instance.set("retrieve"); log.info("resolving token for user {} in context {}",user, context); if (context==null){ @@ -146,7 +176,7 @@ public class TokenManager { @Consumes(MediaType.APPLICATION_XML) public String generateUserToken(UserInfo clientId, @NotNull @QueryParam("context") String context) { - + CalledMethodProvider.instance.set("generate"); try{ log.info("generator called with user {} in context {} ",clientId, context); @@ -181,7 +211,7 @@ public class TokenManager { @Consumes(MediaType.APPLICATION_XML) public String setRoles(ListMapper roles, @NotNull @PathParam("token") String token) { - + CalledMethodProvider.instance.set("setRoles"); try{ log.info("update roles called"); @@ -210,7 +240,7 @@ public class TokenManager { @Consumes(MediaType.APPLICATION_XML) public void removeUserToken(@NotNull @QueryParam("client_id") String clientId, @NotNull @QueryParam("context") String context) { - + CalledMethodProvider.instance.set("delete"); try{ log.info("generator called with user {} in context {} ",clientId, context); @@ -239,6 +269,7 @@ public class TokenManager { @Consumes(MediaType.APPLICATION_XML) public String generateServiceToken(ServiceInfo serviceInfo, @Context HttpServletRequest req) { + CalledMethodProvider.instance.set("generate"); try{ AuthorizationEntry authInfo = (AuthorizationEntry)req.getAttribute(AuthorizedCallFilter.AUTH_ATTRIBUTE); log.info("generator called with service {} in context {} ",serviceInfo.getId(), authInfo.getContext()); @@ -251,7 +282,46 @@ public class TokenManager { } - + /** + * + * Generates a token for a service if it doesn't exist yet. + * + * @param userName + * @param roles + * @return the generated token or the token related to the user (if it was already created) + */ + @Path("service/bunch") + @PUT + @Consumes(MediaType.APPLICATION_XML) + public ListMapper generateServiceTokenBunch(MultiServiceTokenRequest entity, + @Context HttpServletRequest req) { + CalledMethodProvider.instance.set("generate"); + + try{ + + AuthorizationEntry callerInfo = (AuthorizationEntry)req.getAttribute(AuthorizedCallFilter.AUTH_ATTRIBUTE); + + List tokensToReturn = new ArrayList(); + + for (String token: entity.getContainerTokens()) { + AuthorizationEntry authInfo = this.retrieveToken(token); + + if (!authInfo.getClientInfo().getId().equals(callerInfo.getClientInfo().getId())) + log.warn("a token with a different ContainerInfo of the caller used, skipping it"); + else { + String genToken = generateTokenForServiceInfo(entity.getInfo(), authInfo); + tokensToReturn.add(genToken); + } + + } + + return new ListMapper(tokensToReturn); + }catch(Exception e){ + log.error("error generating tokens ",e); + throw new WebApplicationException(Response.status(Response.Status.BAD_REQUEST) + .entity("Error Generating Token: "+e.getMessage()).type(MediaType.TEXT_PLAIN).build()); + } + } /** * @@ -265,6 +335,7 @@ public class TokenManager { @Consumes(MediaType.APPLICATION_XML) public String generateExternalServiceToken(@PathParam("serviceId") String serviceId, @Context HttpServletRequest req) { try{ + CalledMethodProvider.instance.set("generate"); AuthorizationEntry info = (AuthorizationEntry)req.getAttribute(AuthorizedCallFilter.AUTH_ATTRIBUTE); log.info("generator called for external service {} in context {} ",serviceId, info.getContext()); @@ -296,6 +367,7 @@ public class TokenManager { @GET @Consumes(MediaType.APPLICATION_XML) public ExternalServiceList getExternalServiceCreated(@Context HttpServletRequest req) { + CalledMethodProvider.instance.set("retrieve"); try{ AuthorizationEntry info = (AuthorizationEntry)req.getAttribute(AuthorizedCallFilter.AUTH_ATTRIBUTE); log.info("get External Service called in context {} by {} ",info.getContext(), info.getClientInfo().getId()); @@ -313,6 +385,7 @@ public class TokenManager { @Consumes(MediaType.APPLICATION_XML) public String generateContainerToken(@NotNull ContainerInfo containerInfo, @QueryParam("context") String context, @Context HttpServletRequest req) { + CalledMethodProvider.instance.set("generate"); try{ AuthorizationEntry info = (AuthorizationEntry)req.getAttribute(AuthorizedCallFilter.AUTH_ATTRIBUTE); diff --git a/src/main/java/org/gcube/common/authorizationservice/configuration/AuthorizationConfiguration.java b/src/main/java/org/gcube/common/authorizationservice/configuration/AuthorizationConfiguration.java index a2e75d6..a2cdcaf 100644 --- a/src/main/java/org/gcube/common/authorizationservice/configuration/AuthorizationConfiguration.java +++ b/src/main/java/org/gcube/common/authorizationservice/configuration/AuthorizationConfiguration.java @@ -19,6 +19,9 @@ public class AuthorizationConfiguration { @XmlElement(name="AllowedContainerIp") private List allowedContainerIps = new ArrayList(); + @XmlElement(name="AccountingDir") + private String accountingDir = "/tmp"; + protected AuthorizationConfiguration(){} public List getAuthorizationRules() { @@ -28,6 +31,14 @@ public class AuthorizationConfiguration { protected void setAuthorizationRules(List rules) { this.authorizationRules = rules; } + + public String getAccountingDir() { + return accountingDir; + } + + protected void setAccountingDir(String accountingDir) { + this.accountingDir = accountingDir; + } public List getAllowedContainerIps() { return allowedContainerIps; diff --git a/src/main/java/org/gcube/common/authorizationservice/configuration/ConfigurationBuilder.java b/src/main/java/org/gcube/common/authorizationservice/configuration/ConfigurationBuilder.java index 4be30b2..c552ad4 100644 --- a/src/main/java/org/gcube/common/authorizationservice/configuration/ConfigurationBuilder.java +++ b/src/main/java/org/gcube/common/authorizationservice/configuration/ConfigurationBuilder.java @@ -9,7 +9,9 @@ public class ConfigurationBuilder { List rules = new ArrayList(); List ips = new ArrayList(); - + + String accountingDir = null; + public static ConfigurationBuilder getBuilder(){ return new ConfigurationBuilder(); } @@ -19,6 +21,11 @@ public class ConfigurationBuilder { return this; } + public ConfigurationBuilder accountingDir(String accountingDir){ + this.accountingDir= accountingDir; + return this; + } + public ConfigurationBuilder autoConfirmedIp(String ip){ this.ips.add(ip); return this; @@ -28,6 +35,7 @@ public class ConfigurationBuilder { AuthorizationConfiguration configuration = new AuthorizationConfiguration(); configuration.setAuthorizationRules(rules); configuration.setAllowedContainerIps(ips); + if (accountingDir!=null) configuration.setAccountingDir(accountingDir); return configuration; } diff --git a/src/main/java/org/gcube/common/authorizationservice/filters/AuthorizedCallFilter.java b/src/main/java/org/gcube/common/authorizationservice/filters/AuthorizedCallFilter.java index 2e9d4d7..5d6ddcb 100644 --- a/src/main/java/org/gcube/common/authorizationservice/filters/AuthorizedCallFilter.java +++ b/src/main/java/org/gcube/common/authorizationservice/filters/AuthorizedCallFilter.java @@ -15,14 +15,22 @@ import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import lombok.extern.slf4j.Slf4j; - +import org.gcube.accounting.datamodel.UsageRecord.OperationResult; +import org.gcube.accounting.datamodel.usagerecords.ServiceUsageRecord; +import org.gcube.accounting.persistence.AccountingPersistence; +import org.gcube.accounting.persistence.AccountingPersistenceFactory; import org.gcube.common.authorization.library.AuthorizationEntry; +import org.gcube.common.authorization.library.provider.CalledMethodProvider; import org.gcube.common.authorizationservice.configuration.AllowedEntity; import org.gcube.common.authorizationservice.configuration.AuthorizationConfiguration; import org.gcube.common.authorizationservice.configuration.AuthorizationRule; import org.gcube.common.authorizationservice.configuration.ConfigurationHolder; import org.gcube.common.authorizationservice.util.TokenPersistence; +import org.gcube.common.scope.api.ScopeProvider; +import org.jboss.weld.context.ApplicationContext; +import org.omg.PortableInterceptor.SUCCESSFUL; + +import lombok.extern.slf4j.Slf4j; @WebFilter(urlPatterns={"/*"}, filterName="authorizationFilter") @Slf4j @@ -41,12 +49,14 @@ public class AuthorizedCallFilter implements Filter { } @Override - public void doFilter(ServletRequest request, ServletResponse response, - FilterChain chain) throws IOException, ServletException { + public void doFilter(ServletRequest request, ServletResponse response, + FilterChain chain) throws IOException, ServletException { String token = request.getParameter(TOKEN_HEADER)==null?((HttpServletRequest)request).getHeader(TOKEN_HEADER): request.getParameter(TOKEN_HEADER); - + + long startTime = System.currentTimeMillis(); + AuthorizationEntry info = null; if (token!=null){ info = persistence.getAuthorizationEntry(token); @@ -91,6 +101,8 @@ public class AuthorizedCallFilter implements Filter { } chain.doFilter(request, response); + + generateAccounting("Unknown", "Unknown", callerIp, true, startTime, request.getLocalName()); } private boolean requiresToken(String pathInfo) { @@ -163,6 +175,30 @@ public class AuthorizedCallFilter implements Filter { @Override public void destroy() {} - + + void generateAccounting(String caller, String callerQualifier, String remoteHost, boolean success, long startTime, String host){ + AuthorizationConfiguration conf = ConfigurationHolder.getConfiguration(); + AccountingPersistenceFactory.setFallbackLocation(conf.getAccountingDir()); + AccountingPersistence persistence = AccountingPersistenceFactory.getPersistence(); + ServiceUsageRecord serviceUsageRecord = new ServiceUsageRecord(); + try{ + + serviceUsageRecord.setConsumerId(caller); + serviceUsageRecord.setCallerQualifier(callerQualifier); + serviceUsageRecord.setScope(ScopeProvider.instance.get()); + serviceUsageRecord.setServiceClass("Common"); + serviceUsageRecord.setServiceName("Authorization"); + + serviceUsageRecord.setHost(host); + serviceUsageRecord.setCalledMethod(CalledMethodProvider.instance.get()); + serviceUsageRecord.setCallerHost(remoteHost); + serviceUsageRecord.setOperationResult(success?OperationResult.SUCCESS:OperationResult.FAILED); + serviceUsageRecord.setDuration(System.currentTimeMillis()-startTime); + persistence.account(serviceUsageRecord); + + }catch(Exception ex){ + log.warn("invalid record passed to accounting ",ex); + } + } } diff --git a/src/main/webapp/WEB-INF/AuthorizationConfiguration.xml b/src/main/webapp/WEB-INF/AuthorizationConfiguration.xml index 0e31a43..5ec0512 100644 --- a/src/main/webapp/WEB-INF/AuthorizationConfiguration.xml +++ b/src/main/webapp/WEB-INF/AuthorizationConfiguration.xml @@ -6,4 +6,5 @@ + /tmp diff --git a/src/test/java/org/gcube/common/authz/configuration/Binder.java b/src/test/java/org/gcube/common/authz/configuration/Binder.java index deb6d58..f191ac1 100644 --- a/src/test/java/org/gcube/common/authz/configuration/Binder.java +++ b/src/test/java/org/gcube/common/authz/configuration/Binder.java @@ -33,7 +33,7 @@ public class Binder { AuthorizationRule secondRule = new RuleBuilder().path("/newPath").entity(new AllowedEntity(EntityType.ROLE, "ContextManager")).entity(new AllowedEntity(EntityType.IP, "192.168.0.3")).build(); AuthorizationRule thirdRule = new RuleBuilder().path("/anotherPath").entity(new AllowedEntity(EntityType.USER, "user")).build(); - AuthorizationConfiguration authConf = ConfigurationBuilder.getBuilder().rule(firtRule).rule(secondRule).rule(thirdRule).build(); + AuthorizationConfiguration authConf = ConfigurationBuilder.getBuilder().rule(firtRule).rule(secondRule).rule(thirdRule).accountingDir("mydir").build(); StringWriter sw = new StringWriter(); context.createMarshaller().marshal(authConf, sw); System.out.println(sw); diff --git a/src/test/resources/AuthorizationConfiguration.xml b/src/test/resources/AuthorizationConfiguration.xml index 67752f6..5c94e2a 100644 --- a/src/test/resources/AuthorizationConfiguration.xml +++ b/src/test/resources/AuthorizationConfiguration.xml @@ -6,5 +6,6 @@ + mydir