Skip to content

Commit

Permalink
feat: support White IP List (#2299)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
SunnyBoy-WYH authored and imbajin committed Nov 10, 2023
1 parent b116fc8 commit 4bb4881
Show file tree
Hide file tree
Showing 6 changed files with 280 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -35,23 +59,6 @@
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
Expand All @@ -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)) {
Expand Down Expand Up @@ -102,6 +117,26 @@ protected User authenticate(ContainerRequestContext context) {
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);
Expand Down
Original file line number Diff line number Diff line change
@@ -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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1600,6 +1600,26 @@ public UserWithRole validateUser(String token) {
}
}

@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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -264,4 +264,12 @@ public static synchronized ServerOptions instance() {
disallowEmpty(),
true
);
}

public static final ConfigOption<String> WHITE_IP_STATUS =
new ConfigOption<>(
"white_ip.status",
"The status of whether enable white ip.",
disallowEmpty(),
"disable"
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Loading

0 comments on commit 4bb4881

Please sign in to comment.