From 461948ee262cc696853a8c8ba1306e6b371e3e89 Mon Sep 17 00:00:00 2001 From: coderzc Date: Tue, 1 Jun 2021 12:31:01 +0800 Subject: [PATCH] add user-api test (#1456) * add user test * open auth properties * fix auth startup of memory backend bug --- .../hugegraph/auth/ConfigAuthenticator.java | 15 +- .../hugegraph/auth/HugeAuthenticator.java | 2 + .../hugegraph/auth/StandardAuthenticator.java | 18 +- .../baidu/hugegraph/core/GraphManager.java | 14 +- .../src/assembly/travis/run-api-test.sh | 21 ++ .../src/assembly/travis/start-server.sh | 2 +- .../com/baidu/hugegraph/cmd/InitStore.java | 2 +- .../com/baidu/hugegraph/api/ApiTestSuite.java | 3 +- .../com/baidu/hugegraph/api/BaseApiTest.java | 5 + .../baidu/hugegraph/api/GremlinApiTest.java | 14 +- .../com/baidu/hugegraph/api/UserApiTest.java | 182 ++++++++++++++++++ 11 files changed, 257 insertions(+), 21 deletions(-) create mode 100644 hugegraph-test/src/main/java/com/baidu/hugegraph/api/UserApiTest.java diff --git a/hugegraph-api/src/main/java/com/baidu/hugegraph/auth/ConfigAuthenticator.java b/hugegraph-api/src/main/java/com/baidu/hugegraph/auth/ConfigAuthenticator.java index 18529fa244..ede416871e 100644 --- a/hugegraph-api/src/main/java/com/baidu/hugegraph/auth/ConfigAuthenticator.java +++ b/hugegraph-api/src/main/java/com/baidu/hugegraph/auth/ConfigAuthenticator.java @@ -22,6 +22,7 @@ import java.net.InetAddress; import java.util.HashMap; import java.util.Map; +import java.util.Objects; import org.apache.commons.lang.NotImplementedException; import org.apache.tinkerpop.gremlin.groovy.jsr223.dsl.credential.CredentialGraphTokens; @@ -81,11 +82,21 @@ public RolePermission authenticate(final String username, @Override public AuthManager authManager() { - throw new NotImplementedException("AuthManager is unsupported"); + throw new NotImplementedException( + "AuthManager is unsupported by ConfigAuthenticator"); + } + + @Override + public void initAdminUser(String password) throws Exception { + String adminToken = this.tokens.get(USER_ADMIN); + E.checkArgument(Objects.equals(adminToken, password), + "The password can't be changed for " + + "ConfigAuthenticator"); } @Override public SaslNegotiator newSaslNegotiator(InetAddress remoteAddress) { - throw new NotImplementedException("SaslNegotiator is unsupported"); + throw new NotImplementedException( + "SaslNegotiator is unsupported by ConfigAuthenticator"); } } diff --git a/hugegraph-api/src/main/java/com/baidu/hugegraph/auth/HugeAuthenticator.java b/hugegraph-api/src/main/java/com/baidu/hugegraph/auth/HugeAuthenticator.java index c8b39d3191..f0c07b17d8 100644 --- a/hugegraph-api/src/main/java/com/baidu/hugegraph/auth/HugeAuthenticator.java +++ b/hugegraph-api/src/main/java/com/baidu/hugegraph/auth/HugeAuthenticator.java @@ -121,6 +121,8 @@ public default boolean verifyRole(RolePermission role) { } } + public void initAdminUser(String password) throws Exception; + public static HugeAuthenticator loadAuthenticator(HugeConfig conf) { String authClass = conf.get(ServerOptions.AUTHENTICATOR); if (authClass.isEmpty()) { diff --git a/hugegraph-api/src/main/java/com/baidu/hugegraph/auth/StandardAuthenticator.java b/hugegraph-api/src/main/java/com/baidu/hugegraph/auth/StandardAuthenticator.java index 8f11f4b4d7..708a5094e0 100644 --- a/hugegraph-api/src/main/java/com/baidu/hugegraph/auth/StandardAuthenticator.java +++ b/hugegraph-api/src/main/java/com/baidu/hugegraph/auth/StandardAuthenticator.java @@ -47,6 +47,12 @@ private HugeGraph graph() { } private void initAdminUser() throws Exception { + this.initAdminUser(this.inputPassword()); + + this.graph.close(); + } + + public void initAdminUser(String password) throws Exception { // Not allowed to call by non main thread String caller = Thread.currentThread().getName(); E.checkState(caller.equals("main"), "Invalid caller '%s'", caller); @@ -56,12 +62,10 @@ private void initAdminUser() throws Exception { if (StandardAuthManager.isLocal(authManager) && authManager.findUser(HugeAuthenticator.USER_ADMIN) == null) { HugeUser admin = new HugeUser(HugeAuthenticator.USER_ADMIN); - admin.password(StringEncoding.hashPassword(this.inputPassword())); + admin.password(StringEncoding.hashPassword(password)); admin.creator(HugeAuthenticator.USER_SYSTEM); authManager.createUser(admin); } - - this.graph.close(); } private String inputPassword() { @@ -141,15 +145,17 @@ public SaslNegotiator newSaslNegotiator(InetAddress remoteAddress) { throw new NotImplementedException("SaslNegotiator is unsupported"); } - public static void initAdminUser(String restConfFile) throws Exception { + public static void initAdminUserIfNeeded(String confFile) throws Exception { StandardAuthenticator auth = new StandardAuthenticator(); - HugeConfig config = new HugeConfig(restConfFile); + HugeConfig config = new HugeConfig(confFile); String authClass = config.get(ServerOptions.AUTHENTICATOR); if (authClass.isEmpty()) { return; } config.addProperty(INITING_STORE, true); auth.setup(config); - auth.initAdminUser(); + if (auth.graph().backendStoreFeatures().supportsPersistence()) { + auth.initAdminUser(); + } } } diff --git a/hugegraph-api/src/main/java/com/baidu/hugegraph/core/GraphManager.java b/hugegraph-api/src/main/java/com/baidu/hugegraph/core/GraphManager.java index a30f05a2e3..1a9f778ab9 100644 --- a/hugegraph-api/src/main/java/com/baidu/hugegraph/core/GraphManager.java +++ b/hugegraph-api/src/main/java/com/baidu/hugegraph/core/GraphManager.java @@ -82,7 +82,7 @@ public GraphManager(HugeConfig conf) { // this.installLicense(conf, ""); // Raft will load snapshot firstly then launch election and replay log this.waitGraphsStarted(); - this.checkBackendVersionOrExit(); + this.checkBackendVersionOrExit(conf); this.startRpcServer(); this.serverStarted(conf); this.addMetrics(conf); @@ -256,12 +256,22 @@ private void loadGraph(String name, String path) { } } - private void checkBackendVersionOrExit() { + private void checkBackendVersionOrExit(HugeConfig config) { for (String graph : this.graphs()) { // TODO: close tx from main thread HugeGraph hugegraph = this.graph(graph); if (!hugegraph.backendStoreFeatures().supportsPersistence()) { hugegraph.initBackend(); + if (this.requireAuthentication()) { + String token = config.get(ServerOptions.AUTH_ADMIN_TOKEN); + try { + this.authenticator.initAdminUser(token); + } catch (Exception e) { + throw new BackendException( + "The backend store of '%s' can't " + + "initialize admin user", hugegraph.name()); + } + } } BackendStoreSystemInfo info = hugegraph.backendStoreSystemInfo(); if (!info.exists()) { diff --git a/hugegraph-dist/src/assembly/travis/run-api-test.sh b/hugegraph-dist/src/assembly/travis/run-api-test.sh index 8f66abd13d..87db73782e 100755 --- a/hugegraph-dist/src/assembly/travis/run-api-test.sh +++ b/hugegraph-dist/src/assembly/travis/run-api-test.sh @@ -5,9 +5,30 @@ set -ev TRAVIS_DIR=`dirname $0` VERSION=`mvn help:evaluate -Dexpression=project.version -q -DforceStdout` SERVER_DIR=hugegraph-$VERSION +CONF=$SERVER_DIR/conf/hugegraph.properties +REST_SERVER_CONF=$SERVER_DIR/conf/rest-server.properties +GREMLIN_SERVER_CONF=$SERVER_DIR/conf/gremlin-server.yaml mvn package -DskipTests + +# config rest-server +sed -i 's/#auth.authenticator=/auth.authenticator=com.baidu.hugegraph.auth.StandardAuthenticator/' $REST_SERVER_CONF +sed -i 's/#auth.admin_token=/auth.admin_token=pa/' $REST_SERVER_CONF + +# config hugegraph.properties +sed -i 's/gremlin.graph=.*/gremlin.graph=com.baidu.hugegraph.auth.HugeFactoryAuthProxy/' $CONF + +# config gremlin-server +echo " +authentication: { + authenticator: com.baidu.hugegraph.auth.StandardAuthenticator, + authenticationHandler: com.baidu.hugegraph.auth.WsAndHttpBasicAuthHandler, + config: {tokens: conf/rest-server.properties} +}" >> $GREMLIN_SERVER_CONF + $TRAVIS_DIR/start-server.sh $SERVER_DIR || (cat $SERVER_DIR/logs/hugegraph-server.log && exit 1) + +# run api-test mvn test -P api-test,$BACKEND || (cat $SERVER_DIR/logs/hugegraph-server.log && exit 1) $TRAVIS_DIR/build-report.sh $TRAVIS_DIR/stop-server.sh diff --git a/hugegraph-dist/src/assembly/travis/start-server.sh b/hugegraph-dist/src/assembly/travis/start-server.sh index 8716654824..ccda32728b 100755 --- a/hugegraph-dist/src/assembly/travis/start-server.sh +++ b/hugegraph-dist/src/assembly/travis/start-server.sh @@ -37,4 +37,4 @@ fi echo "schema.sync_deletion=true" >> $CONF AGENT_JAR=${HOME_DIR}/${TRAVIS_DIR}/jacocoagent.jar -$BIN/init-store.sh && $BIN/start-hugegraph.sh -j "-javaagent:${AGENT_JAR}=includes=*,port=36320,destfile=jacoco-it.exec,output=tcpserver" -v +echo -e "pa" | $BIN/init-store.sh && $BIN/start-hugegraph.sh -j "-javaagent:${AGENT_JAR}=includes=*,port=36320,destfile=jacoco-it.exec,output=tcpserver" -v diff --git a/hugegraph-dist/src/main/java/com/baidu/hugegraph/cmd/InitStore.java b/hugegraph-dist/src/main/java/com/baidu/hugegraph/cmd/InitStore.java index f8ad7acb6d..a51e66ad26 100644 --- a/hugegraph-dist/src/main/java/com/baidu/hugegraph/cmd/InitStore.java +++ b/hugegraph-dist/src/main/java/com/baidu/hugegraph/cmd/InitStore.java @@ -99,7 +99,7 @@ public static void main(String[] args) throws Exception { initGraph(configPath); } - StandardAuthenticator.initAdminUser(restConfFile); + StandardAuthenticator.initAdminUserIfNeeded(restConfFile); HugeFactory.shutdown(30L); } diff --git a/hugegraph-test/src/main/java/com/baidu/hugegraph/api/ApiTestSuite.java b/hugegraph-test/src/main/java/com/baidu/hugegraph/api/ApiTestSuite.java index af8fadda93..05b69c865b 100644 --- a/hugegraph-test/src/main/java/com/baidu/hugegraph/api/ApiTestSuite.java +++ b/hugegraph-test/src/main/java/com/baidu/hugegraph/api/ApiTestSuite.java @@ -35,7 +35,8 @@ EdgeApiTest.class, TaskApiTest.class, GremlinApiTest.class, - MetricsApiTest.class + MetricsApiTest.class, + UserApiTest.class }) public class ApiTestSuite { diff --git a/hugegraph-test/src/main/java/com/baidu/hugegraph/api/BaseApiTest.java b/hugegraph-test/src/main/java/com/baidu/hugegraph/api/BaseApiTest.java index 6cfb7d359e..8637fffbfd 100644 --- a/hugegraph-test/src/main/java/com/baidu/hugegraph/api/BaseApiTest.java +++ b/hugegraph-test/src/main/java/com/baidu/hugegraph/api/BaseApiTest.java @@ -34,6 +34,7 @@ import javax.ws.rs.client.WebTarget; import javax.ws.rs.core.Response; +import org.glassfish.jersey.client.authentication.HttpAuthenticationFeature; import org.glassfish.jersey.client.filter.EncodingFilter; import org.glassfish.jersey.message.GZipEncoder; import org.junit.After; @@ -52,6 +53,8 @@ public class BaseApiTest { private static String BASE_URL = "http://127.0.0.1:8080"; private static String GRAPH = "hugegraph"; + private static final String USERNAME = "admin"; + private static final String PASSWORD = "pa"; private static final String URL_PREFIX = "graphs/" + GRAPH; private static final String SCHEMA_PKS = "/schema/propertykeys"; @@ -98,6 +101,8 @@ public RestClient(String url) { this.client = ClientBuilder.newClient(); this.client.register(EncodingFilter.class); this.client.register(GZipEncoder.class); + this.client.register(HttpAuthenticationFeature.basic(USERNAME, + PASSWORD)); this.target = this.client.target(url); } diff --git a/hugegraph-test/src/main/java/com/baidu/hugegraph/api/GremlinApiTest.java b/hugegraph-test/src/main/java/com/baidu/hugegraph/api/GremlinApiTest.java index 19ad475add..ee18ddb2b9 100644 --- a/hugegraph-test/src/main/java/com/baidu/hugegraph/api/GremlinApiTest.java +++ b/hugegraph-test/src/main/java/com/baidu/hugegraph/api/GremlinApiTest.java @@ -86,14 +86,12 @@ public void testScript() { @Test public void testClearAndInit() { String body = "{" - + "\"gremlin\":\"hugegraph.clearBackend()\"," - + "\"bindings\":{}," - + "\"language\":\"gremlin-groovy\"," - + "\"aliases\":{\"g\":\"__g_hugegraph\"}}"; - assertResponseStatus(200, client().post(path, body)); - - body = "{" - + "\"gremlin\":\"hugegraph.initBackend()\"," + + "\"gremlin\":\"" + + "def auth = hugegraph.hugegraph().authManager();" + + "def admin = auth.findUser('admin');" + + "hugegraph.clearBackend();" + + "hugegraph.initBackend();" + + "auth.createUser(admin);\"," + "\"bindings\":{}," + "\"language\":\"gremlin-groovy\"," + "\"aliases\":{\"g\":\"__g_hugegraph\"}}"; diff --git a/hugegraph-test/src/main/java/com/baidu/hugegraph/api/UserApiTest.java b/hugegraph-test/src/main/java/com/baidu/hugegraph/api/UserApiTest.java new file mode 100644 index 0000000000..c42276ed7a --- /dev/null +++ b/hugegraph-test/src/main/java/com/baidu/hugegraph/api/UserApiTest.java @@ -0,0 +1,182 @@ +/* + * Copyright 2017 HugeGraph Authors + * + * 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 com.baidu.hugegraph.api; + +import java.util.List; +import java.util.Map; + +import javax.ws.rs.core.Response; + +import org.apache.tinkerpop.shaded.jackson.core.type.TypeReference; +import org.hamcrest.CoreMatchers; +import org.junit.After; +import org.junit.Assert; +import org.junit.Test; + +import com.baidu.hugegraph.util.JsonUtil; +import com.google.common.collect.ImmutableMap; + +public class UserApiTest extends BaseApiTest { + + private static final String path = "graphs/hugegraph/auth/users"; + private static final int NO_LIMIT = -1; + + @After + public void teardown() throws Exception { + super.teardown(); + Response r = this.client().get(path, + ImmutableMap.of("limit", NO_LIMIT)); + String result = r.readEntity(String.class); + Map>> resultMap = + JsonUtil.fromJson(result, + new TypeReference>>>() {}); + List> users = resultMap.get("users"); + for (Map user : users) { + if (user.get("user_name").equals("admin")) { + continue; + } + this.client().delete(path, (String) user.get("id")); + } + } + + @Test + public void testCreate() { + String user1 = "{\"user_name\":\"user1\",\"user_password\":\"p1\"," + + "\"user_email\":\"user1@baidu.com\",\"user_phone\":" + + "\"123456789\",\"user_avatar\":\"image1.jpg\"}"; + + String user2 = "{\"user_name\":\"user2\",\"user_password\":\"p2\"," + + "\"user_email\":\"user2@baidu.com\"," + + "\"user_phone\":\"1357924680\"," + + "\"user_avatar\":\"image2.jpg\"}"; + + Response r = client().post(path, user1); + String result = assertResponseStatus(201, r); + Response r2 = client().post(path, user2); + String result2 = assertResponseStatus(201, r2); + + assertJsonContains(result, "user_name"); + assertJsonContains(result, "user_password"); + assertJsonContains(result, "user_email"); + assertJsonContains(result, "user_phone"); + assertJsonContains(result, "user_avatar"); + + assertJsonContains(result2, "user_name"); + assertJsonContains(result2, "user_password"); + assertJsonContains(result2, "user_email"); + assertJsonContains(result2, "user_phone"); + assertJsonContains(result2, "user_avatar"); + + Response r3 = client().post(path, "{}"); + assertResponseStatus(400, r3); + + String user3 = "{\"user_name\":\"user1\",\"user_password\":\"p1\"," + + "\"user_email\":\"user1@baidu.com\"," + + "\"user_phone\":\"123456789\",\"user_avatar\":\"image1" + + ".jpg\"}"; + Response r4 = client().post(path, user3); + String result4 = assertResponseStatus(400, r4); + String message = assertJsonContains(result4, "message"); + Assert.assertThat(message, + CoreMatchers.containsString("that already exists")); + } + + @Test + public void testList() { + createUser("test1"); + createUser("test2"); + createUser("test3"); + List> users = listUsers(); + Assert.assertEquals(4, users.size()); + } + + @Test + public void testGetUser() { + createUser("test1"); + createUser("test2"); + List> users = listUsers(); + for (Map user : users) { + Response r = client().get(path, (String) user.get("id")); + String result = assertResponseStatus(200, r); + assertJsonContains(result, "user_name"); + } + } + + @Test + public void testUpdate() { + createUser("test1"); + createUser("test2"); + List> users = listUsers(); + for (Map user : users) { + if (user.get("user_name").equals("admin")) { + continue; + } + String user1 = "{\"user_password\":\"p1\"," + + "\"user_email\":\"user1@baidu.com\"," + + "\"user_phone\":\"111111\"," + + "\"user_avatar\":\"image1" + + ".jpg\"}"; + Response r = client().put(path, (String) user.get("id"), user1, + ImmutableMap.of()); + assertResponseStatus(200, r); + } + } + + @Test + public void testDelete() { + createUser("test1"); + createUser("test2"); + createUser("test3"); + + List> users = listUsers(); + for (Map user : users) { + if (user.get("user_name").equals("admin")) { + continue; + } + Response r = client().delete(path, (String) user.get("id")); + } + Response r = client().delete(path, "test1"); + String result = assertResponseStatus(400, r); + String message = assertJsonContains(result, "message"); + Assert.assertThat(message, + CoreMatchers.containsString("Invalid user id:")); + } + + protected void createUser(String name) { + String user = "{\"user_name\":\"" + name + "\",\"user_password\":\"p1" + + "\", \"user_email\":\"user1@baidu.com\"," + + "\"user_phone\":\"123456789\",\"user_avatar\":\"image1" + + ".jpg\"}"; + Response r = this.client().post(path, user); + assertResponseStatus(201, r); + } + + protected List> listUsers() { + Response r = this.client().get(path, ImmutableMap.of("limit", + NO_LIMIT)); + String result = assertResponseStatus(200, r); + + Map>> resultMap = + JsonUtil.fromJson(result, new TypeReference>>>() {}); + return resultMap.get("users"); + } +}