Add multitenancy filter with data-owner 99/119599/3
authorEric Santos <eric.santos@yoppworks.com>
Mon, 22 Mar 2021 17:29:48 +0000 (13:29 -0400)
committerEric Santos <eric.santos@yoppworks.com>
Mon, 19 Apr 2021 16:02:22 +0000 (12:02 -0400)
Issue-ID: AAI-3299
Signed-off-by: Santos, Eric <eric.santos@yoppworks.com>
Change-Id: Ia22001af5a1701bf70d8be6dbf8d09273ebd5575

aai-traversal/src/main/java/org/onap/aai/rest/DslConsumer.java
aai-traversal/src/main/java/org/onap/aai/rest/QueryConsumer.java
aai-traversal/src/main/java/org/onap/aai/rest/TraversalConsumer.java

index b9295a9..41a3a3c 100644 (file)
@@ -104,6 +104,8 @@ public class DslConsumer extends TraversalConsumer {
                                                                 @Context UriInfo info,
                                                                 @DefaultValue("-1") @QueryParam("resultIndex") String resultIndex,
                                                                 @DefaultValue("-1") @QueryParam("resultSize") String resultSize) {
+               Set<String> roles = this.getRoles(req.getUserPrincipal());
+
                return runner(TraversalConstants.AAI_TRAVERSAL_DSL_TIMEOUT_ENABLED,
                                TraversalConstants.AAI_TRAVERSAL_DSL_TIMEOUT_APP,
                                TraversalConstants.AAI_TRAVERSAL_DSL_TIMEOUT_LIMIT,
@@ -114,7 +116,7 @@ public class DslConsumer extends TraversalConsumer {
                                        @Override
                                        public Response process() throws Exception {
                                                return (processExecuteQuery(content, req, versionParam, queryFormat, subgraph, validate, headers, info,
-                                                               resultIndex, resultSize));
+                                                               resultIndex, resultSize, roles));
                                        }
                                }
                );
