This is an automated email from the ASF dual-hosted git repository.

jin pushed a commit to branch pd-store
in repository https://gitbox.apache.org/repos/asf/incubator-hugegraph.git

commit 4bb48814bbd22c09ff1860a0a4cf2cb2006f25b1
Author: SunnyBoy-WYH <[email protected]>
AuthorDate: Mon Oct 2 13:25:45 2023 +0800

    feat: support White IP List (#2299)
    
    tips:
    - this feat works when auth mode was set.
    - this feat works when white ip status was enabled.
    
    because now PD is unavailable,just use java list; when pd ready , we can 
checkout pd.
---
 .../hugegraph/api/filter/AuthenticationFilter.java |  69 ++++++---
 .../hugegraph/api/profile/WhiteIpListAPI.java      | 155 +++++++++++++++++++++
 .../apache/hugegraph/auth/HugeGraphAuthProxy.java  |  20 +++
 .../org/apache/hugegraph/config/ServerOptions.java |  10 +-
 .../org/apache/hugegraph/auth/AuthManager.java     |   8 ++
 .../apache/hugegraph/auth/StandardAuthManager.java |  45 ++++--
 6 files changed, 280 insertions(+), 27 deletions(-)

diff --git 
a/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/api/filter/AuthenticationFilter.java
 
b/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/api/filter/AuthenticationFilter.java
index f534a0ac9..464e695fe 100644
--- 
a/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/api/filter/AuthenticationFilter.java
+++ 
b/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/api/filter/AuthenticationFilter.java
@@ -17,14 +17,38 @@
 
 package org.apache.hugegraph.api.filter;
 
+import static org.apache.hugegraph.config.ServerOptions.WHITE_IP_STATUS;
+
 import java.io.IOException;
 import java.security.Principal;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+import javax.xml.bind.DatatypeConverter;
+
+import org.apache.hugegraph.auth.HugeAuthenticator;
+import org.apache.hugegraph.auth.HugeAuthenticator.RequiredPerm;
+import org.apache.hugegraph.auth.HugeAuthenticator.RolePerm;
+import org.apache.hugegraph.auth.HugeAuthenticator.User;
+import org.apache.hugegraph.auth.RolePermission;
+import org.apache.hugegraph.config.HugeConfig;
+import org.apache.hugegraph.core.GraphManager;
+import org.apache.hugegraph.util.E;
+import org.apache.hugegraph.util.Log;
+import org.apache.tinkerpop.gremlin.server.auth.AuthenticationException;
+import org.glassfish.grizzly.http.server.Request;
+import org.glassfish.grizzly.utils.Charsets;
+import org.slf4j.Logger;
+
+import com.alipay.remoting.util.StringUtils;
+import com.google.common.collect.ImmutableList;
 
 import jakarta.annotation.Priority;
 import jakarta.ws.rs.BadRequestException;
+import jakarta.ws.rs.ForbiddenException;
 import jakarta.ws.rs.NotAuthorizedException;
 import jakarta.ws.rs.Priorities;
 import jakarta.ws.rs.container.ContainerRequestContext;
@@ -35,23 +59,6 @@ import jakarta.ws.rs.core.HttpHeaders;
 import jakarta.ws.rs.core.SecurityContext;
 import jakarta.ws.rs.core.UriInfo;
 import jakarta.ws.rs.ext.Provider;
-import javax.xml.bind.DatatypeConverter;
-
-import org.apache.commons.lang3.StringUtils;
-import org.apache.tinkerpop.gremlin.server.auth.AuthenticationException;
-import org.glassfish.grizzly.http.server.Request;
-import org.glassfish.grizzly.utils.Charsets;
-import org.slf4j.Logger;
-
-import org.apache.hugegraph.auth.HugeAuthenticator;
-import org.apache.hugegraph.auth.HugeAuthenticator.RequiredPerm;
-import org.apache.hugegraph.auth.HugeAuthenticator.RolePerm;
-import org.apache.hugegraph.auth.HugeAuthenticator.User;
-import org.apache.hugegraph.auth.RolePermission;
-import org.apache.hugegraph.core.GraphManager;
-import org.apache.hugegraph.util.E;
-import org.apache.hugegraph.util.Log;
-import com.google.common.collect.ImmutableList;
 
 @Provider
 @PreMatching
@@ -68,12 +75,20 @@ public class AuthenticationFilter implements 
ContainerRequestFilter {
             "versions"
     );
 
+    private static String whiteIpStatus;
+
+    private static final String STRING_WHITE_IP_LIST = "whiteiplist";
+    private static final String STRING_ENABLE = "enable";
+
     @Context
     private jakarta.inject.Provider<GraphManager> managerProvider;
 
     @Context
     private jakarta.inject.Provider<Request> requestProvider;
 
+    @Context
+    private jakarta.inject.Provider<HugeConfig> configProvider;
+
     @Override
     public void filter(ContainerRequestContext context) throws IOException {
         if (AuthenticationFilter.isWhiteAPI(context)) {
@@ -102,6 +117,26 @@ public class AuthenticationFilter implements 
ContainerRequestFilter {
             path = request.getRequestURI();
         }
 
+        // Check whiteIp
+        if (whiteIpStatus == null) {
+            whiteIpStatus = this.configProvider.get().get(WHITE_IP_STATUS);
+        }
+
+        if (Objects.equals(whiteIpStatus, STRING_ENABLE) && request != null) {
+            peer = request.getRemoteAddr() + ":" + request.getRemotePort();
+            path = request.getRequestURI();
+
+            String remoteIp = request.getRemoteAddr();
+            Set<String> whiteIpList = manager.authManager().listWhiteIPs();
+            boolean whiteIpEnabled = manager.authManager().getWhiteIpStatus();
+            if (!path.contains(STRING_WHITE_IP_LIST) && whiteIpEnabled &&
+                !whiteIpList.contains(remoteIp)) {
+                throw new ForbiddenException(
+                        String.format("Remote ip '%s' is not permitted",
+                                      remoteIp));
+            }
+        }
+
         Map<String, String> credentials = new HashMap<>();
         // Extract authentication credentials
         String auth = context.getHeaderString(HttpHeaders.AUTHORIZATION);
diff --git 
a/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/api/profile/WhiteIpListAPI.java
 
b/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/api/profile/WhiteIpListAPI.java
new file mode 100644
index 000000000..7503e1382
--- /dev/null
+++ 
b/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/api/profile/WhiteIpListAPI.java
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.hugegraph.api.profile;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.hugegraph.api.API;
+import org.apache.hugegraph.api.filter.StatusFilter;
+import org.apache.hugegraph.auth.AuthManager;
+import org.apache.hugegraph.core.GraphManager;
+import org.apache.hugegraph.util.E;
+import org.apache.hugegraph.util.Log;
+import org.slf4j.Logger;
+
+import com.codahale.metrics.annotation.Timed;
+import com.google.common.collect.ImmutableMap;
+
+import jakarta.annotation.security.RolesAllowed;
+import jakarta.inject.Singleton;
+import jakarta.ws.rs.Consumes;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.PUT;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.QueryParam;
+import jakarta.ws.rs.core.Context;
+
+@Path("whiteiplist")
+@Singleton
+public class WhiteIpListAPI extends API {
+
+    private static final Logger LOG = Log.logger(WhiteIpListAPI.class);
+
+    @GET
+    @Timed
+    @Produces(APPLICATION_JSON_WITH_CHARSET)
+    @RolesAllowed("admin")
+    public Map<String, Object> list(@Context GraphManager manager) {
+        LOG.debug("List white ips");
+        AuthManager authManager = manager.authManager();
+        Set<String> whiteIpList = authManager.listWhiteIPs();
+        return ImmutableMap.of("whiteIpList", whiteIpList);
+    }
+
+    @POST
+    @Timed
+    @StatusFilter.Status(StatusFilter.Status.ACCEPTED)
+    @Consumes(APPLICATION_JSON)
+    @Produces(APPLICATION_JSON_WITH_CHARSET)
+    @RolesAllowed("admin")
+    public Map<String, Object> updateWhiteIPs(@Context GraphManager manager, 
Map<String, Object> actionMap) {
+        E.checkArgument(actionMap != null,
+                        "Missing argument: actionMap");
+        Set<String> whiteIpList = manager.authManager().listWhiteIPs();
+        Object ipListRaw = actionMap.get("ips");
+        E.checkArgument(ipListRaw instanceof List,
+                        "Invalid ips type '%s', must be list", 
ipListRaw.getClass());
+        List<String> ipList = (List<String>) ipListRaw;
+        Object actionRaw = actionMap.get("action");
+        E.checkArgument(actionRaw != null,
+                        "Missing argument: action");
+        E.checkArgument(actionRaw instanceof String,
+                        "Invalid action type '%s', must be string",
+                        actionRaw.getClass());
+        String action = (String) actionRaw;
+        E.checkArgument(StringUtils.isNotEmpty(action),
+                        "Missing argument: action");
+        Set<String> existedIPs = new HashSet<>();
+        Set<String> loadedIPs = new HashSet<>();
+        Set<String> illegalIPs = new HashSet<>();
+        Map<String, Object> result = new HashMap<>();
+        for (String ip : ipList) {
+            if (whiteIpList.contains(ip)) {
+                existedIPs.add(ip);
+                continue;
+            }
+            if ("load".equals(action)) {
+                boolean rightIp = checkIp(ip) ? loadedIPs.add(ip) : 
illegalIPs.add(ip);
+            }
+        }
+        switch (action) {
+            case "load":
+                LOG.debug("Load to white ip list");
+                result.put("existed_ips", existedIPs);
+                result.put("added_ips", loadedIPs);
+                if (!illegalIPs.isEmpty()) {
+                    result.put("illegal_ips", illegalIPs);
+                }
+                whiteIpList.addAll(loadedIPs);
+                break;
+            case "remove":
+                LOG.debug("Remove from white ip list");
+                result.put("removed_ips", existedIPs);
+                result.put("non_existed_ips", loadedIPs);
+                whiteIpList.removeAll(existedIPs);
+                break;
+            default:
+                throw new AssertionError(String.format("Invalid action '%s', " 
+
+                                                       "supported action is " +
+                                                       "'load' or 'remove'",
+                                                       action));
+        }
+        manager.authManager().setWhiteIPs(whiteIpList);
+        return result;
+    }
+
+    @PUT
+    @Timed
+    @Produces(APPLICATION_JSON_WITH_CHARSET)
+    @RolesAllowed("admin")
+    public Map<String, Object> updateStatus(@Context GraphManager manager, 
@QueryParam("status") String status) {
+        LOG.debug("Enable or disable white ip list");
+        E.checkArgument("true".equals(status) ||
+                        "false".equals(status),
+                        "Invalid status, valid status is 'true' or 'false'");
+        boolean open = Boolean.parseBoolean(status);
+        manager.authManager().enabledWhiteIpList(open);
+        Map<String, Object> map = new HashMap<>();
+        map.put("WhiteIpListOpen", open);
+        return map;
+    }
+
+    private boolean checkIp(String ipStr) {
+        String ip = "^(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|[1-9])\\."
+                    + "(00?\\d|1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)\\."
+                    + "(00?\\d|1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)\\."
+                    + "(00?\\d|1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)$";
+        Pattern pattern = Pattern.compile(ip);
+        Matcher matcher = pattern.matcher(ipStr);
+        return matcher.matches();
+    }
+}
diff --git 
a/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/auth/HugeGraphAuthProxy.java
 
b/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/auth/HugeGraphAuthProxy.java
index 96841dbe6..83303ad51 100644
--- 
a/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/auth/HugeGraphAuthProxy.java
+++ 
b/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/auth/HugeGraphAuthProxy.java
@@ -1600,6 +1600,26 @@ public final class HugeGraphAuthProxy implements 
HugeGraph {
             }
         }
 
+        @Override
+        public Set<String> listWhiteIPs() {
+            return this.authManager.listWhiteIPs();
+        }
+
+        @Override
+        public void setWhiteIPs(Set<String> whiteIpList) {
+            this.authManager.setWhiteIPs(whiteIpList);
+        }
+
+        @Override
+        public boolean getWhiteIpStatus() {
+            return this.authManager.getWhiteIpStatus();
+        }
+
+        @Override
+        public void enabledWhiteIpList(boolean status) {
+            this.authManager.enabledWhiteIpList(status);
+        }
+
         @Override
         public String loginUser(String username, String password) {
             try {
diff --git 
a/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/config/ServerOptions.java
 
b/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/config/ServerOptions.java
index e66b59356..6e41ae87c 100644
--- 
a/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/config/ServerOptions.java
+++ 
b/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/config/ServerOptions.java
@@ -264,4 +264,12 @@ public class ServerOptions extends OptionHolder {
                     disallowEmpty(),
                     true
             );
-}
\ No newline at end of file
+
+    public static final ConfigOption<String> WHITE_IP_STATUS =
+            new ConfigOption<>(
+                    "white_ip.status",
+                    "The status of whether enable white ip.",
+                    disallowEmpty(),
+                    "disable"
+            );
+}
diff --git 
a/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/auth/AuthManager.java
 
b/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/auth/AuthManager.java
index 2dba7c7a1..908eed01f 100644
--- 
a/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/auth/AuthManager.java
+++ 
b/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/auth/AuthManager.java
@@ -126,4 +126,12 @@ public interface AuthManager {
     UserWithRole validateUser(String username, String password);
 
     UserWithRole validateUser(String token);
+
+    Set<String> listWhiteIPs();
+
+    void setWhiteIPs(Set<String> whiteIpList);
+
+    boolean getWhiteIpStatus();
+
+    void enabledWhiteIpList(boolean status);
 }
diff --git 
a/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/auth/StandardAuthManager.java
 
b/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/auth/StandardAuthManager.java
index 910f19cdc..123c8e9ff 100644
--- 
a/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/auth/StandardAuthManager.java
+++ 
b/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/auth/StandardAuthManager.java
@@ -27,31 +27,30 @@ import java.util.concurrent.Callable;
 
 import javax.security.sasl.AuthenticationException;
 
-import jakarta.ws.rs.ForbiddenException;
-
 import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.lang.StringUtils;
+import org.apache.hugegraph.HugeException;
+import org.apache.hugegraph.HugeGraphParams;
+import org.apache.hugegraph.auth.HugeUser.P;
+import org.apache.hugegraph.auth.SchemaDefine.AuthElement;
 import org.apache.hugegraph.backend.cache.Cache;
 import org.apache.hugegraph.backend.cache.CacheManager;
 import org.apache.hugegraph.backend.id.Id;
 import org.apache.hugegraph.backend.id.IdGenerator;
 import org.apache.hugegraph.config.AuthOptions;
+import org.apache.hugegraph.config.HugeConfig;
 import org.apache.hugegraph.type.define.Directions;
+import org.apache.hugegraph.util.E;
 import org.apache.hugegraph.util.LockUtil;
+import org.apache.hugegraph.util.Log;
 import org.apache.hugegraph.util.StringEncoding;
 import org.slf4j.Logger;
 
-import org.apache.hugegraph.HugeException;
-import org.apache.hugegraph.HugeGraphParams;
-import org.apache.hugegraph.auth.HugeUser.P;
-import org.apache.hugegraph.auth.SchemaDefine.AuthElement;
-import org.apache.hugegraph.config.HugeConfig;
-import org.apache.hugegraph.util.E;
-import org.apache.hugegraph.util.Log;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 
 import io.jsonwebtoken.Claims;
+import jakarta.ws.rs.ForbiddenException;
 
 public class StandardAuthManager implements AuthManager {
 
@@ -77,6 +76,10 @@ public class StandardAuthManager implements AuthManager {
     private final TokenGenerator tokenGenerator;
     private final long tokenExpire;
 
+    private Set<String> ipWhiteList;
+
+    private Boolean ipWhiteListEnabled;
+
     public StandardAuthManager(HugeGraphParams graph) {
         E.checkNotNull(graph, "graph");
         HugeConfig config = graph.configuration();
@@ -104,6 +107,10 @@ public class StandardAuthManager implements AuthManager {
                                                 HugeAccess::fromEdge);
 
         this.tokenGenerator = new TokenGenerator(config);
+
+        this.ipWhiteList = new HashSet<>();
+
+        this.ipWhiteListEnabled = false;
     }
 
     private <V> Cache<Id, V> cache(String prefix, long capacity,
@@ -689,6 +696,26 @@ public class StandardAuthManager implements AuthManager {
         return new UserWithRole(user.id(), username, 
this.rolePermission(user));
     }
 
+    @Override
+    public Set<String> listWhiteIPs() {
+        return ipWhiteList;
+    }
+
+    @Override
+    public void setWhiteIPs(Set<String> ipWhiteList) {
+        this.ipWhiteList = ipWhiteList;
+    }
+
+    @Override
+    public boolean getWhiteIpStatus() {
+        return this.ipWhiteListEnabled;
+    }
+
+    @Override
+    public void enabledWhiteIpList(boolean status) {
+        this.ipWhiteListEnabled = status;
+    }
+
     /**
      * Maybe can define an proxy class to choose forward or call local
      */

Reply via email to