blob: 2435e2667acee5cdccc59cc290ff7976801c68a8 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with this
* work for additional information regarding copyright ownership. The ASF
* licenses this file to You 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 org.apache.hugegraph.auth;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.Supplier;
import javax.security.sasl.AuthenticationException;
import org.apache.commons.configuration2.Configuration;
import org.apache.hugegraph.HugeGraph;
import org.apache.hugegraph.auth.HugeAuthenticator.RolePerm;
import org.apache.hugegraph.auth.HugeAuthenticator.User;
import org.apache.hugegraph.auth.SchemaDefine.AuthElement;
import org.apache.hugegraph.backend.cache.Cache;
import org.apache.hugegraph.backend.cache.CacheManager;
import org.apache.hugegraph.backend.id.Id;
import org.apache.hugegraph.backend.id.IdGenerator;
import org.apache.hugegraph.backend.query.Query;
import org.apache.hugegraph.backend.store.BackendFeatures;
import org.apache.hugegraph.backend.store.BackendStoreInfo;
import org.apache.hugegraph.backend.store.raft.RaftGroupManager;
import org.apache.hugegraph.config.AuthOptions;
import org.apache.hugegraph.config.HugeConfig;
import org.apache.hugegraph.config.TypedOption;
import org.apache.hugegraph.exception.NotSupportException;
import org.apache.hugegraph.iterator.FilterIterator;
import org.apache.hugegraph.iterator.MapperIterator;
import org.apache.hugegraph.masterelection.RoleElectionStateMachine;
import org.apache.hugegraph.rpc.RpcServiceConfig4Client;
import org.apache.hugegraph.rpc.RpcServiceConfig4Server;
import org.apache.hugegraph.schema.EdgeLabel;
import org.apache.hugegraph.schema.IndexLabel;
import org.apache.hugegraph.schema.PropertyKey;
import org.apache.hugegraph.schema.SchemaElement;
import org.apache.hugegraph.schema.SchemaLabel;
import org.apache.hugegraph.schema.SchemaManager;
import org.apache.hugegraph.schema.VertexLabel;
import org.apache.hugegraph.structure.HugeEdge;
import org.apache.hugegraph.structure.HugeElement;
import org.apache.hugegraph.structure.HugeFeatures;
import org.apache.hugegraph.structure.HugeVertex;
import org.apache.hugegraph.task.HugeTask;
import org.apache.hugegraph.task.TaskManager;
import org.apache.hugegraph.task.TaskScheduler;
import org.apache.hugegraph.task.TaskStatus;
import org.apache.hugegraph.traversal.optimize.HugeScriptTraversal;
import org.apache.hugegraph.type.HugeType;
import org.apache.hugegraph.type.Nameable;
import org.apache.hugegraph.type.define.GraphMode;
import org.apache.hugegraph.type.define.GraphReadMode;
import org.apache.hugegraph.type.define.NodeRole;
import org.apache.hugegraph.util.E;
import org.apache.hugegraph.util.Log;
import org.apache.hugegraph.util.RateLimiter;
import org.apache.tinkerpop.gremlin.process.computer.GraphComputer;
import org.apache.tinkerpop.gremlin.process.traversal.Bytecode;
import org.apache.tinkerpop.gremlin.process.traversal.Bytecode.Instruction;
import org.apache.tinkerpop.gremlin.process.traversal.Script;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategies;
import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
import org.apache.tinkerpop.gremlin.process.traversal.translator.GroovyTranslator;
import org.apache.tinkerpop.gremlin.structure.Edge;
import org.apache.tinkerpop.gremlin.structure.Element;
import org.apache.tinkerpop.gremlin.structure.Graph;
import org.apache.tinkerpop.gremlin.structure.Property;
import org.apache.tinkerpop.gremlin.structure.Transaction;
import org.apache.tinkerpop.gremlin.structure.Vertex;
import org.apache.tinkerpop.gremlin.structure.VertexProperty;
import org.apache.tinkerpop.gremlin.structure.io.Io;
import org.slf4j.Logger;
import com.alipay.remoting.rpc.RpcServer;
import jakarta.ws.rs.ForbiddenException;
import jakarta.ws.rs.NotAuthorizedException;
public final class HugeGraphAuthProxy implements HugeGraph {
static {
HugeGraph.registerTraversalStrategies(HugeGraphAuthProxy.class);
}
private static final Logger LOG = Log.logger(HugeGraphAuthProxy.class);
private final Cache<Id, UserWithRole> usersRoleCache;
private final Cache<Id, RateLimiter> auditLimiters;
private final double auditLogMaxRate;
private final HugeGraph hugegraph;
private final TaskSchedulerProxy taskScheduler;
private final AuthManagerProxy authManager;
public HugeGraphAuthProxy(HugeGraph hugegraph) {
LOG.info("Wrap graph '{}' with HugeGraphAuthProxy", hugegraph.name());
HugeConfig config = (HugeConfig) hugegraph.configuration();
long expired = config.get(AuthOptions.AUTH_CACHE_EXPIRE);
long capacity = config.get(AuthOptions.AUTH_CACHE_CAPACITY);
this.hugegraph = hugegraph;
this.taskScheduler = new TaskSchedulerProxy(hugegraph.taskScheduler());
this.authManager = new AuthManagerProxy(hugegraph.authManager());
this.auditLimiters = this.cache("audit-log-limiter", capacity, -1L);
this.usersRoleCache = this.cache("users-role", capacity, expired);
this.hugegraph.proxy(this);
// TODO: Consider better way to get, use auth client's config now
this.auditLogMaxRate = config.get(AuthOptions.AUTH_AUDIT_LOG_RATE);
LOG.info("Audit log rate limit is {}/s", this.auditLogMaxRate);
}
@Override
public HugeGraph hugegraph() {
this.verifyAdminPermission();
return this.hugegraph;
}
@Override
public <C extends GraphComputer> C compute(Class<C> clazz)
throws IllegalArgumentException {
this.verifyAnyPermission();
return this.hugegraph.compute(clazz);
}
@Override
public GraphComputer compute() throws IllegalArgumentException {
this.verifyAnyPermission();
return this.hugegraph.compute();
}
@Override
public GraphTraversalSource traversal() {
// Just return proxy
return new GraphTraversalSourceProxy(this);
}
@SuppressWarnings({ "rawtypes", "deprecation" })
@Override
public <I extends Io> I io(final Io.Builder<I> builder) {
this.verifyAnyPermission();
return this.hugegraph.io(builder);
}
@Override
public SchemaManager schema() {
SchemaManager schema = this.hugegraph.schema();
schema.proxy(this);
return schema;
}
@Override
public Id getNextId(HugeType type) {
if (type == HugeType.TASK) {
verifyPermission(HugePermission.WRITE, ResourceType.TASK);
} else {
this.verifyAdminPermission();
}
return this.hugegraph.getNextId(type);
}
@Override
public Id addPropertyKey(PropertyKey key) {
verifySchemaPermission(HugePermission.WRITE, key);
return this.hugegraph.addPropertyKey(key);
}
@Override
public void updatePropertyKey(PropertyKey key) {
verifySchemaPermission(HugePermission.WRITE, key);
this.hugegraph.updatePropertyKey(key);
}
@Override
public Id removePropertyKey(Id key) {
PropertyKey pkey = this.hugegraph.propertyKey(key);
verifySchemaPermission(HugePermission.DELETE, pkey);
return this.hugegraph.removePropertyKey(key);
}
@Override
public Id clearPropertyKey(PropertyKey propertyKey) {
verifySchemaPermission(HugePermission.DELETE, propertyKey);
return this.hugegraph.clearPropertyKey(propertyKey);
}
@Override
public Collection<PropertyKey> propertyKeys() {
Collection<PropertyKey> pkeys = this.hugegraph.propertyKeys();
return verifySchemaPermission(HugePermission.READ, pkeys);
}
@Override
public PropertyKey propertyKey(String key) {
return verifySchemaPermission(HugePermission.READ, () -> {
return this.hugegraph.propertyKey(key);
});
}
@Override
public PropertyKey propertyKey(Id key) {
return verifySchemaPermission(HugePermission.READ, () -> {
return this.hugegraph.propertyKey(key);
});
}
@Override
public boolean existsPropertyKey(String key) {
verifyNameExistsPermission(ResourceType.PROPERTY_KEY, key);
return this.hugegraph.existsPropertyKey(key);
}
@Override
public void addVertexLabel(VertexLabel label) {
verifySchemaPermission(HugePermission.WRITE, label);
this.hugegraph.addVertexLabel(label);
}
@Override
public void updateVertexLabel(VertexLabel label) {
verifySchemaPermission(HugePermission.WRITE, label);
this.hugegraph.updateVertexLabel(label);
}
@Override
public Id removeVertexLabel(Id id) {
VertexLabel label = this.hugegraph.vertexLabel(id);
verifySchemaPermission(HugePermission.DELETE, label);
return this.hugegraph.removeVertexLabel(id);
}
@Override
public Collection<VertexLabel> vertexLabels() {
Collection<VertexLabel> labels = this.hugegraph.vertexLabels();
return verifySchemaPermission(HugePermission.READ, labels);
}
@Override
public VertexLabel vertexLabel(String label) {
return verifySchemaPermission(HugePermission.READ, () -> {
return this.hugegraph.vertexLabel(label);
});
}
@Override
public VertexLabel vertexLabel(Id label) {
return verifySchemaPermission(HugePermission.READ, () -> {
return this.hugegraph.vertexLabel(label);
});
}
@Override
public VertexLabel vertexLabelOrNone(Id label) {
return verifySchemaPermission(HugePermission.READ, () -> {
return this.hugegraph.vertexLabelOrNone(label);
});
}
@Override
public boolean existsVertexLabel(String label) {
verifyNameExistsPermission(ResourceType.VERTEX_LABEL, label);
return this.hugegraph.existsVertexLabel(label);
}
@Override
public boolean existsLinkLabel(Id vertexLabel) {
verifyNameExistsPermission(ResourceType.VERTEX_LABEL,
this.vertexLabel(vertexLabel).name());
return this.hugegraph.existsLinkLabel(vertexLabel);
}
@Override
public void addEdgeLabel(EdgeLabel label) {
verifySchemaPermission(HugePermission.WRITE, label);
this.hugegraph.addEdgeLabel(label);
}
@Override
public void updateEdgeLabel(EdgeLabel label) {
verifySchemaPermission(HugePermission.WRITE, label);
this.hugegraph.updateEdgeLabel(label);
}
@Override
public Id removeEdgeLabel(Id id) {
EdgeLabel label = this.hugegraph.edgeLabel(id);
verifySchemaPermission(HugePermission.DELETE, label);
return this.hugegraph.removeEdgeLabel(id);
}
@Override
public Collection<EdgeLabel> edgeLabels() {
Collection<EdgeLabel> labels = this.hugegraph.edgeLabels();
return verifySchemaPermission(HugePermission.READ, labels);
}
@Override
public EdgeLabel edgeLabel(String label) {
return verifySchemaPermission(HugePermission.READ, () -> {
return this.hugegraph.edgeLabel(label);
});
}
@Override
public EdgeLabel edgeLabel(Id label) {
return verifySchemaPermission(HugePermission.READ, () -> {
return this.hugegraph.edgeLabel(label);
});
}
@Override
public EdgeLabel edgeLabelOrNone(Id label) {
return verifySchemaPermission(HugePermission.READ, () -> {
return this.hugegraph.edgeLabelOrNone(label);
});
}
@Override
public boolean existsEdgeLabel(String label) {
verifyNameExistsPermission(ResourceType.EDGE_LABEL, label);
return this.hugegraph.existsEdgeLabel(label);
}
@Override
public void addIndexLabel(SchemaLabel schemaLabel, IndexLabel indexLabel) {
verifySchemaPermission(HugePermission.WRITE, indexLabel);
this.hugegraph.addIndexLabel(schemaLabel, indexLabel);
}
@Override
public void updateIndexLabel(IndexLabel label) {
verifySchemaPermission(HugePermission.WRITE, label);
this.hugegraph.updateIndexLabel(label);
}
@Override
public Id removeIndexLabel(Id id) {
IndexLabel label = this.hugegraph.indexLabel(id);
verifySchemaPermission(HugePermission.DELETE, label);
return this.hugegraph.removeIndexLabel(id);
}
@Override
public Id rebuildIndex(SchemaElement schema) {
if (schema.type() == HugeType.INDEX_LABEL) {
verifySchemaPermission(HugePermission.WRITE, schema);
} else {
SchemaLabel label = (SchemaLabel) schema;
for (Id il : label.indexLabels()) {
IndexLabel indexLabel = this.hugegraph.indexLabel(il);
verifySchemaPermission(HugePermission.WRITE, indexLabel);
}
}
return this.hugegraph.rebuildIndex(schema);
}
@Override
public Collection<IndexLabel> indexLabels() {
Collection<IndexLabel> labels = this.hugegraph.indexLabels();
return verifySchemaPermission(HugePermission.READ, labels);
}
@Override
public IndexLabel indexLabel(String label) {
return verifySchemaPermission(HugePermission.READ, () -> {
return this.hugegraph.indexLabel(label);
});
}
@Override
public IndexLabel indexLabel(Id label) {
return verifySchemaPermission(HugePermission.READ, () -> {
return this.hugegraph.indexLabel(label);
});
}
@Override
public boolean existsIndexLabel(String label) {
verifyNameExistsPermission(ResourceType.INDEX_LABEL, label);
return this.hugegraph.existsIndexLabel(label);
}
@Override
public Vertex addVertex(Object... keyValues) {
return verifyElemPermission(HugePermission.WRITE, () -> {
return (HugeVertex) this.hugegraph.addVertex(keyValues);
});
}
@Override
public void removeVertex(Vertex vertex) {
verifyElemPermission(HugePermission.DELETE, vertex);
this.hugegraph.removeVertex(vertex);
}
@Override
public void removeVertex(String label, Object id) {
this.removeVertex(this.vertex(id));
}
@Override
public <V> void addVertexProperty(VertexProperty<V> property) {
verifyElemPermission(HugePermission.WRITE, property.element());
this.hugegraph.addVertexProperty(property);
}
@Override
public <V> void removeVertexProperty(VertexProperty<V> property) {
verifyElemPermission(HugePermission.WRITE, property.element());
this.hugegraph.removeVertexProperty(property);
}
@Override
public Edge addEdge(Edge edge) {
return verifyElemPermission(HugePermission.WRITE, () -> {
return (HugeEdge) this.hugegraph.addEdge(edge);
});
}
@Override
public void canAddEdge(Edge edge) {
verifyElemPermission(HugePermission.WRITE, () -> (HugeEdge) edge);
}
@Override
public void removeEdge(Edge edge) {
verifyElemPermission(HugePermission.DELETE, edge);
this.hugegraph.removeEdge(edge);
}
@Override
public void removeEdge(String label, Object id) {
this.removeEdge(this.edge(id));
}
@Override
public <V> void addEdgeProperty(Property<V> property) {
verifyElemPermission(HugePermission.WRITE, property.element());
this.hugegraph.addEdgeProperty(property);
}
@Override
public <V> void removeEdgeProperty(Property<V> property) {
verifyElemPermission(HugePermission.WRITE, property.element());
this.hugegraph.removeEdgeProperty(property);
}
@Override
public Iterator<Vertex> vertices(Query query) {
return verifyElemPermission(HugePermission.READ,
this.hugegraph.vertices(query));
}
@Override
public Iterator<Vertex> vertices(Object... objects) {
return verifyElemPermission(HugePermission.READ,
this.hugegraph.vertices(objects));
}
@Override
public Vertex vertex(Object object) {
Vertex vertex = this.hugegraph.vertex(object);
verifyElemPermission(HugePermission.READ, vertex);
return vertex;
}
@Override
public Iterator<Vertex> adjacentVertex(Object id) {
return verifyElemPermission(HugePermission.READ,
this.hugegraph.adjacentVertex(id));
}
@Override
public Iterator<Vertex> adjacentVertices(Iterator<Edge> edges) {
Iterator<Vertex> vertices = this.hugegraph.adjacentVertices(edges);
return verifyElemPermission(HugePermission.READ, vertices);
}
@Override
public boolean checkAdjacentVertexExist() {
verifyAnyPermission();
return this.hugegraph.checkAdjacentVertexExist();
}
@Override
public Iterator<Edge> edges(Query query) {
return verifyElemPermission(HugePermission.READ,
this.hugegraph.edges(query));
}
@Override
public Iterator<Edge> edges(Object... objects) {
return verifyElemPermission(HugePermission.READ,
this.hugegraph.edges(objects));
}
@Override
public Edge edge(Object id) {
Edge edge = this.hugegraph.edge(id);
verifyElemPermission(HugePermission.READ, edge);
return edge;
}
@Override
public Iterator<Edge> adjacentEdges(Id vertexId) {
Iterator<Edge> edges = this.hugegraph.adjacentEdges(vertexId);
return verifyElemPermission(HugePermission.READ, edges);
}
@Override
public Number queryNumber(Query query) {
ResourceType resType;
if (query.resultType().isVertex()) {
resType = ResourceType.VERTEX_AGGR;
} else {
assert query.resultType().isEdge();
resType = ResourceType.EDGE_AGGR;
}
this.verifyPermission(HugePermission.READ, resType);
return this.hugegraph.queryNumber(query);
}
@Override
public Transaction tx() {
/*
* Can't verifyPermission() here, will be called by rollbackAll().
*/
return this.hugegraph.tx();
}
@Override
public void close() throws Exception {
this.verifyAdminPermission();
this.hugegraph.close();
}
@Override
public HugeFeatures features() {
// Can't verifyPermission() here, will be called by rollbackAll()
//verifyStatusPermission();
return this.hugegraph.features();
}
@Override
public Variables variables() {
// Just return proxy
return new VariablesProxy(this.hugegraph.variables());
}
@Override
public HugeConfig configuration() {
this.verifyAdminPermission();
return (HugeConfig) this.hugegraph.configuration();
}
@Override
public String toString() {
this.verifyAnyPermission();
return this.hugegraph.toString();
}
@Override
public void proxy(HugeGraph graph) {
throw new NotSupportException("Graph.proxy()");
}
@Override
public boolean sameAs(HugeGraph graph) {
if (graph instanceof HugeGraphAuthProxy) {
graph = ((HugeGraphAuthProxy) graph).hugegraph;
}
return this.hugegraph.sameAs(graph);
}
@Override
public long now() {
// It's ok anyone call this method, so not verifyStatusPermission()
return this.hugegraph.now();
}
@Override
public <K, V> V option(TypedOption<K, V> option) {
this.verifyAnyPermission();
return this.hugegraph.option(option);
}
@Override
public String name() {
this.verifyAnyPermission();
return this.hugegraph.name();
}
@Override
public String backend() {
this.verifyAnyPermission();
return this.hugegraph.backend();
}
@Override
public BackendStoreInfo backendStoreInfo() {
this.verifyAdminPermission();
return this.hugegraph.backendStoreInfo();
}
@Override
public BackendFeatures backendStoreFeatures() {
this.verifyAnyPermission();
return this.hugegraph.backendStoreFeatures();
}
@Override
public GraphMode mode() {
this.verifyStatusPermission();
return this.hugegraph.mode();
}
@Override
public void mode(GraphMode mode) {
this.verifyPermission(HugePermission.WRITE, ResourceType.STATUS);
this.hugegraph.mode(mode);
}
@Override
public GraphReadMode readMode() {
this.verifyStatusPermission();
return this.hugegraph.readMode();
}
@Override
public void readMode(GraphReadMode readMode) {
this.verifyPermission(HugePermission.WRITE, ResourceType.STATUS);
this.hugegraph.readMode(readMode);
}
@Override
public void waitReady(RpcServer rpcServer) {
this.verifyAnyPermission();
this.hugegraph.waitReady(rpcServer);
}
@Override
public void serverStarted(Id serverId, NodeRole serverRole) {
this.verifyAdminPermission();
this.hugegraph.serverStarted(serverId, serverRole);
}
@Override
public boolean started() {
this.verifyAdminPermission();
return this.hugegraph.started();
}
@Override
public boolean closed() {
this.verifyAdminPermission();
return this.hugegraph.closed();
}
@Override
public <R> R metadata(HugeType type, String meta, Object... args) {
this.verifyNamePermission(HugePermission.EXECUTE,
ResourceType.META, meta);
return this.hugegraph.metadata(type, meta, args);
}
@Override
public TaskScheduler taskScheduler() {
// Just return proxy
return this.taskScheduler;
}
@Override
public AuthManager authManager() {
// Just return proxy
return this.authManager;
}
@Override
public RoleElectionStateMachine roleElectionStateMachine() {
this.verifyAdminPermission();
return this.hugegraph.roleElectionStateMachine();
}
@Override
public void switchAuthManager(AuthManager authManager) {
this.verifyAdminPermission();
this.authManager.switchAuthManager(authManager);
}
@Override
public RaftGroupManager raftGroupManager() {
this.verifyAdminPermission();
return this.hugegraph.raftGroupManager();
}
@Override
public void registerRpcServices(RpcServiceConfig4Server serverConfig,
RpcServiceConfig4Client clientConfig) {
this.verifyAdminPermission();
this.hugegraph.registerRpcServices(serverConfig, clientConfig);
}
@Override
public void initBackend() {
this.verifyAdminPermission();
this.hugegraph.initBackend();
}
@Override
public void clearBackend() {
this.verifyAdminPermission();
this.hugegraph.clearBackend();
}
@Override
public void truncateBackend() {
this.verifyAdminPermission();
AuthManager userManager = this.hugegraph.authManager();
HugeUser admin = userManager.findUser(HugeAuthenticator.USER_ADMIN);
try {
this.hugegraph.truncateBackend();
} finally {
if (admin != null && StandardAuthManager.isLocal(userManager)) {
// Restore admin user to continue to do any operation
userManager.createUser(admin);
}
}
}
@Override
public void initSystemInfo() {
this.verifyAdminPermission();
this.hugegraph.initSystemInfo();
}
@Override
public void createSnapshot() {
this.verifyPermission(HugePermission.WRITE, ResourceType.STATUS);
this.hugegraph.createSnapshot();
}
@Override
public void resumeSnapshot() {
this.verifyPermission(HugePermission.WRITE, ResourceType.STATUS);
this.hugegraph.resumeSnapshot();
}
@Override
public void create(String configPath, Id server, NodeRole role) {
this.verifyPermission(HugePermission.WRITE, ResourceType.STATUS);
this.hugegraph.create(configPath, server, role);
}
@Override
public void drop() {
this.verifyPermission(HugePermission.WRITE, ResourceType.STATUS);
this.hugegraph.drop();
}
@Override
public HugeConfig cloneConfig(String newGraph) {
this.verifyPermission(HugePermission.WRITE, ResourceType.STATUS);
return this.hugegraph.cloneConfig(newGraph);
}
private <V> Cache<Id, V> cache(String prefix, long capacity,
long expiredTime) {
String name = prefix + "-" + this.hugegraph.name();
Cache<Id, V> cache = CacheManager.instance().cache(name, capacity);
if (expiredTime > 0L) {
cache.expire(Duration.ofSeconds(expiredTime).toMillis());
} else {
cache.expire(expiredTime);
}
return cache;
}
private void verifyAdminPermission() {
verifyPermission(HugePermission.ANY, ResourceType.ROOT);
}
private void verifyStatusPermission() {
verifyPermission(HugePermission.READ, ResourceType.STATUS);
}
private void verifyAnyPermission() {
verifyPermission(HugePermission.READ, ResourceType.NONE);
}
private void verifyPermission(HugePermission actionPerm,
ResourceType resType) {
/*
* The owner role should match the graph name
* NOTE: the graph names in gremlin-server.yaml/graphs and
* hugegraph.properties/store must be the same if enable auth.
*/
verifyResPermission(actionPerm, true, () -> {
String graph = this.hugegraph.name();
Nameable elem = HugeResource.NameObject.ANY;
return ResourceObject.of(graph, resType, elem);
});
}
private <V extends AuthElement> V verifyUserPermission(
HugePermission actionPerm,
V elementFetcher) {
return verifyUserPermission(actionPerm, true, () -> elementFetcher);
}
private <V extends AuthElement> List<V> verifyUserPermission(
HugePermission actionPerm,
List<V> elems) {
List<V> results = new ArrayList<>();
for (V elem : elems) {
V r = verifyUserPermission(actionPerm, false, () -> elem);
if (r != null) {
results.add(r);
}
}
return results;
}
private <V extends AuthElement> V verifyUserPermission(
HugePermission actionPerm,
boolean throwIfNoPerm,
Supplier<V> elementFetcher) {
return verifyResPermission(actionPerm, throwIfNoPerm, () -> {
String graph = this.hugegraph.name();
V elem = elementFetcher.get();
@SuppressWarnings("unchecked")
ResourceObject<V> r = (ResourceObject<V>) ResourceObject.of(graph,
elem);
return r;
});
}
private void verifyElemPermission(HugePermission actionPerm, Element elem) {
verifyElemPermission(actionPerm, true, () -> elem);
}
private <V extends HugeElement> V verifyElemPermission(
HugePermission actionPerm,
Supplier<V> elementFetcher) {
return verifyElemPermission(actionPerm, true, elementFetcher);
}
private <V extends Element> Iterator<V> verifyElemPermission(
HugePermission actionPerm,
Iterator<V> elems) {
return new FilterIterator<>(elems, elem -> {
V r = verifyElemPermission(actionPerm, false, () -> elem);
return r != null;
});
}
private <V extends Element> V verifyElemPermission(
HugePermission actionPerm,
boolean throwIfNoPerm,
Supplier<V> elementFetcher) {
return verifyResPermission(actionPerm, throwIfNoPerm, () -> {
String graph = this.hugegraph.name();
HugeElement elem = (HugeElement) elementFetcher.get();
@SuppressWarnings("unchecked")
ResourceObject<V> r = (ResourceObject<V>) ResourceObject.of(graph,
elem);
return r;
});
}
private void verifyNameExistsPermission(ResourceType resType, String name) {
verifyNamePermission(HugePermission.READ, resType, name);
}
private void verifyNamePermission(HugePermission actionPerm,
ResourceType resType, String name) {
verifyResPermission(actionPerm, true, () -> {
String graph = this.hugegraph.name();
Nameable elem = HugeResource.NameObject.of(name);
return ResourceObject.of(graph, resType, elem);
});
}
private void verifySchemaPermission(HugePermission actionPerm,
SchemaElement schema) {
verifySchemaPermission(actionPerm, true, () -> schema);
}
private <V extends SchemaElement> Collection<V> verifySchemaPermission(
HugePermission actionPerm,
Collection<V> schemas) {
List<V> results = new ArrayList<>();
for (V schema : schemas) {
V r = verifySchemaPermission(actionPerm, false, () -> schema);
if (r != null) {
results.add(r);
}
}
return results;
}
private <V extends SchemaElement> V verifySchemaPermission(
HugePermission actionPerm,
Supplier<V> schemaFetcher) {
return verifySchemaPermission(actionPerm, true, schemaFetcher);
}
private <V extends SchemaElement> V verifySchemaPermission(
HugePermission actionPerm,
boolean throwIfNoPerm,
Supplier<V> schemaFetcher) {
return verifyResPermission(actionPerm, throwIfNoPerm, () -> {
String graph = this.hugegraph.name();
SchemaElement elem = schemaFetcher.get();
@SuppressWarnings("unchecked")
ResourceObject<V> r = (ResourceObject<V>) ResourceObject.of(graph,
elem);
return r;
});
}
private <V> V verifyResPermission(HugePermission actionPerm,
boolean throwIfNoPerm,
Supplier<ResourceObject<V>> fetcher) {
return verifyResPermission(actionPerm, throwIfNoPerm, fetcher, null);
}
private <V> V verifyResPermission(HugePermission actionPerm,
boolean throwIfNoPerm,
Supplier<ResourceObject<V>> fetcher,
Supplier<Boolean> checker) {
// TODO: call verifyPermission() before actual action
Context context = getContext();
E.checkState(context != null,
"Missing authentication context " +
"when verifying resource permission");
String username = context.user().username();
Object role = context.user().role();
ResourceObject<V> ro = fetcher.get();
String action = actionPerm.string();
if (LOG.isDebugEnabled()) {
LOG.debug("Verify permission {} {} for user '{}' with role {}",
action, ro, username, role);
}
V result = ro.operated();
// Verify role permission
if (!RolePerm.match(role, actionPerm, ro)) {
result = null;
}
// Verify permission for one access another, like: granted <= user role
else if (ro.type().isGrantOrUser()) {
AuthElement element = (AuthElement) ro.operated();
RolePermission grant = this.hugegraph.authManager()
.rolePermission(element);
if (!RolePerm.match(role, grant, ro)) {
result = null;
}
}
// Check resource detail if needed
if (result != null && checker != null && !checker.get()) {
result = null;
}
// Log user action, limit rate for each user
Id usrId = context.user().userId();
RateLimiter auditLimiter = this.auditLimiters.getOrFetch(usrId, id -> {
return RateLimiter.create(this.auditLogMaxRate);
});
if (!(actionPerm == HugePermission.READ && ro.type().isSchema()) &&
auditLimiter.tryAcquire()) {
String status = result == null ? "denied" : "allowed";
LOG.info("User '{}' is {} to {} {}", username, status, action, ro);
}
// result = null means no permission, throw if needed
if (result == null && throwIfNoPerm) {
String error = String.format("Permission denied: %s %s",
action, ro);
throw new ForbiddenException(error);
}
return result;
}
class TaskSchedulerProxy implements TaskScheduler {
private final TaskScheduler taskScheduler;
public TaskSchedulerProxy(TaskScheduler origin) {
this.taskScheduler = origin;
}
@Override
public HugeGraph graph() {
return this.taskScheduler.graph();
}
@Override
public void init() {
verifyAdminPermission();
this.taskScheduler.init();
}
@Override
public int pendingTasks() {
verifyTaskPermission(HugePermission.READ);
return this.taskScheduler.pendingTasks();
}
@Override
public <V> void restoreTasks() {
verifyTaskPermission(HugePermission.WRITE);
this.taskScheduler.restoreTasks();
}
@Override
public <V> Future<?> schedule(HugeTask<V> task) {
verifyTaskPermission(HugePermission.EXECUTE);
task.context(getContextString());
return this.taskScheduler.schedule(task);
}
@Override
public <V> void cancel(HugeTask<V> task) {
verifyTaskPermission(HugePermission.WRITE, task);
this.taskScheduler.cancel(task);
}
@Override
public <V> void save(HugeTask<V> task) {
verifyTaskPermission(HugePermission.WRITE, task);
this.taskScheduler.save(task);
}
@Override
public <V> HugeTask<V> task(Id id) {
return verifyTaskPermission(HugePermission.READ,
this.taskScheduler.task(id));
}
@Override
public <V> Iterator<HugeTask<V>> tasks(List<Id> ids) {
return verifyTaskPermission(HugePermission.READ,
this.taskScheduler.tasks(ids));
}
@Override
public <V> Iterator<HugeTask<V>> tasks(TaskStatus status,
long limit, String page) {
Iterator<HugeTask<V>> tasks = this.taskScheduler.tasks(status,
limit, page);
return verifyTaskPermission(HugePermission.READ, tasks);
}
@Override
public <V> HugeTask<V> delete(Id id) {
verifyTaskPermission(HugePermission.DELETE,
this.taskScheduler.task(id));
return this.taskScheduler.delete(id);
}
@Override
public boolean close() {
verifyAdminPermission();
return this.taskScheduler.close();
}
@Override
public <V> HugeTask<V> waitUntilTaskCompleted(Id id, long seconds)
throws TimeoutException {
verifyAnyPermission();
return this.taskScheduler.waitUntilTaskCompleted(id, seconds);
}
@Override
public <V> HugeTask<V> waitUntilTaskCompleted(Id id)
throws TimeoutException {
verifyAnyPermission();
return this.taskScheduler.waitUntilTaskCompleted(id);
}
@Override
public void waitUntilAllTasksCompleted(long seconds)
throws TimeoutException {
verifyAnyPermission();
this.taskScheduler.waitUntilAllTasksCompleted(seconds);
}
@Override
public void checkRequirement(String op) {
verifyAnyPermission();
this.taskScheduler.checkRequirement(op);
}
private void verifyTaskPermission(HugePermission actionPerm) {
verifyPermission(actionPerm, ResourceType.TASK);
}
private <V> HugeTask<V> verifyTaskPermission(HugePermission actionPerm,
HugeTask<V> task) {
return verifyTaskPermission(actionPerm, true, task);
}
private <V> Iterator<HugeTask<V>> verifyTaskPermission(
HugePermission actionPerm,
Iterator<HugeTask<V>> tasks) {
return new FilterIterator<>(tasks, task -> {
return verifyTaskPermission(actionPerm, false, task) != null;
});
}
private <V> HugeTask<V> verifyTaskPermission(HugePermission actionPerm,
boolean throwIfNoPerm,
HugeTask<V> task) {
Object r = verifyResPermission(actionPerm, throwIfNoPerm, () -> {
String graph = HugeGraphAuthProxy.this.hugegraph.name();
String name = task.id().toString();
Nameable elem = HugeResource.NameObject.of(name);
return ResourceObject.of(graph, ResourceType.TASK, elem);
}, () -> {
return hasTaskPermission(task);
});
return r == null ? null : task;
}
private boolean hasTaskPermission(HugeTask<?> task) {
Context context = getContext();
if (context == null) {
return false;
}
User currentUser = context.user();
User taskUser = User.fromJson(task.context());
if (taskUser == null) {
return User.ADMIN.equals(currentUser);
}
return Objects.equals(currentUser.getName(), taskUser.getName()) ||
RolePerm.match(currentUser.role(), taskUser.role(), null);
}
}
class AuthManagerProxy implements AuthManager {
private AuthManager authManager;
public AuthManagerProxy(AuthManager origin) {
this.authManager = origin;
}
private AuthElement updateCreator(AuthElement elem) {
String username = currentUsername();
if (username != null && elem.creator() == null) {
elem.creator(username);
}
return elem;
}
private String currentUsername() {
Context context = getContext();
if (context != null) {
return context.user().username();
}
return null;
}
@Override
public void init() {
verifyAdminPermission();
this.authManager.init();
}
@Override
public boolean close() {
verifyAdminPermission();
return this.authManager.close();
}
@Override
public Id createUser(HugeUser user) {
E.checkArgument(!HugeAuthenticator.USER_ADMIN.equals(user.name()),
"Invalid user name '%s'", user.name());
this.updateCreator(user);
verifyUserPermission(HugePermission.WRITE, user);
return this.authManager.createUser(user);
}
@Override
public Id updateUser(HugeUser updatedUser) {
String username = currentUsername();
HugeUser user = this.authManager.getUser(updatedUser.id());
if (!user.name().equals(username)) {
this.updateCreator(updatedUser);
verifyUserPermission(HugePermission.WRITE, user);
}
this.invalidRoleCache();
return this.authManager.updateUser(updatedUser);
}
@Override
public HugeUser deleteUser(Id id) {
HugeUser user = this.authManager.getUser(id);
E.checkArgument(!HugeAuthenticator.USER_ADMIN.equals(user.name()),
"Can't delete user '%s'", user.name());
verifyUserPermission(HugePermission.DELETE, user);
HugeGraphAuthProxy.this.auditLimiters.invalidate(user.id());
this.invalidRoleCache();
return this.authManager.deleteUser(id);
}
@Override
public HugeUser findUser(String name) {
HugeUser user = this.authManager.findUser(name);
String username = currentUsername();
if (!user.name().equals(username)) {
verifyUserPermission(HugePermission.READ, user);
}
return user;
}
@Override
public HugeUser getUser(Id id) {
HugeUser user = this.authManager.getUser(id);
String username = currentUsername();
if (!user.name().equals(username)) {
verifyUserPermission(HugePermission.READ, user);
}
return user;
}
@Override
public List<HugeUser> listUsers(List<Id> ids) {
return verifyUserPermission(HugePermission.READ,
this.authManager.listUsers(ids));
}
@Override
public List<HugeUser> listAllUsers(long limit) {
return verifyUserPermission(HugePermission.READ,
this.authManager.listAllUsers(limit));
}
@Override
public Id createGroup(HugeGroup group) {
this.updateCreator(group);
verifyUserPermission(HugePermission.WRITE, group);
this.invalidRoleCache();
return this.authManager.createGroup(group);
}
@Override
public Id updateGroup(HugeGroup group) {
this.updateCreator(group);
verifyUserPermission(HugePermission.WRITE, group);
this.invalidRoleCache();
return this.authManager.updateGroup(group);
}
@Override
public HugeGroup deleteGroup(Id id) {
verifyUserPermission(HugePermission.DELETE,
this.authManager.getGroup(id));
this.invalidRoleCache();
return this.authManager.deleteGroup(id);
}
@Override
public HugeGroup getGroup(Id id) {
return verifyUserPermission(HugePermission.READ,
this.authManager.getGroup(id));
}
@Override
public List<HugeGroup> listGroups(List<Id> ids) {
return verifyUserPermission(HugePermission.READ,
this.authManager.listGroups(ids));
}
@Override
public List<HugeGroup> listAllGroups(long limit) {
return verifyUserPermission(HugePermission.READ,
this.authManager.listAllGroups(limit));
}
@Override
public Id createTarget(HugeTarget target) {
this.updateCreator(target);
verifyUserPermission(HugePermission.WRITE, target);
this.invalidRoleCache();
return this.authManager.createTarget(target);
}
@Override
public Id updateTarget(HugeTarget target) {
this.updateCreator(target);
verifyUserPermission(HugePermission.WRITE, target);
this.invalidRoleCache();
return this.authManager.updateTarget(target);
}
@Override
public HugeTarget deleteTarget(Id id) {
verifyUserPermission(HugePermission.DELETE,
this.authManager.getTarget(id));
this.invalidRoleCache();
return this.authManager.deleteTarget(id);
}
@Override
public HugeTarget getTarget(Id id) {
return verifyUserPermission(HugePermission.READ,
this.authManager.getTarget(id));
}
@Override
public List<HugeTarget> listTargets(List<Id> ids) {
return verifyUserPermission(HugePermission.READ,
this.authManager.listTargets(ids));
}
@Override
public List<HugeTarget> listAllTargets(long limit) {
return verifyUserPermission(HugePermission.READ,
this.authManager.listAllTargets(limit));
}
@Override
public Id createBelong(HugeBelong belong) {
this.updateCreator(belong);
verifyUserPermission(HugePermission.WRITE, belong);
this.invalidRoleCache();
return this.authManager.createBelong(belong);
}
@Override
public Id updateBelong(HugeBelong belong) {
this.updateCreator(belong);
verifyUserPermission(HugePermission.WRITE, belong);
this.invalidRoleCache();
return this.authManager.updateBelong(belong);
}
@Override
public HugeBelong deleteBelong(Id id) {
verifyUserPermission(HugePermission.DELETE,
this.authManager.getBelong(id));
this.invalidRoleCache();
return this.authManager.deleteBelong(id);
}
@Override
public HugeBelong getBelong(Id id) {
return verifyUserPermission(HugePermission.READ,
this.authManager.getBelong(id));
}
@Override
public List<HugeBelong> listBelong(List<Id> ids) {
return verifyUserPermission(HugePermission.READ,
this.authManager.listBelong(ids));
}
@Override
public List<HugeBelong> listAllBelong(long limit) {
return verifyUserPermission(HugePermission.READ,
this.authManager.listAllBelong(limit));
}
@Override
public List<HugeBelong> listBelongByUser(Id user, long limit) {
List<HugeBelong> r = this.authManager.listBelongByUser(user, limit);
return verifyUserPermission(HugePermission.READ, r);
}
@Override
public List<HugeBelong> listBelongByGroup(Id group, long limit) {
List<HugeBelong> r = this.authManager.listBelongByGroup(group,
limit);
return verifyUserPermission(HugePermission.READ, r);
}
@Override
public Id createAccess(HugeAccess access) {
this.updateCreator(access);
verifyUserPermission(HugePermission.WRITE, access);
this.invalidRoleCache();
return this.authManager.createAccess(access);
}
@Override
public Id updateAccess(HugeAccess access) {
this.updateCreator(access);
verifyUserPermission(HugePermission.WRITE, access);
this.invalidRoleCache();
return this.authManager.updateAccess(access);
}
@Override
public HugeAccess deleteAccess(Id id) {
verifyUserPermission(HugePermission.DELETE,
this.authManager.getAccess(id));
this.invalidRoleCache();
return this.authManager.deleteAccess(id);
}
@Override
public HugeAccess getAccess(Id id) {
return verifyUserPermission(HugePermission.READ,
this.authManager.getAccess(id));
}
@Override
public List<HugeAccess> listAccess(List<Id> ids) {
return verifyUserPermission(HugePermission.READ,
this.authManager.listAccess(ids));
}
@Override
public List<HugeAccess> listAllAccess(long limit) {
return verifyUserPermission(HugePermission.READ,
this.authManager.listAllAccess(limit));
}
@Override
public List<HugeAccess> listAccessByGroup(Id group, long limit) {
List<HugeAccess> r = this.authManager.listAccessByGroup(group,
limit);
return verifyUserPermission(HugePermission.READ, r);
}
@Override
public List<HugeAccess> listAccessByTarget(Id target, long limit) {
List<HugeAccess> r = this.authManager.listAccessByTarget(target,
limit);
return verifyUserPermission(HugePermission.READ, r);
}
@Override
public Id createProject(HugeProject project) {
this.updateCreator(project);
verifyUserPermission(HugePermission.WRITE, project);
return this.authManager.createProject(project);
}
@Override
public HugeProject deleteProject(Id id) {
verifyUserPermission(HugePermission.DELETE,
this.authManager.getProject(id));
return this.authManager.deleteProject(id);
}
@Override
public Id updateProject(HugeProject project) {
this.updateCreator(project);
verifyUserPermission(HugePermission.WRITE, project);
return this.authManager.updateProject(project);
}
@Override
public Id projectAddGraphs(Id id, Set<String> graphs) {
verifyUserPermission(HugePermission.WRITE,
this.authManager.getProject(id));
return this.authManager.projectAddGraphs(id, graphs);
}
@Override
public Id projectRemoveGraphs(Id id, Set<String> graphs) {
verifyUserPermission(HugePermission.WRITE,
this.authManager.getProject(id));
return this.authManager.projectRemoveGraphs(id, graphs);
}
@Override
public HugeProject getProject(Id id) {
HugeProject project = this.authManager.getProject(id);
verifyUserPermission(HugePermission.READ, project);
return project;
}
@Override
public List<HugeProject> listAllProject(long limit) {
List<HugeProject> projects = this.authManager.listAllProject(limit);
return verifyUserPermission(HugePermission.READ, projects);
}
@Override
public HugeUser matchUser(String name, String password) {
// Unneeded to verify permission
return this.authManager.matchUser(name, password);
}
@Override
public RolePermission rolePermission(AuthElement element) {
String username = currentUsername();
if (!(element instanceof HugeUser) ||
!((HugeUser) element).name().equals(username)) {
verifyUserPermission(HugePermission.READ, element);
}
return this.authManager.rolePermission(element);
}
@Override
public UserWithRole validateUser(String username, String password) {
// Can't verifyPermission() here, validate first with tmp permission
Context context = setContext(Context.admin());
try {
Id userKey = IdGenerator.of(username + password);
return HugeGraphAuthProxy.this.usersRoleCache.getOrFetch(userKey, id -> {
return this.authManager.validateUser(username, password);
});
} catch (Exception e) {
LOG.error("Failed to validate user {} with error: ",
username, e);
throw e;
} finally {
setContext(context);
}
}
@Override
public UserWithRole validateUser(String token) {
// Can't verifyPermission() here, validate first with tmp permission
Context context = setContext(Context.admin());
try {
Id userKey = IdGenerator.of(token);
return HugeGraphAuthProxy.this.usersRoleCache.getOrFetch(userKey, id -> {
return this.authManager.validateUser(token);
});
} catch (Exception e) {
LOG.error("Failed to validate token {} with error: ", token, e);
throw e;
} finally {
setContext(context);
}
}
@Override
public Set<String> listWhiteIPs() {
return this.authManager.listWhiteIPs();
}
@Override
public void setWhiteIPs(Set<String> whiteIpList) {
this.authManager.setWhiteIPs(whiteIpList);
}
@Override
public boolean getWhiteIpStatus() {
return this.authManager.getWhiteIpStatus();
}
@Override
public void enabledWhiteIpList(boolean status) {
this.authManager.enabledWhiteIpList(status);
}
@Override
public String loginUser(String username, String password) {
try {
return this.authManager.loginUser(username, password);
} catch (AuthenticationException e) {
throw new NotAuthorizedException(e.getMessage(), e);
}
}
@Override
public void logoutUser(String token) {
this.authManager.logoutUser(token);
}
private void switchAuthManager(AuthManager authManager) {
this.authManager = authManager;
HugeGraphAuthProxy.this.hugegraph.switchAuthManager(authManager);
}
private void invalidRoleCache() {
HugeGraphAuthProxy.this.usersRoleCache.clear();
}
}
class VariablesProxy implements Variables {
private final Variables variables;
public VariablesProxy(Variables variables) {
this.variables = variables;
}
@Override
public <R> Optional<R> get(String key) {
verifyPermission(HugePermission.READ, ResourceType.VAR);
return this.variables.get(key);
}
@Override
public Set<String> keys() {
verifyPermission(HugePermission.READ, ResourceType.VAR);
return this.variables.keys();
}
@Override
public void set(String key, Object value) {
verifyPermission(HugePermission.WRITE, ResourceType.VAR);
this.variables.set(key, value);
}
@Override
public void remove(String key) {
verifyPermission(HugePermission.DELETE, ResourceType.VAR);
this.variables.remove(key);
}
}
class GraphTraversalSourceProxy extends GraphTraversalSource {
public GraphTraversalSourceProxy(Graph graph) {
super(graph);
}
public GraphTraversalSourceProxy(Graph graph,
TraversalStrategies strategies) {
super(graph, strategies);
}
@Override
public TraversalStrategies getStrategies() {
// getStrategies()/getGraph() is called by super.clone()
return new TraversalStrategiesProxy(super.getStrategies());
}
}
class TraversalStrategiesProxy implements TraversalStrategies {
private static final String REST_WORKER = "grizzly-http-server";
private static final long serialVersionUID = -5424364720492307019L;
private final TraversalStrategies strategies;
public TraversalStrategiesProxy(TraversalStrategies strategies) {
this.strategies = strategies;
}
@Override
public List<TraversalStrategy<?>> toList() {
return this.strategies.toList();
}
@Override
public Iterator<TraversalStrategy<?>> iterator() {
if (this.strategies == null) {
return Collections.emptyIterator();
}
return new MapperIterator<>(this.strategies.iterator(), TraversalStrategyProxy::new);
}
@Override
public TraversalStrategies addStrategies(TraversalStrategy<?>... strategies) {
return this.strategies.addStrategies(strategies);
}
@SuppressWarnings({"unchecked"})
@Override
public TraversalStrategies removeStrategies(
Class<? extends TraversalStrategy>... strategyClasses) {
return this.strategies.removeStrategies(strategyClasses);
}
@Override
public TraversalStrategies clone() {
// CHECKSTYLE:OFF
return this.strategies.clone();
}
@SuppressWarnings("unused")
private String translate(Bytecode bytecode) {
// GroovyTranslator.of("g").translate(bytecode);
List<Instruction> steps = bytecode.getStepInstructions();
StringBuilder sb = new StringBuilder();
sb.append("g");
int stepsPrint = Math.min(10, steps.size());
for (int i = 0; i < stepsPrint; i++) {
Instruction step = steps.get(i);
sb.append('.').append(step);
}
if (stepsPrint < steps.size()) {
sb.append("..");
}
return sb.toString();
}
}
private final class TraversalStrategyProxy<T extends TraversalStrategy<?>>
implements TraversalStrategy<T> {
private static final long serialVersionUID = 2071829024642435735L;
private final TraversalStrategy<T> origin;
public TraversalStrategyProxy(TraversalStrategy<?> origin) {
@SuppressWarnings({ "rawtypes", "unchecked" })
TraversalStrategy<T> strategy = (TraversalStrategy) origin;
this.origin = strategy;
}
@Override
public void apply(Traversal.Admin<?, ?> traversal) {
String script;
if (traversal instanceof HugeScriptTraversal) {
script = ((HugeScriptTraversal<?, ?>) traversal).script();
} else {
GroovyTranslator translator = GroovyTranslator.of("g");
Script script1 = translator.translate(traversal.getBytecode());
if (script1 != null) {
script = script1.getScript();
} else {
script = "";
}
}
/*
* Verify gremlin-execute permission for user gremlin(in gremlin-
* server-exec worker) and gremlin job(in task worker).
* But don't check permission in rest worker, because the following
* places need to call traversal():
* 1.vertices/edges rest api
* 2.oltp rest api (like crosspointpath/neighborrank)
* 3.olap rest api (like centrality/lpa/louvain/subgraph)
*/
String caller = Thread.currentThread().getName();
if (!caller.contains(TraversalStrategiesProxy.REST_WORKER)) {
verifyNamePermission(HugePermission.EXECUTE,
ResourceType.GREMLIN, script);
}
this.origin.apply(traversal);
}
@Override
public Set<Class<? extends T>> applyPrior() {
return this.origin.applyPrior();
}
@Override
public Set<Class<? extends T>> applyPost() {
return this.origin.applyPost();
}
@Override
public Class<T> getTraversalCategory() {
return this.origin.getTraversalCategory();
}
@Override
public Configuration getConfiguration() {
return this.origin.getConfiguration();
}
@Override
public int compareTo(Class<? extends TraversalStrategy> other) {
return this.origin.compareTo(other);
}
@Override
public int hashCode() {
return this.origin.hashCode();
}
@Override
public boolean equals(Object obj) {
return this.origin.equals(obj);
}
@Override
public String toString() {
return this.origin.toString();
}
}
private static final ThreadLocal<Context> CONTEXTS = new InheritableThreadLocal<>();
protected static Context setContext(Context context) {
Context old = CONTEXTS.get();
CONTEXTS.set(context);
return old;
}
protected static void resetContext() {
CONTEXTS.remove();
}
protected static Context getContext() {
// Return task context first
String taskContext = TaskManager.getContext();
User user = User.fromJson(taskContext);
if (user != null) {
return new Context(user);
}
return CONTEXTS.get();
}
protected static String getContextString() {
Context context = getContext();
if (context == null) {
return null;
}
return context.user().toJson();
}
protected static void logUser(User user, String path) {
LOG.info("User '{}' login from client [{}] with path '{}'",
user.username(), user.client(), path);
}
static class Context {
private static final Context ADMIN = new Context(User.ADMIN);
private final User user;
public Context(User user) {
E.checkNotNull(user, "user");
this.user = user;
}
public User user() {
return this.user;
}
public static Context admin() {
return ADMIN;
}
}
static class ContextTask implements Runnable {
private final Runnable runner;
private final Context context;
public ContextTask(Runnable runner) {
this.context = getContext();
this.runner = runner;
}
@Override
public void run() {
setContext(this.context);
try {
this.runner.run();
} finally {
resetContext();
}
}
}
public static class ContextThreadPoolExecutor extends ThreadPoolExecutor {
public ContextThreadPoolExecutor(int corePoolSize, int maxPoolSize,
ThreadFactory threadFactory) {
super(corePoolSize, maxPoolSize, 0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(), threadFactory);
}
@Override
public void execute(Runnable command) {
super.execute(new ContextTask(command));
}
}
}