From d270fda0ea65f7a41687d0a599e30f22e1cf734d Mon Sep 17 00:00:00 2001 From: xinlunanxinlunan Date: Wed, 16 Aug 2023 16:20:35 +0800 Subject: [PATCH] Add sentinel-security-core module as building blocks for zero-trust(certificate manager, authentication) as part of traffic governance module --- pom.xml | 9 +- sentinel-security-core/pom.xml | 89 ++++ .../csp/sentinel/trust/StoreCallback.java | 25 ++ .../csp/sentinel/trust/TrustManager.java | 106 +++++ .../csp/sentinel/trust/auth/Rules.java | 59 +++ .../trust/auth/condition/AuthCondition.java | 93 ++++ .../auth/condition/matcher/IpMatcher.java | 123 ++++++ .../trust/auth/condition/matcher/Matcher.java | 26 ++ .../auth/condition/matcher/PortMatcher.java | 60 +++ .../auth/condition/matcher/StringMatcher.java | 159 +++++++ .../sentinel/trust/auth/rule/AuthRule.java | 133 ++++++ .../sentinel/trust/auth/rule/AuthType.java | 199 +++++++++ .../csp/sentinel/trust/auth/rule/JwtRule.java | 94 +++++ .../csp/sentinel/trust/cert/CertPair.java | 136 ++++++ .../csp/sentinel/trust/tls/TlsMode.java | 133 ++++++ .../csp/sentinel/trust/util/JwtUtil.java | 120 ++++++ .../trust/validator/AuthValidator.java | 262 ++++++++++++ .../trust/validator/UnifiedHttpRequest.java | 214 ++++++++++ .../csp/sentinel/trust/TrustManagerTest.java | 56 +++ .../auth/condition/matcher/IpMatcherTest.java | 51 +++ .../condition/matcher/PortMatcherTest.java | 33 ++ .../condition/matcher/StringMatcherTest.java | 94 +++++ .../csp/sentinel/trust/util/JwtUtilTest.java | 140 ++++++ .../trust/validator/AuthValidatorTest.java | 398 ++++++++++++++++++ 24 files changed, 2810 insertions(+), 2 deletions(-) create mode 100644 sentinel-security-core/pom.xml create mode 100644 sentinel-security-core/src/main/java/com/alibaba/csp/sentinel/trust/StoreCallback.java create mode 100644 sentinel-security-core/src/main/java/com/alibaba/csp/sentinel/trust/TrustManager.java create mode 100644 sentinel-security-core/src/main/java/com/alibaba/csp/sentinel/trust/auth/Rules.java create mode 100644 sentinel-security-core/src/main/java/com/alibaba/csp/sentinel/trust/auth/condition/AuthCondition.java create mode 100644 sentinel-security-core/src/main/java/com/alibaba/csp/sentinel/trust/auth/condition/matcher/IpMatcher.java create mode 100644 sentinel-security-core/src/main/java/com/alibaba/csp/sentinel/trust/auth/condition/matcher/Matcher.java create mode 100644 sentinel-security-core/src/main/java/com/alibaba/csp/sentinel/trust/auth/condition/matcher/PortMatcher.java create mode 100644 sentinel-security-core/src/main/java/com/alibaba/csp/sentinel/trust/auth/condition/matcher/StringMatcher.java create mode 100644 sentinel-security-core/src/main/java/com/alibaba/csp/sentinel/trust/auth/rule/AuthRule.java create mode 100644 sentinel-security-core/src/main/java/com/alibaba/csp/sentinel/trust/auth/rule/AuthType.java create mode 100644 sentinel-security-core/src/main/java/com/alibaba/csp/sentinel/trust/auth/rule/JwtRule.java create mode 100644 sentinel-security-core/src/main/java/com/alibaba/csp/sentinel/trust/cert/CertPair.java create mode 100644 sentinel-security-core/src/main/java/com/alibaba/csp/sentinel/trust/tls/TlsMode.java create mode 100644 sentinel-security-core/src/main/java/com/alibaba/csp/sentinel/trust/util/JwtUtil.java create mode 100644 sentinel-security-core/src/main/java/com/alibaba/csp/sentinel/trust/validator/AuthValidator.java create mode 100644 sentinel-security-core/src/main/java/com/alibaba/csp/sentinel/trust/validator/UnifiedHttpRequest.java create mode 100644 sentinel-security-core/src/test/java/com/alibaba/csp/sentinel/trust/TrustManagerTest.java create mode 100644 sentinel-security-core/src/test/java/com/alibaba/csp/sentinel/trust/auth/condition/matcher/IpMatcherTest.java create mode 100644 sentinel-security-core/src/test/java/com/alibaba/csp/sentinel/trust/auth/condition/matcher/PortMatcherTest.java create mode 100644 sentinel-security-core/src/test/java/com/alibaba/csp/sentinel/trust/auth/condition/matcher/StringMatcherTest.java create mode 100644 sentinel-security-core/src/test/java/com/alibaba/csp/sentinel/trust/util/JwtUtilTest.java create mode 100644 sentinel-security-core/src/test/java/com/alibaba/csp/sentinel/trust/validator/AuthValidatorTest.java diff --git a/pom.xml b/pom.xml index 7dcedf49a4..43fd7f20be 100755 --- a/pom.xml +++ b/pom.xml @@ -69,6 +69,7 @@ sentinel-core + sentinel-security-core sentinel-extension sentinel-transport sentinel-adapter @@ -79,7 +80,6 @@ sentinel-demo sentinel-benchmark - @@ -89,6 +89,11 @@ sentinel-core ${project.version} + + com.alibaba.csp + sentinel-security-core + ${project.version} + com.alibaba.csp sentinel-extension @@ -170,7 +175,7 @@ sentinel-metric-exporter ${project.version} - + com.alibaba fastjson diff --git a/sentinel-security-core/pom.xml b/sentinel-security-core/pom.xml new file mode 100644 index 0000000000..a60befc79f --- /dev/null +++ b/sentinel-security-core/pom.xml @@ -0,0 +1,89 @@ + + + 4.0.0 + + + com.alibaba.csp + sentinel-parent + 2.0.0-alpha2-SNAPSHOT + + sentinel-security-core + jar + The security core of Sentinel + + + 0.8.0 + + + + + com.alibaba.csp + sentinel-core + + + + + org.bitbucket.b_c + jose4j + ${jose4j.version} + + + + junit + junit + test + + + org.hamcrest + hamcrest-core + + + org.hamcrest + hamcrest-libray + + + + + org.mockito + mockito-core + test + + + org.awaitility + awaitility + test + + + org.hamcrest + java-hamcrest + test + + + org.powermock + powermock-module-junit4 + test + + + org.powermock + powermock-api-mockito2 + test + + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + ${project.version} + + + + + + + \ No newline at end of file diff --git a/sentinel-security-core/src/main/java/com/alibaba/csp/sentinel/trust/StoreCallback.java b/sentinel-security-core/src/main/java/com/alibaba/csp/sentinel/trust/StoreCallback.java new file mode 100644 index 0000000000..3fa67127db --- /dev/null +++ b/sentinel-security-core/src/main/java/com/alibaba/csp/sentinel/trust/StoreCallback.java @@ -0,0 +1,25 @@ +/* + * Copyright 1999-2023 Alibaba Group Holding Ltd. + * + * Licensed 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 + * + * https://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.alibaba.csp.sentinel.trust; + +/** + * @author lwj + * @since 2.0.0 + */ +public interface StoreCallback { + + void onUpdate(T newInstance); +} diff --git a/sentinel-security-core/src/main/java/com/alibaba/csp/sentinel/trust/TrustManager.java b/sentinel-security-core/src/main/java/com/alibaba/csp/sentinel/trust/TrustManager.java new file mode 100644 index 0000000000..d6d42202ba --- /dev/null +++ b/sentinel-security-core/src/main/java/com/alibaba/csp/sentinel/trust/TrustManager.java @@ -0,0 +1,106 @@ +/* + * Copyright 1999-2023 Alibaba Group Holding Ltd. + * + * Licensed 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 + * + * https://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.alibaba.csp.sentinel.trust; + +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.csp.sentinel.trust.auth.Rules; +import com.alibaba.csp.sentinel.trust.cert.CertPair; +import com.alibaba.csp.sentinel.trust.tls.TlsMode; + +/** + * Manager of Sentinel zero-trust cert and rules. + * + * @author lwj + * @since 2.0.0 + */ +public class TrustManager { + + private static volatile TrustManager instance = null; + + private CertPair certPair = null; + private List> certStoreCallbackList = new ArrayList<>(); + private TlsMode tlsMode = null; + private List> tlsModeStoreCallbackList = new ArrayList<>(); + private Rules rules = null; + private List> rulesStoreCallbackList = new ArrayList<>(); + + public static TrustManager getInstance() { + if (null != instance) { + return instance; + } + synchronized (TrustManager.class) { + if (null != instance) { + return instance; + } + instance = new TrustManager(); + return instance; + } + } + + public synchronized void storeCertPair(CertPair certPair) { + this.certPair = certPair; + certStoreCallbackList.forEach(c -> c.onUpdate(certPair)); + } + + public synchronized void storeTlsMode(TlsMode tlsMode) { + this.tlsMode = tlsMode; + tlsModeStoreCallbackList.forEach(c -> c.onUpdate(tlsMode)); + } + + public synchronized void storeRules(Rules rules) { + this.rules = rules; + rulesStoreCallbackList.forEach(c -> c.onUpdate(rules)); + } + + public void registerCertCallback(StoreCallback callback) { + certStoreCallbackList.add(callback); + } + + public void registerTlsModeCallback(StoreCallback callback) { + tlsModeStoreCallbackList.add(callback); + } + + public void registerRulesCallback(StoreCallback callback) { + rulesStoreCallbackList.add(callback); + } + + public void removeAllCertCallback() { + certStoreCallbackList.clear(); + } + + public void removeAllTlsModeCallback() { + tlsModeStoreCallbackList.clear(); + } + + public void removeAllRulesCallback() { + rulesStoreCallbackList.clear(); + } + + public CertPair getCertPair() { + return certPair; + } + + public TlsMode getTlsMode() { + return tlsMode; + } + + public Rules getRules() { + return rules; + } + +} diff --git a/sentinel-security-core/src/main/java/com/alibaba/csp/sentinel/trust/auth/Rules.java b/sentinel-security-core/src/main/java/com/alibaba/csp/sentinel/trust/auth/Rules.java new file mode 100644 index 0000000000..ab22fcbdaa --- /dev/null +++ b/sentinel-security-core/src/main/java/com/alibaba/csp/sentinel/trust/auth/Rules.java @@ -0,0 +1,59 @@ +/* + * Copyright 1999-2019 Alibaba Group Holding Ltd. + * + * Licensed 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 + * + * https://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.alibaba.csp.sentinel.trust.auth; + +import java.util.Map; + +import com.alibaba.csp.sentinel.trust.auth.rule.AuthRule; +import com.alibaba.csp.sentinel.trust.auth.rule.JwtRule; + +/** + * @author lwj + * @since 2.0.0 + */ +public class Rules { + + private final Map allowAuthRules; + + private final Map denyAuthRules; + + private final Map jwtRules; + + public Rules(Map allowAuthRules, Map denyAuthRules, + Map jwtRules) { + this.allowAuthRules = allowAuthRules; + this.denyAuthRules = denyAuthRules; + this.jwtRules = jwtRules; + } + + public Map getAllowAuthRules() { + return allowAuthRules; + } + + public Map getDenyAuthRules() { + return denyAuthRules; + } + + public Map getJwtRules() { + return jwtRules; + } + + @Override + public String toString() { + return "Rules{" + "allowAuthRules=" + allowAuthRules + ", denyAuthRules=" + denyAuthRules + ", jwtRules=" + + jwtRules + '}'; + } +} diff --git a/sentinel-security-core/src/main/java/com/alibaba/csp/sentinel/trust/auth/condition/AuthCondition.java b/sentinel-security-core/src/main/java/com/alibaba/csp/sentinel/trust/auth/condition/AuthCondition.java new file mode 100644 index 0000000000..610b66411b --- /dev/null +++ b/sentinel-security-core/src/main/java/com/alibaba/csp/sentinel/trust/auth/condition/AuthCondition.java @@ -0,0 +1,93 @@ +/* + * Copyright 1999-2019 Alibaba Group Holding Ltd. + * + * Licensed 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 + * + * https://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.alibaba.csp.sentinel.trust.auth.condition; + +import java.util.Objects; + +import com.alibaba.csp.sentinel.trust.auth.condition.matcher.Matcher; +import com.alibaba.csp.sentinel.trust.auth.rule.AuthType; + +/** + * @author lwj + * @since 2.0.0 + */ +public class AuthCondition { + + /** + * authType , depending on the judgment, request values in different places + */ + private final AuthType type; + /** + * If the request is header and claim, + * it indicates which key to get from + */ + private final String key; + /** + * Matcher, which is used to make matches after a request is received + */ + private final Matcher matcher; + + public AuthCondition(AuthType type, Matcher matcher) { + this.type = type; + this.matcher = matcher; + this.key = null; + } + + public AuthCondition(AuthType type, String key, Matcher matcher) { + this.type = type; + this.matcher = matcher; + this.key = key; + + } + + public AuthType getType() { + return type; + } + + public String getKey() { + return key; + } + + public Matcher getMatcher() { + return matcher; + } + + @Override + public String toString() { + return "AuthCondition{" + + "type=" + type + + ", key='" + key + '\'' + + ", matcher=" + matcher + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + AuthCondition that = (AuthCondition) o; + return type == that.type && Objects.equals(key, that.key) && Objects.equals(matcher, that.matcher); + } + + @Override + public int hashCode() { + return Objects.hash(type, key, matcher); + } +} diff --git a/sentinel-security-core/src/main/java/com/alibaba/csp/sentinel/trust/auth/condition/matcher/IpMatcher.java b/sentinel-security-core/src/main/java/com/alibaba/csp/sentinel/trust/auth/condition/matcher/IpMatcher.java new file mode 100644 index 0000000000..b7a65671ab --- /dev/null +++ b/sentinel-security-core/src/main/java/com/alibaba/csp/sentinel/trust/auth/condition/matcher/IpMatcher.java @@ -0,0 +1,123 @@ +/* + * Copyright 1999-2019 Alibaba Group Holding Ltd. + * + * Licensed 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 + * + * https://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.alibaba.csp.sentinel.trust.auth.condition.matcher; + +import java.util.Objects; + +import com.alibaba.csp.sentinel.log.RecordLog; +import com.alibaba.csp.sentinel.util.StringUtil; + +/** + * @author lwj + * @since 2.0.0 + */ +public class IpMatcher implements Matcher { + + /** + * Prefix length in CIDR case + */ + private final int prefixLen; + + /** + * Ip address to be matched + */ + private final String ipBinaryString; + + public IpMatcher(int prefixLen, String ipBinaryString) { + this.prefixLen = prefixLen; + this.ipBinaryString = ip2BinaryString(ipBinaryString); + } + + /** + * @param ip dotted ip string, + * @return + */ + public static String ip2BinaryString(String ip) { + try { + String[] ips = ip.split("\\."); + if (4 != ips.length) { + RecordLog.error("Error ip={}", ip); + return ""; + } + long[] ipLong = new long[4]; + for (int i = 0; i < 4; ++i) { + ipLong[i] = Long.parseLong(ips[i]); + if (ipLong[i] < 0 || ipLong[i] > 255) { + RecordLog.error("Error ip={}", ip); + return ""; + } + } + return String.format("%32s", Long.toBinaryString((ipLong[0] << 24) + + (ipLong[1] << 16) + + (ipLong[2] << 8) + + ipLong[3])).replace(" ", "0"); + } catch (Exception e) { + RecordLog.error("Error ip={}", ip); + } + return ""; + } + + public boolean match(String object) { + if (StringUtil.isEmpty(ipBinaryString)) { + return false; + } + String ipBinary = ip2BinaryString(object); + if (StringUtil.isEmpty(ipBinary)) { + return false; + } + if (prefixLen <= 0) { + return ipBinaryString.equals(ipBinary); + } + if (ipBinaryString.length() >= prefixLen && ipBinary.length() >= prefixLen) { + return ipBinaryString.substring(0, prefixLen) + .equals(ipBinary.substring(0, prefixLen)); + } + return false; + } + + public int getPrefixLen() { + return prefixLen; + } + + public String getIpBinaryString() { + return ipBinaryString; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + IpMatcher ipMatcher = (IpMatcher) o; + return prefixLen == ipMatcher.prefixLen && Objects.equals(ipBinaryString, ipMatcher.ipBinaryString); + } + + @Override + public int hashCode() { + return Objects.hash(prefixLen, ipBinaryString); + } + + @Override + public String toString() { + return "IpMatcher{" + + "prefixLen=" + prefixLen + + ", ip='" + ipBinaryString + '\'' + + '}'; + } +} diff --git a/sentinel-security-core/src/main/java/com/alibaba/csp/sentinel/trust/auth/condition/matcher/Matcher.java b/sentinel-security-core/src/main/java/com/alibaba/csp/sentinel/trust/auth/condition/matcher/Matcher.java new file mode 100644 index 0000000000..5914a478e9 --- /dev/null +++ b/sentinel-security-core/src/main/java/com/alibaba/csp/sentinel/trust/auth/condition/matcher/Matcher.java @@ -0,0 +1,26 @@ +/* + * Copyright 1999-2019 Alibaba Group Holding Ltd. + * + * Licensed 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 + * + * https://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.alibaba.csp.sentinel.trust.auth.condition.matcher; + +/** + * @author lwj + * @since 2.0.0 + */ +public interface Matcher { + + boolean match(T obj); + +} diff --git a/sentinel-security-core/src/main/java/com/alibaba/csp/sentinel/trust/auth/condition/matcher/PortMatcher.java b/sentinel-security-core/src/main/java/com/alibaba/csp/sentinel/trust/auth/condition/matcher/PortMatcher.java new file mode 100644 index 0000000000..a9fe831e19 --- /dev/null +++ b/sentinel-security-core/src/main/java/com/alibaba/csp/sentinel/trust/auth/condition/matcher/PortMatcher.java @@ -0,0 +1,60 @@ +/* + * Copyright 1999-2019 Alibaba Group Holding Ltd. + * + * Licensed 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 + * + * https://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.alibaba.csp.sentinel.trust.auth.condition.matcher; + +import java.util.Objects; + +/** + * @author lwj + * @since 2.0.0 + */ +public class PortMatcher implements Matcher { + + private final int port; + + public PortMatcher(int port) { + this.port = port; + } + + @Override + public boolean match(Integer object) { + return object.equals(port); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + PortMatcher that = (PortMatcher) o; + return port == that.port; + } + + @Override + public int hashCode() { + return Objects.hash(port); + } + + @Override + public String toString() { + return "PortMatcher{" + + "port=" + port + + '}'; + } +} diff --git a/sentinel-security-core/src/main/java/com/alibaba/csp/sentinel/trust/auth/condition/matcher/StringMatcher.java b/sentinel-security-core/src/main/java/com/alibaba/csp/sentinel/trust/auth/condition/matcher/StringMatcher.java new file mode 100644 index 0000000000..f94cbd168f --- /dev/null +++ b/sentinel-security-core/src/main/java/com/alibaba/csp/sentinel/trust/auth/condition/matcher/StringMatcher.java @@ -0,0 +1,159 @@ +/* + * Copyright 1999-2019 Alibaba Group Holding Ltd. + * + * Licensed 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 + * + * https://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.alibaba.csp.sentinel.trust.auth.condition.matcher; + +import java.util.Locale; +import java.util.Objects; +import java.util.regex.Pattern; + +import com.alibaba.csp.sentinel.log.RecordLog; +import com.alibaba.csp.sentinel.util.StringUtil; + +/** + * @author lwj + * @since 2.0.0 + */ +public class StringMatcher implements Matcher { + + /** + * Matched string. + * If it is a regular match, + * it is a regular match string + */ + private final String key; + + private final MatcherType type; + + /** + * Whether to ignore case + */ + private final boolean ignoreCase; + + public StringMatcher(String key, MatcherType type, boolean ignoreCase) { + if (ignoreCase) { + key = key.toLowerCase(Locale.ROOT); + } + this.key = key; + this.type = type; + this.ignoreCase = ignoreCase; + } + + public boolean match(String object) { + if (StringUtil.isEmpty(object)) { + return false; + } + if (ignoreCase) { + object = object.toLowerCase(Locale.ROOT); + } + switch (type) { + case EXACT: + return object.equals(key); + case PREFIX: + return object.startsWith(key); + case SUFFIX: + return object.endsWith(key); + case CONTAIN: + return object.contains(key); + case REGEX: + try { + return Pattern.matches(key, object); + } catch (Exception e) { + RecordLog.warn("Irregular matching,key={},str={}", key, object, e); + return false; + } + default: + throw new UnsupportedOperationException( + "unsupported string compare operation"); + } + } + + public String getKey() { + return key; + } + + public MatcherType getType() { + return type; + } + + public boolean isIgnoreCase() { + return ignoreCase; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + StringMatcher that = (StringMatcher) o; + return ignoreCase == that.ignoreCase && Objects.equals(key, that.key) && type == that.type; + } + + @Override + public int hashCode() { + return Objects.hash(key, type, ignoreCase); + } + + @Override + public String toString() { + return "StringMatcher{" + + "key='" + key + '\'' + + ", type=" + type + + ", ignoreCase=" + ignoreCase + + '}'; + } + + public enum MatcherType { + + /** + * exact match. + */ + EXACT("exact"), + /** + * prefix match. + */ + PREFIX("prefix"), + /** + * suffix match. + */ + SUFFIX("suffix"), + /** + * regex match. + */ + REGEX("regex"), + /** + * contain match. + */ + CONTAIN("contain"); + + /** + * type of matcher. + */ + public final String key; + + MatcherType(String type) { + this.key = type; + } + + @Override + public String toString() { + return this.key; + } + + } +} diff --git a/sentinel-security-core/src/main/java/com/alibaba/csp/sentinel/trust/auth/rule/AuthRule.java b/sentinel-security-core/src/main/java/com/alibaba/csp/sentinel/trust/auth/rule/AuthRule.java new file mode 100644 index 0000000000..6fc796d7cc --- /dev/null +++ b/sentinel-security-core/src/main/java/com/alibaba/csp/sentinel/trust/auth/rule/AuthRule.java @@ -0,0 +1,133 @@ +/* + * Copyright 1999-2019 Alibaba Group Holding Ltd. + * + * Licensed 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 + * + * https://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.alibaba.csp.sentinel.trust.auth.rule; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import com.alibaba.csp.sentinel.trust.auth.condition.AuthCondition; +import com.alibaba.csp.sentinel.util.CollectionUtil; + +/** + * @author lwj + * @since 2.0.0 + */ +public class AuthRule { + + /** + * Child rule chaining Type + */ + private final ChildChainType childChainType; + private final List children; + private final AuthCondition condition; + private final boolean not; + + public AuthRule(ChildChainType childChainType) { + this.childChainType = childChainType; + this.children = new ArrayList<>(); + this.condition = null; + this.not = false; + + } + + public AuthRule(ChildChainType childChainType, boolean not) { + this.childChainType = childChainType; + this.children = new ArrayList<>(); + this.condition = null; + this.not = not; + } + + public AuthRule(AuthCondition condition) { + this.childChainType = ChildChainType.LEAF; + this.children = null; + this.condition = condition; + this.not = false; + } + + public void addChildren(AuthRule rule) { + children.add(rule); + } + + public boolean isEmpty() { + if (isLeaf()) { + return true; + } + return CollectionUtil.isEmpty(children); + } + + public boolean isLeaf() { + return childChainType == ChildChainType.LEAF; + } + + public ChildChainType getChildChainType() { + return childChainType; + } + + public List getChildren() { + return children; + } + + public AuthCondition getCondition() { + return condition; + } + + public boolean isNot() { + return not; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + AuthRule authRule = (AuthRule) o; + return not == authRule.not && childChainType == authRule.childChainType && Objects.equals(children, + authRule.children) && Objects.equals(condition, authRule.condition); + } + + @Override + public int hashCode() { + return Objects.hash(childChainType, children, condition, not); + } + + @Override + public String toString() { + return "AuthRule{" + + "op=" + childChainType + + ", children=" + children + + ", condition=" + condition + + ", isNot=" + not + + '}'; + } + + /** + * Child rule chaining + */ + public enum ChildChainType { + /** + * It's already a leaf node + */ + LEAF, + AND, + OR, + ; + + } +} diff --git a/sentinel-security-core/src/main/java/com/alibaba/csp/sentinel/trust/auth/rule/AuthType.java b/sentinel-security-core/src/main/java/com/alibaba/csp/sentinel/trust/auth/rule/AuthType.java new file mode 100644 index 0000000000..1d3ed37c8b --- /dev/null +++ b/sentinel-security-core/src/main/java/com/alibaba/csp/sentinel/trust/auth/rule/AuthType.java @@ -0,0 +1,199 @@ +/* + * Copyright 1999-2019 Alibaba Group Holding Ltd. + * + * Licensed 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 + * + * https://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.alibaba.csp.sentinel.trust.auth.rule; + +/** + *

All types of auth validation.

+ * + * @author lwj + * @since 2.0.0 + */ +public enum AuthType { + + /** + * Request header + * Rule attribution:principal + *

+ * Rule modification section: + *

+ * when: + * 1)rules:when (request.headers[xxx]) + */ + HEADER, + /** + * Direct request ip address + * Rule attribution:principal + *

+ * Rule modification section: + *

+ * from: + * 1)rules:from:source:ipBlocks + * 2)rules:from:source:notIpBlocks + *

+ * when: + * 1)rules:when (source.ip) + */ + DIRECT_REMOTE_IP, + /** + * The original client IP address determined by the X-Forwarded-For request header or proxy protocol + * Rule attribution:principal + *

+ * Rule modification section: + *

+ * from: + * 1)rules:from:source:remoteIpBlocks + * 2)rules:from:source:notRemoteIpBlocks + *

+ * when: + * 1)rules:when (remote.ip) + */ + REMOTE_IP, + + /** + * Identity in jwt = issuer + "/" + subject + * Rule attribution:principal + *

+ * Rule modification section: + *

+ * from: + * 1)rules:from:source:requestPrincipals + *

+ * when: + * 1)rules:when (request.auth.principal) + */ + JWT_PRINCIPALS, + + /** + * Audience in jwt + * Rule attribution:principal + *

+ * Rule modification section: + *

+ * when: + * 1)rules:when (request.auth.audiences) + */ + JWT_AUDIENCES, + /** + * Audience in jwt + * Rule attribution:principal + *

+ * Rule modification section: + *

+ * when: + * 1)rules:when (request.auth.claims[xxx]) + */ + JWT_CLAIMS, + + /** + * Azp in jwt: Authorized party - the party to which the ID Token was issued + * rule attribution:principal + *

+ * Rule modification section: + *

+ * when: + * 1)rules:when (request.auth.presenter) + */ + JWT_PRESENTERS, + /** + * What should the requester's identity be + * Rule attribution:principal + *

+ * Rule modification section: + *

+ * from: + * 1)rules:from:source:principals + * 2)rules:from:source:notPrincipals + * 3)rules:from:namespaces + * Concatenate regular expressions as formal principals,for example:namespaces: ["namespace1"]-> .* + * /ns/namespace1/.* + * 4)rules:from:notNamespaces + *

+ * when: + * 1)rules:when (source.principal) + * 2)rules:when (source.namespace) + */ + AUTHENTICATED, + + /** + * Server ip + * Rule attribution:permission + *

+ * Rule modification section: + *

+ * when: + * 1)rules:when (destination.ip) + */ + DESTINATION_IP, + /** + * Server hosts + *

+ * Rule modification section: + *

+ * to: + * 1)rules:to:operation:hosts + * 2)rules:to:operation:notHosts + */ + HOSTS, + /** + * Server url path + * Rule attribution:permission + *

+ * Rule modification section: + *

+ * to: + * 1)rules:to:operation:paths + * 2)rules:to:operation:notPaths + */ + URL_PATH, + /** + * Server port + * Rule attribution:permission + *

+ * Rule modification section: + *

+ * to: + * 1)rules:to:operation:ports + * 2)rules:to:operation:notPorts + *

+ * when: + * 1)rules:when (destination.port) + */ + DESTINATION_PORT, + /** + * Server methods + * Rule attribution:permission + *

+ * Rule modification section: + *

+ * to: + * 1)rules:to:operation:methods + * 2)rules:to:operation:notMethods + */ + METHODS, + + /** + * Server sni : request.getServerName() + * Rule attribution:permission + *

+ * Rule modification section: + *

+ * when: + * 1)rules:when (connection.sni) + */ + REQUESTED_SERVER_NAME, + ; + +} diff --git a/sentinel-security-core/src/main/java/com/alibaba/csp/sentinel/trust/auth/rule/JwtRule.java b/sentinel-security-core/src/main/java/com/alibaba/csp/sentinel/trust/auth/rule/JwtRule.java new file mode 100644 index 0000000000..b4f2b0e4ec --- /dev/null +++ b/sentinel-security-core/src/main/java/com/alibaba/csp/sentinel/trust/auth/rule/JwtRule.java @@ -0,0 +1,94 @@ +/* + * Copyright 1999-2019 Alibaba Group Holding Ltd. + * + * Licensed 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 + * + * https://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.alibaba.csp.sentinel.trust.auth.rule; + +import java.util.List; +import java.util.Map; + +/** + * For details see + * https://istio.io/latest/docs/reference/config/security/jwt/ + *

+ *

+ * Not supported yet + *
1.jwks + *
2.outputPayloadToHeader + *
3.outputClaimToHeaders + *
4.forwardOriginalToken + * + * @author lwj + * @since 2.0.0 + */ +public class JwtRule { + + private final String name; + + private final Map fromHeaders; + + private final String issuer; + + private final List audiences; + + private final String jwks; + + private final List fromParams; + + public JwtRule(String name, Map fromHeaders, String issuer, + List audiences, String jwks, List fromParams) { + this.name = name; + this.fromHeaders = fromHeaders; + this.issuer = issuer; + this.audiences = audiences; + this.jwks = jwks; + this.fromParams = fromParams; + } + + public String getName() { + return name; + } + + public Map getFromHeaders() { + return fromHeaders; + } + + public String getIssuer() { + return issuer; + } + + public List getAudiences() { + return audiences; + } + + public String getJwks() { + return jwks; + } + + public List getFromParams() { + return fromParams; + } + + @Override + public String toString() { + return "JwtRule{" + + "name='" + name + '\'' + + ", fromHeaders=" + fromHeaders + + ", issuer='" + issuer + '\'' + + ", audiences=" + audiences + + ", jwks='" + jwks + '\'' + + ", fromParams=" + fromParams + + '}'; + } +} diff --git a/sentinel-security-core/src/main/java/com/alibaba/csp/sentinel/trust/cert/CertPair.java b/sentinel-security-core/src/main/java/com/alibaba/csp/sentinel/trust/cert/CertPair.java new file mode 100644 index 0000000000..4c0910cc47 --- /dev/null +++ b/sentinel-security-core/src/main/java/com/alibaba/csp/sentinel/trust/cert/CertPair.java @@ -0,0 +1,136 @@ +/* + * Copyright 1999-2019 Alibaba Group Holding Ltd. + * + * Licensed 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 + * + * https://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.alibaba.csp.sentinel.trust.cert; + +import java.security.PrivateKey; +import java.security.cert.Certificate; +import java.util.Arrays; +import java.util.Objects; + +/** + * Certificate. + * + * @author lwj + * @since 2.0.0 + */ +public class CertPair { + + private Certificate[] certificateChain; + + private PrivateKey privateKey; + + private byte[] rawCertificateChain; + + private byte[] rawPrivateKey; + + private Certificate rootCA; + + /** + * Expiration time: certificate becomes invalid after this time + */ + private long expireTime; + + public CertPair() { + + } + + public PrivateKey getPrivateKey() { + return privateKey; + } + + public void setPrivateKey(PrivateKey privateKey) { + this.privateKey = privateKey; + } + + public Certificate[] getCertificateChain() { + return certificateChain; + } + + public void setCertificateChain(Certificate[] certificateChain) { + this.certificateChain = certificateChain; + } + + public long getExpireTime() { + return expireTime; + } + + public void setExpireTime(long expireTime) { + this.expireTime = expireTime; + } + + public byte[] getRawCertificateChain() { + return rawCertificateChain; + } + + public void setRawCertificateChain(byte[] rawCertificateChain) { + this.rawCertificateChain = rawCertificateChain; + } + + public byte[] getRawPrivateKey() { + return rawPrivateKey; + } + + public void setRawPrivateKey(byte[] rawPrivateKey) { + this.rawPrivateKey = rawPrivateKey; + } + + public Certificate getRootCA() { + if (rootCA == null) { + return certificateChain[certificateChain.length - 1]; + } + return rootCA; + } + + public void setRootCA(Certificate rootCA) { + this.rootCA = rootCA; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + CertPair certPair = (CertPair) o; + return expireTime == certPair.expireTime && Arrays.equals(certificateChain, certPair.certificateChain) + && Objects.equals(privateKey, certPair.privateKey) && Arrays.equals(rawCertificateChain, + certPair.rawCertificateChain) && Arrays.equals(rawPrivateKey, certPair.rawPrivateKey) && Objects.equals( + rootCA, certPair.rootCA); + } + + @Override + public int hashCode() { + int result = Objects.hash(privateKey, rootCA, expireTime); + result = 31 * result + Arrays.hashCode(certificateChain); + result = 31 * result + Arrays.hashCode(rawCertificateChain); + result = 31 * result + Arrays.hashCode(rawPrivateKey); + return result; + } + + @Override + public String toString() { + return "CertPair{" + + "certificateChain=" + Arrays.toString(certificateChain) + + ", privateKey=" + privateKey + + ", rawCertificateChain=" + Arrays.toString(rawCertificateChain) + + ", rawPrivateKey=" + Arrays.toString(rawPrivateKey) + + ", rootCA=" + rootCA + + ", expireTime=" + expireTime + + '}'; + } +} diff --git a/sentinel-security-core/src/main/java/com/alibaba/csp/sentinel/trust/tls/TlsMode.java b/sentinel-security-core/src/main/java/com/alibaba/csp/sentinel/trust/tls/TlsMode.java new file mode 100644 index 0000000000..dc42acd28c --- /dev/null +++ b/sentinel-security-core/src/main/java/com/alibaba/csp/sentinel/trust/tls/TlsMode.java @@ -0,0 +1,133 @@ +/* + * Copyright 1999-2019 Alibaba Group Holding Ltd. + * + * Licensed 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 + * + * https://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.alibaba.csp.sentinel.trust.tls; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +/** + * TLS mode: + *

    + *
  • DISABLE:Connection is not tunneled.
  • + *
  • PERMISSIVE:Connection can be either plaintext or mTLS tunnel.
  • + *
  • STRICT:Connection is an mTLS tunnel (TLS with client cert must be presented).
  • + *
+ * + * @author lwj + * @since 2.0.0 + */ +public final class TlsMode { + + private TlsType globalTls = TlsType.DISABLE; + + private Map portToTls = new HashMap<>(); + + public TlsType getGlobalTls() { + return globalTls; + } + + public void setGlobalTls(TlsType tlsType) { + globalTls = tlsType; + } + + /** + * Get the tls mode corresponding to the port, + * or the global tls mode if it does not exist + * + * @param port + * @return + */ + public TlsType getPortTls(Integer port) { + return portToTls.getOrDefault(port, globalTls); + } + + public int getPortsSize() { + return portToTls.size(); + } + + public void setPortTls(Integer port, TlsType tlsType) { + portToTls.put(port, tlsType); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + TlsMode tlsMode = (TlsMode) o; + return globalTls == tlsMode.globalTls && Objects.equals(portToTls, tlsMode.portToTls); + } + + @Override + public int hashCode() { + return Objects.hash(globalTls, portToTls); + } + + @Override + public String toString() { + return "TlsMode{" + + "globalTls=" + globalTls + + ", portToTls=" + portToTls + + '}'; + } + + public enum TlsType { + STRICT(0, "Strict Mode"), + PERMISSIVE(1, "Permissive Mode"), + DISABLE(2, "Disable Mode"), + ; + public static Map map = new HashMap<>(); + + static { + for (TlsType tlsEnum : TlsType.values()) { + map.put(tlsEnum.code, tlsEnum); + } + } + + private int code; + private String msg; + + TlsType(int code, String msg) { + this.code = code; + this.msg = msg; + } + + public static TlsType getFromCode(int code) { + return map.get(code); + } + + @Override + public String toString() { + return "TlsType{" + + "code=" + code + + ", msg='" + msg + '\'' + + '}'; + } + + public int getCode() { + return code; + } + + public String getMsg() { + return msg; + } + } + +} diff --git a/sentinel-security-core/src/main/java/com/alibaba/csp/sentinel/trust/util/JwtUtil.java b/sentinel-security-core/src/main/java/com/alibaba/csp/sentinel/trust/util/JwtUtil.java new file mode 100644 index 0000000000..2c77f9ced3 --- /dev/null +++ b/sentinel-security-core/src/main/java/com/alibaba/csp/sentinel/trust/util/JwtUtil.java @@ -0,0 +1,120 @@ +/* + * Copyright 1999-2019 Alibaba Group Holding Ltd. + * + * Licensed 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 + * + * https://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.alibaba.csp.sentinel.trust.util; + +import java.util.List; +import java.util.Map; + +import com.alibaba.csp.sentinel.log.RecordLog; +import com.alibaba.csp.sentinel.trust.auth.rule.JwtRule; +import com.alibaba.csp.sentinel.util.CollectionUtil; +import com.alibaba.csp.sentinel.util.StringUtil; + +import org.jose4j.jwk.JsonWebKeySet; +import org.jose4j.jws.JsonWebSignature; +import org.jose4j.jwt.JwtClaims; +import org.jose4j.jwt.consumer.InvalidJwtException; +import org.jose4j.jwt.consumer.JwtConsumer; +import org.jose4j.jwt.consumer.JwtConsumerBuilder; +import org.jose4j.jwt.consumer.JwtContext; +import org.jose4j.keys.resolvers.JwksVerificationKeyResolver; +import org.jose4j.lang.JoseException; + +/** + * @author lwj + * @since 2.0.0 + */ +public final class JwtUtil { + + public static final String AUTHORIZATION = "Authorization"; + + private static final String BEARER_PREFIX = "Bearer" + " "; + + private JwtUtil() { + } + + public static String getTokenFromJwtRule(Map> params, Map> headers, + JwtRule jwtRule) { + if (null == jwtRule) { + return StringUtil.EMPTY; + } + //Firstly,get token from the header + if (!CollectionUtil.isEmpty(headers)) { + Map jwtHeaders = jwtRule.getFromHeaders(); + if (!CollectionUtil.isEmpty(jwtHeaders)) { + for (Map.Entry entry : jwtHeaders.entrySet()) { + String headerName = entry.getKey(); + String prefix = entry.getValue(); + List auths = headers.get(headerName); + if (!CollectionUtil.isEmpty(auths)) { + String token = auths.get(0); + if (token.startsWith(prefix)) { + return token.substring(prefix.length()); + } + } + } + } + } + //Secondly,get token from the parma first + if (!CollectionUtil.isEmpty(params)) { + List fromParams = jwtRule.getFromParams(); + if (!CollectionUtil.isEmpty(fromParams)) { + for (String fromParam : fromParams) { + List auths = params.get(fromParam); + if (!CollectionUtil.isEmpty(auths)) { + return auths.get(0); + } + } + } + } + //Thirdly,get token from default header + if (!CollectionUtil.isEmpty(headers)) { + List auths = headers.get(AUTHORIZATION); + if (!CollectionUtil.isEmpty(auths)) { + String token = auths.get(0); + if (token.startsWith(BEARER_PREFIX)) { + return token.substring(BEARER_PREFIX.length()); + } + } + } + return StringUtil.EMPTY; + } + + public static JwtClaims extractJwtClaims(String jwks, String token) { + if (StringUtil.isBlank(jwks) || StringUtil.isBlank(token)) { + return null; + } + try { + // don't validate jwt's attribute, just validate the sign + JwtConsumerBuilder jwtConsumerBuilder = new JwtConsumerBuilder() + .setSkipAllValidators(); + JsonWebSignature jws = new JsonWebSignature(); + jws.setCompactSerialization(token); + JsonWebKeySet jsonWebKeySet = new JsonWebKeySet(jwks); + JwksVerificationKeyResolver jwksResolver = new JwksVerificationKeyResolver( + jsonWebKeySet.getJsonWebKeys()); + jwtConsumerBuilder.setVerificationKeyResolver(jwksResolver); + JwtConsumer jwtConsumer = jwtConsumerBuilder.build(); + JwtContext jwtContext = jwtConsumer.process(token); + return jwtContext.getJwtClaims(); + } catch (JoseException e) { + RecordLog.warn("Invalid jwks = {}", jwks); + } catch (InvalidJwtException e) { + RecordLog.warn("Invalid jwt token {} for jwks {}", token, jwks); + } + return null; + } +} diff --git a/sentinel-security-core/src/main/java/com/alibaba/csp/sentinel/trust/validator/AuthValidator.java b/sentinel-security-core/src/main/java/com/alibaba/csp/sentinel/trust/validator/AuthValidator.java new file mode 100644 index 0000000000..dc81d8590b --- /dev/null +++ b/sentinel-security-core/src/main/java/com/alibaba/csp/sentinel/trust/validator/AuthValidator.java @@ -0,0 +1,262 @@ +/* + * Copyright 1999-2019 Alibaba Group Holding Ltd. + * + * Licensed 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 + * + * https://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.alibaba.csp.sentinel.trust.validator; + +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.alibaba.csp.sentinel.log.RecordLog; +import com.alibaba.csp.sentinel.trust.auth.Rules; +import com.alibaba.csp.sentinel.trust.auth.condition.AuthCondition; +import com.alibaba.csp.sentinel.trust.auth.condition.matcher.Matcher; +import com.alibaba.csp.sentinel.trust.auth.rule.AuthRule; +import com.alibaba.csp.sentinel.trust.auth.rule.JwtRule; +import com.alibaba.csp.sentinel.trust.util.JwtUtil; +import com.alibaba.csp.sentinel.util.CollectionUtil; +import com.alibaba.csp.sentinel.util.StringUtil; + +import org.jose4j.jwt.JwtClaims; +import org.jose4j.jwt.MalformedClaimException; + +/** + * A validator that verifies whether the request complies with Rules. + * + * @author lwj + * @since 2.0.0 + */ +public class AuthValidator { + private AuthValidator() { + + } + + /** + * The specific rules are: + * (1) If there is a JWT rule matching the request, the request is matched. If the matching result is rejected, the + * request is rejected. + * (2) If any DENY policy matches the request, the request is denied. + * (3) ALLOW the request if the workload does not have an Allow policy. + * (4) ALLOW any Allow policy if it matches the request. + * + * @param request + * @param rules + * @return + */ + public static boolean validate(UnifiedHttpRequest request, Rules rules) { + if (null == request) { + return false; + } + if (null == rules) { + return true; + } + + // The first step is to extract the corresponding token. + for (JwtRule jwtRule : rules.getJwtRules().values()) { + String token = JwtUtil.getTokenFromJwtRule(request.getParams(), request.getHeaders(), jwtRule); + if (!StringUtil.isEmpty(token)) { + JwtClaims jwtClaims = JwtUtil.extractJwtClaims(jwtRule.getJwks(), token); + if (!validateJwtRule(jwtRule, jwtClaims)) { + return false; + } + request.setJwtClaims(jwtClaims); + break; + } + } + + Map denyRules = rules.getDenyAuthRules(); + for (AuthRule denyRule : denyRules.values()) { + if (validateRule(denyRule, request)) { + return false; + } + } + + Map allowRules = rules.getAllowAuthRules(); + + if (CollectionUtil.isEmpty(allowRules)) { + return true; + } + + for (AuthRule allowRule : allowRules.values()) { + if (validateRule(allowRule, request)) { + return true; + } + } + return false; + } + + /** + * Verify that the Jwt rule passes + * + * @param jwtRule + * @param jwtClaims + * @return + */ + public static boolean validateJwtRule(JwtRule jwtRule, JwtClaims jwtClaims) { + if (null == jwtClaims) { + return false; + } + try { + if (!StringUtil.isBlank(jwtRule.getIssuer()) + && !jwtRule.getIssuer().equals(jwtClaims.getIssuer())) { + return false; + } + + //Include any one to pass + if (!CollectionUtil.isEmpty(jwtRule.getAudiences())) { + Set acceptAud = new HashSet<>(jwtRule.getAudiences()); + List audiences = jwtClaims.getAudience(); + boolean flag = false; + for (String aud : audiences) { + if (acceptAud.contains(aud)) { + flag = true; + break; + } + } + if (!flag) { + return false; + } + + } + //Guarantee not expired + if (null == jwtClaims.getExpirationTime() + || jwtClaims.getExpirationTime().getValueInMillis() <= System.currentTimeMillis()) { + return false; + } + return true; + } catch (MalformedClaimException e) { + RecordLog.warn("Invalid jwt jwtClaims ={}", jwtClaims); + } + return false; + } + + public static boolean validateRule(AuthRule authRule, UnifiedHttpRequest request) { + if (authRule.isLeaf()) { + return validateLeafRule(authRule, request); + } + List ruleChildren = authRule.getChildren(); + boolean res = authRule.getChildChainType() == AuthRule.ChildChainType.AND ? true : false; + for (AuthRule ruleChild : ruleChildren) { + boolean childRes = validateRule(ruleChild, request); + if (!childRes && authRule.getChildChainType() == AuthRule.ChildChainType.AND) { + res = false; + break; + } + if (childRes && authRule.getChildChainType() == AuthRule.ChildChainType.OR) { + res = true; + break; + } + } + // if "is not" is true, reverse the res + return authRule.isNot() ? !res : res; + } + + public static boolean validateLeafRule(AuthRule rule, UnifiedHttpRequest request) { + try { + AuthCondition condition = rule.getCondition(); + Matcher matcher = condition.getMatcher(); + String key = condition.getKey(); + if (matcher == null) { + return false; + } + JwtClaims claims = request.getJwtClaims(); + switch (condition.getType()) { + case DIRECT_REMOTE_IP: + return matcher.match(request.getSourceIp()); + case REMOTE_IP: + return matcher.match(request.getRemoteIp()); + case DESTINATION_IP: + return matcher.match(request.getDestIp()); + case HOSTS: + return matcher.match(request.getHost()); + case METHODS: + return matcher.match(request.getMethod()); + case URL_PATH: + return matcher.match(request.getPath()); + case AUTHENTICATED: + return matcher.match(request.getPrincipal()); + case DESTINATION_PORT: + return matcher.match(request.getPort()); + case HEADER: + Map> headers = request.getHeaders(); + if (null == headers) { + return false; + } + if (!headers.containsKey(key)) { + return false; + } + List headerList = headers.get(key); + if (null == headerList) { + return false; + } + for (String header : headerList) { + if (matcher.match(header)) { + return true; + } + } + return false; + + case REQUESTED_SERVER_NAME: + return matcher.match(request.getSni()); + case JWT_PRINCIPALS: + if (claims == null) { + return false; + } + String issuer = claims.getIssuer(); + String subject = claims.getSubject(); + return matcher.match(issuer + "/" + subject); + case JWT_AUDIENCES: + if (claims == null) { + return false; + } + List audiences = claims.getAudience(); + for (String audience : audiences) { + if (matcher.match(audience)) { + return true; + } + } + return false; + case JWT_PRESENTERS: + if (claims == null) { + return false; + } + return matcher.match(claims.getClaimValueAsString("azp")); + case JWT_CLAIMS: + if (claims == null) { + return false; + } + Object claimValue = claims.getClaimValue(key); + if (claimValue instanceof List) { + for (String claim : claims.getStringListClaimValue(key)) { + if (matcher.match(claim)) { + return true; + } + } + } else { + return matcher.match(claims.getStringClaimValue(key)); + } + return false; + default: + RecordLog.warn("Unsupported AuthType={}", condition.getType()); + return false; + } + } catch (Exception e) { + RecordLog.warn("Request {} doesn't match rule {}", request, rule); + } + return false; + } + +} diff --git a/sentinel-security-core/src/main/java/com/alibaba/csp/sentinel/trust/validator/UnifiedHttpRequest.java b/sentinel-security-core/src/main/java/com/alibaba/csp/sentinel/trust/validator/UnifiedHttpRequest.java new file mode 100644 index 0000000000..24e407d18d --- /dev/null +++ b/sentinel-security-core/src/main/java/com/alibaba/csp/sentinel/trust/validator/UnifiedHttpRequest.java @@ -0,0 +1,214 @@ +/* + * Copyright 1999-2019 Alibaba Group Holding Ltd. + * + * Licensed 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 + * + * https://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.alibaba.csp.sentinel.trust.validator; + +import java.util.List; +import java.util.Map; + +import org.jose4j.jwt.JwtClaims; + +/** + * @author lwj + * @since 2.0.0 + */ +public class UnifiedHttpRequest { + + private final String sourceIp; + + private final String destIp; + + private final String remoteIp; + + private final String host; + + private final int port; + + private final String method; + + private final String path; + + private final Map> headers; + + private final Map> params; + + private JwtClaims jwtClaims; + + private String principal; + + private String sni; + + private UnifiedHttpRequest(String sourceIp, String destIp, String remoteIp, + String host, int port, String method, String path, Map> headers, + Map> params, String principal, String sni) { + this.sourceIp = sourceIp; + this.destIp = destIp; + this.remoteIp = remoteIp; + this.host = host; + this.port = port; + this.method = method; + this.path = path; + this.headers = headers; + this.params = params; + this.principal = principal; + this.sni = sni; + } + + public String getSourceIp() { + return sourceIp; + } + + public String getDestIp() { + return destIp; + } + + public String getRemoteIp() { + return remoteIp; + } + + public String getHost() { + return host; + } + + public int getPort() { + return port; + } + + public String getMethod() { + return method; + } + + public String getPath() { + return path; + } + + public Map> getHeaders() { + return headers; + } + + public Map> getParams() { + return params; + } + + public JwtClaims getJwtClaims() { + return jwtClaims; + } + + public void setJwtClaims(JwtClaims jwtClaims) { + this.jwtClaims = jwtClaims; + } + + public String getPrincipal() { + return principal; + } + + public void setPrincipal(String principal) { + this.principal = principal; + } + + public String getSni() { + return sni; + } + + public void setSni(String sni) { + this.sni = sni; + } + + public static class UnifiedHttpRequestBuilder { + + private String sourceIp; + + private String destIp; + + private String remoteIp; + + private String host; + + private int port; + + private String method; + + private String path; + + private Map> headers; + + private String principal; + + private Map> params; + + private String sni; + + public UnifiedHttpRequestBuilder setSourceIp(String sourceIp) { + this.sourceIp = sourceIp; + return this; + } + + public UnifiedHttpRequestBuilder setDestIp(String destIp) { + this.destIp = destIp; + return this; + } + + public UnifiedHttpRequestBuilder setRemoteIp(String remoteIp) { + this.remoteIp = remoteIp; + return this; + } + + public UnifiedHttpRequestBuilder setHost(String host) { + this.host = host; + return this; + } + + public UnifiedHttpRequestBuilder setPort(int port) { + this.port = port; + return this; + } + + public UnifiedHttpRequestBuilder setMethod(String method) { + this.method = method; + return this; + } + + public UnifiedHttpRequestBuilder setPath(String path) { + this.path = path; + return this; + } + + public UnifiedHttpRequestBuilder setHeaders(Map> headers) { + this.headers = headers; + return this; + } + + public UnifiedHttpRequestBuilder setParams(Map> params) { + this.params = params; + return this; + } + + public UnifiedHttpRequestBuilder setPrincipal(String principal) { + this.principal = principal; + return this; + } + + public UnifiedHttpRequestBuilder setSni(String sni) { + this.sni = sni; + return this; + } + + public UnifiedHttpRequest build() { + return new UnifiedHttpRequest(sourceIp, destIp, remoteIp, host, port, + method, path, headers, params, principal, sni); + } + + } +} diff --git a/sentinel-security-core/src/test/java/com/alibaba/csp/sentinel/trust/TrustManagerTest.java b/sentinel-security-core/src/test/java/com/alibaba/csp/sentinel/trust/TrustManagerTest.java new file mode 100644 index 0000000000..449831c095 --- /dev/null +++ b/sentinel-security-core/src/test/java/com/alibaba/csp/sentinel/trust/TrustManagerTest.java @@ -0,0 +1,56 @@ +/* + * Copyright 1999-2019 Alibaba Group Holding Ltd. + * + * Licensed 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 + * + * https://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.alibaba.csp.sentinel.trust; + +import org.junit.Assert; +import org.junit.Test; + +/** + * @author lwj + * @since 2.0.0 + */ +public class TrustManagerTest { + + @Test + public void testStoreCertPair() { + int[] s = {0}; + TrustManager.getInstance().registerCertCallback(cert -> s[0]++); + for (int i = 0; i < 100; i++) { + TrustManager.getInstance().storeCertPair(null); + } + Assert.assertEquals(100, s[0]); + } + + @Test + public void testStoreTlsMode() { + int[] s = {0}; + TrustManager.getInstance().registerTlsModeCallback(tlsMode -> s[0]++); + for (int i = 0; i < 100; i++) { + TrustManager.getInstance().storeTlsMode(null); + } + Assert.assertEquals(100, s[0]); + } + + @Test + public void testStoreRules() { + int[] s = {0}; + TrustManager.getInstance().registerRulesCallback(rules -> s[0]++); + for (int i = 0; i < 100; i++) { + TrustManager.getInstance().storeRules(null); + } + Assert.assertEquals(100, s[0]); + } +} \ No newline at end of file diff --git a/sentinel-security-core/src/test/java/com/alibaba/csp/sentinel/trust/auth/condition/matcher/IpMatcherTest.java b/sentinel-security-core/src/test/java/com/alibaba/csp/sentinel/trust/auth/condition/matcher/IpMatcherTest.java new file mode 100644 index 0000000000..c029ed1df9 --- /dev/null +++ b/sentinel-security-core/src/test/java/com/alibaba/csp/sentinel/trust/auth/condition/matcher/IpMatcherTest.java @@ -0,0 +1,51 @@ +/* + * Copyright 1999-2019 Alibaba Group Holding Ltd. + * + * Licensed 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 + * + * https://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.alibaba.csp.sentinel.trust.auth.condition.matcher; + +import org.junit.Assert; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + * @author lwj + * @since 2.0.0 + */ +public class IpMatcherTest { + + @Test + public void testIp2BinaryString() { + assertEquals("01111111000000000000000000000001", IpMatcher.ip2BinaryString("127.0.0.1")); + assertEquals("", IpMatcher.ip2BinaryString("127.0.0")); + assertEquals("11111111111111111111111111111111", IpMatcher.ip2BinaryString("255.255.255.255")); + assertEquals("", IpMatcher.ip2BinaryString("256.0.0.1")); + assertEquals("", IpMatcher.ip2BinaryString("127.0.0.0.1")); + assertEquals("", IpMatcher.ip2BinaryString("-127.0.0.0.1")); + assertEquals("", IpMatcher.ip2BinaryString("-xx.0.0.0.1")); + } + + @Test + public void testMatch() { + IpMatcher ipMatcher = new IpMatcher(32, "127.0.0.1"); + Assert.assertTrue(ipMatcher.match("127.0.0.1")); + Assert.assertFalse(ipMatcher.match("127.0.0.2")); + + ipMatcher = new IpMatcher(8, "127.0.0.0"); + Assert.assertTrue(ipMatcher.match("127.0.0.0")); + Assert.assertTrue(ipMatcher.match("127.255.0.0")); + Assert.assertFalse(ipMatcher.match("128.0.0.0")); + } +} \ No newline at end of file diff --git a/sentinel-security-core/src/test/java/com/alibaba/csp/sentinel/trust/auth/condition/matcher/PortMatcherTest.java b/sentinel-security-core/src/test/java/com/alibaba/csp/sentinel/trust/auth/condition/matcher/PortMatcherTest.java new file mode 100644 index 0000000000..9e2919b358 --- /dev/null +++ b/sentinel-security-core/src/test/java/com/alibaba/csp/sentinel/trust/auth/condition/matcher/PortMatcherTest.java @@ -0,0 +1,33 @@ +/* + * Copyright 1999-2019 Alibaba Group Holding Ltd. + * + * Licensed 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 + * + * https://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.alibaba.csp.sentinel.trust.auth.condition.matcher; + +import org.junit.Assert; +import org.junit.Test; + +/** + * @author lwj + * @since 2.0.0 + */ +public class PortMatcherTest { + + @Test + public void testMatch() { + PortMatcher portMatcher = new PortMatcher(2555); + Assert.assertTrue(portMatcher.match(2555)); + Assert.assertFalse(portMatcher.match(2556)); + } +} \ No newline at end of file diff --git a/sentinel-security-core/src/test/java/com/alibaba/csp/sentinel/trust/auth/condition/matcher/StringMatcherTest.java b/sentinel-security-core/src/test/java/com/alibaba/csp/sentinel/trust/auth/condition/matcher/StringMatcherTest.java new file mode 100644 index 0000000000..a243040832 --- /dev/null +++ b/sentinel-security-core/src/test/java/com/alibaba/csp/sentinel/trust/auth/condition/matcher/StringMatcherTest.java @@ -0,0 +1,94 @@ +/* + * Copyright 1999-2019 Alibaba Group Holding Ltd. + * + * Licensed 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 + * + * https://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.alibaba.csp.sentinel.trust.auth.condition.matcher; + +import org.junit.Assert; +import org.junit.Test; + +/** + * @author lwj + * @since 2.0.0 + */ +public class StringMatcherTest { + + @Test + public void testExactMatch() { + StringMatcher stringMatcher = new StringMatcher("Abc", StringMatcher.MatcherType.EXACT, true); + Assert.assertTrue(stringMatcher.match("abc")); + Assert.assertFalse(stringMatcher.match("abcd")); + + stringMatcher = new StringMatcher("Abc", StringMatcher.MatcherType.EXACT, false); + Assert.assertTrue(stringMatcher.match("Abc")); + Assert.assertFalse(stringMatcher.match("abc")); + Assert.assertFalse(stringMatcher.match("abcd")); + } + + @Test + public void testPrefixMatch() { + StringMatcher stringMatcher = new StringMatcher("Abc", StringMatcher.MatcherType.PREFIX, true); + Assert.assertTrue(stringMatcher.match("abc")); + Assert.assertTrue(stringMatcher.match("abcd")); + Assert.assertFalse(stringMatcher.match("acdd")); + + stringMatcher = new StringMatcher("Abc", StringMatcher.MatcherType.PREFIX, false); + Assert.assertTrue(stringMatcher.match("Abc")); + Assert.assertTrue(stringMatcher.match("Abcd")); + Assert.assertFalse(stringMatcher.match("abc")); + Assert.assertFalse(stringMatcher.match("abcd")); + } + + @Test + public void testSuffixMatch() { + StringMatcher stringMatcher = new StringMatcher("Abc", StringMatcher.MatcherType.SUFFIX, true); + Assert.assertTrue(stringMatcher.match("abc")); + Assert.assertTrue(stringMatcher.match("1abc")); + Assert.assertFalse(stringMatcher.match("1acd")); + + stringMatcher = new StringMatcher("Abc", StringMatcher.MatcherType.SUFFIX, false); + Assert.assertTrue(stringMatcher.match("Abc")); + Assert.assertTrue(stringMatcher.match("1Abc")); + Assert.assertFalse(stringMatcher.match("1abc")); + Assert.assertFalse(stringMatcher.match("1abcd")); + } + + @Test + public void testContainMatch() { + StringMatcher stringMatcher = new StringMatcher("Abc", StringMatcher.MatcherType.CONTAIN, true); + Assert.assertTrue(stringMatcher.match("1abc1")); + Assert.assertTrue(stringMatcher.match("abc")); + Assert.assertFalse(stringMatcher.match("1acd1")); + + stringMatcher = new StringMatcher("Abc", StringMatcher.MatcherType.CONTAIN, false); + Assert.assertTrue(stringMatcher.match("Abc")); + Assert.assertTrue(stringMatcher.match("1Abc1")); + Assert.assertFalse(stringMatcher.match("1abc1")); + Assert.assertFalse(stringMatcher.match("1abcd1")); + } + + @Test + public void testRegexMatch() { + StringMatcher stringMatcher = new StringMatcher("A.c", StringMatcher.MatcherType.REGEX, true); + Assert.assertTrue(stringMatcher.match("amc")); + Assert.assertTrue(stringMatcher.match("abc")); + Assert.assertFalse(stringMatcher.match("acd")); + + stringMatcher = new StringMatcher("A.c", StringMatcher.MatcherType.REGEX, false); + Assert.assertTrue(stringMatcher.match("Abc")); + Assert.assertTrue(stringMatcher.match("A*c")); + Assert.assertFalse(stringMatcher.match("abc")); + Assert.assertFalse(stringMatcher.match("1abcd1")); + } +} \ No newline at end of file diff --git a/sentinel-security-core/src/test/java/com/alibaba/csp/sentinel/trust/util/JwtUtilTest.java b/sentinel-security-core/src/test/java/com/alibaba/csp/sentinel/trust/util/JwtUtilTest.java new file mode 100644 index 0000000000..52e3f64186 --- /dev/null +++ b/sentinel-security-core/src/test/java/com/alibaba/csp/sentinel/trust/util/JwtUtilTest.java @@ -0,0 +1,140 @@ +/* + * Copyright 1999-2019 Alibaba Group Holding Ltd. + * + * Licensed 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 + * + * https://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.alibaba.csp.sentinel.trust.util; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.alibaba.csp.sentinel.trust.auth.rule.JwtRule; + +import org.jose4j.jwt.JwtClaims; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +/** + * @author lwj + * @since 2.0.0 + */ +public class JwtUtilTest { + + @Test + public void testGetTokenFromJwtRule() { + Map fromHeaders = new HashMap() {{ + put("header1", "pre1"); + put("header2", "pre2"); + }}; + List fromParams = new ArrayList() {{ + add("param1"); + add("param2"); + }}; + Map> headers = new HashMap>() {{ + put("header1", null); + put("header0", new ArrayList() {{ + add("pre"); + }}); + put("header1", new ArrayList() {{ + add("pretokenheader1"); + }}); + put("header2", new ArrayList() {{ + add("pre2token2"); + }}); + }}; + + Map> noHeaders = new HashMap>() {{ + put("header1", null); + put("header0", new ArrayList() {{ + add("pre"); + }}); + put("header1", new ArrayList() {{ + add("pretokenheader1"); + }}); + put("header2", new ArrayList() {{ + add("pre3token2"); + }}); + }}; + + Map> params = new HashMap>() {{ + put("param1", null); + put("parma", new ArrayList() {{ + add("pre"); + }}); + put("param1", new ArrayList() {{ + add("tokenparam"); + }}); + }}; + + Map> noParams = new HashMap>() {{ + put("param1", null); + put("parma", new ArrayList() {{ + add("pre"); + }}); + put("param3", new ArrayList() {{ + add("tokenparam"); + }}); + }}; + + assertEquals("", JwtUtil.getTokenFromJwtRule(params, headers, null)); + + JwtRule nullJwtRule = new JwtRule("", null, "", null, "", null); + + assertEquals("", JwtUtil.getTokenFromJwtRule(params, headers, nullJwtRule)); + + JwtRule nullHeaderJwtRule = new JwtRule("", null, "", null, "", fromParams); + + assertEquals("tokenparam", JwtUtil.getTokenFromJwtRule(params, headers, nullHeaderJwtRule)); + + JwtRule nullParamJwtRule = new JwtRule("", fromHeaders, "", null, "", null); + + assertEquals("token2", JwtUtil.getTokenFromJwtRule(params, headers, nullParamJwtRule)); + + JwtRule allJwtRule = new JwtRule("", fromHeaders, "", null, "", fromParams); + + assertEquals("", JwtUtil.getTokenFromJwtRule(null, null, allJwtRule)); + + assertEquals("token2", JwtUtil.getTokenFromJwtRule(null, headers, allJwtRule)); + assertEquals("", JwtUtil.getTokenFromJwtRule(null, noHeaders, allJwtRule)); + assertEquals("", JwtUtil.getTokenFromJwtRule(noParams, noHeaders, allJwtRule)); + + assertEquals("", JwtUtil.getTokenFromJwtRule(noParams, null, allJwtRule)); + assertEquals("tokenparam", JwtUtil.getTokenFromJwtRule(params, noHeaders, allJwtRule)); + + } + + @Test + public void testExtractJwtClaims() { + String token + = "eyJhbGciOiJSUzI1NiIsImtpZCI6Im15a2V5IiwidHlwIjoiSldUIn0" + + ".eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ" + + + ".neCPSh0cOM4XFze8BFM3lbAqh3WRdKNTPZ1ZpWL1W3BB4OckPKRGn9S72O_wTWpCesnikN0OG7sk2fpQq_s6Ls6YiQHii61e5Km54H1xlqY2FRZXaho9TVSJVv_2xNYczCBVZizXkXt_VTPwcE_qeUDb90NMgZ9UBv_Hj83g6unN2OgM9HwYfxVwlw4G7pqq8tQo5686aE2KnoTgw_TtE6oULSORyYVBK-MWUfmSv_zqfTAH2A0R2_ne1FYKlVEo-mao_ix8ocK0eqtxBbGx7dMysR3ON1MeRDTW2AVRW4LIxDG8_obVRzhnTcx0W7zFJPYastPnPo8t78nx7nGEIA"; + String jwks + = "{\"keys\": [{\"kty\": \"RSA\", \"alg\": \"RS256\", \"kid\": \"mykey\", \"use\": \"sig\", \"n\": " + + "\"njX2CK2OdZwFVcDfQYrcUlu5Sede1y6rGIcc270aO_Ga1BChAyy1Gj-mzrIJGDZJPmsc_I8Svgy6LuXLXLJHGby" + + + "-QQVkiWGvTpazWKm3JQG7RXYvfvnxOn9GAUbcX65p7dSkY3KpDaJiQaV08lCWhx9qX304wIIWyL0maXGB8PkDtJHncPnlhEZoU7Kcm_Tra0QLk1f-rHQc5U7XfQxMvoexQ6QCdSQT39kdKn9P5ubu_rK8c62qan4FFx-qcMrlvcB9Jom7JMIpSYDSewev37FYyUr_EZ7nXKFYXJB6jtXc-pGsCY3MOy9Pqi43ZjqKatjSO_UlX8ReOBMNL7QBlQ==\", \"e\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQ==\"}]}"; + assertNull(JwtUtil.extractJwtClaims(null, token)); + assertNull(JwtUtil.extractJwtClaims(jwks, null)); + JwtClaims jwtClaims = JwtUtil.extractJwtClaims(jwks, token); + assertEquals("1234567890", jwtClaims.getClaimValueAsString("sub")); + assertEquals("John Doe", jwtClaims.getClaimValueAsString("name")); + assertEquals("1516239022", jwtClaims.getClaimValueAsString("iat")); + } + +} \ No newline at end of file diff --git a/sentinel-security-core/src/test/java/com/alibaba/csp/sentinel/trust/validator/AuthValidatorTest.java b/sentinel-security-core/src/test/java/com/alibaba/csp/sentinel/trust/validator/AuthValidatorTest.java new file mode 100644 index 0000000000..1b9d4a5437 --- /dev/null +++ b/sentinel-security-core/src/test/java/com/alibaba/csp/sentinel/trust/validator/AuthValidatorTest.java @@ -0,0 +1,398 @@ +/* + * Copyright 1999-2019 Alibaba Group Holding Ltd. + * + * Licensed 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 + * + * https://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.alibaba.csp.sentinel.trust.validator; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.alibaba.csp.sentinel.trust.auth.Rules; +import com.alibaba.csp.sentinel.trust.auth.condition.AuthCondition; +import com.alibaba.csp.sentinel.trust.auth.condition.matcher.IpMatcher; +import com.alibaba.csp.sentinel.trust.auth.condition.matcher.PortMatcher; +import com.alibaba.csp.sentinel.trust.auth.condition.matcher.StringMatcher; +import com.alibaba.csp.sentinel.trust.auth.rule.AuthRule; +import com.alibaba.csp.sentinel.trust.auth.rule.AuthType; +import com.alibaba.csp.sentinel.trust.auth.rule.JwtRule; + +import org.jose4j.jwt.JwtClaims; +import org.jose4j.jwt.NumericDate; +import org.junit.Assert; +import org.junit.Test; + +/** + * @author lwj + * @since 2.0.0 + */ +public class AuthValidatorTest { + + @Test + public void testValidate() { + String okToken + = "eyJhbGciOiJSUzI1NiIsImtpZCI6Im15a2V5IiwidHlwIjoiSldUIn0" + + ".eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjI2ODk5MDM2NzN9" + + ".M4o8ziktarFbPIHI9h7zbczbbr8p0xc5kzDVgzhH98Zc3WvEiUf1aNlnJWzeRLKgZe7omk-Jfk7X" + + "-RCi0QTMQVsCW7xJ6OqfV4ndIHAaf-F7Vbz9AbD08EmlEJCmEMq5l3nih-sTn2wwXg3neddJLHOxy" + + "-ikGWpf_WnvvDDoExZzHzwGZVGYoanWgDzn5VQxdM-h3ZiB9YNCCAtP3PIi37v69A-23UCLjJxKXMSnmrR93-qwjvD4MCvC4aY-qQ" + + "-lmU-rlt0otnBKpuYtVviMWAVMGVqI5PdWw4IcTWIQufZwal3albNOlvWnXUT2As0rNIeDC7OclK6tcY5aca7Z7Q"; + String okJwks + = "{\"keys\": [{\"kty\": \"RSA\", \"alg\": \"RS256\", \"kid\": \"mykey\", \"use\": \"sig\", \"n\": " + + "\"qXrqGpzWeY5TFtBMklVb0wgMqNV_H-CO78ZIN7NIkUfWocRown4vesgi" + + "-84RROCOF0lQSuiXi4o8y685cw8FA8ikDL94o9OPeV5ENzfku5tuxCEF5pDURbYDTeg" + + "-Hawu6NhczKH8vfFKOMUOgvDtD4GezY7SfB4dg2j3Gi3xeOKHsDh2uyJDeNen_coO" + + + "-o7pdlLCYC9cbmqxb66hyhFEUT5cfyggLMXo_rKnUfst2oPbWJOgD_UOpsn_qyNM3qdzMsCcANNn0SLIboUNMcDWemxKtZXQS3XvHXdGVQ__OoUOGqyP3hoO9qRcXoxWLZD6Jq7DoJ-UyR8O-nDqn9-UFw==\", \"e\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQ==\"}]}"; + + String notToken + = "eyJhbGciOiJSUzI1NiIsImtpZCI6Im15a2V5IiwidHlwIjoiSldUIn0" + + ".eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjY4OTkwMzcyMH0" + + + ".Wl9MgSjsR13UZKh9i9tt5bhkbWsVJzD4dSiwA8czZND4DJYOOlWqGwCD496aY2H66AyOOKo40b5fM36fVHBTK9eoqeHQI8LNOuJwnq9EoijkFq1dmzdagqBz7pkLyB_J9AKvIlH1TrZXcXtZ5CwdEivi3YjjVo5T-Ot6ME5-XKIdvRo9GzGRSqjko9poL2wyt-t_lWA5aBxtrYRYIOHVDM76ow6fnwX8NwAynySq2uw11mpc7MBuOfRe2U9UyVjjZdDPzEOGpXHCR6gVtO4zzFdeC7P2tp_-i3c09GvORMVnGpb2xhGiuIgG3vbiVEpr--S1739qiwVG5m6btVr3XA"; + String notJwks + = "{\"keys\": [{\"kty\": \"RSA\", \"alg\": \"RS256\", \"kid\": \"mykey\", \"use\": \"sig\", \"n\": " + + "\"q27wGbMTXsC8zhC1jDQARMf1L-70vhZuYaGZZacPdTFpRQNDT85P-ygi80jspPJdwprj1IQbdMXGKD" + + + "-sZonmqdXfAkg1Muq_YkbQBn_fqmx8ye0nURv7vxhfWb8ONPVFX28o9lCt1dToOvaTVxD39KhDhSwKgilNvrynr2exsHHDVHtsHxDf26rgtX3WB2avH-tQBi1eHfwKKLk1bDX4IMRvDbxOygXTHZrp4xK-nUs5I6VU9FfmGNILhe7-SLQp4jDAM7r0ndAi3noEPrZNd6oa_m0vhiOwWOAocnX4TIquXllqY7CEQc149PSNYa0WqMnax109MizizODdEI2KHw==\", \"e\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQ==\"}]}"; + + UnifiedHttpRequest request = new UnifiedHttpRequest.UnifiedHttpRequestBuilder() + .setPort(8080) + .setHeaders(new HashMap>() {{ + put("header1", new ArrayList() {{ + add("pre12 " + okToken); + }}); + }}) + .build(); + + AuthRule okAuthRule = new AuthRule(new AuthCondition(AuthType.DESTINATION_PORT, new PortMatcher(8080))); + AuthRule notAuthRule = new AuthRule(new AuthCondition(AuthType.DESTINATION_PORT, new PortMatcher(8081))); + + Map fromHeader = new HashMap() {{ + put("header1", "pre12 "); + }}; + JwtRule jwtRule = new JwtRule("", fromHeader, null, null, okJwks, null); + + JwtRule notJwtRule = new JwtRule("", fromHeader, null, null, notJwks, null); + + Rules rules = new Rules(new HashMap<>(), new HashMap<>(), new HashMap<>()); + + Assert.assertTrue(AuthValidator.validate(request, rules)); + + rules = new Rules(new HashMap<>(), new HashMap<>(), new HashMap() {{ + put("x", jwtRule); + }}); + + Assert.assertTrue(AuthValidator.validate(request, rules)); + + rules = new Rules(new HashMap() {{ + put("x", okAuthRule); + }}, new HashMap<>(), new HashMap() {{ + put("x", jwtRule); + }}); + + Assert.assertTrue(AuthValidator.validate(request, rules)); + + rules = new Rules(new HashMap() {{ + put("x", okAuthRule); + }}, new HashMap() {{ + put("x", notAuthRule); + }}, new HashMap() {{ + put("x", jwtRule); + }}); + + Assert.assertTrue(AuthValidator.validate(request, rules)); + + rules = new Rules(new HashMap() {{ + put("x", notAuthRule); + }}, new HashMap() {{ + put("x", notAuthRule); + }}, new HashMap() {{ + put("x", jwtRule); + }}); + + Assert.assertFalse(AuthValidator.validate(request, rules)); + + rules = new Rules(new HashMap() {{ + put("x", okAuthRule); + }}, new HashMap() {{ + put("x", okAuthRule); + }}, new HashMap() {{ + put("x", jwtRule); + }}); + + Assert.assertFalse(AuthValidator.validate(request, rules)); + + request = new UnifiedHttpRequest.UnifiedHttpRequestBuilder() + .setPort(8080) + .setHeaders(new HashMap>() {{ + put("header1", new ArrayList() {{ + add("pre12 " + notJwks); + }}); + }}) + .build(); + rules = new Rules(new HashMap() {{ + put("x", okAuthRule); + }}, new HashMap() {{ + put("x", notAuthRule); + }}, new HashMap() {{ + put("x", jwtRule); + }}); + + Assert.assertFalse(AuthValidator.validate(request, rules)); + + } + + @Test + public void testValidateJwtRule() { + JwtClaims jwtClaims = new JwtClaims(); + jwtClaims.setIssuer("iss"); + jwtClaims.setSubject("sub"); + jwtClaims.setAudience("aud1", "aud2"); + jwtClaims.setClaim("azp", "azpvalue"); + jwtClaims.setClaim("abc", "abc1"); + jwtClaims.setExpirationTime(NumericDate.fromMilliseconds(System.currentTimeMillis() + 1000000)); + + List audis = new ArrayList() {{ + add("aud1"); + add("aud3"); + }}; + JwtRule jwtRule = new JwtRule(null, null, null, audis, null, null); + + Assert.assertTrue(AuthValidator.validateJwtRule(jwtRule, jwtClaims)); + + jwtRule = new JwtRule(null, null, "iss", null, null, null); + + Assert.assertTrue(AuthValidator.validateJwtRule(jwtRule, jwtClaims)); + + jwtRule = new JwtRule(null, null, "iss", audis, null, null); + Assert.assertTrue(AuthValidator.validateJwtRule(jwtRule, jwtClaims)); + + jwtRule = new JwtRule(null, null, "iss1", audis, null, null); + Assert.assertFalse(AuthValidator.validateJwtRule(jwtRule, jwtClaims)); + + List audisErr = new ArrayList() {{ + add("aud3"); + add("aud4"); + }}; + + jwtRule = new JwtRule(null, null, "iss1", audis, null, null); + Assert.assertFalse(AuthValidator.validateJwtRule(jwtRule, jwtClaims)); + + jwtRule = new JwtRule(null, null, "iss", audisErr, null, null); + Assert.assertFalse(AuthValidator.validateJwtRule(jwtRule, jwtClaims)); + + jwtRule = new JwtRule(null, null, "iss", audis, null, null); + jwtClaims.setExpirationTime(NumericDate.fromMilliseconds(System.currentTimeMillis() - 1000000)); + Assert.assertFalse(AuthValidator.validateJwtRule(jwtRule, jwtClaims)); + } + + @Test + public void testValidateRule() { + UnifiedHttpRequest request = new UnifiedHttpRequest.UnifiedHttpRequestBuilder() + .setPort(8080) + .build(); + + AuthRule authRuleChild1 = new AuthRule(new AuthCondition(AuthType.DESTINATION_PORT, new PortMatcher(8080))); + AuthRule authRuleChild2 = new AuthRule(new AuthCondition(AuthType.DESTINATION_PORT, new PortMatcher(8081))); + + AuthRule authRuleAnd = new AuthRule(AuthRule.ChildChainType.AND); + authRuleAnd.addChildren(authRuleChild1); + authRuleAnd.addChildren(authRuleChild2); + + Assert.assertFalse(AuthValidator.validateRule(authRuleAnd, request)); + + AuthRule authRuleAndNot = new AuthRule(AuthRule.ChildChainType.AND, true); + authRuleAndNot.addChildren(authRuleChild1); + authRuleAndNot.addChildren(authRuleChild2); + + Assert.assertTrue(AuthValidator.validateRule(authRuleAndNot, request)); + + AuthRule authRuleOr = new AuthRule(AuthRule.ChildChainType.OR); + authRuleOr.addChildren(authRuleChild1); + authRuleOr.addChildren(authRuleChild2); + + Assert.assertTrue(AuthValidator.validateRule(authRuleOr, request)); + + AuthRule authRuleOrNot = new AuthRule(AuthRule.ChildChainType.OR, true); + authRuleOrNot.addChildren(authRuleChild1); + authRuleOrNot.addChildren(authRuleChild2); + + Assert.assertFalse(AuthValidator.validateRule(authRuleOrNot, request)); + + } + + @Test + public void testValidateLeafRule() { + UnifiedHttpRequest request = new UnifiedHttpRequest.UnifiedHttpRequestBuilder() + .setDestIp("11.1.1.1") + .setRemoteIp("12.1.1.1") + .setSourceIp("13.1.1.1") + .setHost("www.abc.com") + .setMethod("put") + .setPath("/abc/cdf") + .setPort(8080) + .setPrincipal("principal") + .setSni("sni") + .setHeaders(new HashMap>() {{ + put("header1", new ArrayList() {{ + add("pre11"); + add("pre12"); + }}); + put("header2", new ArrayList() {{ + add("pre21"); + add("pre22"); + }}); + }}) + .setParams(new HashMap>() {{ + put("param1", new ArrayList() {{ + add("p11"); + add("12"); + }}); + put("param2", new ArrayList() {{ + add("p21"); + add("22"); + }}); + }}) + .build(); + JwtClaims jwtClaims = new JwtClaims(); + jwtClaims.setIssuer("iss"); + jwtClaims.setSubject("sub"); + jwtClaims.setAudience("aud1", "aud2"); + jwtClaims.setClaim("azp", "azpvalue"); + jwtClaims.setClaim("abc", "abc1"); + request.setJwtClaims(jwtClaims); + + AuthRule authRuleTrue = new AuthRule(new AuthCondition(AuthType.DESTINATION_IP, new IpMatcher(32, "11.1.1.1"))); + AuthRule authRuleFalse = new AuthRule( + new AuthCondition(AuthType.DESTINATION_IP, new IpMatcher(32, "11.1.1.0"))); + Assert.assertTrue(AuthValidator.validateLeafRule(authRuleTrue, request)); + Assert.assertFalse(AuthValidator.validateLeafRule(authRuleFalse, request)); + + authRuleTrue = new AuthRule(new AuthCondition(AuthType.REMOTE_IP, new IpMatcher(32, "12.1.1.1"))); + authRuleFalse = new AuthRule(new AuthCondition(AuthType.REMOTE_IP, new IpMatcher(32, "12.1.1.0"))); + Assert.assertTrue(AuthValidator.validateLeafRule(authRuleTrue, request)); + Assert.assertFalse(AuthValidator.validateLeafRule(authRuleFalse, request)); + + authRuleTrue = new AuthRule(new AuthCondition(AuthType.DIRECT_REMOTE_IP, new IpMatcher(32, "13.1.1.1"))); + authRuleFalse = new AuthRule(new AuthCondition(AuthType.DIRECT_REMOTE_IP, new IpMatcher(32, "13.1.1.0"))); + Assert.assertTrue(AuthValidator.validateLeafRule(authRuleTrue, request)); + Assert.assertFalse(AuthValidator.validateLeafRule(authRuleFalse, request)); + + authRuleTrue = new AuthRule(new AuthCondition(AuthType.HOSTS, + new StringMatcher("www.abc.com", StringMatcher.MatcherType.EXACT, false))); + authRuleFalse = new AuthRule(new AuthCondition(AuthType.HOSTS, + new StringMatcher("www.abc.com1", StringMatcher.MatcherType.EXACT, false))); + Assert.assertTrue(AuthValidator.validateLeafRule(authRuleTrue, request)); + Assert.assertFalse(AuthValidator.validateLeafRule(authRuleFalse, request)); + + authRuleTrue = new AuthRule( + new AuthCondition(AuthType.METHODS, new StringMatcher("put", StringMatcher.MatcherType.EXACT, false))); + authRuleFalse = new AuthRule( + new AuthCondition(AuthType.METHODS, new StringMatcher("get", StringMatcher.MatcherType.EXACT, false))); + Assert.assertTrue(AuthValidator.validateLeafRule(authRuleTrue, request)); + Assert.assertFalse(AuthValidator.validateLeafRule(authRuleFalse, request)); + + authRuleTrue = new AuthRule(new AuthCondition(AuthType.URL_PATH, + new StringMatcher("/abc/cdf", StringMatcher.MatcherType.EXACT, false))); + authRuleFalse = new AuthRule(new AuthCondition(AuthType.URL_PATH, + new StringMatcher("/abc/cdferr", StringMatcher.MatcherType.EXACT, false))); + Assert.assertTrue(AuthValidator.validateLeafRule(authRuleTrue, request)); + Assert.assertFalse(AuthValidator.validateLeafRule(authRuleFalse, request)); + + authRuleTrue = new AuthRule(new AuthCondition(AuthType.DESTINATION_PORT, new PortMatcher(8080))); + authRuleFalse = new AuthRule(new AuthCondition(AuthType.DESTINATION_PORT, new PortMatcher(8081))); + Assert.assertTrue(AuthValidator.validateLeafRule(authRuleTrue, request)); + Assert.assertFalse(AuthValidator.validateLeafRule(authRuleFalse, request)); + + authRuleTrue = new AuthRule(new AuthCondition(AuthType.AUTHENTICATED, + new StringMatcher("principal", StringMatcher.MatcherType.EXACT, false))); + authRuleFalse = new AuthRule(new AuthCondition(AuthType.AUTHENTICATED, + new StringMatcher("principalerr", StringMatcher.MatcherType.EXACT, false))); + Assert.assertTrue(AuthValidator.validateLeafRule(authRuleTrue, request)); + Assert.assertFalse(AuthValidator.validateLeafRule(authRuleFalse, request)); + + authRuleTrue = new AuthRule(new AuthCondition(AuthType.REQUESTED_SERVER_NAME, + new StringMatcher("sni", StringMatcher.MatcherType.EXACT, false))); + authRuleFalse = new AuthRule(new AuthCondition(AuthType.REQUESTED_SERVER_NAME, + new StringMatcher("snierr", StringMatcher.MatcherType.EXACT, false))); + Assert.assertTrue(AuthValidator.validateLeafRule(authRuleTrue, request)); + Assert.assertFalse(AuthValidator.validateLeafRule(authRuleFalse, request)); + + authRuleTrue = new AuthRule(new AuthCondition(AuthType.HEADER, "header1", + new StringMatcher("pre12", StringMatcher.MatcherType.EXACT, false))); + authRuleFalse = new AuthRule(new AuthCondition(AuthType.HEADER, "header1", + new StringMatcher("preerr", StringMatcher.MatcherType.EXACT, false))); + AuthRule authRuleFalse1 = new AuthRule(new AuthCondition(AuthType.HEADER, "header3", + new StringMatcher("pre1", StringMatcher.MatcherType.EXACT, false))); + Assert.assertTrue(AuthValidator.validateLeafRule(authRuleTrue, request)); + Assert.assertFalse(AuthValidator.validateLeafRule(authRuleFalse, request)); + Assert.assertFalse(AuthValidator.validateLeafRule(authRuleFalse1, request)); + + authRuleTrue = new AuthRule(new AuthCondition(AuthType.JWT_PRINCIPALS, + new StringMatcher("iss/sub", StringMatcher.MatcherType.EXACT, false))); + authRuleFalse = new AuthRule(new AuthCondition(AuthType.JWT_PRINCIPALS, + new StringMatcher("iss/err", StringMatcher.MatcherType.EXACT, false))); + Assert.assertTrue(AuthValidator.validateLeafRule(authRuleTrue, request)); + Assert.assertFalse(AuthValidator.validateLeafRule(authRuleFalse, request)); + + authRuleTrue = new AuthRule(new AuthCondition(AuthType.JWT_AUDIENCES, + new StringMatcher("aud1", StringMatcher.MatcherType.EXACT, false))); + authRuleFalse = new AuthRule(new AuthCondition(AuthType.JWT_AUDIENCES, + new StringMatcher("aud3", StringMatcher.MatcherType.EXACT, false))); + Assert.assertTrue(AuthValidator.validateLeafRule(authRuleTrue, request)); + Assert.assertFalse(AuthValidator.validateLeafRule(authRuleFalse, request)); + + authRuleTrue = new AuthRule(new AuthCondition(AuthType.JWT_PRESENTERS, + new StringMatcher("azpvalue", StringMatcher.MatcherType.EXACT, false))); + authRuleFalse = new AuthRule(new AuthCondition(AuthType.JWT_PRESENTERS, + new StringMatcher("azpvalueerr", StringMatcher.MatcherType.EXACT, false))); + Assert.assertTrue(AuthValidator.validateLeafRule(authRuleTrue, request)); + Assert.assertFalse(AuthValidator.validateLeafRule(authRuleFalse, request)); + + authRuleTrue = new AuthRule(new AuthCondition(AuthType.JWT_CLAIMS, "abc", + new StringMatcher("abc1", StringMatcher.MatcherType.EXACT, false))); + authRuleFalse = new AuthRule(new AuthCondition(AuthType.JWT_CLAIMS, "abc", + new StringMatcher("abc3", StringMatcher.MatcherType.EXACT, false))); + authRuleFalse1 = new AuthRule(new AuthCondition(AuthType.JWT_CLAIMS, "abcd", + new StringMatcher("a", StringMatcher.MatcherType.EXACT, false))); + Assert.assertTrue(AuthValidator.validateLeafRule(authRuleTrue, request)); + Assert.assertFalse(AuthValidator.validateLeafRule(authRuleFalse, request)); + Assert.assertFalse(AuthValidator.validateLeafRule(authRuleFalse1, request)); + + request = new UnifiedHttpRequest.UnifiedHttpRequestBuilder() + .setDestIp("11.1.1.1") + .setRemoteIp("12.1.1.1") + .setSourceIp("13.1.1.1") + .setHost("www.abc.com") + .setMethod("put") + .setPath("/abc/cdf") + .setPort(8080) + .setPrincipal("principal") + .setSni("sni") + .setHeaders(null) + .setParams(null) + .build(); + + authRuleFalse = new AuthRule( + new AuthCondition(AuthType.HEADER, "abcd", new StringMatcher("a", StringMatcher.MatcherType.EXACT, false))); + authRuleFalse1 = new AuthRule(new AuthCondition(AuthType.JWT_CLAIMS, "abcd", + new StringMatcher("a", StringMatcher.MatcherType.EXACT, false))); + Assert.assertFalse(AuthValidator.validateLeafRule(authRuleFalse, request)); + Assert.assertFalse(AuthValidator.validateLeafRule(authRuleFalse1, request)); + + } + +} \ No newline at end of file