@@ -122,7 +124,7 @@ public class DslConsumer extends TraversalConsumer {
 
        public Response processExecuteQuery(String content, HttpServletRequest req, String versionParam, String queryFormat, String subgraph,
                                                                                String validate, HttpHeaders headers, UriInfo info, String resultIndex,
-                                                                               String resultSize) {
+                                                                               String resultSize, Set<String> roles) {
 
                String sourceOfTruth = headers.getRequestHeaders().getFirst("X-FromAppId");
                String dslOverride = headers.getRequestHeaders().getFirst("X-DslOverride");
@@ -172,7 +174,7 @@ public class DslConsumer extends TraversalConsumer {
                                validateHistoryParams(format, info.getQueryParameters());
                        }
 
-                       GraphTraversalSource traversalSource = getTraversalSource(dbEngine, format, info);
+                       GraphTraversalSource traversalSource = getTraversalSource(dbEngine, format, info, roles);
 
                        GenericQueryProcessor processor = new GenericQueryProcessor.Builder(dbEngine, gremlinServerSingleton)
                                        .queryFrom(dsl, "dsl").queryProcessor(dslQueryProcessor).version(dslApiVersion).processWith(processorType)
index 5297de5..affee3a 100644 (file)
@@ -101,6 +101,8 @@ public class QueryConsumer extends TraversalConsumer {
                                                                 @Context UriInfo info,
                                                                 @DefaultValue("-1") @QueryParam("resultIndex") String resultIndex,
                                                                 @DefaultValue("-1") @QueryParam("resultSize") String resultSize) {
+               Set<String> roles = this.getRoles(req.getUserPrincipal());
+
                return runner(TraversalConstants.AAI_TRAVERSAL_TIMEOUT_ENABLED,
                                TraversalConstants.AAI_TRAVERSAL_TIMEOUT_APP,
                                TraversalConstants.AAI_TRAVERSAL_TIMEOUT_LIMIT,
@@ -110,14 +112,14 @@ public class QueryConsumer extends TraversalConsumer {
                                new AaiCallable<Response>() {
                        @Override
                        public Response process() {
-                               return processExecuteQuery(content, req, versionParam, queryFormat, subgraph, headers, info, resultIndex, resultSize);
+                               return processExecuteQuery(content, req, versionParam, queryFormat, subgraph, headers, info, resultIndex, resultSize, roles);
                        }
                });
        }
 
        public Response processExecuteQuery(String content, HttpServletRequest req, String versionParam, String queryFormat, String subgraph,
                                                                                HttpHeaders headers, UriInfo info, String resultIndex,
-                                                                               String resultSize) {
+                                                                               String resultSize, Set<String> roles) {
 
                String sourceOfTruth = headers.getRequestHeaders().getFirst("X-FromAppId");
                String queryProcessor = headers.getRequestHeaders().getFirst("QueryProcessor");
@@ -193,7 +195,7 @@ public class QueryConsumer extends TraversalConsumer {
                        if(isHistory(format)){
                                validateHistoryParams(format, info.getQueryParameters());
                        }
-                       GraphTraversalSource traversalSource = getTraversalSource(dbEngine, format, info);
+                       GraphTraversalSource traversalSource = getTraversalSource(dbEngine, format, info, roles);
                        QueryStyle queryStyle = getQueryStyle(format, traversalUriHttpEntry);
 
                        if (!startURIs.isEmpty()) {
index 059d2c8..a8f88ec 100644 (file)
  */
 package org.onap.aai.rest;
 
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.tinkerpop.gremlin.process.traversal.P;
 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.SubgraphStrategy;
+import org.keycloak.adapters.springsecurity.account.SimpleKeycloakAccount;
+import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
 import org.onap.aai.config.SpringContextAware;
 import org.onap.aai.db.props.AAIProperties;
 import org.onap.aai.exceptions.AAIException;
+import org.onap.aai.introspection.sideeffect.OwnerCheck;
 import org.onap.aai.rest.db.HttpEntry;
 import org.onap.aai.restcore.RESTAPI;
 import org.onap.aai.serialization.engines.QueryStyle;
@@ -34,12 +39,17 @@ import org.onap.aai.serialization.queryformats.Format;
 
 import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.UriInfo;
+import java.security.Principal;
+import java.util.Collections;
+import java.util.Set;
 import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
 
 public abstract class TraversalConsumer extends RESTAPI {
 
     private static final String HISTORICAL_FORMAT = "state,lifecycle";
     private final boolean historyEnabled;
+    private final boolean multiTenancyEnabled;
     private final int historyTruncateWindow;
     private final long currentTime = System.currentTimeMillis();
     private Long startTime = null;
@@ -51,6 +61,8 @@ public abstract class TraversalConsumer extends RESTAPI {
                 SpringContextAware.getApplicationContext().getEnvironment().getProperty("history.truncate.window.days", "365"));
         this.historyEnabled = Boolean.parseBoolean(
                 SpringContextAware.getApplicationContext().getEnvironment().getProperty("history.enabled", "false"));
+        this.multiTenancyEnabled = Boolean.parseBoolean(
+                SpringContextAware.getApplicationContext().getEnvironment().getProperty("multi.tenancy.enabled", "false"));
     }
 
     public boolean isHistory(Format queryFormat) {
@@ -137,15 +149,46 @@ public abstract class TraversalConsumer extends RESTAPI {
                 ).create();
     }
 
+    private SubgraphStrategy getDataOwnerSubgraphStrategy(Set<String> roles) {
+        return SubgraphStrategy.build()
+                .vertices(
+                        __.or(__.has("data-owner", P.within(roles)), __.hasNot("data-owner"))
+                ).create();
+    }
 
+    protected GraphTraversalSource getTraversalSource(TransactionalGraphEngine dbEngine, Format format, UriInfo info, Set<String> roles) throws AAIException {
+        GraphTraversalSource traversalSource;
 
-    protected GraphTraversalSource getTraversalSource(TransactionalGraphEngine dbEngine, Format format, UriInfo info) throws AAIException {
         if (isHistory(format)) {
             long localStartTime = this.getStartTime(format, info.getQueryParameters());
             long localEndTime = this.getEndTime(info.getQueryParameters());
-            return dbEngine.asAdmin().getTraversalSource().withStrategies(getSubgraphStrategy(localStartTime, localEndTime, format));
+            traversalSource = dbEngine.asAdmin().getTraversalSource().withStrategies(getSubgraphStrategy(localStartTime, localEndTime, format));
+        } else {
+            traversalSource = dbEngine.asAdmin().getTraversalSource();
+
+            if (multiTenancyEnabled) {
+                return traversalSource.withStrategies(this.getDataOwnerSubgraphStrategy(roles));
+            }
         }
-        return dbEngine.asAdmin().getTraversalSource();
+
+        return traversalSource;
+    }
+
+    protected Set<String> getRoles(Principal userPrincipal) {
+        KeycloakAuthenticationToken token = (KeycloakAuthenticationToken) userPrincipal;
+        if (ObjectUtils.isEmpty(token)) {
+            return Collections.EMPTY_SET;
+        }
+
+        SimpleKeycloakAccount account = (SimpleKeycloakAccount) token.getDetails();
+        if (ObjectUtils.isEmpty(account)) {
+            return Collections.EMPTY_SET;
+        }
+
+        return account.getRoles()
+                .stream()
+                .map(role -> StringUtils.removeEnd(role, OwnerCheck.READ_ONLY_SUFFIX))
+                .collect(Collectors.toSet());
     }
 
     protected void validateHistoryParams(Format format, MultivaluedMap<String, String> params) throws AAIException {