From 6322e8c43c0208cf49928a161d82c2ed84b19c72 Mon Sep 17 00:00:00 2001 From: Thespica Date: Sun, 20 Apr 2025 11:51:10 +0800 Subject: [PATCH 1/4] loginAPI support token_expire field --- .../apache/hugegraph/api/auth/LoginAPI.java | 5 +- .../hugegraph/auth/HugeGraphAuthProxy.java | 189 +++++++++--------- .../apache/hugegraph/auth/AuthManager.java | 2 +- .../hugegraph/auth/StandardAuthManager.java | 16 +- .../org/apache/hugegraph/core/AuthTest.java | 138 ++++++------- 5 files changed, 176 insertions(+), 174 deletions(-) diff --git a/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/api/auth/LoginAPI.java b/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/api/auth/LoginAPI.java index 50bf1a78de..5e1bdb6361 100644 --- a/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/api/auth/LoginAPI.java +++ b/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/api/auth/LoginAPI.java @@ -70,7 +70,8 @@ public String login(@Context GraphManager manager, @PathParam("graph") String gr checkCreatingBody(jsonLogin); try { - String token = manager.authManager().loginUser(jsonLogin.name, jsonLogin.password); + String token = manager.authManager() + .loginUser(jsonLogin.name, jsonLogin.password, jsonLogin.expire); HugeGraph g = graph(manager, graph); return manager.serializer(g).writeMap(ImmutableMap.of("token", token)); } catch (AuthenticationException e) { @@ -131,6 +132,8 @@ private static class JsonLogin implements Checkable { private String name; @JsonProperty("user_password") private String password; + @JsonProperty("token_expire") + private long expire; @Override public void checkCreate(boolean isBatch) { diff --git a/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/auth/HugeGraphAuthProxy.java b/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/auth/HugeGraphAuthProxy.java index 63d3912793..a774ce363a 100644 --- a/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/auth/HugeGraphAuthProxy.java +++ b/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/auth/HugeGraphAuthProxy.java @@ -110,15 +110,16 @@ public final class HugeGraphAuthProxy implements HugeGraph { + private static final Logger LOG = Log.logger(HugeGraphAuthProxy.class); + private static final ThreadLocal CONTEXTS = new InheritableThreadLocal<>(); + static { HugeGraph.registerTraversalStrategies(HugeGraphAuthProxy.class); } - private static final Logger LOG = Log.logger(HugeGraphAuthProxy.class); private final Cache usersRoleCache; private final Cache auditLimiters; private final double auditLogMaxRate; - private final HugeGraph hugegraph; private final TaskSchedulerProxy taskScheduler; private final AuthManagerProxy authManager; @@ -141,6 +142,40 @@ public HugeGraphAuthProxy(HugeGraph hugegraph) { LOG.info("Audit log rate limit is {}/s", this.auditLogMaxRate); } + static Context setContext(Context context) { + Context old = CONTEXTS.get(); + CONTEXTS.set(context); + return old; + } + + static void resetContext() { + CONTEXTS.remove(); + } + + private 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(); + } + + private static String getContextString() { + Context context = getContext(); + if (context == null) { + return null; + } + return context.user().toJson(); + } + + static void logUser(User user, String path) { + LOG.info("User '{}' login from client [{}] with path '{}'", + user.username(), user.client(), path); + } + @Override public HugeGraph hugegraph() { this.verifyAdminPermission(); @@ -1016,6 +1051,61 @@ else if (ro.type().isGrantOrUser()) { return result; } + 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 static Context admin() { + return ADMIN; + } + + public User user() { + return this.user; + } + } + + 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)); + } + } + class TaskSchedulerProxy implements TaskScheduler { private final TaskScheduler taskScheduler; @@ -1621,9 +1711,9 @@ public void enabledWhiteIpList(boolean status) { } @Override - public String loginUser(String username, String password) { + public String loginUser(String username, String password, long expire) { try { - return this.authManager.loginUser(username, password); + return this.authManager.loginUser(username, password, expire); } catch (AuthenticationException e) { throw new NotAuthorizedException(e.getMessage(), e); } @@ -1841,95 +1931,4 @@ public String toString() { return this.origin.toString(); } } - - private static final ThreadLocal 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)); - } - } } diff --git a/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/auth/AuthManager.java b/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/auth/AuthManager.java index 51c72fba47..c401ec99a0 100644 --- a/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/auth/AuthManager.java +++ b/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/auth/AuthManager.java @@ -119,7 +119,7 @@ public interface AuthManager { RolePermission rolePermission(AuthElement element); - String loginUser(String username, String password) throws AuthenticationException; + String loginUser(String username, String password, long expire) throws AuthenticationException; void logoutUser(String token); diff --git a/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/auth/StandardAuthManager.java b/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/auth/StandardAuthManager.java index 103c58afca..a3d9d187ae 100644 --- a/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/auth/StandardAuthManager.java +++ b/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/auth/StandardAuthManager.java @@ -114,6 +114,13 @@ public StandardAuthManager(HugeGraphParams graph) { this.ipWhiteListEnabled = false; } + /** + * Maybe can define an proxy class to choose forward or call local + */ + public static boolean isLocal(AuthManager authManager) { + return authManager instanceof StandardAuthManager; + } + private Cache cache(String prefix, long capacity, long expiredTime) { String name = prefix + "-" + this.graph.name(); @@ -634,7 +641,7 @@ private RolePermission rolePermission(HugeTarget target) { } @Override - public String loginUser(String username, String password) + public String loginUser(String username, String password, long expire) throws AuthenticationException { HugeUser user = this.matchUser(username, password); if (user == null) { @@ -717,13 +724,6 @@ public void enabledWhiteIpList(boolean status) { this.ipWhiteListEnabled = status; } - /** - * Maybe can define an proxy class to choose forward or call local - */ - public static boolean isLocal(AuthManager authManager) { - return authManager instanceof StandardAuthManager; - } - public R commit(Callable callable) { this.groups.autoCommit(false); this.access.autoCommit(false); diff --git a/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/AuthTest.java b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/AuthTest.java index 1db7af9244..60bfdace8a 100644 --- a/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/AuthTest.java +++ b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/AuthTest.java @@ -55,6 +55,63 @@ public class AuthTest extends BaseCoreTest { + private static Id makeProjectAndAddGraph(HugeGraph graph, + String projectName, + String graphName) { + HugeProject project = makeProject(projectName, ""); + AuthManager authManager = graph.authManager(); + Id projectId = authManager.createProject(project); + projectId = authManager.projectAddGraphs(projectId, + ImmutableSet.of(graphName)); + Assert.assertNotNull(projectId); + return projectId; + } + + private static HugeProject makeProject(String name, String desc) { + HugeProject project = new HugeProject(name, desc); + project.creator("admin"); + return project; + } + + private static HugeUser makeUser(String name, String password) { + HugeUser user = new HugeUser(name); + user.password(password); + user.creator("admin"); + return user; + } + + private static HugeGroup makeGroup(String name) { + HugeGroup group = new HugeGroup(name); + group.creator("admin"); + return group; + } + + private static HugeTarget makeTarget(String name, String url) { + HugeTarget target = new HugeTarget(name, url); + target.creator("admin"); + return target; + } + + private static HugeTarget makeTarget(String name, String graph, String url, + List ress) { + HugeTarget target = new HugeTarget(name, graph, url, ress); + target.creator("admin"); + return target; + } + + private static HugeBelong makeBelong(Id user, Id group) { + HugeBelong belong = new HugeBelong(user, group); + belong.creator("admin"); + return belong; + } + + private static HugeAccess makeAccess(Id group, Id target, + HugePermission permission) { + HugeAccess access = new HugeAccess(group, target, permission); + access.creator("admin"); + return access; + } + @After public void clearAll() { HugeGraph graph = graph(); @@ -301,7 +358,7 @@ public void testCreateGroup() { group = authManager.getGroup(id); Assert.assertEquals("group1", group.name()); - Assert.assertEquals(null, group.description()); + Assert.assertNull(group.description()); Assert.assertEquals(group.create(), group.update()); Assert.assertEquals(ImmutableMap.of("group_name", "group1", @@ -632,7 +689,7 @@ public void testCreateBelong() { HugeBelong belong = authManager.getBelong(id1); Assert.assertEquals(user, belong.source()); Assert.assertEquals(group1, belong.target()); - Assert.assertEquals(null, belong.description()); + Assert.assertNull(belong.description()); Assert.assertEquals(belong.create(), belong.update()); Map expected = new HashMap<>(); @@ -647,7 +704,7 @@ public void testCreateBelong() { belong = authManager.getBelong(id2); Assert.assertEquals(user, belong.source()); Assert.assertEquals(group2, belong.target()); - Assert.assertEquals(null, belong.description()); + Assert.assertNull(belong.description()); Assert.assertEquals(belong.create(), belong.update()); expected = new HashMap<>(); @@ -1321,11 +1378,11 @@ public void testLogin() throws AuthenticationException { authManager.createUser(user); // Login - authManager.loginUser("test", "pass"); + authManager.loginUser("test", "pass", 10080L); // Invalid username or password Assert.assertThrows(AuthenticationException.class, () -> { - authManager.loginUser("huge", "graph"); + authManager.loginUser("huge", "graph", 10080L); }, e -> { Assert.assertContains("Incorrect username or password", e.getMessage()); }); @@ -1338,7 +1395,7 @@ public void testValidateUserByToken() throws AuthenticationException { HugeUser user = makeUser("test", StringEncoding.hashPassword("pass")); Id userId = authManager.createUser(user); - String token = authManager.loginUser("test", "pass"); + String token = authManager.loginUser("test", "pass", 10080L); UserWithRole userWithRole; userWithRole = authManager.validateUser(token); @@ -1375,7 +1432,7 @@ public void testLogout() throws AuthenticationException { Id userId = authManager.createUser(user); // Login - String token = authManager.loginUser("test", "pass"); + String token = authManager.loginUser("test", "pass", 10080L); // Logout Cache tokenCache = Whitebox.getInternalState(authManager, @@ -1500,79 +1557,22 @@ public void testListProject() { List projects = authManager.listAllProject(1); Assert.assertNotNull(projects); - Assert.assertTrue(projects.size() == 1); + Assert.assertEquals(1, projects.size()); projects = authManager.listAllProject(-1); Assert.assertNotNull(projects); - Assert.assertTrue(projects.size() == 3); + Assert.assertEquals(3, projects.size()); projects = authManager.listAllProject(3); Assert.assertNotNull(projects); - Assert.assertTrue(projects.size() == 3); + Assert.assertEquals(3, projects.size()); projects = authManager.listAllProject(4); Assert.assertNotNull(projects); - Assert.assertTrue(projects.size() == 3); + Assert.assertEquals(3, projects.size()); projects = authManager.listAllProject(2); Assert.assertNotNull(projects); - Assert.assertTrue(projects.size() == 2); - } - - private static Id makeProjectAndAddGraph(HugeGraph graph, - String projectName, - String graphName) { - HugeProject project = makeProject(projectName, ""); - AuthManager authManager = graph.authManager(); - Id projectId = authManager.createProject(project); - projectId = authManager.projectAddGraphs(projectId, - ImmutableSet.of(graphName)); - Assert.assertNotNull(projectId); - return projectId; - } - - private static HugeProject makeProject(String name, String desc) { - HugeProject project = new HugeProject(name, desc); - project.creator("admin"); - return project; - } - - private static HugeUser makeUser(String name, String password) { - HugeUser user = new HugeUser(name); - user.password(password); - user.creator("admin"); - return user; - } - - private static HugeGroup makeGroup(String name) { - HugeGroup group = new HugeGroup(name); - group.creator("admin"); - return group; - } - - private static HugeTarget makeTarget(String name, String url) { - HugeTarget target = new HugeTarget(name, url); - target.creator("admin"); - return target; - } - - private static HugeTarget makeTarget(String name, String graph, String url, - List ress) { - HugeTarget target = new HugeTarget(name, graph, url, ress); - target.creator("admin"); - return target; - } - - private static HugeBelong makeBelong(Id user, Id group) { - HugeBelong belong = new HugeBelong(user, group); - belong.creator("admin"); - return belong; - } - - private static HugeAccess makeAccess(Id group, Id target, - HugePermission permission) { - HugeAccess access = new HugeAccess(group, target, permission); - access.creator("admin"); - return access; + Assert.assertEquals(2, projects.size()); } } From abfd0429b8a3c6bb187eae1a9658552f3b107a7d Mon Sep 17 00:00:00 2001 From: Thespica Date: Tue, 22 Apr 2025 13:01:54 +0800 Subject: [PATCH 2/4] upgrade server's ci system from ubuntu-20.04 to ubuntu-24.04 --- .github/workflows/server-ci.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/server-ci.yml b/.github/workflows/server-ci.yml index 05f8ae9bc0..d81d413a14 100644 --- a/.github/workflows/server-ci.yml +++ b/.github/workflows/server-ci.yml @@ -10,8 +10,7 @@ on: jobs: build-server: - # TODO: we need test & replace it to ubuntu-24.04 or ubuntu-latest - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 env: USE_STAGE: 'false' # Whether to include the stage repository. TRAVIS_DIR: hugegraph-server/hugegraph-dist/src/assembly/travis From 7c0f6cb1ce178b907c4316b4bcd8d03e3d05cc1a Mon Sep 17 00:00:00 2001 From: imbajin Date: Mon, 28 Apr 2025 15:20:16 +0800 Subject: [PATCH 3/4] Update .github/workflows/server-ci.yml --- .github/workflows/server-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/server-ci.yml b/.github/workflows/server-ci.yml index d81d413a14..06942bb8e3 100644 --- a/.github/workflows/server-ci.yml +++ b/.github/workflows/server-ci.yml @@ -10,7 +10,7 @@ on: jobs: build-server: - runs-on: ubuntu-24.04 + runs-on: ubuntu-22.04 env: USE_STAGE: 'false' # Whether to include the stage repository. TRAVIS_DIR: hugegraph-server/hugegraph-dist/src/assembly/travis From 1829aadec27203b8c599cc2336a0632148354329 Mon Sep 17 00:00:00 2001 From: Thespica Date: Thu, 1 May 2025 22:36:54 +0800 Subject: [PATCH 4/4] add overload for loginUser and mark as TODO --- .../java/org/apache/hugegraph/auth/HugeGraphAuthProxy.java | 6 ++++++ .../main/java/org/apache/hugegraph/auth/AuthManager.java | 2 ++ .../org/apache/hugegraph/auth/StandardAuthManager.java | 7 +++++++ 3 files changed, 15 insertions(+) diff --git a/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/auth/HugeGraphAuthProxy.java b/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/auth/HugeGraphAuthProxy.java index a774ce363a..383504e805 100644 --- a/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/auth/HugeGraphAuthProxy.java +++ b/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/auth/HugeGraphAuthProxy.java @@ -1710,6 +1710,12 @@ public void enabledWhiteIpList(boolean status) { this.authManager.enabledWhiteIpList(status); } + @Override + public String loginUser(String username, String password) { + return this.loginUser(username, password, -1L); + } + + // TODO: the expire haven't been implemented yet @Override public String loginUser(String username, String password, long expire) { try { diff --git a/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/auth/AuthManager.java b/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/auth/AuthManager.java index c401ec99a0..a2c76d3957 100644 --- a/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/auth/AuthManager.java +++ b/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/auth/AuthManager.java @@ -119,6 +119,8 @@ public interface AuthManager { RolePermission rolePermission(AuthElement element); + String loginUser(String username, String password) throws AuthenticationException; + String loginUser(String username, String password, long expire) throws AuthenticationException; void logoutUser(String token); diff --git a/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/auth/StandardAuthManager.java b/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/auth/StandardAuthManager.java index a3d9d187ae..1ec2711d72 100644 --- a/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/auth/StandardAuthManager.java +++ b/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/auth/StandardAuthManager.java @@ -640,6 +640,13 @@ private RolePermission rolePermission(HugeTarget target) { return role; } + @Override + public String loginUser(String username, String password) + throws AuthenticationException { + return this.loginUser(username, password, -1L); + } + + // TODO: the expire haven't been implemented yet @Override public String loginUser(String username, String password, long expire) throws AuthenticationException {