diff --git a/client/pom.xml b/client/pom.xml
index 08ba5119d..9c0b9691f 100644
--- a/client/pom.xml
+++ b/client/pom.xml
@@ -5,7 +5,7 @@
com.vesoft
nebula
- 3.0-SNAPSHOT
+ 3.7.0-auth
4.0.0
diff --git a/client/src/main/java/com/vesoft/nebula/client/storage/StorageClient.java b/client/src/main/java/com/vesoft/nebula/client/storage/StorageClient.java
index 256c9bba8..b6cf73fd9 100644
--- a/client/src/main/java/com/vesoft/nebula/client/storage/StorageClient.java
+++ b/client/src/main/java/com/vesoft/nebula/client/storage/StorageClient.java
@@ -7,7 +7,14 @@
import com.vesoft.nebula.HostAddr;
import com.vesoft.nebula.client.graph.data.HostAddress;
+import com.vesoft.nebula.client.graph.data.ResultSet;
import com.vesoft.nebula.client.graph.data.SSLParam;
+import com.vesoft.nebula.client.graph.data.ValueWrapper;
+import com.vesoft.nebula.client.graph.exception.AuthFailedException;
+import com.vesoft.nebula.client.graph.exception.ClientServerIncompatibleException;
+import com.vesoft.nebula.client.graph.exception.IOErrorException;
+import com.vesoft.nebula.client.graph.net.AuthResult;
+import com.vesoft.nebula.client.graph.net.SyncConnection;
import com.vesoft.nebula.client.meta.MetaManager;
import com.vesoft.nebula.client.storage.scan.PartScanInfo;
import com.vesoft.nebula.client.storage.scan.ScanEdgeResultIterator;
@@ -19,10 +26,13 @@
import com.vesoft.nebula.storage.ScanVertexRequest;
import com.vesoft.nebula.storage.VertexProp;
import java.io.Serializable;
+import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -41,6 +51,13 @@ public class StorageClient implements Serializable {
private boolean enableSSL = false;
private SSLParam sslParam = null;
+ private String user = null;
+ private String password = null;
+
+ private String graphAddress = null;
+
+ // the write list for users with read permission
+ private Map> spaceLabelWriteList = null;
private String version = null;
/**
@@ -94,6 +111,24 @@ public StorageClient(List addresses, int timeout, int connectionRet
}
}
+ public StorageClient setUser(String user) {
+ this.user = user;
+ return this;
+ }
+
+ public StorageClient setPassword(String password) {
+ this.password = password;
+ return this;
+ }
+
+ public String getGraphAddress() {
+ return graphAddress;
+ }
+
+ public void setGraphAddress(String graphAddress) {
+ this.graphAddress = graphAddress;
+ }
+
public StorageClient setVersion(String version) {
this.version = version;
return this;
@@ -105,6 +140,7 @@ public StorageClient setVersion(String version) {
* @return true if connect successfully.
*/
public boolean connect() throws Exception {
+ authUser();
connection.open(addresses.get(0), timeout, enableSSL, sslParam);
StoragePoolConfig config = new StoragePoolConfig();
config.setEnableSSL(enableSSL);
@@ -561,6 +597,15 @@ private ScanVertexResultIterator scanVertex(String spaceName,
partScanInfoSet.add(new PartScanInfo(part, new HostAddress(leader.getHost(),
leader.getPort())));
}
+
+ // check the user permission after the 'getLeader',
+ // if the space is not exist, 'getLeader' can throw it first.
+ if (!checkWriteList(spaceName, tagName)) {
+ throw new IllegalArgumentException(
+ String.format("user %s has no read permission for %s.%s", user, spaceName,
+ tagName));
+ }
+
List addrs = new ArrayList<>();
for (HostAddr addr : metaManager.listHosts()) {
addrs.add(new HostAddress(addr.getHost(), addr.getPort()));
@@ -1004,6 +1049,10 @@ private ScanEdgeResultIterator scanEdge(String spaceName,
long endTime,
boolean allowPartSuccess,
boolean allowReadFromFollower) {
+ if (!checkWriteList(spaceName, edgeName)) {
+ throw new IllegalArgumentException(
+ String.format("user has no read permission for %s.%s", spaceName, edgeName));
+ }
if (spaceName == null || spaceName.trim().isEmpty()) {
throw new IllegalArgumentException("space name is empty.");
}
@@ -1139,6 +1188,110 @@ private long getEdgeId(String spaceName, String edgeName) {
return metaManager.getEdge(spaceName, edgeName).getEdge_type();
}
+
+ /**
+ * auth user with graphd server, and get the space and labels WriteList with read permission
+ * for user
+ */
+ private void authUser() throws AuthFailedException, IOErrorException,
+ ClientServerIncompatibleException, UnsupportedEncodingException {
+ if (user == null || password == null || graphAddress == null) {
+ throw new IllegalArgumentException(
+ "the user,password,graphAddress can not be null,"
+ + " please config them first by setXXX()");
+ }
+ SyncConnection graphConnection = new SyncConnection();
+ String[] graphAddrAndPort = graphAddress.split(":");
+ if (graphAddrAndPort.length != 2) {
+ throw new IllegalArgumentException("the graph address is invalid.");
+ }
+ if (sslParam == null) {
+ graphConnection.open(new HostAddress(graphAddrAndPort[0].trim(),
+ Integer.valueOf(graphAddrAndPort[1].trim())), timeout, false,
+ new HashMap<>(),
+ version);
+ } else {
+ graphConnection.open(new HostAddress(graphAddrAndPort[0].trim(),
+ Integer.valueOf(graphAddrAndPort[1].trim())), timeout, sslParam, false,
+ new HashMap<>(),
+ version);
+ }
+ AuthResult authResult = graphConnection.authenticate(user, password);
+ long sessionId = authResult.getSessionId();
+
+ if (user.equals("root")) {
+ return;
+ }
+
+ spaceLabelWriteList = new HashMap<>();
+ ResultSet resultSet = new ResultSet(
+ graphConnection.execute(sessionId, "DESC USER " + user),
+ authResult.getTimezoneOffset());
+ if (!resultSet.isSucceeded()) {
+ throw new RuntimeException("get spaces for user " + user + " failed, "
+ + resultSet.getErrorMessage());
+ }
+ if (resultSet.isEmpty()) {
+ throw new RuntimeException("there's no space for user " + user + " to have permission"
+ + " to access.");
+ }
+
+ for (int i = 0; i < resultSet.getRows().size(); i++) {
+ List values = resultSet.rowValues(i).values();
+ String role = values.get(0).asString();
+ String space = values.get(1).asString();
+ if (!role.equalsIgnoreCase("BASIC")) {
+ spaceLabelWriteList.put(space, null);
+ } else {
+ List labels = new ArrayList<>();
+ // get the tags and edges that the user has read permission for
+ String showGrants = String.format("USE %s; show grants %s", space, user);
+ ResultSet userGrantResult = new ResultSet(graphConnection.execute(sessionId,
+ showGrants),
+ authResult.getTimezoneOffset());
+ if (!userGrantResult.isSucceeded()) {
+ throw new RuntimeException("get tags for user " + user
+ + " failed, " + userGrantResult.getErrorMessage());
+ }
+ List readTags = userGrantResult.colValues("READ(TAG)");
+ if (!readTags.isEmpty()) {
+ for (ValueWrapper v : readTags.get(0).asList()) {
+ labels.add(v.asString());
+ }
+ }
+ List readEdges = userGrantResult.colValues("READ(EDGE)");
+ if (!readEdges.isEmpty()) {
+ for (ValueWrapper v : readEdges.get(0).asList()) {
+ labels.add(v.asString());
+ }
+ }
+ spaceLabelWriteList.put(space, labels);
+ }
+ }
+ }
+
+
+ /**
+ * check if the space and the label is in the WriteList
+ *
+ * @param spaceName space name
+ * @param label tag name or edge type name
+ * @return true if spaceName and label in the WriteList
+ */
+ private boolean checkWriteList(String spaceName, String label) {
+ if (spaceLabelWriteList == null) {
+ return true;
+ }
+ if (!spaceLabelWriteList.containsKey(spaceName)) {
+ return false;
+ }
+ if (spaceLabelWriteList.get(spaceName) != null
+ && !spaceLabelWriteList.get(spaceName).contains(label)) {
+ return false;
+ }
+ return true;
+ }
+
private static final int DEFAULT_LIMIT = 1000;
private static final long DEFAULT_START_TIME = 0;
private static final long DEFAULT_END_TIME = Long.MAX_VALUE;
diff --git a/client/src/test/java/com/vesoft/nebula/client/storage/StorageClientTest.java b/client/src/test/java/com/vesoft/nebula/client/storage/StorageClientTest.java
index 020298b0d..95977ce38 100644
--- a/client/src/test/java/com/vesoft/nebula/client/storage/StorageClientTest.java
+++ b/client/src/test/java/com/vesoft/nebula/client/storage/StorageClientTest.java
@@ -8,7 +8,6 @@
import com.vesoft.nebula.client.graph.data.CASignedSSLParam;
import com.vesoft.nebula.client.graph.data.HostAddress;
import com.vesoft.nebula.client.graph.data.SSLParam;
-import com.vesoft.nebula.client.graph.data.SelfSignedSSLParam;
import com.vesoft.nebula.client.storage.data.EdgeRow;
import com.vesoft.nebula.client.storage.data.EdgeTableRow;
import com.vesoft.nebula.client.storage.data.VertexRow;
@@ -17,8 +16,6 @@
import com.vesoft.nebula.client.storage.scan.ScanEdgeResultIterator;
import com.vesoft.nebula.client.storage.scan.ScanVertexResult;
import com.vesoft.nebula.client.storage.scan.ScanVertexResultIterator;
-import com.vesoft.nebula.client.util.ProcessUtil;
-import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import java.util.List;
@@ -43,6 +40,9 @@ public void before() {
assert (false);
}
client = new StorageClient(address);
+ client.setGraphAddress(ip + ":9669");
+ client.setUser("root");
+ client.setPassword("nebula");
}
@After
@@ -57,6 +57,9 @@ public void testStorageClientWithVersionInWhiteList() {
List address = Arrays.asList(new HostAddress(ip, 9559));
StorageClient storageClient = new StorageClient(address);
try {
+ storageClient.setGraphAddress("127.0.0.1:9669");
+ storageClient.setUser("root");
+ storageClient.setPassword("nebula");
storageClient.setVersion("3.0.0");
assert (storageClient.connect());
@@ -73,6 +76,9 @@ public void testStorageClientWithVersionNotInWhiteList() {
List address = Arrays.asList(new HostAddress(ip, 9559));
StorageClient storageClient = new StorageClient(address);
try {
+ storageClient.setGraphAddress("127.0.0.1:9669");
+ storageClient.setUser("root");
+ storageClient.setPassword("nebula");
storageClient.setVersion("INVALID_VERSION");
storageClient.connect();
assert false;
@@ -417,6 +423,10 @@ public void testCASignedSSL() {
"src/test/resources/ssl/client.crt",
"src/test/resources/ssl/client.key");
sslClient = new StorageClient(address, 1000, 1, 1, true, sslParam);
+ sslClient.setGraphAddress("127.0.0.1:8669");
+ sslClient.setUser("root");
+ sslClient.setPassword("nebula");
+ sslClient.setVersion("3.0.0");
sslClient.connect();
ScanVertexResultIterator resultIterator = sslClient.scanVertex(
@@ -424,6 +434,7 @@ public void testCASignedSSL() {
"person");
assertIterator(resultIterator);
} catch (Exception e) {
+ System.out.println("scan failed for cs ssl." + e.getMessage());
e.printStackTrace();
assert (false);
} finally {
diff --git a/examples/pom.xml b/examples/pom.xml
index 0bf73b3c8..1fdb55590 100644
--- a/examples/pom.xml
+++ b/examples/pom.xml
@@ -5,7 +5,7 @@
com.vesoft
nebula
- 3.0-SNAPSHOT
+ 3.7.0-auth
4.0.0
diff --git a/examples/src/main/java/com/vesoft/nebula/examples/StorageClientExample.java b/examples/src/main/java/com/vesoft/nebula/examples/StorageClientExample.java
index 66e1498ce..1f216ec6b 100644
--- a/examples/src/main/java/com/vesoft/nebula/examples/StorageClientExample.java
+++ b/examples/src/main/java/com/vesoft/nebula/examples/StorageClientExample.java
@@ -25,6 +25,10 @@ public static void main(String[] args) {
// input params are the metad's ip and port
StorageClient client = new StorageClient("127.0.0.1", 9559);
try {
+ client.setGraphAddress("127.0.0.1:9669");
+ client.setUser("root");
+ client.setPassword("nebula");
+ client.setVersion("test");
client.connect();
} catch (Exception e) {
LOGGER.error("storage client connect error, ", e);
diff --git a/pom.xml b/pom.xml
index 40f8b3ac2..23e346e20 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
com.vesoft
nebula
pom
- 3.0-SNAPSHOT
+ 3.7.0-auth
UTF-8