Skip to content
7 changes: 7 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@
<spring-ai.version>1.1.2</spring-ai.version>
<mcp.version>0.17.0</mcp.version>
<swagger-parser.version>2.1.30</swagger-parser.version>
<re2j.version>1.8</re2j.version>
<!-- dependency version end -->
</properties>

Expand Down Expand Up @@ -590,6 +591,12 @@
<version>${swagger-parser.version}</version>
</dependency>

<dependency>
<groupId>com.google.re2j</groupId>
<artifactId>re2j</artifactId>
<version>${re2j.version}</version>
</dependency>

</dependencies>
</dependencyManagement>

Expand Down
5 changes: 5 additions & 0 deletions shenyu-admin/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,11 @@
<groupId>io.swagger.parser.v3</groupId>
<artifactId>swagger-parser</artifactId>
</dependency>

<dependency>
<groupId>com.google.re2j</groupId>
<artifactId>re2j</artifactId>
</dependency>
</dependencies>

<profiles>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,9 @@
import org.apache.shenyu.admin.model.result.ConfigImportResult;
import org.apache.shenyu.admin.model.vo.RuleVO;
import org.apache.shenyu.admin.service.configs.ConfigsImportContext;
import org.apache.shenyu.admin.validation.validator.UriConditionValidator;
import org.apache.shenyu.common.dto.RuleData;
import org.apache.shenyu.common.enums.OperatorEnum;
import org.apache.shenyu.common.enums.ParamTypeEnum;
import org.springframework.web.util.pattern.PathPatternParser;

import java.util.List;

