From 67fb95b7c74c6a863ef436bf5fa3bf055f063704 Mon Sep 17 00:00:00 2001 From: guoshoujing Date: Thu, 3 Jun 2021 15:15:09 +0800 Subject: [PATCH 01/15] add token auth interface --- .../hugegraph/auth/HugeGraphAuthProxy.java | 33 ++++++++++++++++--- .../hugegraph/auth/StandardAuthenticator.java | 7 ++-- .../com/baidu/hugegraph/auth/AuthManager.java | 6 +++- .../hugegraph/auth/StandardAuthManager.java | 18 +++++++++- 4 files changed, 56 insertions(+), 8 deletions(-) diff --git a/hugegraph-api/src/main/java/com/baidu/hugegraph/auth/HugeGraphAuthProxy.java b/hugegraph-api/src/main/java/com/baidu/hugegraph/auth/HugeGraphAuthProxy.java index 807d3c4f71..3ce09cf28f 100644 --- a/hugegraph-api/src/main/java/com/baidu/hugegraph/auth/HugeGraphAuthProxy.java +++ b/hugegraph-api/src/main/java/com/baidu/hugegraph/auth/HugeGraphAuthProxy.java @@ -1365,19 +1365,44 @@ public RolePermission rolePermission(AuthElement element) { } @Override - public RolePermission loginUser(String username, String password) { - // Can't verifyPermission() here, login first with temp permission + public RolePermission validateUser(String username, String password) { + // Can't verifyPermission() here, validate first with tmp permission Context context = setContext(Context.admin()); try { - return this.authManager.loginUser(username, password); + return this.authManager.validateUser(username, password); } catch (Exception e) { - LOG.error("Failed to login user {} with error: ", username, e); + LOG.error("Failed to validate user {} with error: ", + username, e); throw e; } finally { setContext(context); } } + @Override + public RolePermission validateUser(String token) { + // Can't verifyPermission() here, validate first with tmp permission + Context context = setContext(Context.admin()); + try { + 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 String loginUser(String username, String password) { + return null; + } + + @Override + public void logoutUser(String token) { + + } + private void switchAuthManager(AuthManager authManager) { this.authManager = authManager; HugeGraphAuthProxy.this.hugegraph.switchAuthManager(authManager); 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 708a5094e0..3f428c1834 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 @@ -125,8 +125,11 @@ public RolePermission authenticate(String username, String password) { E.checkArgumentNotNull(password, "The password parameter can't be null"); - RolePermission role = this.graph().authManager().loginUser(username, - password); + // switch here + + RolePermission role = this.authManager().validateUser(username, + password); + if (role == null) { role = ROLE_NONE; } else if (username.equals(USER_ADMIN)) { diff --git a/hugegraph-core/src/main/java/com/baidu/hugegraph/auth/AuthManager.java b/hugegraph-core/src/main/java/com/baidu/hugegraph/auth/AuthManager.java index 57561466ee..e990202999 100644 --- a/hugegraph-core/src/main/java/com/baidu/hugegraph/auth/AuthManager.java +++ b/hugegraph-core/src/main/java/com/baidu/hugegraph/auth/AuthManager.java @@ -72,5 +72,9 @@ public interface AuthManager { public HugeUser matchUser(String name, String password); public RolePermission rolePermission(AuthElement element); - public RolePermission loginUser(String username, String password); + public String loginUser(String username, String password); + public void logoutUser(String token); + + public RolePermission validateUser(String username, String password); + public RolePermission validateUser(String token); } diff --git a/hugegraph-core/src/main/java/com/baidu/hugegraph/auth/StandardAuthManager.java b/hugegraph-core/src/main/java/com/baidu/hugegraph/auth/StandardAuthManager.java index e5f013b9c8..6abb67390b 100644 --- a/hugegraph-core/src/main/java/com/baidu/hugegraph/auth/StandardAuthManager.java +++ b/hugegraph-core/src/main/java/com/baidu/hugegraph/auth/StandardAuthManager.java @@ -343,6 +343,7 @@ public List listAccessByTarget(Id target, long limit) { public HugeUser matchUser(String name, String password) { E.checkArgumentNotNull(name, "User name can't be null"); E.checkArgumentNotNull(password, "User password can't be null"); + HugeUser user = this.findUser(name); if (user == null) { return null; @@ -425,7 +426,7 @@ private RolePermission rolePermission(HugeTarget target) { } @Override - public RolePermission loginUser(String username, String password) { + public RolePermission validateUser(String username, String password) { HugeUser user = this.matchUser(username, password); if (user == null) { return null; @@ -433,6 +434,21 @@ public RolePermission loginUser(String username, String password) { return this.rolePermission(user); } + @Override + public String loginUser(String username, String password) { + return null; + } + + @Override + public void logoutUser(String token) { + + } + + @Override + public RolePermission validateUser(String token) { + return null; + } + /** * Maybe can define an proxy class to choose forward or call local */ From 266f5190aedbb32a3dac2c06fc7e0e775552fddd Mon Sep 17 00:00:00 2001 From: guoshoujing Date: Tue, 15 Jun 2021 13:34:09 +0800 Subject: [PATCH 02/15] implement login auth token module --- .../baidu/hugegraph/api/auth/LoginAPI.java | 144 ++++++++++++++++++ .../api/filter/AuthenticationFilter.java | 76 ++++++--- .../hugegraph/auth/ConfigAuthenticator.java | 7 +- .../hugegraph/auth/HugeAuthenticator.java | 11 +- .../hugegraph/auth/HugeGraphAuthProxy.java | 12 +- .../hugegraph/auth/StandardAuthenticator.java | 30 ++-- hugegraph-core/pom.xml | 17 +++ .../com/baidu/hugegraph/auth/AuthManager.java | 7 +- .../hugegraph/auth/StandardAuthManager.java | 68 +++++++-- .../baidu/hugegraph/auth/TokenGenerator.java | 60 ++++++++ .../baidu/hugegraph/auth/UserWithRole.java | 43 ++++++ .../baidu/hugegraph/config/AuthOptions.java | 95 ++++++++++++ .../baidu/hugegraph/dist/RegisterUtil.java | 2 + 13 files changed, 514 insertions(+), 58 deletions(-) create mode 100644 hugegraph-api/src/main/java/com/baidu/hugegraph/api/auth/LoginAPI.java create mode 100644 hugegraph-core/src/main/java/com/baidu/hugegraph/auth/TokenGenerator.java create mode 100644 hugegraph-core/src/main/java/com/baidu/hugegraph/auth/UserWithRole.java create mode 100644 hugegraph-core/src/main/java/com/baidu/hugegraph/config/AuthOptions.java diff --git a/hugegraph-api/src/main/java/com/baidu/hugegraph/api/auth/LoginAPI.java b/hugegraph-api/src/main/java/com/baidu/hugegraph/api/auth/LoginAPI.java new file mode 100644 index 0000000000..08545e7054 --- /dev/null +++ b/hugegraph-api/src/main/java/com/baidu/hugegraph/api/auth/LoginAPI.java @@ -0,0 +1,144 @@ +/* + * 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.auth; + +import java.util.Map; + +import javax.inject.Singleton; +import javax.ws.rs.BadRequestException; +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.HeaderParam; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.HttpHeaders; + +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; + +import com.baidu.hugegraph.HugeGraph; +import com.baidu.hugegraph.api.API; +import com.baidu.hugegraph.api.filter.StatusFilter; +import com.baidu.hugegraph.api.filter.StatusFilter.Status; +import com.baidu.hugegraph.core.GraphManager; +import com.baidu.hugegraph.define.Checkable; +import com.baidu.hugegraph.server.RestServer; +import com.baidu.hugegraph.util.E; +import com.baidu.hugegraph.util.Log; +import com.codahale.metrics.annotation.Timed; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.collect.ImmutableMap; + +@Path("graphs/{graph}/auth/login") +@Singleton +public class LoginAPI extends API { + + private static final Logger LOG = Log.logger(RestServer.class); + + @POST + @Timed + @Status(StatusFilter.Status.OK) + @Consumes(APPLICATION_JSON) + @Produces(APPLICATION_JSON_WITH_CHARSET) + public String login(@Context GraphManager manager, + @PathParam("graph") String graph, + JsonLogin jsonLogin) { + LOG.debug("Graph [{}] user login: {}", graph, jsonLogin); + checkCreatingBody(jsonLogin); + + String token = manager.authManager() + .loginUser(jsonLogin.name, jsonLogin.password); + HugeGraph g = graph(manager, graph); + return manager.serializer(g) + .writeMap(ImmutableMap.of("token", token)); + } + + @DELETE + @Timed + @Status(StatusFilter.Status.OK) + @Consumes(APPLICATION_JSON) + @Produces(APPLICATION_JSON_WITH_CHARSET) + public void logout(@Context GraphManager manager, + @PathParam("graph") String graph, + @HeaderParam(HttpHeaders.AUTHORIZATION) String auth) { + E.checkArgument(StringUtils.isNotEmpty(auth), + "Request header Authorization must not be null"); + LOG.debug("Graph [{}] user logout: {}", graph, auth); + + if (!auth.startsWith("Bearer ")) { + throw new BadRequestException( + "Only HTTP Bearer authentication is supported"); + } + + String token = auth.substring("Bearer ".length()); + + manager.authManager().logoutUser(token); + } + + @GET + @Timed + @Status(StatusFilter.Status.OK) + @Consumes(APPLICATION_JSON) + @Produces(APPLICATION_JSON_WITH_CHARSET) + public String verifyToken(@Context GraphManager manager, + @PathParam("graph") String graph, + @HeaderParam(HttpHeaders.AUTHORIZATION) + String token) { + E.checkArgument(StringUtils.isNotEmpty(token), + "Request header Authorization must not be null"); + LOG.debug("Graph [{}] get user: {}", graph, token); + + if (!token.startsWith("Bearer ")) { + throw new BadRequestException( + "Only HTTP Bearer authentication is supported"); + } + + token = token.substring("Bearer ".length()); + Map claims = manager.authManager().verifyToken(token); + + HugeGraph g = graph(manager, graph); + return manager.serializer(g).writeMap(claims); + } + + private static class JsonLogin implements Checkable { + + @JsonProperty("user_name") + private String name; + @JsonProperty("user_password") + private String password; + + @Override + public void checkCreate(boolean isBatch) { + E.checkArgument(!StringUtils.isEmpty(this.name), + "The name of user can't be null"); + E.checkArgument(!StringUtils.isEmpty(this.password), + "The password of user can't be null"); + } + + @Override + public void checkUpdate() { + // pass + } + } +} diff --git a/hugegraph-api/src/main/java/com/baidu/hugegraph/api/filter/AuthenticationFilter.java b/hugegraph-api/src/main/java/com/baidu/hugegraph/api/filter/AuthenticationFilter.java index d63b5ca2b5..66d21c27ea 100644 --- a/hugegraph-api/src/main/java/com/baidu/hugegraph/api/filter/AuthenticationFilter.java +++ b/hugegraph-api/src/main/java/com/baidu/hugegraph/api/filter/AuthenticationFilter.java @@ -21,7 +21,10 @@ import java.io.IOException; import java.security.Principal; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.Set; import javax.annotation.Priority; import javax.ws.rs.BadRequestException; @@ -32,6 +35,7 @@ import javax.ws.rs.container.PreMatching; import javax.ws.rs.core.Context; import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.PathSegment; import javax.ws.rs.core.SecurityContext; import javax.ws.rs.core.UriInfo; import javax.ws.rs.ext.Provider; @@ -52,6 +56,7 @@ import com.baidu.hugegraph.util.E; import com.baidu.hugegraph.util.Log; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; @Provider @PreMatching @@ -60,6 +65,11 @@ public class AuthenticationFilter implements ContainerRequestFilter { private static final Logger LOG = Log.logger(AuthenticationFilter.class); + private static final Set WHITE_API_LIST = ImmutableSet.of( + "login", + "versions" + ); + @Context private javax.inject.Provider managerProvider; @@ -68,6 +78,9 @@ public class AuthenticationFilter implements ContainerRequestFilter { @Override public void filter(ContainerRequestContext context) throws IOException { + if (AuthenticationFilter.isWhiteAPI(context)) { + return; + } User user = this.authenticate(context); Authorizer authorizer = new Authorizer(user, context.getUriInfo()); context.setSecurityContext(authorizer); @@ -91,6 +104,7 @@ protected User authenticate(ContainerRequestContext context) { path = request.getRequestURI(); } + Map credentials = new HashMap<>(); // Extract authentication credentials String auth = context.getHeaderString(HttpHeaders.AUTHORIZATION); if (auth == null) { @@ -98,39 +112,45 @@ protected User authenticate(ContainerRequestContext context) { "Authentication credentials are required", "Missing authentication credentials"); } - if (!auth.startsWith("Basic ")) { - throw new BadRequestException( - "Only HTTP Basic authentication is supported"); - } - auth = auth.substring("Basic ".length()); - auth = new String(DatatypeConverter.parseBase64Binary(auth), - Charsets.ASCII_CHARSET); - String[] values = auth.split(":"); - if (values.length != 2) { - throw new BadRequestException( - "Invalid syntax for username and password"); - } + if (auth.startsWith("Basic ")) { + auth = auth.substring("Basic ".length()); + auth = new String(DatatypeConverter.parseBase64Binary(auth), + Charsets.ASCII_CHARSET); + String[] values = auth.split(":"); + if (values.length != 2) { + throw new BadRequestException( + "Invalid syntax for username and password"); + } + + final String username = values[0]; + final String password = values[1]; - final String username = values[0]; - final String password = values[1]; + if (StringUtils.isEmpty(username) || + StringUtils.isEmpty(password)) { + throw new BadRequestException( + "Invalid syntax for username and password"); + } - if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) { + credentials.put(HugeAuthenticator.KEY_USERNAME, username); + credentials.put(HugeAuthenticator.KEY_PASSWORD, password); + } else if (auth.startsWith("Bearer ")) { + String token = auth.substring("Bearer ".length()); + credentials.put(HugeAuthenticator.KEY_TOKEN, token); + } else { throw new BadRequestException( - "Invalid syntax for username and password"); + "Only HTTP Basic/Bearer authentication is supported"); } + credentials.put(HugeAuthenticator.KEY_ADDRESS, peer); + credentials.put(HugeAuthenticator.KEY_PATH, path); + // Validate the extracted credentials try { - return manager.authenticate(ImmutableMap.of( - HugeAuthenticator.KEY_USERNAME, username, - HugeAuthenticator.KEY_PASSWORD, password, - HugeAuthenticator.KEY_ADDRESS, peer, - HugeAuthenticator.KEY_PATH, path)); + return manager.authenticate(ImmutableMap.copyOf(credentials)); } catch (AuthenticationException e) { - String msg = String.format("Authentication failed for user '%s'", - username); - throw new NotAuthorizedException(msg, e.getMessage()); + throw new NotAuthorizedException("Authentication failed", + e.getMessage()); } } @@ -257,4 +277,12 @@ public boolean equals(Object obj) { } } } + + public static boolean isWhiteAPI(ContainerRequestContext context) { + List segments = context.getUriInfo().getPathSegments(); + E.checkArgument(segments.size() > 0, "Invalid request uri '%s'", + context.getUriInfo().getPath()); + String rootPath = segments.get(segments.size() - 1).getPath(); + return WHITE_API_LIST.contains(rootPath); + } } 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 ede416871e..6601716d7e 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 @@ -58,8 +58,9 @@ public void setup(HugeConfig config) { * @return String No permission if return ROLE_NONE else return a role */ @Override - public RolePermission authenticate(final String username, - final String password) { + public UserWithRole authenticate(final String username, + final String password, + final String token) { E.checkArgumentNotNull(username, "The username parameter can't be null"); E.checkArgumentNotNull(password, @@ -77,7 +78,7 @@ public RolePermission authenticate(final String username, role = ROLE_NONE; } - return role; + return new UserWithRole(username, role); } @Override 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 f0c07b17d8..31a051820a 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 @@ -45,6 +45,7 @@ public interface HugeAuthenticator extends Authenticator { CredentialGraphTokens.PROPERTY_USERNAME; public static final String KEY_PASSWORD = CredentialGraphTokens.PROPERTY_PASSWORD; + public static final String KEY_TOKEN = "token"; public static final String KEY_ROLE = "role"; public static final String KEY_ADDRESS = "address"; public static final String KEY_PATH = "path"; @@ -63,7 +64,8 @@ public interface HugeAuthenticator extends Authenticator { public void setup(HugeConfig config); - public RolePermission authenticate(String username, String password); + public UserWithRole authenticate(String username, String password, + String token); public AuthManager authManager(); @Override @@ -86,15 +88,16 @@ public default User authenticate(final Map credentials) if (this.requireAuthentication()) { String username = credentials.get(KEY_USERNAME); String password = credentials.get(KEY_PASSWORD); + String token = credentials.get(KEY_TOKEN); // Currently we just use config tokens to authenticate - RolePermission role = this.authenticate(username, password); - if (!verifyRole(role)) { + UserWithRole role = this.authenticate(username, password, token); + if (!verifyRole(role.role())) { // Throw if not certified String message = "Incorrect username or password"; throw new AuthenticationException(message); } - user = new User(username, role); + user = new User(role.username(), role.role()); user.client(credentials.get(KEY_ADDRESS)); } diff --git a/hugegraph-api/src/main/java/com/baidu/hugegraph/auth/HugeGraphAuthProxy.java b/hugegraph-api/src/main/java/com/baidu/hugegraph/auth/HugeGraphAuthProxy.java index 3ce09cf28f..671a9c4e82 100644 --- a/hugegraph-api/src/main/java/com/baidu/hugegraph/auth/HugeGraphAuthProxy.java +++ b/hugegraph-api/src/main/java/com/baidu/hugegraph/auth/HugeGraphAuthProxy.java @@ -23,6 +23,7 @@ import java.util.Collection; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; @@ -1365,7 +1366,7 @@ public RolePermission rolePermission(AuthElement element) { } @Override - public RolePermission validateUser(String username, String password) { + public UserWithRole validateUser(String username, String password) { // Can't verifyPermission() here, validate first with tmp permission Context context = setContext(Context.admin()); try { @@ -1380,7 +1381,7 @@ public RolePermission validateUser(String username, String password) { } @Override - public RolePermission validateUser(String token) { + public UserWithRole validateUser(String token) { // Can't verifyPermission() here, validate first with tmp permission Context context = setContext(Context.admin()); try { @@ -1395,12 +1396,17 @@ public RolePermission validateUser(String token) { @Override public String loginUser(String username, String password) { - return null; + return this.authManager.loginUser(username, password); } @Override public void logoutUser(String token) { + this.authManager.logoutUser(token); + } + @Override + public Map verifyToken(String token) { + return this.authManager.verifyToken(token); } private void switchAuthManager(AuthManager authManager) { 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 3f428c1834..e842e89790 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 @@ -116,26 +116,32 @@ public void setup(HugeConfig config) { * Verify if a user is legal * @param username the username for authentication * @param password the password for authentication + * @param token the token for authentication * @return String No permission if return ROLE_NONE else return a role */ @Override - public RolePermission authenticate(String username, String password) { - E.checkArgumentNotNull(username, - "The username parameter can't be null"); - E.checkArgumentNotNull(password, - "The password parameter can't be null"); - - // switch here - - RolePermission role = this.authManager().validateUser(username, - password); + public UserWithRole authenticate(String username, String password, + String token) { + UserWithRole userWithRole; + if (StringUtils.isNotEmpty(token)) { + userWithRole = this.authManager().validateUser(token); + } else { + E.checkArgumentNotNull(username, + "The username parameter can't be null"); + E.checkArgumentNotNull(password, + "The password parameter can't be null"); + userWithRole = this.authManager().validateUser(username, password); + } + RolePermission role = userWithRole.role(); if (role == null) { role = ROLE_NONE; - } else if (username.equals(USER_ADMIN)) { + } else if (userWithRole.username().equals(USER_ADMIN)) { role = ROLE_ADMIN; } - return role; + userWithRole.role(role); + + return userWithRole; } @Override diff --git a/hugegraph-core/pom.xml b/hugegraph-core/pom.xml index efd45b978e..8182269448 100644 --- a/hugegraph-core/pom.xml +++ b/hugegraph-core/pom.xml @@ -174,6 +174,23 @@ fastutil 8.1.0 + + io.jsonwebtoken + jjwt-api + 0.11.2 + + + io.jsonwebtoken + jjwt-impl + 0.11.2 + runtime + + + io.jsonwebtoken + jjwt-jackson + 0.11.2 + runtime + diff --git a/hugegraph-core/src/main/java/com/baidu/hugegraph/auth/AuthManager.java b/hugegraph-core/src/main/java/com/baidu/hugegraph/auth/AuthManager.java index e990202999..10e46c705a 100644 --- a/hugegraph-core/src/main/java/com/baidu/hugegraph/auth/AuthManager.java +++ b/hugegraph-core/src/main/java/com/baidu/hugegraph/auth/AuthManager.java @@ -20,8 +20,8 @@ package com.baidu.hugegraph.auth; import java.util.List; +import java.util.Map; -import com.baidu.hugegraph.auth.RolePermission; import com.baidu.hugegraph.auth.SchemaDefine.AuthElement; import com.baidu.hugegraph.backend.id.Id; @@ -75,6 +75,7 @@ public interface AuthManager { public String loginUser(String username, String password); public void logoutUser(String token); - public RolePermission validateUser(String username, String password); - public RolePermission validateUser(String token); + public UserWithRole validateUser(String username, String password); + public UserWithRole validateUser(String token); + public Map verifyToken(String token); } diff --git a/hugegraph-core/src/main/java/com/baidu/hugegraph/auth/StandardAuthManager.java b/hugegraph-core/src/main/java/com/baidu/hugegraph/auth/StandardAuthManager.java index 6abb67390b..4c90a07b1d 100644 --- a/hugegraph-core/src/main/java/com/baidu/hugegraph/auth/StandardAuthManager.java +++ b/hugegraph-core/src/main/java/com/baidu/hugegraph/auth/StandardAuthManager.java @@ -22,8 +22,11 @@ import java.time.Duration; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.Set; +import javax.ws.rs.NotAuthorizedException; + import com.baidu.hugegraph.HugeGraphParams; import com.baidu.hugegraph.auth.HugeUser.P; import com.baidu.hugegraph.auth.SchemaDefine.AuthElement; @@ -36,16 +39,23 @@ import com.baidu.hugegraph.util.E; import com.baidu.hugegraph.util.Events; import com.baidu.hugegraph.util.StringEncoding; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; +import io.jsonwebtoken.Claims; + public class StandardAuthManager implements AuthManager { private static final long CACHE_EXPIRE = Duration.ofDays(1L).toMillis(); + private static final String TOKEN_USER_NAME = "user_name"; + private static final String TOKEN_USER_ID = "user_id"; + private final HugeGraphParams graph; private final EventListener eventListener; private final Cache usersCache; private final Cache pwdCache; + private final Cache tokenCache; private final EntityManager users; private final EntityManager groups; @@ -54,6 +64,8 @@ public class StandardAuthManager implements AuthManager { private final RelationshipManager belong; private final RelationshipManager access; + private final TokenGenerator tokenGenerator; + public StandardAuthManager(HugeGraphParams graph) { E.checkNotNull(graph, "graph"); @@ -61,6 +73,7 @@ public StandardAuthManager(HugeGraphParams graph) { this.eventListener = this.listenChanges(); this.usersCache = this.cache("users"); this.pwdCache = this.cache("users_pwd"); + this.tokenCache = this.cache("token"); this.users = new EntityManager<>(this.graph, HugeUser.P.USER, HugeUser::fromVertex); @@ -73,6 +86,8 @@ public StandardAuthManager(HugeGraphParams graph) { HugeBelong::fromEdge); this.access = new RelationshipManager<>(this.graph, HugeAccess.P.ACCESS, HugeAccess::fromEdge); + + this.tokenGenerator = new TokenGenerator(graph.configuration()); } private Cache cache(String prefix) { @@ -369,7 +384,7 @@ public RolePermission rolePermission(AuthElement element) { return this.rolePermission((HugeTarget) element); } - List accesses = new ArrayList<>();; + List accesses = new ArrayList<>(); if (element instanceof HugeBelong) { HugeBelong belong = (HugeBelong) element; accesses.addAll(this.listAccessByGroup(belong.target(), -1)); @@ -426,27 +441,62 @@ private RolePermission rolePermission(HugeTarget target) { } @Override - public RolePermission validateUser(String username, String password) { + public String loginUser(String username, String password) { HugeUser user = this.matchUser(username, password); if (user == null) { - return null; + String msg = String.format("Authentication failed for user '%s'", + username); + throw new NotAuthorizedException(msg); } - return this.rolePermission(user); + + Map payload = ImmutableMap.of(TOKEN_USER_NAME, username, + TOKEN_USER_ID, + user.id.asString()); + String token = this.tokenGenerator.create(payload, CACHE_EXPIRE); + + this.tokenCache.update(IdGenerator.of(token), username); + return token; } @Override - public String loginUser(String username, String password) { - return null; + public void logoutUser(String token) { + this.tokenCache.invalidate(IdGenerator.of(token)); } @Override - public void logoutUser(String token) { + public Map verifyToken(String token) { + return this.tokenGenerator.verify(token); + } + @Override + public UserWithRole validateUser(String username, String password) { + HugeUser user = this.matchUser(username, password); + if (user == null) { + return null; + } + return new UserWithRole(username, this.rolePermission(user)); } @Override - public RolePermission validateUser(String token) { - return null; + public UserWithRole validateUser(String token) { + String username = (String) this.tokenCache.get(IdGenerator.of(token)); + if (username == null) { + Claims payload = this.tokenGenerator.verify(token); + username = (String) payload.get(TOKEN_USER_NAME); + + long expireAt = payload.getExpiration().getTime(); + long bornTime = CACHE_EXPIRE - + (expireAt - System.currentTimeMillis()); + this.tokenCache.update(IdGenerator.of(token), username, + Math.negateExact(bornTime)); + } + + HugeUser user = this.findUser(username); + if (user == null) { + return null; + } + + return new UserWithRole(username, this.rolePermission(user)); } /** diff --git a/hugegraph-core/src/main/java/com/baidu/hugegraph/auth/TokenGenerator.java b/hugegraph-core/src/main/java/com/baidu/hugegraph/auth/TokenGenerator.java new file mode 100644 index 0000000000..22808f59b9 --- /dev/null +++ b/hugegraph-core/src/main/java/com/baidu/hugegraph/auth/TokenGenerator.java @@ -0,0 +1,60 @@ +/* + * 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.auth; + +import java.nio.charset.StandardCharsets; +import java.util.Date; +import java.util.Map; + +import javax.crypto.SecretKey; + +import com.baidu.hugegraph.config.AuthOptions; +import com.baidu.hugegraph.config.HugeConfig; + +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jws; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; +import io.jsonwebtoken.security.Keys; + +public class TokenGenerator { + + private final SecretKey key; + + public TokenGenerator(HugeConfig config) { + String secretKey = config.get(AuthOptions.AUTH_TOKEN_SECRET); + this.key = Keys.hmacShaKeyFor(secretKey.getBytes(StandardCharsets.UTF_8)); + } + + public String create(Map payload, long expire) { + return Jwts.builder() + .setClaims(payload) + .setExpiration(new Date(System.currentTimeMillis() + expire)) + .signWith(this.key, SignatureAlgorithm.HS256).compact(); + } + + public Claims verify(String token) { + Jws claimsJws = Jwts.parserBuilder() + .setSigningKey(key) + .build() + .parseClaimsJws(token); + return claimsJws.getBody(); + } +} diff --git a/hugegraph-core/src/main/java/com/baidu/hugegraph/auth/UserWithRole.java b/hugegraph-core/src/main/java/com/baidu/hugegraph/auth/UserWithRole.java new file mode 100644 index 0000000000..a40e4f9d6e --- /dev/null +++ b/hugegraph-core/src/main/java/com/baidu/hugegraph/auth/UserWithRole.java @@ -0,0 +1,43 @@ +/* + * 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.auth; + +public class UserWithRole { + + private final String username; + private RolePermission role; + + public UserWithRole(String username, RolePermission role) { + this.username = username; + this.role = role; + } + + public String username() { + return this.username; + } + + public RolePermission role() { + return this.role; + } + + public void role(RolePermission role) { + this.role = role; + } +} diff --git a/hugegraph-core/src/main/java/com/baidu/hugegraph/config/AuthOptions.java b/hugegraph-core/src/main/java/com/baidu/hugegraph/config/AuthOptions.java new file mode 100644 index 0000000000..874209d855 --- /dev/null +++ b/hugegraph-core/src/main/java/com/baidu/hugegraph/config/AuthOptions.java @@ -0,0 +1,95 @@ +/* + * 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.config; + +import static com.baidu.hugegraph.config.OptionChecker.disallowEmpty; + +public class AuthOptions extends OptionHolder { + + private AuthOptions() { + super(); + } + + private static volatile AuthOptions instance; + + public static synchronized AuthOptions instance() { + if (instance == null) { + instance = new AuthOptions(); + instance.registerOptions(); + } + return instance; + } + + public static final ConfigOption AUTHENTICATOR = + new ConfigOption<>( + "auth.authenticator", + "The class path of authenticator implemention. " + + "e.g., com.baidu.hugegraph.auth.StandardAuthenticator, " + + "or com.baidu.hugegraph.auth.ConfigAuthenticator.", + null, + "" + ); + + public static final ConfigOption AUTH_GRAPH_STORE = + new ConfigOption<>( + "auth.graph_store", + "The name of graph used to store authentication information, " + + "like users, only for com.baidu.hugegraph.auth.StandardAuthenticator.", + disallowEmpty(), + "hugegraph" + ); + + public static final ConfigOption AUTH_ADMIN_TOKEN = + new ConfigOption<>( + "auth.admin_token", + "Token for administrator operations, " + + "only for com.baidu.hugegraph.auth.ConfigAuthenticator.", + disallowEmpty(), + "162f7848-0b6d-4faf-b557-3a0797869c55" + ); + + public static final ConfigListOption AUTH_USER_TOKENS = + new ConfigListOption<>( + "auth.user_tokens", + "The map of user tokens with name and password, " + + "only for com.baidu.hugegraph.auth.ConfigAuthenticator.", + disallowEmpty(), + "hugegraph:9fd95c9c-711b-415b-b85f-d4df46ba5c31" + ); + + public static final ConfigOption AUTH_REMOTE_URL = + new ConfigOption<>( + "auth.remote_url", + "If the address is empty, it provide auth service, " + + "otherwise it is auth client and also provide auth service " + + "through rpc forwarding. The remote url can be set to " + + "multiple addresses, which are concat by ','.", + null, + "" + ); + + public static final ConfigOption AUTH_TOKEN_SECRET = + new ConfigOption<>( + "auth.token_secret", + "Secret key of HS256 algorithm.", + disallowEmpty(), + "FXQXbJtbCLxODc6tGci732pkH1cyf8Qg" + ); +} diff --git a/hugegraph-dist/src/main/java/com/baidu/hugegraph/dist/RegisterUtil.java b/hugegraph-dist/src/main/java/com/baidu/hugegraph/dist/RegisterUtil.java index b1e6c8df98..8ce07f7b21 100644 --- a/hugegraph-dist/src/main/java/com/baidu/hugegraph/dist/RegisterUtil.java +++ b/hugegraph-dist/src/main/java/com/baidu/hugegraph/dist/RegisterUtil.java @@ -187,6 +187,8 @@ public static void registerServer() { OptionSpace.register("server", "com.baidu.hugegraph.config.ServerOptions"); // Register RpcOptions (rpc-server) OptionSpace.register("rpc", "com.baidu.hugegraph.config.RpcOptions"); + // Register AuthOptions (auth-server) + OptionSpace.register("auth", "com.baidu.hugegraph.config.AuthOptions"); } /** From 91ef8ac1e7dabb90f0f892d03c77ae7f56728037 Mon Sep 17 00:00:00 2001 From: guoshoujing Date: Wed, 16 Jun 2021 11:27:52 +0800 Subject: [PATCH 03/15] catch token verify exception and improve code --- .../baidu/hugegraph/api/auth/LoginAPI.java | 18 ++++++++----- .../api/filter/AuthenticationFilter.java | 16 ++++++----- .../hugegraph/auth/ConfigAuthenticator.java | 4 ++- .../hugegraph/auth/StandardAuthenticator.java | 6 +++-- .../hugegraph/auth/StandardAuthManager.java | 8 +++--- .../baidu/hugegraph/auth/TokenGenerator.java | 27 ++++++++++++++----- .../baidu/hugegraph/auth/UserWithRole.java | 20 +++++++++----- 7 files changed, 67 insertions(+), 32 deletions(-) diff --git a/hugegraph-api/src/main/java/com/baidu/hugegraph/api/auth/LoginAPI.java b/hugegraph-api/src/main/java/com/baidu/hugegraph/api/auth/LoginAPI.java index 08545e7054..c8f7b719b1 100644 --- a/hugegraph-api/src/main/java/com/baidu/hugegraph/api/auth/LoginAPI.java +++ b/hugegraph-api/src/main/java/com/baidu/hugegraph/api/auth/LoginAPI.java @@ -39,6 +39,7 @@ import com.baidu.hugegraph.HugeGraph; import com.baidu.hugegraph.api.API; +import com.baidu.hugegraph.api.filter.AuthenticationFilter; import com.baidu.hugegraph.api.filter.StatusFilter; import com.baidu.hugegraph.api.filter.StatusFilter.Status; import com.baidu.hugegraph.core.GraphManager; @@ -50,7 +51,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.collect.ImmutableMap; -@Path("graphs/{graph}/auth/login") +@Path("graphs/{graph}/auth") @Singleton public class LoginAPI extends API { @@ -58,6 +59,7 @@ public class LoginAPI extends API { @POST @Timed + @Path("login") @Status(StatusFilter.Status.OK) @Consumes(APPLICATION_JSON) @Produces(APPLICATION_JSON_WITH_CHARSET) @@ -76,6 +78,7 @@ public String login(@Context GraphManager manager, @DELETE @Timed + @Path("logout") @Status(StatusFilter.Status.OK) @Consumes(APPLICATION_JSON) @Produces(APPLICATION_JSON_WITH_CHARSET) @@ -86,18 +89,20 @@ public void logout(@Context GraphManager manager, "Request header Authorization must not be null"); LOG.debug("Graph [{}] user logout: {}", graph, auth); - if (!auth.startsWith("Bearer ")) { + if (!auth.startsWith(AuthenticationFilter.BEARER_AUTH_PREFIX)) { throw new BadRequestException( "Only HTTP Bearer authentication is supported"); } - String token = auth.substring("Bearer ".length()); + String token = auth.substring(AuthenticationFilter.BEARER_AUTH_PREFIX + .length()); manager.authManager().logoutUser(token); } @GET @Timed + @Path("verify") @Status(StatusFilter.Status.OK) @Consumes(APPLICATION_JSON) @Produces(APPLICATION_JSON_WITH_CHARSET) @@ -109,12 +114,13 @@ public String verifyToken(@Context GraphManager manager, "Request header Authorization must not be null"); LOG.debug("Graph [{}] get user: {}", graph, token); - if (!token.startsWith("Bearer ")) { + if (!token.startsWith(AuthenticationFilter.BEARER_AUTH_PREFIX)) { throw new BadRequestException( - "Only HTTP Bearer authentication is supported"); + "Only HTTP Bearer authentication is supported"); } - token = token.substring("Bearer ".length()); + token = token.substring(AuthenticationFilter.BEARER_AUTH_PREFIX + .length()); Map claims = manager.authManager().verifyToken(token); HugeGraph g = graph(manager, graph); diff --git a/hugegraph-api/src/main/java/com/baidu/hugegraph/api/filter/AuthenticationFilter.java b/hugegraph-api/src/main/java/com/baidu/hugegraph/api/filter/AuthenticationFilter.java index 66d21c27ea..b58d3b8ea8 100644 --- a/hugegraph-api/src/main/java/com/baidu/hugegraph/api/filter/AuthenticationFilter.java +++ b/hugegraph-api/src/main/java/com/baidu/hugegraph/api/filter/AuthenticationFilter.java @@ -55,7 +55,6 @@ import com.baidu.hugegraph.core.GraphManager; import com.baidu.hugegraph.util.E; import com.baidu.hugegraph.util.Log; -import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; @Provider @@ -63,6 +62,9 @@ @Priority(Priorities.AUTHENTICATION) public class AuthenticationFilter implements ContainerRequestFilter { + public static final String BASIC_AUTH_PREFIX = "Basic "; + public static final String BEARER_AUTH_PREFIX = "Bearer "; + private static final Logger LOG = Log.logger(AuthenticationFilter.class); private static final Set WHITE_API_LIST = ImmutableSet.of( @@ -113,8 +115,8 @@ protected User authenticate(ContainerRequestContext context) { "Missing authentication credentials"); } - if (auth.startsWith("Basic ")) { - auth = auth.substring("Basic ".length()); + if (auth.startsWith(BASIC_AUTH_PREFIX)) { + auth = auth.substring(BASIC_AUTH_PREFIX.length()); auth = new String(DatatypeConverter.parseBase64Binary(auth), Charsets.ASCII_CHARSET); String[] values = auth.split(":"); @@ -134,12 +136,12 @@ protected User authenticate(ContainerRequestContext context) { credentials.put(HugeAuthenticator.KEY_USERNAME, username); credentials.put(HugeAuthenticator.KEY_PASSWORD, password); - } else if (auth.startsWith("Bearer ")) { - String token = auth.substring("Bearer ".length()); + } else if (auth.startsWith(BEARER_AUTH_PREFIX)) { + String token = auth.substring(BEARER_AUTH_PREFIX.length()); credentials.put(HugeAuthenticator.KEY_TOKEN, token); } else { throw new BadRequestException( - "Only HTTP Basic/Bearer authentication is supported"); + "Only HTTP Basic or Bearer authentication is supported"); } credentials.put(HugeAuthenticator.KEY_ADDRESS, peer); @@ -147,7 +149,7 @@ protected User authenticate(ContainerRequestContext context) { // Validate the extracted credentials try { - return manager.authenticate(ImmutableMap.copyOf(credentials)); + return manager.authenticate(credentials); } catch (AuthenticationException e) { throw new NotAuthorizedException("Authentication failed", e.getMessage()); 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 6601716d7e..5e230c033d 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 @@ -27,6 +27,7 @@ import org.apache.commons.lang.NotImplementedException; import org.apache.tinkerpop.gremlin.groovy.jsr223.dsl.credential.CredentialGraphTokens; +import com.baidu.hugegraph.backend.id.IdGenerator; import com.baidu.hugegraph.config.HugeConfig; import com.baidu.hugegraph.config.ServerOptions; import com.baidu.hugegraph.util.E; @@ -65,6 +66,7 @@ public UserWithRole authenticate(final String username, "The username parameter can't be null"); E.checkArgumentNotNull(password, "The password parameter can't be null"); + E.checkArgument(token == null, "The token must be null"); RolePermission role; if (password.equals(this.tokens.get(username))) { @@ -78,7 +80,7 @@ public UserWithRole authenticate(final String username, role = ROLE_NONE; } - return new UserWithRole(username, role); + return new UserWithRole(IdGenerator.of(username), username, role); } @Override 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 e842e89790..b1065bd892 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 @@ -138,10 +138,12 @@ public UserWithRole authenticate(String username, String password, role = ROLE_NONE; } else if (userWithRole.username().equals(USER_ADMIN)) { role = ROLE_ADMIN; + } else { + return userWithRole; } - userWithRole.role(role); - return userWithRole; + return new UserWithRole(userWithRole.userId(), + userWithRole.username(), role); } @Override diff --git a/hugegraph-core/src/main/java/com/baidu/hugegraph/auth/StandardAuthManager.java b/hugegraph-core/src/main/java/com/baidu/hugegraph/auth/StandardAuthManager.java index 4c90a07b1d..14ac2598a3 100644 --- a/hugegraph-core/src/main/java/com/baidu/hugegraph/auth/StandardAuthManager.java +++ b/hugegraph-core/src/main/java/com/baidu/hugegraph/auth/StandardAuthManager.java @@ -472,9 +472,9 @@ public Map verifyToken(String token) { public UserWithRole validateUser(String username, String password) { HugeUser user = this.matchUser(username, password); if (user == null) { - return null; + return new UserWithRole(username); } - return new UserWithRole(username, this.rolePermission(user)); + return new UserWithRole(user.id, username, this.rolePermission(user)); } @Override @@ -493,10 +493,10 @@ public UserWithRole validateUser(String token) { HugeUser user = this.findUser(username); if (user == null) { - return null; + return new UserWithRole(username); } - return new UserWithRole(username, this.rolePermission(user)); + return new UserWithRole(user.id, username, this.rolePermission(user)); } /** diff --git a/hugegraph-core/src/main/java/com/baidu/hugegraph/auth/TokenGenerator.java b/hugegraph-core/src/main/java/com/baidu/hugegraph/auth/TokenGenerator.java index 22808f59b9..1068c9cecf 100644 --- a/hugegraph-core/src/main/java/com/baidu/hugegraph/auth/TokenGenerator.java +++ b/hugegraph-core/src/main/java/com/baidu/hugegraph/auth/TokenGenerator.java @@ -24,15 +24,19 @@ import java.util.Map; import javax.crypto.SecretKey; +import javax.ws.rs.NotAuthorizedException; import com.baidu.hugegraph.config.AuthOptions; import com.baidu.hugegraph.config.HugeConfig; import io.jsonwebtoken.Claims; +import io.jsonwebtoken.ExpiredJwtException; import io.jsonwebtoken.Jws; +import io.jsonwebtoken.JwtException; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import io.jsonwebtoken.security.Keys; +import io.jsonwebtoken.security.SignatureException; public class TokenGenerator { @@ -47,14 +51,25 @@ public String create(Map payload, long expire) { return Jwts.builder() .setClaims(payload) .setExpiration(new Date(System.currentTimeMillis() + expire)) - .signWith(this.key, SignatureAlgorithm.HS256).compact(); + .signWith(this.key, SignatureAlgorithm.HS256) + .compact(); } public Claims verify(String token) { - Jws claimsJws = Jwts.parserBuilder() - .setSigningKey(key) - .build() - .parseClaimsJws(token); - return claimsJws.getBody(); + try { + Jws claimsJws = Jwts.parserBuilder() + .setSigningKey(key) + .build() + .parseClaimsJws(token); + return claimsJws.getBody(); + } catch (ExpiredJwtException e) { + throw new NotAuthorizedException("The token has expired"); + } catch (SignatureException e) { + throw new NotAuthorizedException("The token signature does not " + + "match"); + } catch (JwtException e) { + throw new NotAuthorizedException("The token validity cannot be " + + "asserted and can't be trusted"); + } } } diff --git a/hugegraph-core/src/main/java/com/baidu/hugegraph/auth/UserWithRole.java b/hugegraph-core/src/main/java/com/baidu/hugegraph/auth/UserWithRole.java index a40e4f9d6e..ee1a84c18c 100644 --- a/hugegraph-core/src/main/java/com/baidu/hugegraph/auth/UserWithRole.java +++ b/hugegraph-core/src/main/java/com/baidu/hugegraph/auth/UserWithRole.java @@ -19,16 +19,28 @@ package com.baidu.hugegraph.auth; +import com.baidu.hugegraph.backend.id.Id; + public class UserWithRole { + private final Id userId; private final String username; - private RolePermission role; + private final RolePermission role; + + public UserWithRole(String username) { + this(null, username, null); + } - public UserWithRole(String username, RolePermission role) { + public UserWithRole(Id userId, String username, RolePermission role) { + this.userId = userId; this.username = username; this.role = role; } + public Id userId() { + return this.userId; + } + public String username() { return this.username; } @@ -36,8 +48,4 @@ public String username() { public RolePermission role() { return this.role; } - - public void role(RolePermission role) { - this.role = role; - } } From 6064d8e16ec542a50976a42c1bf9d3c2a84b8816 Mon Sep 17 00:00:00 2001 From: guoshoujing Date: Wed, 16 Jun 2021 17:27:12 +0800 Subject: [PATCH 04/15] improve code --- .../baidu/hugegraph/api/auth/LoginAPI.java | 39 ++++++++++++------- .../com/baidu/hugegraph/api/auth/UserAPI.java | 9 +++++ .../api/filter/AuthenticationFilter.java | 7 ++-- .../hugegraph/auth/HugeGraphAuthProxy.java | 13 ++++--- .../baidu/hugegraph/auth/AuthConstant.java | 29 ++++++++++++++ .../com/baidu/hugegraph/auth/AuthManager.java | 7 ++-- .../com/baidu/hugegraph/auth/HugeUser.java | 9 +++++ .../hugegraph/auth/StandardAuthManager.java | 25 +++++------- .../baidu/hugegraph/auth/TokenGenerator.java | 9 +---- 9 files changed, 97 insertions(+), 50 deletions(-) create mode 100644 hugegraph-core/src/main/java/com/baidu/hugegraph/auth/AuthConstant.java diff --git a/hugegraph-api/src/main/java/com/baidu/hugegraph/api/auth/LoginAPI.java b/hugegraph-api/src/main/java/com/baidu/hugegraph/api/auth/LoginAPI.java index c8f7b719b1..682c434954 100644 --- a/hugegraph-api/src/main/java/com/baidu/hugegraph/api/auth/LoginAPI.java +++ b/hugegraph-api/src/main/java/com/baidu/hugegraph/api/auth/LoginAPI.java @@ -19,14 +19,14 @@ package com.baidu.hugegraph.api.auth; -import java.util.Map; - import javax.inject.Singleton; +import javax.security.sasl.AuthenticationException; import javax.ws.rs.BadRequestException; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.HeaderParam; +import javax.ws.rs.NotAuthorizedException; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.PathParam; @@ -42,6 +42,8 @@ import com.baidu.hugegraph.api.filter.AuthenticationFilter; import com.baidu.hugegraph.api.filter.StatusFilter; import com.baidu.hugegraph.api.filter.StatusFilter.Status; +import com.baidu.hugegraph.auth.AuthConstant; +import com.baidu.hugegraph.auth.UserWithRole; import com.baidu.hugegraph.core.GraphManager; import com.baidu.hugegraph.define.Checkable; import com.baidu.hugegraph.server.RestServer; @@ -51,7 +53,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.collect.ImmutableMap; -@Path("graphs/{graph}/auth") +@Path("graphs/{graph}/auth/login") @Singleton public class LoginAPI extends API { @@ -59,7 +61,6 @@ public class LoginAPI extends API { @POST @Timed - @Path("login") @Status(StatusFilter.Status.OK) @Consumes(APPLICATION_JSON) @Produces(APPLICATION_JSON_WITH_CHARSET) @@ -69,11 +70,15 @@ public String login(@Context GraphManager manager, LOG.debug("Graph [{}] user login: {}", graph, jsonLogin); checkCreatingBody(jsonLogin); - String token = manager.authManager() - .loginUser(jsonLogin.name, jsonLogin.password); - HugeGraph g = graph(manager, graph); - return manager.serializer(g) - .writeMap(ImmutableMap.of("token", token)); + try { + String token = manager.authManager() + .loginUser(jsonLogin.name, jsonLogin.password); + HugeGraph g = graph(manager, graph); + return manager.serializer(g) + .writeMap(ImmutableMap.of("token", token)); + } catch (AuthenticationException e) { + throw new NotAuthorizedException(e.getMessage(), e); + } } @DELETE @@ -89,12 +94,12 @@ public void logout(@Context GraphManager manager, "Request header Authorization must not be null"); LOG.debug("Graph [{}] user logout: {}", graph, auth); - if (!auth.startsWith(AuthenticationFilter.BEARER_AUTH_PREFIX)) { + if (!auth.startsWith(AuthenticationFilter.BEARER_TOKEN_PREFIX)) { throw new BadRequestException( "Only HTTP Bearer authentication is supported"); } - String token = auth.substring(AuthenticationFilter.BEARER_AUTH_PREFIX + String token = auth.substring(AuthenticationFilter.BEARER_TOKEN_PREFIX .length()); manager.authManager().logoutUser(token); @@ -114,17 +119,21 @@ public String verifyToken(@Context GraphManager manager, "Request header Authorization must not be null"); LOG.debug("Graph [{}] get user: {}", graph, token); - if (!token.startsWith(AuthenticationFilter.BEARER_AUTH_PREFIX)) { + if (!token.startsWith(AuthenticationFilter.BEARER_TOKEN_PREFIX)) { throw new BadRequestException( "Only HTTP Bearer authentication is supported"); } - token = token.substring(AuthenticationFilter.BEARER_AUTH_PREFIX + token = token.substring(AuthenticationFilter.BEARER_TOKEN_PREFIX .length()); - Map claims = manager.authManager().verifyToken(token); + UserWithRole userWithRole = manager.authManager().validateUser(token); HugeGraph g = graph(manager, graph); - return manager.serializer(g).writeMap(claims); + return manager.serializer(g) + .writeMap(ImmutableMap.of(AuthConstant.TOKEN_USER_NAME, + userWithRole.username(), + AuthConstant.TOKEN_USER_ID, + userWithRole.userId())); } private static class JsonLogin implements Checkable { diff --git a/hugegraph-api/src/main/java/com/baidu/hugegraph/api/auth/UserAPI.java b/hugegraph-api/src/main/java/com/baidu/hugegraph/api/auth/UserAPI.java index 6b947b6ead..3e055de5e6 100644 --- a/hugegraph-api/src/main/java/com/baidu/hugegraph/api/auth/UserAPI.java +++ b/hugegraph-api/src/main/java/com/baidu/hugegraph/api/auth/UserAPI.java @@ -179,6 +179,8 @@ private static class JsonUser implements Checkable { private String email; @JsonProperty("user_avatar") private String avatar; + @JsonProperty("user_description") + private String description; public HugeUser build(HugeUser user) { E.checkArgument(this.name == null || user.name().equals(this.name), @@ -195,6 +197,12 @@ public HugeUser build(HugeUser user) { if (this.avatar != null) { user.avatar(this.avatar); } + if (this.avatar != null) { + user.avatar(this.avatar); + } + if (this.description != null) { + user.description(this.description); + } return user; } @@ -204,6 +212,7 @@ public HugeUser build() { user.phone(this.phone); user.email(this.email); user.avatar(this.avatar); + user.description(this.description); return user; } diff --git a/hugegraph-api/src/main/java/com/baidu/hugegraph/api/filter/AuthenticationFilter.java b/hugegraph-api/src/main/java/com/baidu/hugegraph/api/filter/AuthenticationFilter.java index b58d3b8ea8..021b50d8ee 100644 --- a/hugegraph-api/src/main/java/com/baidu/hugegraph/api/filter/AuthenticationFilter.java +++ b/hugegraph-api/src/main/java/com/baidu/hugegraph/api/filter/AuthenticationFilter.java @@ -63,7 +63,7 @@ public class AuthenticationFilter implements ContainerRequestFilter { public static final String BASIC_AUTH_PREFIX = "Basic "; - public static final String BEARER_AUTH_PREFIX = "Bearer "; + public static final String BEARER_TOKEN_PREFIX = "Bearer "; private static final Logger LOG = Log.logger(AuthenticationFilter.class); @@ -136,8 +136,8 @@ protected User authenticate(ContainerRequestContext context) { credentials.put(HugeAuthenticator.KEY_USERNAME, username); credentials.put(HugeAuthenticator.KEY_PASSWORD, password); - } else if (auth.startsWith(BEARER_AUTH_PREFIX)) { - String token = auth.substring(BEARER_AUTH_PREFIX.length()); + } else if (auth.startsWith(BEARER_TOKEN_PREFIX)) { + String token = auth.substring(BEARER_TOKEN_PREFIX.length()); credentials.put(HugeAuthenticator.KEY_TOKEN, token); } else { throw new BadRequestException( @@ -282,6 +282,7 @@ public boolean equals(Object obj) { public static boolean isWhiteAPI(ContainerRequestContext context) { List segments = context.getUriInfo().getPathSegments(); + E.checkArgument(segments.size() > 0, "Invalid request uri '%s'", context.getUriInfo().getPath()); String rootPath = segments.get(segments.size() - 1).getPath(); diff --git a/hugegraph-api/src/main/java/com/baidu/hugegraph/auth/HugeGraphAuthProxy.java b/hugegraph-api/src/main/java/com/baidu/hugegraph/auth/HugeGraphAuthProxy.java index 671a9c4e82..7051fe6448 100644 --- a/hugegraph-api/src/main/java/com/baidu/hugegraph/auth/HugeGraphAuthProxy.java +++ b/hugegraph-api/src/main/java/com/baidu/hugegraph/auth/HugeGraphAuthProxy.java @@ -35,7 +35,9 @@ import java.util.concurrent.TimeoutException; import java.util.function.Supplier; +import javax.security.sasl.AuthenticationException; import javax.ws.rs.ForbiddenException; +import javax.ws.rs.NotAuthorizedException; import org.apache.tinkerpop.gremlin.groovy.jsr223.GroovyTranslator; import org.apache.tinkerpop.gremlin.process.computer.GraphComputer; @@ -1396,7 +1398,11 @@ public UserWithRole validateUser(String token) { @Override public String loginUser(String username, String password) { - return this.authManager.loginUser(username, password); + try { + return this.authManager.loginUser(username, password); + } catch (AuthenticationException e) { + throw new NotAuthorizedException(e.getMessage(), e); + } } @Override @@ -1404,11 +1410,6 @@ public void logoutUser(String token) { this.authManager.logoutUser(token); } - @Override - public Map verifyToken(String token) { - return this.authManager.verifyToken(token); - } - private void switchAuthManager(AuthManager authManager) { this.authManager = authManager; HugeGraphAuthProxy.this.hugegraph.switchAuthManager(authManager); diff --git a/hugegraph-core/src/main/java/com/baidu/hugegraph/auth/AuthConstant.java b/hugegraph-core/src/main/java/com/baidu/hugegraph/auth/AuthConstant.java new file mode 100644 index 0000000000..59379481c9 --- /dev/null +++ b/hugegraph-core/src/main/java/com/baidu/hugegraph/auth/AuthConstant.java @@ -0,0 +1,29 @@ +/* + * 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.auth; + +public interface AuthConstant { + + /* + * Fields in token + */ + String TOKEN_USER_NAME = "user_name"; + String TOKEN_USER_ID = "user_id"; +} diff --git a/hugegraph-core/src/main/java/com/baidu/hugegraph/auth/AuthManager.java b/hugegraph-core/src/main/java/com/baidu/hugegraph/auth/AuthManager.java index 10e46c705a..2c0a14426a 100644 --- a/hugegraph-core/src/main/java/com/baidu/hugegraph/auth/AuthManager.java +++ b/hugegraph-core/src/main/java/com/baidu/hugegraph/auth/AuthManager.java @@ -20,7 +20,8 @@ package com.baidu.hugegraph.auth; import java.util.List; -import java.util.Map; + +import javax.security.sasl.AuthenticationException; import com.baidu.hugegraph.auth.SchemaDefine.AuthElement; import com.baidu.hugegraph.backend.id.Id; @@ -72,10 +73,10 @@ public interface AuthManager { public HugeUser matchUser(String name, String password); public RolePermission rolePermission(AuthElement element); - public String loginUser(String username, String password); + public String loginUser(String username, String password) + throws AuthenticationException; public void logoutUser(String token); public UserWithRole validateUser(String username, String password); public UserWithRole validateUser(String token); - public Map verifyToken(String token); } diff --git a/hugegraph-core/src/main/java/com/baidu/hugegraph/auth/HugeUser.java b/hugegraph-core/src/main/java/com/baidu/hugegraph/auth/HugeUser.java index f327c596ad..a5777bd707 100644 --- a/hugegraph-core/src/main/java/com/baidu/hugegraph/auth/HugeUser.java +++ b/hugegraph-core/src/main/java/com/baidu/hugegraph/auth/HugeUser.java @@ -43,6 +43,7 @@ public class HugeUser extends Entity { private String phone; private String email; private String avatar; + private String description; // This field is just for cache private RolePermission role; @@ -107,6 +108,14 @@ public void avatar(String avatar) { this.avatar = avatar; } + public String description() { + return this.description; + } + + public void description(String description) { + this.description = description; + } + public RolePermission role() { return this.role; } diff --git a/hugegraph-core/src/main/java/com/baidu/hugegraph/auth/StandardAuthManager.java b/hugegraph-core/src/main/java/com/baidu/hugegraph/auth/StandardAuthManager.java index 14ac2598a3..d10667c184 100644 --- a/hugegraph-core/src/main/java/com/baidu/hugegraph/auth/StandardAuthManager.java +++ b/hugegraph-core/src/main/java/com/baidu/hugegraph/auth/StandardAuthManager.java @@ -25,7 +25,7 @@ import java.util.Map; import java.util.Set; -import javax.ws.rs.NotAuthorizedException; +import javax.security.sasl.AuthenticationException; import com.baidu.hugegraph.HugeGraphParams; import com.baidu.hugegraph.auth.HugeUser.P; @@ -48,9 +48,6 @@ public class StandardAuthManager implements AuthManager { private static final long CACHE_EXPIRE = Duration.ofDays(1L).toMillis(); - private static final String TOKEN_USER_NAME = "user_name"; - private static final String TOKEN_USER_ID = "user_id"; - private final HugeGraphParams graph; private final EventListener eventListener; private final Cache usersCache; @@ -441,16 +438,17 @@ private RolePermission rolePermission(HugeTarget target) { } @Override - public String loginUser(String username, String password) { + public String loginUser(String username, String password) + throws AuthenticationException { HugeUser user = this.matchUser(username, password); if (user == null) { - String msg = String.format("Authentication failed for user '%s'", - username); - throw new NotAuthorizedException(msg); + String msg = "Incorrect username or password"; + throw new AuthenticationException(msg); } - Map payload = ImmutableMap.of(TOKEN_USER_NAME, username, - TOKEN_USER_ID, + Map payload = ImmutableMap.of(AuthConstant.TOKEN_USER_NAME, + username, + AuthConstant.TOKEN_USER_ID, user.id.asString()); String token = this.tokenGenerator.create(payload, CACHE_EXPIRE); @@ -463,11 +461,6 @@ public void logoutUser(String token) { this.tokenCache.invalidate(IdGenerator.of(token)); } - @Override - public Map verifyToken(String token) { - return this.tokenGenerator.verify(token); - } - @Override public UserWithRole validateUser(String username, String password) { HugeUser user = this.matchUser(username, password); @@ -482,7 +475,7 @@ public UserWithRole validateUser(String token) { String username = (String) this.tokenCache.get(IdGenerator.of(token)); if (username == null) { Claims payload = this.tokenGenerator.verify(token); - username = (String) payload.get(TOKEN_USER_NAME); + username = (String) payload.get(AuthConstant.TOKEN_USER_NAME); long expireAt = payload.getExpiration().getTime(); long bornTime = CACHE_EXPIRE - diff --git a/hugegraph-core/src/main/java/com/baidu/hugegraph/auth/TokenGenerator.java b/hugegraph-core/src/main/java/com/baidu/hugegraph/auth/TokenGenerator.java index 1068c9cecf..7059846013 100644 --- a/hugegraph-core/src/main/java/com/baidu/hugegraph/auth/TokenGenerator.java +++ b/hugegraph-core/src/main/java/com/baidu/hugegraph/auth/TokenGenerator.java @@ -36,7 +36,6 @@ import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import io.jsonwebtoken.security.Keys; -import io.jsonwebtoken.security.SignatureException; public class TokenGenerator { @@ -63,13 +62,9 @@ public Claims verify(String token) { .parseClaimsJws(token); return claimsJws.getBody(); } catch (ExpiredJwtException e) { - throw new NotAuthorizedException("The token has expired"); - } catch (SignatureException e) { - throw new NotAuthorizedException("The token signature does not " + - "match"); + throw new NotAuthorizedException("The token has expired", e); } catch (JwtException e) { - throw new NotAuthorizedException("The token validity cannot be " + - "asserted and can't be trusted"); + throw new NotAuthorizedException("Invalid token", e); } } } From 1df1ff4fa0531cd20969d7485cb87943e018f030 Mon Sep 17 00:00:00 2001 From: guoshoujing Date: Thu, 17 Jun 2021 11:13:11 +0800 Subject: [PATCH 05/15] improve code --- .../java/com/baidu/hugegraph/api/auth/LoginAPI.java | 3 ++- .../hugegraph/api/filter/AuthenticationFilter.java | 11 +++++------ .../java/com/baidu/hugegraph/auth/TokenGenerator.java | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/hugegraph-api/src/main/java/com/baidu/hugegraph/api/auth/LoginAPI.java b/hugegraph-api/src/main/java/com/baidu/hugegraph/api/auth/LoginAPI.java index 682c434954..51cd1f3976 100644 --- a/hugegraph-api/src/main/java/com/baidu/hugegraph/api/auth/LoginAPI.java +++ b/hugegraph-api/src/main/java/com/baidu/hugegraph/api/auth/LoginAPI.java @@ -53,7 +53,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.collect.ImmutableMap; -@Path("graphs/{graph}/auth/login") +@Path("graphs/{graph}/auth") @Singleton public class LoginAPI extends API { @@ -61,6 +61,7 @@ public class LoginAPI extends API { @POST @Timed + @Path("login") @Status(StatusFilter.Status.OK) @Consumes(APPLICATION_JSON) @Produces(APPLICATION_JSON_WITH_CHARSET) diff --git a/hugegraph-api/src/main/java/com/baidu/hugegraph/api/filter/AuthenticationFilter.java b/hugegraph-api/src/main/java/com/baidu/hugegraph/api/filter/AuthenticationFilter.java index 021b50d8ee..54abbcd95e 100644 --- a/hugegraph-api/src/main/java/com/baidu/hugegraph/api/filter/AuthenticationFilter.java +++ b/hugegraph-api/src/main/java/com/baidu/hugegraph/api/filter/AuthenticationFilter.java @@ -68,7 +68,7 @@ public class AuthenticationFilter implements ContainerRequestFilter { private static final Logger LOG = Log.logger(AuthenticationFilter.class); private static final Set WHITE_API_LIST = ImmutableSet.of( - "login", + "auth/login", "versions" ); @@ -281,11 +281,10 @@ public boolean equals(Object obj) { } public static boolean isWhiteAPI(ContainerRequestContext context) { - List segments = context.getUriInfo().getPathSegments(); + String path = context.getUriInfo().getPath(); - E.checkArgument(segments.size() > 0, "Invalid request uri '%s'", - context.getUriInfo().getPath()); - String rootPath = segments.get(segments.size() - 1).getPath(); - return WHITE_API_LIST.contains(rootPath); + E.checkArgument(StringUtils.isNotEmpty(path), + "Invalid request uri '%s'", path); + return WHITE_API_LIST.stream().anyMatch(path::contains); } } diff --git a/hugegraph-core/src/main/java/com/baidu/hugegraph/auth/TokenGenerator.java b/hugegraph-core/src/main/java/com/baidu/hugegraph/auth/TokenGenerator.java index 7059846013..50937ec923 100644 --- a/hugegraph-core/src/main/java/com/baidu/hugegraph/auth/TokenGenerator.java +++ b/hugegraph-core/src/main/java/com/baidu/hugegraph/auth/TokenGenerator.java @@ -62,7 +62,7 @@ public Claims verify(String token) { .parseClaimsJws(token); return claimsJws.getBody(); } catch (ExpiredJwtException e) { - throw new NotAuthorizedException("The token has expired", e); + throw new NotAuthorizedException("The token is expired", e); } catch (JwtException e) { throw new NotAuthorizedException("Invalid token", e); } From 81feedaeac2833d62bf0b5de0e7fc51dcc661448 Mon Sep 17 00:00:00 2001 From: guoshoujing Date: Thu, 17 Jun 2021 17:35:18 +0800 Subject: [PATCH 06/15] improve white api check --- .../hugegraph/api/filter/AuthenticationFilter.java | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/hugegraph-api/src/main/java/com/baidu/hugegraph/api/filter/AuthenticationFilter.java b/hugegraph-api/src/main/java/com/baidu/hugegraph/api/filter/AuthenticationFilter.java index 54abbcd95e..487859121b 100644 --- a/hugegraph-api/src/main/java/com/baidu/hugegraph/api/filter/AuthenticationFilter.java +++ b/hugegraph-api/src/main/java/com/baidu/hugegraph/api/filter/AuthenticationFilter.java @@ -55,7 +55,7 @@ import com.baidu.hugegraph.core.GraphManager; import com.baidu.hugegraph.util.E; import com.baidu.hugegraph.util.Log; -import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableList; @Provider @PreMatching @@ -67,7 +67,7 @@ public class AuthenticationFilter implements ContainerRequestFilter { private static final Logger LOG = Log.logger(AuthenticationFilter.class); - private static final Set WHITE_API_LIST = ImmutableSet.of( + private static final List WHITE_API_LIST = ImmutableList.of( "auth/login", "versions" ); @@ -285,6 +285,12 @@ public static boolean isWhiteAPI(ContainerRequestContext context) { E.checkArgument(StringUtils.isNotEmpty(path), "Invalid request uri '%s'", path); - return WHITE_API_LIST.stream().anyMatch(path::contains); + + for (String whiteApi : WHITE_API_LIST) { + if (path.endsWith(whiteApi)) { + return true; + } + } + return false; } } From a4bcca46165a9511ac7d251eb3cb1f7f531e293d Mon Sep 17 00:00:00 2001 From: guoshoujing Date: Mon, 21 Jun 2021 16:58:10 +0800 Subject: [PATCH 07/15] add LoginAPI unit test --- .../api/filter/AuthenticationFilter.java | 2 - .../com/baidu/hugegraph/api/ApiTestSuite.java | 3 +- .../com/baidu/hugegraph/api/BaseApiTest.java | 12 ++ .../com/baidu/hugegraph/api/LoginApiTest.java | 143 ++++++++++++++++++ 4 files changed, 157 insertions(+), 3 deletions(-) create mode 100644 hugegraph-test/src/main/java/com/baidu/hugegraph/api/LoginApiTest.java diff --git a/hugegraph-api/src/main/java/com/baidu/hugegraph/api/filter/AuthenticationFilter.java b/hugegraph-api/src/main/java/com/baidu/hugegraph/api/filter/AuthenticationFilter.java index 487859121b..bbc6745b85 100644 --- a/hugegraph-api/src/main/java/com/baidu/hugegraph/api/filter/AuthenticationFilter.java +++ b/hugegraph-api/src/main/java/com/baidu/hugegraph/api/filter/AuthenticationFilter.java @@ -24,7 +24,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Set; import javax.annotation.Priority; import javax.ws.rs.BadRequestException; @@ -35,7 +34,6 @@ import javax.ws.rs.container.PreMatching; import javax.ws.rs.core.Context; import javax.ws.rs.core.HttpHeaders; -import javax.ws.rs.core.PathSegment; import javax.ws.rs.core.SecurityContext; import javax.ws.rs.core.UriInfo; import javax.ws.rs.ext.Provider; 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 05b69c865b..3200d742d3 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 @@ -36,7 +36,8 @@ TaskApiTest.class, GremlinApiTest.class, MetricsApiTest.class, - UserApiTest.class + UserApiTest.class, + LoginApiTest.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 8637fffbfd..9f17e4c201 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 @@ -32,6 +32,7 @@ import javax.ws.rs.client.ClientBuilder; import javax.ws.rs.client.Entity; import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Response; import org.glassfish.jersey.client.authentication.HttpAuthenticationFeature; @@ -126,6 +127,11 @@ public Response get(String path, String id) { return this.target.path(path).path(id).request().get(); } + public Response get(String path, + MultivaluedMap headers) { + return this.target.path(path).request().headers(headers).get(); + } + public Response get(String path, Map params) { WebTarget target = this.target.path(path); for (Map.Entry i : params.entrySet()) { @@ -162,6 +168,12 @@ public Response delete(String path, Map params) { } return target.request().delete(); } + + public Response delete(String path, + MultivaluedMap headers) { + WebTarget target = this.target.path(path); + return target.request().headers(headers).delete(); + } } /** diff --git a/hugegraph-test/src/main/java/com/baidu/hugegraph/api/LoginApiTest.java b/hugegraph-test/src/main/java/com/baidu/hugegraph/api/LoginApiTest.java new file mode 100644 index 0000000000..086b724cfc --- /dev/null +++ b/hugegraph-test/src/main/java/com/baidu/hugegraph/api/LoginApiTest.java @@ -0,0 +1,143 @@ +/* + * 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.nio.file.Paths; +import java.util.Map; + +import javax.ws.rs.core.GenericType; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.MultivaluedHashMap; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.Response; + +import org.apache.tinkerpop.shaded.jackson.core.type.TypeReference; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import com.baidu.hugegraph.testutil.Assert; +import com.baidu.hugegraph.util.JsonUtil; + +public class LoginApiTest extends BaseApiTest { + + private static final String PATH = "graphs/hugegraph/auth"; + private static final String USER_PATH = "graphs/hugegraph/auth/users"; + private Map testUser; + + @Before + public void setup() { + Response r = this.createUser("test", "test"); + this.testUser = r.readEntity(new GenericType>(){}); + } + + @After + public void teardown() { + Response r = this.deleteUser((String) this.testUser.get("id")); + } + + @Test + public void testLogin() { + Response r; + + r = this.login("test", "test"); + String result = assertResponseStatus(200, r); + assertJsonContains(result, "token"); + + r = this.login("test", "pass"); + assertResponseStatus(401, r); + + r = this.login("pass", "pass"); + assertResponseStatus(401, r); + } + + @Test + public void testLogout() { + Response r; + + r = this.login("test", "test"); + String result = assertResponseStatus(200, r); + assertJsonContains(result, "token"); + + String token = this.tokenFromResponse(result); + + String path = Paths.get(PATH, "logout").toString(); + MultivaluedMap headers = new MultivaluedHashMap<>(); + headers.add(HttpHeaders.AUTHORIZATION, "Bearer " + token); + r = client().delete(path, headers); + + assertResponseStatus(204, r); + } + + @Test + public void testVerify() { + Response r; + String result; + + r = this.login("test", "test"); + result = assertResponseStatus(200, r); + assertJsonContains(result, "token"); + + String token = this.tokenFromResponse(result); + + String path = Paths.get(PATH, "verify").toString(); + MultivaluedMap headers = new MultivaluedHashMap<>(); + headers.add(HttpHeaders.AUTHORIZATION, "Bearer " + token); + r = client().get(path, headers); + + result = assertResponseStatus(200, r); + assertJsonContains(result, "user_id"); + assertJsonContains(result, "user_name"); + + Map user = JsonUtil.fromJson( + result, new TypeReference>(){}); + Assert.assertEquals(this.testUser.get("id"), user.get("user_id")); + Assert.assertEquals(this.testUser.get("user_name"), + user.get("user_name")); + } + + private Response createUser(String name, String password) { + String user = "{\"user_name\":\"%s\",\"user_password\":\"%s" + + "\",\"user_email\":\"user1@baidu.com\"," + + "\"user_phone\":\"123456789\",\"user_avatar\":\"image1" + + ".jpg\"}"; + return this.client().post(USER_PATH, + String.format(user, name, password)); + } + + private Response deleteUser(String id) { + return this.client().delete(USER_PATH, id); + } + + private Response login(String name, String password) { + String login = Paths.get(PATH, "login").toString(); + String loginUser = "{\"user_name\":\"%s\"," + + "\"user_password\":\"%s\"}"; + + return client().post(login, String.format(loginUser, + name, password)); + } + + private String tokenFromResponse(String result) { + Map loginResult = JsonUtil.fromJson( + result, new TypeReference>(){}); + return (String) loginResult.get("token"); + } +} From f94d5b2f1107499afb7b1e2a07aac06ea7a7103e Mon Sep 17 00:00:00 2001 From: guoshoujing Date: Mon, 21 Jun 2021 17:18:42 +0800 Subject: [PATCH 08/15] improve and add case --- .../com/baidu/hugegraph/api/LoginApiTest.java | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/hugegraph-test/src/main/java/com/baidu/hugegraph/api/LoginApiTest.java b/hugegraph-test/src/main/java/com/baidu/hugegraph/api/LoginApiTest.java index 086b724cfc..3f0b54c80e 100644 --- a/hugegraph-test/src/main/java/com/baidu/hugegraph/api/LoginApiTest.java +++ b/hugegraph-test/src/main/java/com/baidu/hugegraph/api/LoginApiTest.java @@ -40,17 +40,19 @@ public class LoginApiTest extends BaseApiTest { private static final String PATH = "graphs/hugegraph/auth"; private static final String USER_PATH = "graphs/hugegraph/auth/users"; - private Map testUser; + private String userId4Test; @Before public void setup() { Response r = this.createUser("test", "test"); - this.testUser = r.readEntity(new GenericType>(){}); + Map user = r.readEntity( + new GenericType>(){}); + this.userId4Test = (String) user.get("id"); } @After public void teardown() { - Response r = this.deleteUser((String) this.testUser.get("id")); + Response r = this.deleteUser(userId4Test); } @Test @@ -71,9 +73,10 @@ public void testLogin() { @Test public void testLogout() { Response r; + String result; r = this.login("test", "test"); - String result = assertResponseStatus(200, r); + result = assertResponseStatus(200, r); assertJsonContains(result, "token"); String token = this.tokenFromResponse(result); @@ -82,7 +85,12 @@ public void testLogout() { MultivaluedMap headers = new MultivaluedHashMap<>(); headers.add(HttpHeaders.AUTHORIZATION, "Bearer " + token); r = client().delete(path, headers); + assertResponseStatus(204, r); + String invalidToken = "eyJhbGciOiJIUzI1NiJ9.eyJ1caVyX25hbWUiOiJ0ZXN0IiwidXNlcl9pZCI6Ii02Mzp0ZXN0IiwiZXhwIjoxNjI0MzUzMjUyfQ.kYot-3mSGlfSbEMzxrTs84q8YanhTTxtsKPPG25CNxA"; + headers = new MultivaluedHashMap<>(); + headers.add(HttpHeaders.AUTHORIZATION, "Bearer " + token); + r = client().delete(path, headers); assertResponseStatus(204, r); } @@ -108,9 +116,8 @@ public void testVerify() { Map user = JsonUtil.fromJson( result, new TypeReference>(){}); - Assert.assertEquals(this.testUser.get("id"), user.get("user_id")); - Assert.assertEquals(this.testUser.get("user_name"), - user.get("user_name")); + Assert.assertEquals(this.userId4Test, user.get("user_id")); + Assert.assertEquals("test", user.get("user_name")); } private Response createUser(String name, String password) { From e68901852ddcc50cb41f89dea344203e95858915 Mon Sep 17 00:00:00 2001 From: guoshoujing Date: Mon, 21 Jun 2021 17:21:39 +0800 Subject: [PATCH 09/15] update version --- hugegraph-api/pom.xml | 2 +- .../src/main/java/com/baidu/hugegraph/version/ApiVersion.java | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/hugegraph-api/pom.xml b/hugegraph-api/pom.xml index 5457c08092..5c43a2b4f0 100644 --- a/hugegraph-api/pom.xml +++ b/hugegraph-api/pom.xml @@ -153,7 +153,7 @@ - 0.62.0.0 + 0.63.0.0 diff --git a/hugegraph-api/src/main/java/com/baidu/hugegraph/version/ApiVersion.java b/hugegraph-api/src/main/java/com/baidu/hugegraph/version/ApiVersion.java index f9f69f325a..8cb00c5b86 100644 --- a/hugegraph-api/src/main/java/com/baidu/hugegraph/version/ApiVersion.java +++ b/hugegraph-api/src/main/java/com/baidu/hugegraph/version/ApiVersion.java @@ -112,10 +112,11 @@ public final class ApiVersion { * [0.60] Issue-1392: Support create and resume snapshot * [0.61] Issue-1433: Unify naming of degree for oltp algorithms * [0.62] Issue-1378: Add compact api for rocksdb/cassandra/hbase backend + * [0.63] HugeGraph-1500: Add login RESTful API */ // The second parameter of Version.of() is for IDE running without JAR - public static final Version VERSION = Version.of(ApiVersion.class, "0.62"); + public static final Version VERSION = Version.of(ApiVersion.class, "0.63"); public static final void check() { // Check version of hugegraph-core. Firstly do check from version 0.3 From 046c7db4d942eea9a7697dca52c906c6f0d52349 Mon Sep 17 00:00:00 2001 From: guoshoujing Date: Mon, 21 Jun 2021 20:47:01 +0800 Subject: [PATCH 10/15] add case in LoginApiTest#testVerify --- .../baidu/hugegraph/version/ApiVersion.java | 2 +- .../com/baidu/hugegraph/api/LoginApiTest.java | 22 +++++++++++++------ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/hugegraph-api/src/main/java/com/baidu/hugegraph/version/ApiVersion.java b/hugegraph-api/src/main/java/com/baidu/hugegraph/version/ApiVersion.java index 8cb00c5b86..15f0a9b604 100644 --- a/hugegraph-api/src/main/java/com/baidu/hugegraph/version/ApiVersion.java +++ b/hugegraph-api/src/main/java/com/baidu/hugegraph/version/ApiVersion.java @@ -112,7 +112,7 @@ public final class ApiVersion { * [0.60] Issue-1392: Support create and resume snapshot * [0.61] Issue-1433: Unify naming of degree for oltp algorithms * [0.62] Issue-1378: Add compact api for rocksdb/cassandra/hbase backend - * [0.63] HugeGraph-1500: Add login RESTful API + * [0.63] Issue-1500: Add user-login RESTful API */ // The second parameter of Version.of() is for IDE running without JAR diff --git a/hugegraph-test/src/main/java/com/baidu/hugegraph/api/LoginApiTest.java b/hugegraph-test/src/main/java/com/baidu/hugegraph/api/LoginApiTest.java index 3f0b54c80e..f03e8bf4ac 100644 --- a/hugegraph-test/src/main/java/com/baidu/hugegraph/api/LoginApiTest.java +++ b/hugegraph-test/src/main/java/com/baidu/hugegraph/api/LoginApiTest.java @@ -89,9 +89,9 @@ public void testLogout() { String invalidToken = "eyJhbGciOiJIUzI1NiJ9.eyJ1caVyX25hbWUiOiJ0ZXN0IiwidXNlcl9pZCI6Ii02Mzp0ZXN0IiwiZXhwIjoxNjI0MzUzMjUyfQ.kYot-3mSGlfSbEMzxrTs84q8YanhTTxtsKPPG25CNxA"; headers = new MultivaluedHashMap<>(); - headers.add(HttpHeaders.AUTHORIZATION, "Bearer " + token); + headers.add(HttpHeaders.AUTHORIZATION, "Bearer " + invalidToken); r = client().delete(path, headers); - assertResponseStatus(204, r); + assertResponseStatus(401, r); } @Test @@ -115,9 +115,16 @@ public void testVerify() { assertJsonContains(result, "user_name"); Map user = JsonUtil.fromJson( - result, new TypeReference>(){}); + result, + new TypeReference>(){}); Assert.assertEquals(this.userId4Test, user.get("user_id")); Assert.assertEquals("test", user.get("user_name")); + + String invalidToken = "eyJhbGciOiJIUzI1NiJ9.eyJ1caVyX25hbWUiOiJ0ZXN0IiwidXNlcl9pZCI6Ii02Mzp0ZXN0IiwiZXhwIjoxNjI0MzUzMjUyfQ.kYot-3mSGlfSbEMzxrTs84q8YanhTTxtsKPPG25CNxA"; + headers = new MultivaluedHashMap<>(); + headers.add(HttpHeaders.AUTHORIZATION, "Bearer " + invalidToken); + r = client().get(path, headers); + assertResponseStatus(401, r); } private Response createUser(String name, String password) { @@ -142,9 +149,10 @@ private Response login(String name, String password) { name, password)); } - private String tokenFromResponse(String result) { - Map loginResult = JsonUtil.fromJson( - result, new TypeReference>(){}); - return (String) loginResult.get("token"); + private String tokenFromResponse(String content) { + Map data = JsonUtil.fromJson( + content, + new TypeReference>(){}); + return (String) data.get("token"); } } From 5001c72c0b464fa68785f26d9359170f656e6406 Mon Sep 17 00:00:00 2001 From: guoshoujing Date: Mon, 21 Jun 2021 22:02:42 +0800 Subject: [PATCH 11/15] add case in LoginApiTest#testVerify --- .../src/main/java/com/baidu/hugegraph/api/LoginApiTest.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hugegraph-test/src/main/java/com/baidu/hugegraph/api/LoginApiTest.java b/hugegraph-test/src/main/java/com/baidu/hugegraph/api/LoginApiTest.java index f03e8bf4ac..dde5eac175 100644 --- a/hugegraph-test/src/main/java/com/baidu/hugegraph/api/LoginApiTest.java +++ b/hugegraph-test/src/main/java/com/baidu/hugegraph/api/LoginApiTest.java @@ -125,6 +125,12 @@ public void testVerify() { headers.add(HttpHeaders.AUTHORIZATION, "Bearer " + invalidToken); r = client().get(path, headers); assertResponseStatus(401, r); + + invalidToken = "123.ansfaf"; + headers = new MultivaluedHashMap<>(); + headers.add(HttpHeaders.AUTHORIZATION, "Bearer " + invalidToken); + r = client().get(path, headers); + assertResponseStatus(401, r); } private Response createUser(String name, String password) { From e6318850ef502f45608516feecb4322c1a83e798 Mon Sep 17 00:00:00 2001 From: guoshoujing Date: Tue, 22 Jun 2021 16:11:34 +0800 Subject: [PATCH 12/15] add test case for StandardAuthManager --- .../hugegraph/auth/HugeGraphAuthProxy.java | 1 - .../hugegraph/auth/StandardAuthenticator.java | 6 +- hugegraph-core/pom.xml | 1 + .../hugegraph/auth/StandardAuthManager.java | 16 ++-- .../com/baidu/hugegraph/core/AuthTest.java | 96 +++++++++++++++++++ 5 files changed, 110 insertions(+), 10 deletions(-) diff --git a/hugegraph-api/src/main/java/com/baidu/hugegraph/auth/HugeGraphAuthProxy.java b/hugegraph-api/src/main/java/com/baidu/hugegraph/auth/HugeGraphAuthProxy.java index 7051fe6448..ae7e7e36e2 100644 --- a/hugegraph-api/src/main/java/com/baidu/hugegraph/auth/HugeGraphAuthProxy.java +++ b/hugegraph-api/src/main/java/com/baidu/hugegraph/auth/HugeGraphAuthProxy.java @@ -23,7 +23,6 @@ import java.util.Collection; import java.util.Iterator; import java.util.List; -import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; 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 b1065bd892..76ce0b4ca8 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 @@ -114,8 +114,8 @@ public void setup(HugeConfig config) { /** * Verify if a user is legal - * @param username the username for authentication - * @param password the password for authentication + * @param username the username for authentication + * @param password the password for authentication * @param token the token for authentication * @return String No permission if return ROLE_NONE else return a role */ @@ -136,7 +136,7 @@ public UserWithRole authenticate(String username, String password, RolePermission role = userWithRole.role(); if (role == null) { role = ROLE_NONE; - } else if (userWithRole.username().equals(USER_ADMIN)) { + } else if (USER_ADMIN.equals(userWithRole.username())) { role = ROLE_ADMIN; } else { return userWithRole; diff --git a/hugegraph-core/pom.xml b/hugegraph-core/pom.xml index 8182269448..fdd2e7652d 100644 --- a/hugegraph-core/pom.xml +++ b/hugegraph-core/pom.xml @@ -174,6 +174,7 @@ fastutil 8.1.0 + io.jsonwebtoken jjwt-api diff --git a/hugegraph-core/src/main/java/com/baidu/hugegraph/auth/StandardAuthManager.java b/hugegraph-core/src/main/java/com/baidu/hugegraph/auth/StandardAuthManager.java index d10667c184..63db657ba1 100644 --- a/hugegraph-core/src/main/java/com/baidu/hugegraph/auth/StandardAuthManager.java +++ b/hugegraph-core/src/main/java/com/baidu/hugegraph/auth/StandardAuthManager.java @@ -473,10 +473,19 @@ public UserWithRole validateUser(String username, String password) { @Override public UserWithRole validateUser(String token) { String username = (String) this.tokenCache.get(IdGenerator.of(token)); + + Claims payload = null; + boolean needBuildCache = false; if (username == null) { - Claims payload = this.tokenGenerator.verify(token); + payload = this.tokenGenerator.verify(token); username = (String) payload.get(AuthConstant.TOKEN_USER_NAME); + needBuildCache = true; + } + HugeUser user = this.findUser(username); + if (user == null) { + return new UserWithRole(username); + } else if (needBuildCache) { long expireAt = payload.getExpiration().getTime(); long bornTime = CACHE_EXPIRE - (expireAt - System.currentTimeMillis()); @@ -484,11 +493,6 @@ public UserWithRole validateUser(String token) { Math.negateExact(bornTime)); } - HugeUser user = this.findUser(username); - if (user == null) { - return new UserWithRole(username); - } - return new UserWithRole(user.id, username, this.rolePermission(user)); } diff --git a/hugegraph-test/src/main/java/com/baidu/hugegraph/core/AuthTest.java b/hugegraph-test/src/main/java/com/baidu/hugegraph/core/AuthTest.java index 4f65a2d928..e64177a6df 100644 --- a/hugegraph-test/src/main/java/com/baidu/hugegraph/core/AuthTest.java +++ b/hugegraph-test/src/main/java/com/baidu/hugegraph/core/AuthTest.java @@ -24,6 +24,8 @@ import java.util.List; import java.util.Map; +import javax.security.sasl.AuthenticationException; + import org.junit.After; import org.junit.Test; @@ -37,10 +39,13 @@ import com.baidu.hugegraph.auth.HugeTarget; import com.baidu.hugegraph.auth.HugeUser; import com.baidu.hugegraph.auth.RolePermission; +import com.baidu.hugegraph.auth.UserWithRole; +import com.baidu.hugegraph.backend.cache.Cache; import com.baidu.hugegraph.backend.id.Id; import com.baidu.hugegraph.backend.id.IdGenerator; import com.baidu.hugegraph.exception.NotFoundException; import com.baidu.hugegraph.testutil.Assert; +import com.baidu.hugegraph.testutil.Whitebox; import com.baidu.hugegraph.util.JsonUtil; import com.baidu.hugegraph.util.StringEncoding; import com.google.common.collect.ImmutableList; @@ -1270,6 +1275,97 @@ public void testRolePermission() { Assert.assertEquals(expected, role.toJson()); } + @Test + public void testLogin() throws AuthenticationException { + AuthManager authManager = graph().authManager(); + + HugeUser user = makeUser("test", StringEncoding.hashPassword("pass")); + authManager.createUser(user); + + // Login + authManager.loginUser("test", "pass"); + + // Invalid username or password + Assert.assertThrows(AuthenticationException.class, () -> { + authManager.loginUser("huge", "graph"); + }, e -> { + Assert.assertContains("Incorrect username or password", e.getMessage()); + }); + } + + @Test + public void testValidateUserByToken() throws AuthenticationException { + AuthManager authManager = graph().authManager(); + + HugeUser user = makeUser("test", StringEncoding.hashPassword("pass")); + Id userId = authManager.createUser(user); + + String token = authManager.loginUser("test", "pass"); + + UserWithRole userWithRole; + userWithRole = authManager.validateUser(token); + Assert.assertEquals(userId, userWithRole.userId()); + Assert.assertEquals("test", userWithRole.username()); + Assert.assertEquals("{\"roles\":{}}", userWithRole.role().toJson()); + + // Token cache missed + Cache tokenCache = Whitebox.getInternalState(authManager, + "tokenCache"); + tokenCache.invalidate(IdGenerator.of(token)); + Assert.assertFalse(tokenCache.containsKey(IdGenerator.of(token))); + + userWithRole = authManager.validateUser(token); + Assert.assertEquals(userId, userWithRole.userId()); + Assert.assertEquals("test", userWithRole.username()); + Assert.assertEquals("{\"roles\":{}}", userWithRole.role().toJson()); + Assert.assertTrue(tokenCache.containsKey(IdGenerator.of(token))); + + // User deleted after login and token not expire + authManager.deleteUser(userId); + userWithRole = authManager.validateUser(token); + Assert.assertNull(null, userWithRole.userId()); + Assert.assertEquals("test", userWithRole.username()); + Assert.assertNull(userWithRole.role()); + } + + @Test + public void testLogout() throws AuthenticationException { + AuthManager authManager = graph().authManager(); + + HugeUser user = makeUser("test", StringEncoding.hashPassword("pass")); + Id userId = authManager.createUser(user); + + // Login + String token = authManager.loginUser("test", "pass"); + + // Logout + Cache tokenCache = Whitebox.getInternalState(authManager, + "tokenCache"); + Assert.assertTrue(tokenCache.containsKey(IdGenerator.of(token))); + authManager.logoutUser(token); + Assert.assertFalse(tokenCache.containsKey(IdGenerator.of(token))); + } + + @Test + public void testValidateUserByNameAndPassword() { + AuthManager authManager = graph().authManager(); + + HugeUser user = makeUser("test", StringEncoding.hashPassword("pass")); + Id userId = authManager.createUser(user); + + UserWithRole userWithRole; + userWithRole = authManager.validateUser("test", "pass"); + Assert.assertEquals(userId, userWithRole.userId()); + Assert.assertEquals("test", userWithRole.username()); + Assert.assertEquals("{\"roles\":{}}", userWithRole.role().toJson()); + + // Error case + userWithRole = authManager.validateUser("huge", "graph"); + Assert.assertNull(userWithRole.userId()); + Assert.assertEquals("huge", userWithRole.username()); + Assert.assertNull(userWithRole.role()); + } + private static HugeUser makeUser(String name, String password) { HugeUser user = new HugeUser(name); user.password(password); From a1fe010867a77177beb4e76c2e928bafedd71ef2 Mon Sep 17 00:00:00 2001 From: guoshoujing Date: Tue, 22 Jun 2021 17:30:16 +0800 Subject: [PATCH 13/15] remove repeat code --- .../src/main/java/com/baidu/hugegraph/api/auth/UserAPI.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/hugegraph-api/src/main/java/com/baidu/hugegraph/api/auth/UserAPI.java b/hugegraph-api/src/main/java/com/baidu/hugegraph/api/auth/UserAPI.java index 3e055de5e6..0bb744a976 100644 --- a/hugegraph-api/src/main/java/com/baidu/hugegraph/api/auth/UserAPI.java +++ b/hugegraph-api/src/main/java/com/baidu/hugegraph/api/auth/UserAPI.java @@ -197,9 +197,6 @@ public HugeUser build(HugeUser user) { if (this.avatar != null) { user.avatar(this.avatar); } - if (this.avatar != null) { - user.avatar(this.avatar); - } if (this.description != null) { user.description(this.description); } From 6549e93f6097f4e909829c9adad090aa993711e0 Mon Sep 17 00:00:00 2001 From: guoshoujing Date: Wed, 23 Jun 2021 16:13:55 +0800 Subject: [PATCH 14/15] improve code style --- .../main/java/com/baidu/hugegraph/auth/StandardAuthManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hugegraph-core/src/main/java/com/baidu/hugegraph/auth/StandardAuthManager.java b/hugegraph-core/src/main/java/com/baidu/hugegraph/auth/StandardAuthManager.java index 63db657ba1..795a3b440a 100644 --- a/hugegraph-core/src/main/java/com/baidu/hugegraph/auth/StandardAuthManager.java +++ b/hugegraph-core/src/main/java/com/baidu/hugegraph/auth/StandardAuthManager.java @@ -493,7 +493,7 @@ public UserWithRole validateUser(String token) { Math.negateExact(bornTime)); } - return new UserWithRole(user.id, username, this.rolePermission(user)); + return new UserWithRole(user.id(), username, this.rolePermission(user)); } /** From a94a07146b8f34f8ee3a85b1fbbe1b028e3e3caf Mon Sep 17 00:00:00 2001 From: guoshoujing Date: Thu, 24 Jun 2021 19:45:56 +0800 Subject: [PATCH 15/15] Empty test for CI