Expand All @@ -55,17 +54,13 @@ public interface RuleService extends PageService<RuleQueryCondition, RuleVO> {
* @return rows int
*/
default int createOrUpdate(final RuleDTO ruleDTO) {

// now, only check rule uri condition in pathPattern mode
// todo check uri in other modes

try {
final List<RuleConditionDTO> ruleConditions = ruleDTO.getRuleConditions();
ruleConditions.stream()
.filter(conditionData -> ParamTypeEnum.URI.getName().equals(conditionData.getParamType()))
.filter(conditionData -> OperatorEnum.PATH_PATTERN.getAlias().equals(conditionData.getOperator()))
.map(RuleConditionDTO::getParamValue)
.forEach(PathPatternParser.defaultInstance::parse);
.forEach(conditionData -> {
UriConditionValidator.validate(conditionData.getOperator(), conditionData.getParamValue());
});
} catch (Exception e) {
throw new ShenyuAdminException("uri validation of Condition failed, please check.", e);
}
Expand All @@ -91,7 +86,7 @@ default int createOrUpdate(final RuleDTO ruleDTO) {
/**
* delete rules by ids and namespaceId.
*
* @param ids primary key.
* @param ids primary key.
* @param namespaceId namespaceId.
* @return rows int
*/
Expand Down Expand Up @@ -171,7 +166,7 @@ default int createOrUpdate(final RuleDTO ruleDTO) {
* Find by selector id and name rule do.
*
* @param selectorId selector id
* @param name rule name
* @param name rule name
* @return {@link RuleDO}
*/
RuleDO findBySelectorIdAndName(String selectorId, String name);
Expand All @@ -188,17 +183,17 @@ default int createOrUpdate(final RuleDTO ruleDTO) {
* Import data.
*
* @param namespace namespace
* @param ruleList rule list
* @param context import context
* @param ruleList rule list
* @param context import context
* @return config import result
*/
ConfigImportResult importData(String namespace, List<RuleDTO> ruleList, ConfigsImportContext context);

/**
* Enabled string by ids and namespaceId.
*
* @param ids the ids
* @param enabled the enabled
* @param ids the ids
* @param enabled the enabled
* @param namespaceId the namespaceId.
* @return the result
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* 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.shenyu.admin.validation.validator;

import com.google.re2j.Pattern;
import org.apache.commons.lang3.StringUtils;
import org.apache.shenyu.common.enums.OperatorEnum;
import org.springframework.web.util.pattern.PathPatternParser;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.function.Consumer;

public class UriConditionValidator {

private static final Map<String, Consumer<String>> VALIDATOR_MAP = new HashMap<>();

static {
Consumer<String> commonPathValidator = value -> {
if (!value.startsWith("/")) {
throw new IllegalArgumentException("The URI must start with '/'");
}
if (StringUtils.containsAny(value, " ", "\t", "\n")) {
throw new IllegalArgumentException(
"The URI cannot contain whitespaces. Current value: " + value);
}
};
Consumer<String> blankPathValidator = value -> {
if (StringUtils.isNotBlank(value)) {
throw new IllegalArgumentException("The URI must be blank");
}
};
VALIDATOR_MAP.put(OperatorEnum.PATH_PATTERN.getAlias(),
PathPatternParser.defaultInstance::parse);
VALIDATOR_MAP.put(OperatorEnum.REGEX.getAlias(), Pattern::compile);
VALIDATOR_MAP.put(OperatorEnum.EQ.getAlias(), commonPathValidator);
VALIDATOR_MAP.put(OperatorEnum.STARTS_WITH.getAlias(), commonPathValidator);
VALIDATOR_MAP.put(OperatorEnum.ENDS_WITH.getAlias(), commonPathValidator);
VALIDATOR_MAP.put(OperatorEnum.MATCH.getAlias(), commonPathValidator);
VALIDATOR_MAP.put(OperatorEnum.EXCLUDE.getAlias(), commonPathValidator);
VALIDATOR_MAP.put(OperatorEnum.CONTAINS.getAlias(), commonPathValidator);
VALIDATOR_MAP.put(OperatorEnum.IS_BLANK.getAlias(), blankPathValidator);
}
Comment thread
hengyuss marked this conversation as resolved.

public static void validate(final String operator, final String value) {
if (!OperatorEnum.IS_BLANK.getAlias().equals(operator) && StringUtils.isBlank(value)) {
throw new IllegalArgumentException("The URI condition value cannot be empty.");
}
Consumer<String> validator = VALIDATOR_MAP.get(operator);
if (Objects.nonNull(validator)) {
validator.accept(value);
}
Comment thread
hengyuss marked this conversation as resolved.
Comment thread
hengyuss marked this conversation as resolved.
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*
* 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.shenyu.admin.validation.validator;

import com.google.re2j.PatternSyntaxException;
import org.apache.shenyu.common.enums.OperatorEnum;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.web.util.pattern.PatternParseException;


/**
* Test cases for {@link UriConditionValidator}.
*/
public class UriConditionValidatorTest {

@Test
public void testValidateErrorRegex() {
String pattern = "[abc";
Assertions.assertThrows(PatternSyntaxException.class,
() -> UriConditionValidator.validate(OperatorEnum.REGEX.getAlias(), pattern));
}

@Test
public void testValidateHappyRegex() {
String pattern = "^\\/[a-zA-Z0-9\\-_\\/]+$";
Assertions.assertDoesNotThrow(() -> UriConditionValidator.validate(OperatorEnum.REGEX.getAlias(), pattern));
}

@Test
public void testValidateHappyPathPattern() {
String pattern = "/http/**";
Assertions.assertDoesNotThrow(() -> UriConditionValidator.validate(OperatorEnum.PATH_PATTERN.getAlias(), pattern));
}

@Test
public void testValidateErrorPathPattern() {
String pattern = "/http/{abc";
Assertions.assertThrows(PatternParseException.class,
() -> UriConditionValidator.validate(OperatorEnum.PATH_PATTERN.getAlias(), pattern));
}

@Test
public void testValidateHappyIsBlank() {
String pattern = "";
Assertions.assertDoesNotThrow(() -> UriConditionValidator.validate(OperatorEnum.IS_BLANK.getAlias(), pattern));
}

@Test
public void testValidateErrorIsBlank() {
String pattern = "/http";
Assertions.assertThrows(IllegalArgumentException.class,
() -> UriConditionValidator.validate(OperatorEnum.IS_BLANK.getAlias(), pattern));
}

@Test
public void testValidateEmptyValueForNormalOperator() {
String pattern = " ";
Assertions.assertThrows(IllegalArgumentException.class,
() -> UriConditionValidator.validate(OperatorEnum.EQ.getAlias(), pattern));
}

@Test
public void testValidateHappyOtherCondition() {
String pattern = "/http/test";
Assertions.assertDoesNotThrow(() -> UriConditionValidator.validate(OperatorEnum.STARTS_WITH.getAlias(), pattern));
Assertions.assertDoesNotThrow(() -> UriConditionValidator.validate(OperatorEnum.ENDS_WITH.getAlias(), pattern));
Assertions.assertDoesNotThrow(() -> UriConditionValidator.validate(OperatorEnum.MATCH.getAlias(), pattern));
Assertions.assertDoesNotThrow(() -> UriConditionValidator.validate(OperatorEnum.EQ.getAlias(), pattern));
Assertions.assertDoesNotThrow(() -> UriConditionValidator.validate(OperatorEnum.EXCLUDE.getAlias(), pattern));
Assertions.assertDoesNotThrow(() -> UriConditionValidator.validate(OperatorEnum.CONTAINS.getAlias(), pattern));
}

@Test
public void testValidateErrorOtherCondition() {
String pattern = "http/test";
Assertions.assertThrows(IllegalArgumentException.class, () -> UriConditionValidator.validate(OperatorEnum.STARTS_WITH.getAlias(), pattern));
Assertions.assertThrows(IllegalArgumentException.class, () -> UriConditionValidator.validate(OperatorEnum.ENDS_WITH.getAlias(), pattern));
Assertions.assertThrows(IllegalArgumentException.class, () -> UriConditionValidator.validate(OperatorEnum.MATCH.getAlias(), pattern));
Assertions.assertThrows(IllegalArgumentException.class, () -> UriConditionValidator.validate(OperatorEnum.EQ.getAlias(), pattern));
Assertions.assertThrows(IllegalArgumentException.class, () -> UriConditionValidator.validate(OperatorEnum.EXCLUDE.getAlias(), pattern));
Assertions.assertThrows(IllegalArgumentException.class, () -> UriConditionValidator.validate(OperatorEnum.CONTAINS.getAlias(), pattern));
}
}
Loading