diff --git a/polaris-auth/polaris-auth-api/pom.xml b/polaris-auth/polaris-auth-api/pom.xml new file mode 100644 index 000000000..cb44f790a --- /dev/null +++ b/polaris-auth/polaris-auth-api/pom.xml @@ -0,0 +1,29 @@ + + + + polaris-auth + com.tencent.polaris + ${revision} + ../pom.xml + + 4.0.0 + + polaris-auth-api + Polaris Auth API + Polaris Auth API JAR + + + + com.tencent.polaris + polaris-plugin-api + ${project.version} + + + com.tencent.polaris + polaris-client + ${project.version} + + + \ No newline at end of file diff --git a/polaris-auth/polaris-auth-api/src/main/java/com/tencent/polaris/auth/api/core/AuthAPI.java b/polaris-auth/polaris-auth-api/src/main/java/com/tencent/polaris/auth/api/core/AuthAPI.java new file mode 100644 index 000000000..dc1cf43e8 --- /dev/null +++ b/polaris-auth/polaris-auth-api/src/main/java/com/tencent/polaris/auth/api/core/AuthAPI.java @@ -0,0 +1,46 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.polaris.auth.api.core; + +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.auth.api.rpc.AuthRequest; +import com.tencent.polaris.auth.api.rpc.AuthResponse; + +import java.io.Closeable; + +public interface AuthAPI extends AutoCloseable, Closeable { + + /** + * 鉴权 + * + * @param authRequest 鉴权请求(服务及标签信息) + * @return 鉴权通过情况 + * @throws PolarisException 异常信息 + */ + AuthResponse authenticate(AuthRequest authRequest) throws PolarisException; + + /** + * 清理并释放资源 + */ + void destroy(); + + @Override + default void close() { + destroy(); + } +} diff --git a/polaris-auth/polaris-auth-api/src/main/java/com/tencent/polaris/auth/api/flow/AuthFlow.java b/polaris-auth/polaris-auth-api/src/main/java/com/tencent/polaris/auth/api/flow/AuthFlow.java new file mode 100644 index 000000000..a7c7c9b56 --- /dev/null +++ b/polaris-auth/polaris-auth-api/src/main/java/com/tencent/polaris/auth/api/flow/AuthFlow.java @@ -0,0 +1,37 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.polaris.auth.api.flow; + +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.auth.api.rpc.AuthRequest; +import com.tencent.polaris.auth.api.rpc.AuthResponse; +import com.tencent.polaris.client.flow.AbstractFlow; + +public interface AuthFlow extends AbstractFlow { + + /** + * 鉴权 + * + * @param authRequest 鉴权请求(服务及标签信息) + * @return 鉴权通过情况 + * @throws PolarisException 异常信息 + */ + default AuthResponse authenticate(AuthRequest authRequest) { + return null; + } +} diff --git a/polaris-auth/polaris-auth-api/src/main/java/com/tencent/polaris/auth/api/rpc/AuthRequest.java b/polaris-auth/polaris-auth-api/src/main/java/com/tencent/polaris/auth/api/rpc/AuthRequest.java new file mode 100644 index 000000000..9babda03b --- /dev/null +++ b/polaris-auth/polaris-auth-api/src/main/java/com/tencent/polaris/auth/api/rpc/AuthRequest.java @@ -0,0 +1,86 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.polaris.auth.api.rpc; + +import com.tencent.polaris.api.rpc.RequestBaseEntity; +import com.tencent.polaris.metadata.core.manager.MetadataContext; + +/** + * 鉴权请求 + * + * @author Haotian Zhang + */ +public class AuthRequest extends RequestBaseEntity { + + private final String namespace; + + private final String service; + + private final String path; + + private final String protocol; + + private final String method; + + private final MetadataContext metadataContext; + + public AuthRequest(String namespace, String service, String path, String protocol, String method, MetadataContext metadataContext) { + this.namespace = namespace; + this.service = service; + this.path = path; + this.protocol = protocol; + this.method = method; + this.metadataContext = metadataContext; + } + + public String getNamespace() { + return namespace; + } + + public String getService() { + return service; + } + + public String getPath() { + return path; + } + + public String getProtocol() { + return protocol; + } + + public String getMethod() { + return method; + } + + public MetadataContext getMetadataContext() { + return metadataContext; + } + + @Override + public String toString() { + return "AuthRequest{" + + "namespace='" + namespace + '\'' + + ", service='" + service + '\'' + + ", path='" + path + '\'' + + ", protocol='" + protocol + '\'' + + ", method='" + method + '\'' + + ", metadataContext=" + metadataContext + + '}'; + } +} diff --git a/polaris-auth/polaris-auth-api/src/main/java/com/tencent/polaris/auth/api/rpc/AuthResponse.java b/polaris-auth/polaris-auth-api/src/main/java/com/tencent/polaris/auth/api/rpc/AuthResponse.java new file mode 100644 index 000000000..90be24022 --- /dev/null +++ b/polaris-auth/polaris-auth-api/src/main/java/com/tencent/polaris/auth/api/rpc/AuthResponse.java @@ -0,0 +1,45 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.polaris.auth.api.rpc; + +import com.tencent.polaris.api.plugin.auth.AuthResult; + +/** + * 鉴权响应 + * + * @author Haotian Zhang + */ +public class AuthResponse { + + private final AuthResult authResult; + + public AuthResponse(AuthResult authResult) { + this.authResult = authResult; + } + + public AuthResult getAuthResult() { + return authResult; + } + + @Override + public String toString() { + return "AuthResponse{" + + "authResult=" + authResult + + '}'; + } +} diff --git a/polaris-auth/polaris-auth-client/pom.xml b/polaris-auth/polaris-auth-client/pom.xml new file mode 100644 index 000000000..2edbf9729 --- /dev/null +++ b/polaris-auth/polaris-auth-client/pom.xml @@ -0,0 +1,31 @@ + + + + polaris-auth + com.tencent.polaris + ${revision} + ../pom.xml + + 4.0.0 + + polaris-auth-client + Polaris Auth Client + Polaris Auth Client JAR + + + + com.tencent.polaris + polaris-auth-api + ${project.version} + + + + org.slf4j + slf4j-api + ${slf4j.version} + provided + + + \ No newline at end of file diff --git a/polaris-auth/polaris-auth-client/src/main/java/com/tencent/polaris/auth/client/api/DefaultAuthAPI.java b/polaris-auth/polaris-auth-client/src/main/java/com/tencent/polaris/auth/client/api/DefaultAuthAPI.java new file mode 100644 index 000000000..cbff1ff1d --- /dev/null +++ b/polaris-auth/polaris-auth-client/src/main/java/com/tencent/polaris/auth/client/api/DefaultAuthAPI.java @@ -0,0 +1,53 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.polaris.auth.client.api; + +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.auth.api.core.AuthAPI; +import com.tencent.polaris.auth.api.flow.AuthFlow; +import com.tencent.polaris.auth.api.rpc.AuthRequest; +import com.tencent.polaris.auth.api.rpc.AuthResponse; +import com.tencent.polaris.auth.client.utils.AuthValidator; +import com.tencent.polaris.client.api.BaseEngine; +import com.tencent.polaris.client.api.SDKContext; + +/** + * 默认的鉴权API实现 + * + * @author Haotian Zhang + */ +public class DefaultAuthAPI extends BaseEngine implements AuthAPI { + + private AuthFlow authFlow; + + public DefaultAuthAPI(SDKContext sdkContext) { + super(sdkContext); + } + + @Override + protected void subInit() { + authFlow = sdkContext.getOrInitFlow(AuthFlow.class); + } + + @Override + public AuthResponse authenticate(AuthRequest authRequest) throws PolarisException { + checkAvailable("AuthFlow"); + AuthValidator.validateAuthRequest(authRequest); + return authFlow.authenticate(authRequest); + } +} diff --git a/polaris-auth/polaris-auth-client/src/main/java/com/tencent/polaris/auth/client/flow/DefaultAuthFlow.java b/polaris-auth/polaris-auth-client/src/main/java/com/tencent/polaris/auth/client/flow/DefaultAuthFlow.java new file mode 100644 index 000000000..421ec010d --- /dev/null +++ b/polaris-auth/polaris-auth-client/src/main/java/com/tencent/polaris/auth/client/flow/DefaultAuthFlow.java @@ -0,0 +1,81 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.polaris.auth.client.flow; + +import com.tencent.polaris.api.config.global.FlowConfig; +import com.tencent.polaris.api.config.provider.AuthConfig; +import com.tencent.polaris.api.plugin.auth.AuthInfo; +import com.tencent.polaris.api.plugin.auth.AuthResult; +import com.tencent.polaris.api.plugin.auth.Authenticator; +import com.tencent.polaris.api.plugin.compose.Extensions; +import com.tencent.polaris.auth.api.flow.AuthFlow; +import com.tencent.polaris.auth.api.rpc.AuthRequest; +import com.tencent.polaris.auth.api.rpc.AuthResponse; +import com.tencent.polaris.client.api.SDKContext; +import com.tencent.polaris.logging.LoggerFactory; +import org.slf4j.Logger; + +import java.util.List; + +/** + * 默认的鉴权Flow实现 + * + * @author Haotian Zhang + */ +public class DefaultAuthFlow implements AuthFlow { + + private static final Logger LOG = LoggerFactory.getLogger(DefaultAuthFlow.class); + + private SDKContext sdkContext; + + private AuthConfig authConfig; + + private Extensions extensions; + + private List authenticatorList; + + @Override + public String getName() { + return FlowConfig.DEFAULT_FLOW_NAME; + } + + @Override + public void setSDKContext(SDKContext sdkContext) { + this.sdkContext = sdkContext; + this.authConfig = sdkContext.getConfig().getProvider().getAuth(); + this.extensions = sdkContext.getExtensions(); + this.authenticatorList = extensions.getAuthenticatorList(); + } + + @Override + public AuthResponse authenticate(AuthRequest authRequest) { + if (authConfig == null || !authConfig.isEnable()) { + return new AuthResponse(new AuthResult(AuthResult.Code.AuthResultOk)); + } + AuthInfo authInfo = new AuthInfo(authRequest.getNamespace(), authRequest.getService(), authRequest.getPath(), + authRequest.getProtocol(), authRequest.getMethod(), authRequest.getMetadataContext()); + AuthResponse authResponse = new AuthResponse(new AuthResult(AuthResult.Code.AuthResultOk)); + for (Authenticator authenticator : authenticatorList) { + AuthResult authResult = authenticator.authenticate(authInfo); + if (authResult.getCode().equals(AuthResult.Code.AuthResultForbidden)) { + return new AuthResponse(authResult); + } + } + return authResponse; + } +} diff --git a/polaris-auth/polaris-auth-client/src/main/java/com/tencent/polaris/auth/client/utils/AuthValidator.java b/polaris-auth/polaris-auth-client/src/main/java/com/tencent/polaris/auth/client/utils/AuthValidator.java new file mode 100644 index 000000000..8ffcab53d --- /dev/null +++ b/polaris-auth/polaris-auth-client/src/main/java/com/tencent/polaris/auth/client/utils/AuthValidator.java @@ -0,0 +1,39 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.polaris.auth.client.utils; + +import com.tencent.polaris.api.exception.ErrorCode; +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.auth.api.rpc.AuthRequest; +import com.tencent.polaris.client.util.CommonValidator; + +public class AuthValidator { + + /** + * 校验鉴权请求 + * + * @param authRequest 鉴权请求参数 + * @throws PolarisException 校验失败 + */ + public static void validateAuthRequest(AuthRequest authRequest) throws PolarisException { + if (null == authRequest) { + throw new PolarisException(ErrorCode.API_INVALID_ARGUMENT, "AuthRequest can not be null"); + } + CommonValidator.validateNamespaceService(authRequest.getNamespace(), authRequest.getService()); + } +} diff --git a/polaris-auth/polaris-auth-client/src/main/resources/META-INF/services/com.tencent.polaris.auth.api.flow.AuthFlow b/polaris-auth/polaris-auth-client/src/main/resources/META-INF/services/com.tencent.polaris.auth.api.flow.AuthFlow new file mode 100644 index 000000000..889ce40b4 --- /dev/null +++ b/polaris-auth/polaris-auth-client/src/main/resources/META-INF/services/com.tencent.polaris.auth.api.flow.AuthFlow @@ -0,0 +1 @@ +com.tencent.polaris.auth.client.flow.DefaultAuthFlow \ No newline at end of file diff --git a/polaris-auth/polaris-auth-factory/pom.xml b/polaris-auth/polaris-auth-factory/pom.xml new file mode 100644 index 000000000..22bbf7fe0 --- /dev/null +++ b/polaris-auth/polaris-auth-factory/pom.xml @@ -0,0 +1,35 @@ + + + + polaris-auth + com.tencent.polaris + ${revision} + ../pom.xml + + 4.0.0 + + polaris-auth-factory + Polaris Auth Factory + Polaris Auth Factory JAR + + + + com.tencent.polaris + polaris-auth-client + ${project.version} + + + com.tencent.polaris + polaris-client + ${project.version} + + + org.slf4j + slf4j-api + ${slf4j.version} + provided + + + \ No newline at end of file diff --git a/polaris-auth/polaris-auth-factory/src/main/java/com/tencent/polaris/auth/factory/AuthAPIFactory.java b/polaris-auth/polaris-auth-factory/src/main/java/com/tencent/polaris/auth/factory/AuthAPIFactory.java new file mode 100644 index 000000000..fd60ae5e5 --- /dev/null +++ b/polaris-auth/polaris-auth-factory/src/main/java/com/tencent/polaris/auth/factory/AuthAPIFactory.java @@ -0,0 +1,69 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.polaris.auth.factory; + +import com.tencent.polaris.api.config.Configuration; +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.auth.api.core.AuthAPI; +import com.tencent.polaris.auth.client.api.DefaultAuthAPI; +import com.tencent.polaris.client.api.SDKContext; +import com.tencent.polaris.factory.ConfigAPIFactory; + +/** + * Factory to create AuthAPI. + * + * @author Haotian Zhang + */ +public class AuthAPIFactory { + + /** + * 通过默认配置创建 AuthAPI + * + * @return AuthAPI 对象 + * @throws PolarisException 初始化过程异常 + */ + public static AuthAPI createAuthAPI() throws PolarisException { + Configuration configuration = ConfigAPIFactory.defaultConfig(); + return createAuthAPIByConfig(configuration); + } + + /** + * 通过 SDKContext 创建 AuthAPI + * + * @param sdkContext 上下文信息 + * @return AuthAPI 对象 + * @throws PolarisException 创建过程的初始化异常 + */ + public static AuthAPI createAuthAPIByContext(SDKContext sdkContext) throws PolarisException { + DefaultAuthAPI defaultAuthAPI = new DefaultAuthAPI(sdkContext); + defaultAuthAPI.init(); + return defaultAuthAPI; + } + + /** + * 通过配置对象创建 AuthAPI + * + * @param config 配置对象 + * @return AuthAPI 对象 + * @throws PolarisException 初始化过程的异常 + */ + public static AuthAPI createAuthAPIByConfig(Configuration config) throws PolarisException { + SDKContext context = SDKContext.initContextByConfig(config); + return createAuthAPIByContext(context); + } +} diff --git a/polaris-auth/pom.xml b/polaris-auth/pom.xml new file mode 100644 index 000000000..a872fe10a --- /dev/null +++ b/polaris-auth/pom.xml @@ -0,0 +1,23 @@ + + + + polaris-parent + com.tencent.polaris + ${revision} + ../pom.xml + + 4.0.0 + + polaris-auth + pom + Polaris Auth + Polaris Auth POM + + + polaris-auth-api + polaris-auth-client + polaris-auth-factory + + \ No newline at end of file diff --git a/polaris-common/polaris-config-default/src/main/resources/conf/default-config.yml b/polaris-common/polaris-config-default/src/main/resources/conf/default-config.yml index 87e13b1de..c52f22391 100644 --- a/polaris-common/polaris-config-default/src/main/resources/conf/default-config.yml +++ b/polaris-common/polaris-config-default/src/main/resources/conf/default-config.yml @@ -303,6 +303,14 @@ provider: delayRegisterInterval: 30s # 优雅上线的探测周期 healthCheckInterval: 5s + # 服务鉴权 + auth: + # 是否启用服务鉴权 + enable: false + #描述: 服务鉴权插件链 + chain: + # 黑白名单鉴权插件 + - blockAllowList # 限流配置 rateLimit: # 是否开启限流功能 diff --git a/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/plugin/DefaultPlugins.java b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/plugin/DefaultPlugins.java index f0f101122..1b83d2cb8 100644 --- a/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/plugin/DefaultPlugins.java +++ b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/plugin/DefaultPlugins.java @@ -89,4 +89,9 @@ public interface DefaultPlugins { * TSF 事件上报插件名 */ String TSF_EVENT_REPORTER_TYPE = "tsf"; + + /** + * 黑白名单鉴权插件名 + */ + String BLOCK_ALLOW_LIST_AUTHENTICATOR_TYPE = "blockAllowList"; } diff --git a/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/provider/AuthConfig.java b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/provider/AuthConfig.java new file mode 100644 index 000000000..582fe7143 --- /dev/null +++ b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/provider/AuthConfig.java @@ -0,0 +1,41 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.polaris.api.config.provider; + +import com.tencent.polaris.api.config.verify.Verifier; + +import java.util.List; + +public interface AuthConfig extends Verifier { + + /** + * 是否启用服务鉴权 + * provider.auth.enable + * + * @return 启用鉴权 + */ + boolean isEnable(); + + /** + * 服务鉴权插件链配置 + * provider.auth.chain + * + * @return 鉴权插件链列表 + */ + List getChain(); +} diff --git a/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/provider/ProviderConfig.java b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/provider/ProviderConfig.java index 55c0f5dff..9db70e48f 100644 --- a/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/provider/ProviderConfig.java +++ b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/provider/ProviderConfig.java @@ -18,6 +18,7 @@ package com.tencent.polaris.api.config.provider; import com.tencent.polaris.api.config.verify.Verifier; + import java.util.List; import java.util.Map; @@ -72,7 +73,15 @@ public interface ProviderConfig extends Verifier { /** * get the lossless configuration + * * @return configuration */ LosslessConfig getLossless(); + + /** + * get the auth configuration + * + * @return AuthConfig + */ + AuthConfig getAuth(); } diff --git a/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/provider/AuthConfigImpl.java b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/provider/AuthConfigImpl.java new file mode 100644 index 000000000..ae6904b6a --- /dev/null +++ b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/provider/AuthConfigImpl.java @@ -0,0 +1,81 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.polaris.factory.config.provider; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.tencent.polaris.api.config.provider.AuthConfig; +import com.tencent.polaris.api.utils.CollectionUtils; +import com.tencent.polaris.factory.util.ConfigUtils; + +import java.util.List; + +public class AuthConfigImpl implements AuthConfig { + + @JsonProperty + private Boolean enable; + + @JsonProperty + private List chain; + + @Override + public boolean isEnable() { + return enable; + } + + public void setEnable(Boolean enable) { + this.enable = enable; + } + + @Override + public List getChain() { + return chain; + } + + public void setChain(List chain) { + this.chain = chain; + } + + @Override + public void verify() { + ConfigUtils.validateNull(enable, "provider.auth.enable"); + if (CollectionUtils.isEmpty(chain)) { + throw new IllegalArgumentException("provider.auth.chain must not be empty"); + } + } + + @Override + public void setDefault(Object defaultObject) { + if (null != defaultObject) { + AuthConfig authConfig = (AuthConfig) defaultObject; + if (null == enable) { + setEnable(authConfig.isEnable()); + } + if (CollectionUtils.isEmpty(chain)) { + setChain(authConfig.getChain()); + } + } + } + + @Override + public String toString() { + return "AuthConfigImpl{" + + "enable=" + enable + + ", chain=" + chain + + '}'; + } +} diff --git a/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/provider/ProviderConfigImpl.java b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/provider/ProviderConfigImpl.java index 55b5df368..3d51fa315 100644 --- a/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/provider/ProviderConfigImpl.java +++ b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/provider/ProviderConfigImpl.java @@ -23,6 +23,7 @@ import com.tencent.polaris.api.config.provider.ProviderConfig; import com.tencent.polaris.api.utils.CollectionUtils; import com.tencent.polaris.factory.util.ConfigUtils; + import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -63,6 +64,9 @@ public class ProviderConfigImpl implements ProviderConfig { @JsonProperty private LosslessConfigImpl lossless; + @JsonProperty + private AuthConfigImpl auth; + @Override public RateLimitConfigImpl getRateLimit() { return rateLimit; @@ -83,6 +87,11 @@ public LosslessConfig getLossless() { return lossless; } + @Override + public AuthConfigImpl getAuth() { + return auth; + } + @Override public List getRegisters() { if (CollectionUtils.isEmpty(registers)) { @@ -113,6 +122,7 @@ public Map getRegisterConfigMap() { public void verify() { ConfigUtils.validateNull(rateLimit, "rateLimitConfig"); ConfigUtils.validateNull(lossless, "losslessConfig"); + ConfigUtils.validateNull(auth, "authConfig"); rateLimit.verify(); if (CollectionUtils.isNotEmpty(registers)) { @@ -131,6 +141,9 @@ public void setDefault(Object defaultObject) { if (null == lossless) { lossless = new LosslessConfigImpl(); } + if (null == auth) { + auth = new AuthConfigImpl(); + } if (null == service) { service = new ServiceConfigImpl(); } @@ -144,6 +157,7 @@ public void setDefault(Object defaultObject) { ProviderConfig providerConfig = (ProviderConfig) defaultObject; rateLimit.setDefault(providerConfig.getRateLimit()); lossless.setDefault(providerConfig.getLossless()); + auth.setDefault(providerConfig.getAuth()); if (CollectionUtils.isNotEmpty(registers)) { for (RegisterConfigImpl registerConfig : registers) { registerConfig.setDefault(providerConfig.getRegisters().get(0)); @@ -175,6 +189,7 @@ public String toString() { ", minRegisterInterval=" + minRegisterInterval + ", heartbeatWorkerSize=" + heartbeatWorkerSize + ", lossless=" + lossless + + ", auth=" + auth + '}'; } } diff --git a/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/ServiceEventKey.java b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/ServiceEventKey.java index 5168805b4..6cd7464a9 100644 --- a/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/ServiceEventKey.java +++ b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/ServiceEventKey.java @@ -34,6 +34,7 @@ public enum EventType { LANE_RULE, NEARBY_ROUTE_RULE, LOSSLESS, + BLOCK_ALLOW_RULE, } private final ServiceKey serviceKey; diff --git a/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/utils/RuleUtils.java b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/utils/RuleUtils.java index 68ac7ee9a..a880d791b 100644 --- a/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/utils/RuleUtils.java +++ b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/utils/RuleUtils.java @@ -22,6 +22,7 @@ import com.tencent.polaris.api.pojo.TrieNode; import com.tencent.polaris.logging.LoggerFactory; import com.tencent.polaris.metadata.core.manager.MetadataContainerGroup; +import com.tencent.polaris.specification.api.v1.model.ModelProto; import com.tencent.polaris.specification.api.v1.model.ModelProto.MatchString; import com.tencent.polaris.specification.api.v1.model.ModelProto.MatchString.MatchStringType; import org.slf4j.Logger; @@ -94,7 +95,7 @@ public static boolean matchStringValue(MatchStringType matchType, String actualV if (useTrieNode && trieNodeFunction != null) { return ApiTrieUtil.checkSimple(trieNodeFunction.apply(matchValue), actualValue); } - return StringUtils.equals(actualValue, matchValue); + return StringUtils.equalsIgnoreCase(actualValue, matchValue); } case REGEX: { //正则表达式匹配 @@ -105,7 +106,7 @@ public static boolean matchStringValue(MatchStringType matchType, String actualV if (useTrieNode && trieNodeFunction != null) { return !ApiTrieUtil.checkSimple(trieNodeFunction.apply(matchValue), actualValue); } - return !StringUtils.equals(actualValue, matchValue); + return !StringUtils.equalsIgnoreCase(actualValue, matchValue); } case IN: { String[] tokens = matchValue.split(","); @@ -115,7 +116,7 @@ public static boolean matchStringValue(MatchStringType matchType, String actualV return true; } } else { - if (StringUtils.equals(token, actualValue)) { + if (StringUtils.equalsIgnoreCase(token, actualValue)) { return true; } } @@ -130,7 +131,7 @@ public static boolean matchStringValue(MatchStringType matchType, String actualV return false; } } else { - if (StringUtils.equals(token, actualValue)) { + if (StringUtils.equalsIgnoreCase(token, actualValue)) { return false; } } @@ -342,4 +343,17 @@ public static boolean matchService(ServiceKey serviceKey, String namespace, Stri } return true; } + + public static boolean matchMethod(String path, String protocol, String method, ModelProto.API api, + Function regexToPattern, Function> trieNodeFunction) { + if (trieNodeFunction != null) { + return RuleUtils.matchStringValue(MatchString.MatchStringType.EXACT, protocol, api.getProtocol()) + && RuleUtils.matchStringValue(MatchString.MatchStringType.EXACT, method, api.getMethod()) + && RuleUtils.matchStringValue(api.getPath().getType(), path, api.getPath().getValue().getValue(), regexToPattern, true, trieNodeFunction); + } else { + return RuleUtils.matchStringValue(MatchString.MatchStringType.EXACT, protocol, api.getProtocol()) + && RuleUtils.matchStringValue(MatchString.MatchStringType.EXACT, method, api.getMethod()) + && RuleUtils.matchStringValue(api.getPath(), path, regexToPattern); + } + } } diff --git a/polaris-dependencies/pom.xml b/polaris-dependencies/pom.xml index a399d0210..b24b6434a 100644 --- a/polaris-dependencies/pom.xml +++ b/polaris-dependencies/pom.xml @@ -145,7 +145,29 @@ ${project.version} - + + + com.tencent.polaris + polaris-auth-api + ${project.version} + + + com.tencent.polaris + polaris-auth-client + ${project.version} + + + com.tencent.polaris + polaris-auth-factory + ${project.version} + + + com.tencent.polaris + auth-block-allow-list + ${project.version} + + + com.tencent.polaris polaris-assembly-api diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/auth/AuthInfo.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/auth/AuthInfo.java new file mode 100644 index 000000000..eca475fe4 --- /dev/null +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/auth/AuthInfo.java @@ -0,0 +1,97 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.polaris.api.plugin.auth; + +import com.tencent.polaris.metadata.core.manager.MetadataContext; + +/** + * 鉴权信息 + * + * @author Haotian Zhang + */ +public class AuthInfo { + + private String namespace; + + private String service; + + private String path; + + private String protocol; + + private String method; + + private MetadataContext metadataContext; + + public AuthInfo(String namespace, String service, String path, String protocol, String method, MetadataContext metadataContext) { + this.namespace = namespace; + this.service = service; + this.path = path; + this.protocol = protocol; + this.method = method; + this.metadataContext = metadataContext; + } + + public String getNamespace() { + return namespace; + } + + public void setNamespace(String namespace) { + this.namespace = namespace; + } + + public String getService() { + return service; + } + + public void setService(String service) { + this.service = service; + } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + public String getProtocol() { + return protocol; + } + + public void setProtocol(String protocol) { + this.protocol = protocol; + } + + public String getMethod() { + return method; + } + + public void setMethod(String method) { + this.method = method; + } + + public MetadataContext getMetadataContext() { + return metadataContext; + } + + public void setMetadataContext(MetadataContext metadataContext) { + this.metadataContext = metadataContext; + } +} diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/auth/AuthResult.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/auth/AuthResult.java new file mode 100644 index 000000000..31dabc86f --- /dev/null +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/auth/AuthResult.java @@ -0,0 +1,57 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.polaris.api.plugin.auth; + +/** + * 鉴权结果 + * + * @author Haotian Zhang + */ +public class AuthResult { + + /** + * 限流返回码 + */ + public enum Code { + /** + * OK,代表请求可以通过 + */ + AuthResultOk, + /** + * FORBIDDEN,代表本次请求被禁止 + */ + AuthResultForbidden, + } + + private final Code code; + + public AuthResult(Code code) { + this.code = code; + } + + public Code getCode() { + return code; + } + + @Override + public String toString() { + return "AuthResult{" + + "code=" + code + + '}'; + } +} diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/auth/Authenticator.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/auth/Authenticator.java new file mode 100644 index 000000000..6413dd46a --- /dev/null +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/auth/Authenticator.java @@ -0,0 +1,36 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.polaris.api.plugin.auth; + +import com.tencent.polaris.api.plugin.Plugin; + +/** + * 【扩展点接口】服务鉴权 + * + * @author Haotian Zhang + */ +public interface Authenticator extends Plugin { + + /** + * 鉴权 + * + * @param authInfo + * @return AuthResult + */ + AuthResult authenticate(AuthInfo authInfo); +} diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/common/PluginTypes.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/common/PluginTypes.java index 514548327..dfd83fa1e 100644 --- a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/common/PluginTypes.java +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/common/PluginTypes.java @@ -18,6 +18,7 @@ package com.tencent.polaris.api.plugin.common; import com.tencent.polaris.api.plugin.PluginType; +import com.tencent.polaris.api.plugin.auth.Authenticator; import com.tencent.polaris.api.plugin.cache.FlowCache; import com.tencent.polaris.api.plugin.circuitbreaker.CircuitBreaker; import com.tencent.polaris.api.plugin.circuitbreaker.InstanceCircuitBreaker; @@ -135,7 +136,12 @@ public enum PluginTypes { /** * 无损上下线策略扩展点 */ - LOSSLESS_POLICY(new PluginType(LosslessPolicy.class, 2)); + LOSSLESS_POLICY(new PluginType(LosslessPolicy.class, 2)), + + /** + * 服务鉴权扩展点 + */ + AUTHENTICATOR(new PluginType(Authenticator.class, 2)); private PluginType baseType; diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/compose/Extensions.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/compose/Extensions.java index b6ffe8cfa..3deae0d8b 100644 --- a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/compose/Extensions.java +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/compose/Extensions.java @@ -31,6 +31,7 @@ import com.tencent.polaris.api.plugin.HttpServerAware; import com.tencent.polaris.api.plugin.Plugin; import com.tencent.polaris.api.plugin.Supplier; +import com.tencent.polaris.api.plugin.auth.Authenticator; import com.tencent.polaris.api.plugin.cache.FlowCache; import com.tencent.polaris.api.plugin.circuitbreaker.CircuitBreaker; import com.tencent.polaris.api.plugin.circuitbreaker.InstanceCircuitBreaker; @@ -117,6 +118,9 @@ public class Extensions extends Destroyable { private List weightAdjusters; + // 服务鉴权插件列表 + private List authenticatorList; + public static List loadServiceRouters(List routerChain, Supplier plugins, boolean force) { List routers = new ArrayList<>(); if (CollectionUtils.isNotEmpty(routerChain)) { @@ -213,6 +217,9 @@ public void init(Configuration config, Supplier plugins, ValueContext valueConte // 加载预热插件 loadWeightAdjusters(plugins); + // 加载服务鉴权插件 + loadAuthenticatorList(config, plugins); + initLocation(config, valueContext); } @@ -353,6 +360,24 @@ private void loadWeightAdjusters(Supplier plugins) throws PolarisException { } } + private void loadAuthenticatorList(Configuration config, Supplier plugins) throws PolarisException { + if (!config.getProvider().getAuth().isEnable()) { + return; + } + List authenticators = config.getProvider().getAuth().getChain(); + authenticatorList = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(authenticators)) { + for (String pluginName : authenticators) { + Plugin authenticator = plugins.getPlugin(PluginTypes.AUTHENTICATOR.getBaseType(), pluginName); + if (!(authenticator instanceof Authenticator)) { + LOG.warn("authenticator {} not found", pluginName); + continue; + } + authenticatorList.add((Authenticator) authenticator); + } + } + } + public void initHttpServer(Configuration configuration, Supplier plugins) { // 遍历插件并获取监听器 Map allHandlers = buildHttpHandlers(plugins); @@ -513,6 +538,10 @@ public List getWeightAdjusters() { return weightAdjusters; } + public List getAuthenticatorList() { + return authenticatorList; + } + @Override protected void doDestroy() { if (MapUtils.isNotEmpty(httpServers)) { diff --git a/polaris-plugins/polaris-plugins-auth/auth-block-allow-list/pom.xml b/polaris-plugins/polaris-plugins-auth/auth-block-allow-list/pom.xml new file mode 100644 index 000000000..189ee1606 --- /dev/null +++ b/polaris-plugins/polaris-plugins-auth/auth-block-allow-list/pom.xml @@ -0,0 +1,30 @@ + + + + com.tencent.polaris + polaris-plugins-auth + ${revision} + ../pom.xml + + 4.0.0 + + auth-block-allow-list + Polaris Plugins Auth Block Allow List + Polaris Plugins Auth Block Allow List JAR + + + + com.tencent.polaris + polaris-client + ${project.version} + + + org.slf4j + slf4j-api + ${slf4j.version} + provided + + + \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-auth/auth-block-allow-list/src/main/java/com/tencent/polaris/plugins/auth/blockallowlist/BlockAllowListAuthenticator.java b/polaris-plugins/polaris-plugins-auth/auth-block-allow-list/src/main/java/com/tencent/polaris/plugins/auth/blockallowlist/BlockAllowListAuthenticator.java new file mode 100644 index 000000000..05fea3f2f --- /dev/null +++ b/polaris-plugins/polaris-plugins-auth/auth-block-allow-list/src/main/java/com/tencent/polaris/plugins/auth/blockallowlist/BlockAllowListAuthenticator.java @@ -0,0 +1,232 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.polaris.plugins.auth.blockallowlist; + +import com.tencent.polaris.api.config.plugin.DefaultPlugins; +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.api.plugin.PluginType; +import com.tencent.polaris.api.plugin.auth.AuthInfo; +import com.tencent.polaris.api.plugin.auth.AuthResult; +import com.tencent.polaris.api.plugin.auth.Authenticator; +import com.tencent.polaris.api.plugin.cache.FlowCache; +import com.tencent.polaris.api.plugin.common.InitContext; +import com.tencent.polaris.api.plugin.common.PluginTypes; +import com.tencent.polaris.api.plugin.compose.Extensions; +import com.tencent.polaris.api.pojo.*; +import com.tencent.polaris.api.rpc.RequestBaseEntity; +import com.tencent.polaris.api.utils.ApiTrieUtil; +import com.tencent.polaris.api.utils.CollectionUtils; +import com.tencent.polaris.api.utils.RuleUtils; +import com.tencent.polaris.api.utils.StringUtils; +import com.tencent.polaris.client.flow.BaseFlow; +import com.tencent.polaris.client.flow.DefaultFlowControlParam; +import com.tencent.polaris.client.flow.ResourcesResponse; +import com.tencent.polaris.logging.LoggerFactory; +import com.tencent.polaris.metadata.core.MessageMetadataContainer; +import com.tencent.polaris.metadata.core.MetadataType; +import com.tencent.polaris.metadata.core.manager.MetadataContext; +import com.tencent.polaris.specification.api.v1.security.BlockAllowListProto; +import com.tencent.polaris.specification.api.v1.service.manage.ResponseProto; +import org.slf4j.Logger; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.function.Function; +import java.util.regex.Pattern; + +import static com.tencent.polaris.api.plugin.cache.CacheConstants.API_ID; +import static com.tencent.polaris.api.utils.RuleUtils.matchMethod; +import static com.tencent.polaris.metadata.core.constant.MetadataConstants.LOCAL_NAMESPACE; +import static com.tencent.polaris.metadata.core.constant.MetadataConstants.LOCAL_SERVICE; + +/** + * 黑白名单服务鉴权插件. + * + * @author Haotian Zhang + */ +public class BlockAllowListAuthenticator implements Authenticator { + + private static final Logger LOG = LoggerFactory.getLogger(BlockAllowListAuthenticator.class); + + protected Extensions extensions; + + private Function regexFunction; + + private Function> trieNodeFunction; + + @Override + public AuthResult authenticate(AuthInfo authInfo) { + List blockAllowListRuleList = getBlockAllowListRule(authInfo); + if (CollectionUtils.isNotEmpty(blockAllowListRuleList)) { + if (!checkAllow(authInfo, blockAllowListRuleList)) { + return new AuthResult(AuthResult.Code.AuthResultForbidden); + } + } + return new AuthResult(AuthResult.Code.AuthResultOk); + } + + /** + * 获取黑白名单鉴权规则 + * + * @param authInfo 鉴权信息 + * @return 目标服务黑白名单鉴权规则 + */ + private List getBlockAllowListRule(AuthInfo authInfo) { + // 获取服务鉴权规则 + DefaultFlowControlParam engineFlowControlParam = new DefaultFlowControlParam(); + BaseFlow.buildFlowControlParam(new RequestBaseEntity(), extensions.getConfiguration(), engineFlowControlParam); + Set serviceEventKeys = new HashSet<>(); + ServiceEventKey dstSvcEventKey = new ServiceEventKey(new ServiceKey(authInfo.getNamespace(), authInfo.getService()), + ServiceEventKey.EventType.BLOCK_ALLOW_RULE); + serviceEventKeys.add(dstSvcEventKey); + DefaultServiceEventKeysProvider svcKeysProvider = new DefaultServiceEventKeysProvider(); + svcKeysProvider.setSvcEventKeys(serviceEventKeys); + ResourcesResponse resourcesResponse = BaseFlow + .syncGetResources(extensions, false, svcKeysProvider, engineFlowControlParam); + ServiceRule serviceRule = resourcesResponse.getServiceRule(dstSvcEventKey); + + List blockAllowListRuleList = new ArrayList<>(); + if (serviceRule != null && serviceRule.getRule() != null) { + ResponseProto.DiscoverResponse discoverResponse = (ResponseProto.DiscoverResponse) serviceRule.getRule(); + blockAllowListRuleList = discoverResponse.getBlockAllowListRuleList(); + } + + return blockAllowListRuleList; + } + + /** + * 1、如果全是白名单策略,那么只要有一个匹配,才算通过。 + * 2、如果全是黑名单策略,那么只要有一个匹配,才算不通过。 + * 3、如果又有白名单策略,又有黑名单策略,只要有一个白名单策略匹配,才算通过。 + * + * @param authInfo + * @param blockAllowListRuleList + * @return + */ + protected boolean checkAllow(AuthInfo authInfo, List blockAllowListRuleList) { + boolean containsAllowList = false; + if (CollectionUtils.isNotEmpty(blockAllowListRuleList)) { + for (BlockAllowListProto.BlockAllowListRule balr : blockAllowListRuleList) { + if (balr.getEnable()) { + for (BlockAllowListProto.BlockAllowConfig config : balr.getBlockAllowConfigList()) { + if (config.getBlockAllowPolicy().equals(BlockAllowListProto.BlockAllowConfig.BlockAllowPolicy.ALLOW_LIST)) { + containsAllowList = true; + } + boolean methodMatched = matchMethod(authInfo.getPath(), authInfo.getProtocol(), + authInfo.getMethod(), config.getApi(), regexFunction, trieNodeFunction); + if (methodMatched) { + boolean matched = true; + MetadataContext metadataContext = authInfo.getMetadataContext(); + List argumentsList = config.getArgumentsList(); + if (CollectionUtils.isNotEmpty(argumentsList)) { + for (BlockAllowListProto.BlockAllowConfig.MatchArgument matchArgument : argumentsList) { + String labelValue = null; + if (metadataContext != null) { + labelValue = getLabelValue(matchArgument, metadataContext); + } + matched = RuleUtils.matchStringValue(matchArgument.getValue(), labelValue, regexFunction); + if (!matched) { + LOG.debug("match fail because label value [{}] is null or not match [{}]", labelValue, matchArgument.getValue()); + break; + } + } + } + if (matched) { + return config.getBlockAllowPolicy().equals(BlockAllowListProto.BlockAllowConfig.BlockAllowPolicy.ALLOW_LIST); + } + } + } + } + } + } + if (containsAllowList) { + LOG.debug("check allow fail because no matched allow list."); + } + return !containsAllowList; + } + + private static String getLabelValue(BlockAllowListProto.BlockAllowConfig.MatchArgument matchArgument, + MetadataContext metadataContext) { + MessageMetadataContainer messageMetadataContainer = metadataContext.getMetadataContainer(MetadataType.MESSAGE, true); + if (messageMetadataContainer == null) { + return null; + } + switch (matchArgument.getType()) { + case HEADER: + return messageMetadataContainer.getHeader(matchArgument.getKey()); + case QUERY: + return messageMetadataContainer.getQuery(matchArgument.getKey()); + case CALLER_SERVICE: { + String namespace = metadataContext.getMetadataContainer(MetadataType.APPLICATION, true).getRawMetadataStringValue(LOCAL_NAMESPACE); + if (StringUtils.equals(matchArgument.getKey(), namespace) || StringUtils.equals("*", matchArgument.getKey())) { + return metadataContext.getMetadataContainer(MetadataType.APPLICATION, true).getRawMetadataStringValue(LOCAL_SERVICE); + } else { + return null; + } + } + case CALLER_IP: + return messageMetadataContainer.getCallerIP(); + case CALLER_METADATA: + return metadataContext.getMetadataContainer(MetadataType.APPLICATION, true).getRawMetadataStringValue(matchArgument.getKey()); + case CUSTOM: + default: + return metadataContext.getMetadataContainer(MetadataType.CUSTOM, false).getRawMetadataStringValue(matchArgument.getKey()); + } + } + + @Override + public String getName() { + return DefaultPlugins.BLOCK_ALLOW_LIST_AUTHENTICATOR_TYPE; + } + + @Override + public PluginType getType() { + return PluginTypes.AUTHENTICATOR.getBaseType(); + } + + @Override + public void init(InitContext ctx) throws PolarisException { + + } + + @Override + public void postContextInit(Extensions ctx) throws PolarisException { + this.extensions = ctx; + this.regexFunction = regex -> { + if (null == extensions) { + return Pattern.compile(regex); + } + FlowCache flowCache = extensions.getFlowCache(); + return flowCache.loadOrStoreCompiledRegex(regex); + }; + this.trieNodeFunction = key -> { + if (null == extensions) { + return null; + } + FlowCache flowCache = extensions.getFlowCache(); + return flowCache.loadPluginCacheObject(API_ID, key, path -> ApiTrieUtil.buildSimpleTrieNode((String) path)); + }; + } + + @Override + public void destroy() { + + } +} diff --git a/polaris-plugins/polaris-plugins-auth/auth-block-allow-list/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.auth.Authenticator b/polaris-plugins/polaris-plugins-auth/auth-block-allow-list/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.auth.Authenticator new file mode 100644 index 000000000..5492206d0 --- /dev/null +++ b/polaris-plugins/polaris-plugins-auth/auth-block-allow-list/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.auth.Authenticator @@ -0,0 +1 @@ +com.tencent.polaris.plugins.auth.blockallowlist.BlockAllowListAuthenticator diff --git a/polaris-plugins/polaris-plugins-auth/auth-block-allow-list/src/test/java/com/tencent/polaris/plugins/auth/blockallowlist/BlockAllowListAuthenticatorTest.java b/polaris-plugins/polaris-plugins-auth/auth-block-allow-list/src/test/java/com/tencent/polaris/plugins/auth/blockallowlist/BlockAllowListAuthenticatorTest.java new file mode 100644 index 000000000..93fd46ef0 --- /dev/null +++ b/polaris-plugins/polaris-plugins-auth/auth-block-allow-list/src/test/java/com/tencent/polaris/plugins/auth/blockallowlist/BlockAllowListAuthenticatorTest.java @@ -0,0 +1,213 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.polaris.plugins.auth.blockallowlist; + +import com.google.protobuf.StringValue; +import com.tencent.polaris.api.plugin.auth.AuthInfo; +import com.tencent.polaris.metadata.core.manager.MetadataContext; +import com.tencent.polaris.specification.api.v1.model.ModelProto; +import com.tencent.polaris.specification.api.v1.security.BlockAllowListProto; +import org.junit.Test; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Test for {@link BlockAllowListAuthenticator}. + * + * @author Haotian Zhang + */ +public class BlockAllowListAuthenticatorTest { + + private final String testNamespace = "testNamespace"; + private final String testService = "testService"; + private final String testPath = "/path"; + private final String testProtocol = "HTTP"; + private final String testMethod = "GET"; + private final MetadataContext testMetadataContext = new MetadataContext(); + + /** + * 测试空的黑白名单规则列表,预期返回true + */ + @Test + public void testCheckAllow_WithEmptyBlockAllowListRuleList_ShouldReturnTrue() { + BlockAllowListAuthenticator authenticator = new BlockAllowListAuthenticator(); + AuthInfo authInfo = new AuthInfo(testNamespace, testService, testPath, testProtocol, testMethod, testMetadataContext); + List blockAllowListRuleList = Collections.emptyList(); + + boolean result = authenticator.checkAllow(authInfo, blockAllowListRuleList); + + assertThat(result).isTrue(); + } + + /** + * 测试非空的黑白名单规则列表,且禁用了允许列表,预期返回true + */ + @Test + public void testCheckAllow_WithNonEmptyBlockAllowListRuleListAndDisableAllowList_ShouldReturnTrue() { + BlockAllowListAuthenticator authenticator = new BlockAllowListAuthenticator(); + AuthInfo authInfo = new AuthInfo(testNamespace, testService, testPath, testProtocol, testMethod, testMetadataContext); + List blockAllowListRuleList = Collections.singletonList( + createBlockAllowListRule(false, testPath, BlockAllowListProto.BlockAllowConfig.BlockAllowPolicy.ALLOW_LIST) + ); + + boolean result = authenticator.checkAllow(authInfo, blockAllowListRuleList); + + assertThat(result).isTrue(); + } + + /** + * 测试非空的黑白名单规则列表,且只有允许列表,预期返回true + */ + @Test + public void testCheckAllow_WithNonEmptyBlockAllowListRuleListAndOnlyAllowList_ShouldReturnTrue() { + BlockAllowListAuthenticator authenticator = new BlockAllowListAuthenticator(); + AuthInfo authInfo = new AuthInfo(testNamespace, testService, testPath, testProtocol, testMethod, testMetadataContext); + List blockAllowListRuleList = Collections.singletonList( + createBlockAllowListRule(true, testPath, BlockAllowListProto.BlockAllowConfig.BlockAllowPolicy.ALLOW_LIST) + ); + + boolean result = authenticator.checkAllow(authInfo, blockAllowListRuleList); + + assertThat(result).isTrue(); + } + + /** + * 测试非空的黑白名单规则列表,且只有阻止列表,预期返回false + */ + @Test + public void testCheckAllow_WithNonEmptyBlockAllowListRuleListAndOnlyBlockList_ShouldReturnFalse() { + BlockAllowListAuthenticator authenticator = new BlockAllowListAuthenticator(); + AuthInfo authInfo = new AuthInfo(testNamespace, testService, testPath, testProtocol, testMethod, testMetadataContext); + List blockAllowListRuleList = Collections.singletonList( + createBlockAllowListRule(true, testPath, BlockAllowListProto.BlockAllowConfig.BlockAllowPolicy.BLOCK_LIST) + ); + + boolean result = authenticator.checkAllow(authInfo, blockAllowListRuleList); + + assertThat(result).isFalse(); + } + + /** + * 测试非空的黑白名单规则列表,且只有允许列表,但请求路径不匹配,预期返回false + */ + @Test + public void testCheckAllow_WithNonEmptyBlockAllowListRuleListAndOnlyAllowListButNotMatch_ShouldReturnFalse() { + BlockAllowListAuthenticator authenticator = new BlockAllowListAuthenticator(); + AuthInfo authInfo = new AuthInfo(testNamespace, testService, "/no-test", testProtocol, testMethod, testMetadataContext); + List blockAllowListRuleList = Collections.singletonList( + createBlockAllowListRule(true, testPath, BlockAllowListProto.BlockAllowConfig.BlockAllowPolicy.ALLOW_LIST) + ); + + boolean result = authenticator.checkAllow(authInfo, blockAllowListRuleList); + + assertThat(result).isFalse(); + } + + /** + * 测试非空的黑白名单规则列表,且只有阻止列表,但请求路径不匹配,预期返回true + */ + @Test + public void testCheckAllow_WithNonEmptyBlockAllowListRuleListAndOnlyBlockListButNotMatch_ShouldReturnTrue() { + BlockAllowListAuthenticator authenticator = new BlockAllowListAuthenticator(); + AuthInfo authInfo = new AuthInfo(testNamespace, testService, "/no-test", testProtocol, testMethod, testMetadataContext); + List blockAllowListRuleList = Collections.singletonList( + createBlockAllowListRule(true, testPath, BlockAllowListProto.BlockAllowConfig.BlockAllowPolicy.BLOCK_LIST) + ); + + boolean result = authenticator.checkAllow(authInfo, blockAllowListRuleList); + + assertThat(result).isTrue(); + } + + /** + * 测试非空的黑白名单规则列表,允许列表匹配且阻止列表不匹配,预期返回true + */ + @Test + public void testCheckAllow_WithNonEmptyBlockAllowListRuleListAndAllowListMatchAndBlockListNotMatch_ShouldReturnTrue() { + BlockAllowListAuthenticator authenticator = new BlockAllowListAuthenticator(); + AuthInfo authInfo = new AuthInfo(testNamespace, testService, testPath, testProtocol, testMethod, testMetadataContext); + List blockAllowListRuleList = Arrays.asList( + createBlockAllowListRule(true, testPath, BlockAllowListProto.BlockAllowConfig.BlockAllowPolicy.ALLOW_LIST), + createBlockAllowListRule(true, "/no-test", BlockAllowListProto.BlockAllowConfig.BlockAllowPolicy.BLOCK_LIST) + ); + + boolean result = authenticator.checkAllow(authInfo, blockAllowListRuleList); + + assertThat(result).isTrue(); + } + + /** + * 测试非空的黑白名单规则列表,允许列表不匹配且阻止列表匹配,预期返回false + */ + @Test + public void testCheckAllow_WithNonEmptyBlockAllowListRuleListAndAllowListNotMatchAndBlockListMatch_ShouldReturnFalse() { + BlockAllowListAuthenticator authenticator = new BlockAllowListAuthenticator(); + AuthInfo authInfo = new AuthInfo(testNamespace, testService, "/no-test", testProtocol, testMethod, testMetadataContext); + List blockAllowListRuleList = Arrays.asList( + createBlockAllowListRule(true, testPath, BlockAllowListProto.BlockAllowConfig.BlockAllowPolicy.ALLOW_LIST), + createBlockAllowListRule(true, "/no-test", BlockAllowListProto.BlockAllowConfig.BlockAllowPolicy.BLOCK_LIST) + ); + + boolean result = authenticator.checkAllow(authInfo, blockAllowListRuleList); + + assertThat(result).isFalse(); + } + + /** + * 测试非空的黑白名单规则列表,允许列表和阻止列表都不匹配,预期返回false + */ + @Test + public void testCheckAllow_WithNonEmptyBlockAllowListRuleListAndAllowListNotMatchAndBlockListNotMatch_ShouldReturnFalse() { + BlockAllowListAuthenticator authenticator = new BlockAllowListAuthenticator(); + AuthInfo authInfo = new AuthInfo(testNamespace, testService, "/no-no-test", testProtocol, testMethod, testMetadataContext); + List blockAllowListRuleList = Arrays.asList( + createBlockAllowListRule(true, testPath, BlockAllowListProto.BlockAllowConfig.BlockAllowPolicy.ALLOW_LIST), + createBlockAllowListRule(true, "/no-test", BlockAllowListProto.BlockAllowConfig.BlockAllowPolicy.BLOCK_LIST) + ); + + boolean result = authenticator.checkAllow(authInfo, blockAllowListRuleList); + + assertThat(result).isFalse(); + } + + /** + * 创建一个BlockAllowListProto.BlockAllowListRule实例 + * + * @param enable 是否启用规则 + * @param path 请求路径 + * @param policy 黑白名单策略 + * @return BlockAllowListProto.BlockAllowListRule实例 + */ + private BlockAllowListProto.BlockAllowListRule createBlockAllowListRule(boolean enable, String path, BlockAllowListProto.BlockAllowConfig.BlockAllowPolicy policy) { + return BlockAllowListProto.BlockAllowListRule.newBuilder() + .setEnable(enable) + .addBlockAllowConfig(BlockAllowListProto.BlockAllowConfig.newBuilder() + .setApi(ModelProto.API.newBuilder() + .setPath(ModelProto.MatchString.newBuilder().setValue(StringValue.of(path)).build()) + .setProtocol(testProtocol) + .setMethod(testMethod) + .build()) + .setBlockAllowPolicy(policy) + .build()) + .build(); + } +} diff --git a/polaris-plugins/polaris-plugins-auth/pom.xml b/polaris-plugins/polaris-plugins-auth/pom.xml new file mode 100644 index 000000000..e3ffa3043 --- /dev/null +++ b/polaris-plugins/polaris-plugins-auth/pom.xml @@ -0,0 +1,29 @@ + + + + polaris-plugins + com.tencent.polaris + ${revision} + ../pom.xml + + 4.0.0 + + polaris-plugins-auth + pom + Polaris Plugins Auth + Polaris Plugins Auth POM + + + auth-block-allow-list + + + + + com.tencent.polaris + polaris-plugin-api + ${project.version} + + + \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-connector/connector-common/src/main/java/com/tencent/polaris/plugins/connector/common/DestroyableServerConnector.java b/polaris-plugins/polaris-plugins-connector/connector-common/src/main/java/com/tencent/polaris/plugins/connector/common/DestroyableServerConnector.java index 17efe7ea5..4865058ca 100644 --- a/polaris-plugins/polaris-plugins-connector/connector-common/src/main/java/com/tencent/polaris/plugins/connector/common/DestroyableServerConnector.java +++ b/polaris-plugins/polaris-plugins-connector/connector-common/src/main/java/com/tencent/polaris/plugins/connector/common/DestroyableServerConnector.java @@ -23,9 +23,10 @@ import com.tencent.polaris.api.pojo.Services; import com.tencent.polaris.logging.LoggerFactory; import com.tencent.polaris.plugins.connector.common.constant.ServiceUpdateTaskConstant.Status; +import org.slf4j.Logger; + import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import org.slf4j.Logger; /** * Destroyable server connector. @@ -52,7 +53,7 @@ public abstract class DestroyableServerConnector extends Destroyable implements */ public void retryServiceUpdateTask(ServiceUpdateTask updateTask) { LOG.info("[ServerConnector]retry schedule task for {}, retry delay {}", updateTask, TASK_RETRY_INTERVAL_MS); - updateTask.setStatus(Status.RUNNING, Status.READY); + updateTask.setStatus(Status.RUNNING, Status.READY, true); if (isDestroyed()) { return; } diff --git a/polaris-plugins/polaris-plugins-connector/connector-common/src/main/java/com/tencent/polaris/plugins/connector/common/ServiceUpdateTask.java b/polaris-plugins/polaris-plugins-connector/connector-common/src/main/java/com/tencent/polaris/plugins/connector/common/ServiceUpdateTask.java index 0d6dd63aa..5d0d5f5c6 100644 --- a/polaris-plugins/polaris-plugins-connector/connector-common/src/main/java/com/tencent/polaris/plugins/connector/common/ServiceUpdateTask.java +++ b/polaris-plugins/polaris-plugins-connector/connector-common/src/main/java/com/tencent/polaris/plugins/connector/common/ServiceUpdateTask.java @@ -83,10 +83,14 @@ public boolean setType(Type last, Type current) { return taskType.compareAndSet(last, current); } - public boolean setStatus(Status last, Status current) { + public boolean setStatus(Status last, Status current, boolean isSpread) { return taskStatus.compareAndSet(last, current); } + public boolean setStatus(Status last, Status current) { + return setStatus(last, current, false); + } + public void setLastUpdateTime(long currentTime) { lastUpdateTime.set(currentTime); } diff --git a/polaris-plugins/polaris-plugins-connector/connector-composite/src/main/java/com/tencent/polaris/plugins/connector/composite/CompositeServiceUpdateTask.java b/polaris-plugins/polaris-plugins-connector/connector-composite/src/main/java/com/tencent/polaris/plugins/connector/composite/CompositeServiceUpdateTask.java index 7c90874a8..c67aed03b 100644 --- a/polaris-plugins/polaris-plugins-connector/connector-composite/src/main/java/com/tencent/polaris/plugins/connector/composite/CompositeServiceUpdateTask.java +++ b/polaris-plugins/polaris-plugins-connector/connector-composite/src/main/java/com/tencent/polaris/plugins/connector/composite/CompositeServiceUpdateTask.java @@ -113,7 +113,6 @@ public void execute() { entry.getValue().execute(this); } } - // TODO 全部规则实现完后改成StringUtils.equals(mainConnectorType, SERVER_CONNECTOR_CONSUL) if (ifMainConnectorTypeSet && isServiceUpdateTaskExecuted && (StringUtils.equals(mainConnectorType, SERVER_CONNECTOR_GRPC) || (serviceEventKey.getEventType().equals(EventType.INSTANCE) @@ -123,7 +122,8 @@ public void execute() { || serviceEventKey.getEventType().equals(EventType.LOSSLESS) || serviceEventKey.getEventType().equals(EventType.CIRCUIT_BREAKING) || serviceEventKey.getEventType().equals(EventType.RATE_LIMITING) - || serviceEventKey.getEventType().equals(EventType.LANE_RULE)))) { + || serviceEventKey.getEventType().equals(EventType.LANE_RULE) + || serviceEventKey.getEventType().equals(EventType.BLOCK_ALLOW_RULE)))) { return; } boolean svcDeleted = this.notifyServerEvent( @@ -407,6 +407,15 @@ public boolean notifyServerEvent(ServerEvent serverEvent) { return svcDeleted; } + public boolean setStatus(Status last, Status current, boolean isSpread) { + if (isSpread) { + for (Map.Entry entry : subServiceUpdateTaskMap.entrySet()) { + entry.getValue().setStatus(last, current); + } + } + return taskStatus.compareAndSet(last, current); + } + private boolean canExecute(String connectorType, ServiceUpdateTask serviceUpdateTask) { boolean canConnectorExecute = StringUtils.equalsIgnoreCase(mainConnectorType, connectorType) || serviceEventKey.getEventType().equals(EventType.INSTANCE) diff --git a/polaris-plugins/polaris-plugins-connector/connector-consul/src/main/java/com/tencent/polaris/plugins/connector/consul/ConsulAPIConnector.java b/polaris-plugins/polaris-plugins-connector/connector-consul/src/main/java/com/tencent/polaris/plugins/connector/consul/ConsulAPIConnector.java index 6f713731d..828e3229e 100644 --- a/polaris-plugins/polaris-plugins-connector/connector-consul/src/main/java/com/tencent/polaris/plugins/connector/consul/ConsulAPIConnector.java +++ b/polaris-plugins/polaris-plugins-connector/connector-consul/src/main/java/com/tencent/polaris/plugins/connector/consul/ConsulAPIConnector.java @@ -49,6 +49,7 @@ import com.tencent.polaris.plugins.connector.consul.service.ConsulService; import com.tencent.polaris.plugins.connector.consul.service.InstanceService; import com.tencent.polaris.plugins.connector.consul.service.ServiceService; +import com.tencent.polaris.plugins.connector.consul.service.authority.AuthorityService; import com.tencent.polaris.plugins.connector.consul.service.circuitbreaker.CircuitBreakingService; import com.tencent.polaris.plugins.connector.consul.service.lossless.LosslessService; import com.tencent.polaris.plugins.connector.consul.service.lane.LaneService; @@ -236,6 +237,7 @@ private void initActually(InitContext ctx, ServerConnectorConfig connectorConfig consulServiceMap.put(ServiceEventKey.EventType.CIRCUIT_BREAKING, new CircuitBreakingService(consulClient, consulRawClient, consulContext, "consul-circuit-breaking", mapper)); consulServiceMap.put(ServiceEventKey.EventType.RATE_LIMITING, new RateLimitingService(consulClient, consulRawClient, consulContext, "consul-rate-limiting", mapper)); consulServiceMap.put(ServiceEventKey.EventType.LANE_RULE, new LaneService(consulClient, consulRawClient, consulContext, "consul-lane", mapper)); + consulServiceMap.put(ServiceEventKey.EventType.BLOCK_ALLOW_RULE, new AuthorityService(consulClient, consulRawClient, consulContext, "consul-auth", mapper)); initialized = true; } diff --git a/polaris-plugins/polaris-plugins-connector/connector-consul/src/main/java/com/tencent/polaris/plugins/connector/consul/service/authority/AuthorityService.java b/polaris-plugins/polaris-plugins-connector/connector-consul/src/main/java/com/tencent/polaris/plugins/connector/consul/service/authority/AuthorityService.java new file mode 100644 index 000000000..2adad08d1 --- /dev/null +++ b/polaris-plugins/polaris-plugins-connector/connector-consul/src/main/java/com/tencent/polaris/plugins/connector/consul/service/authority/AuthorityService.java @@ -0,0 +1,321 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.polaris.plugins.connector.consul.service.authority; + +import com.ecwid.consul.v1.ConsulClient; +import com.ecwid.consul.v1.ConsulRawClient; +import com.ecwid.consul.v1.QueryParams; +import com.ecwid.consul.v1.Response; +import com.ecwid.consul.v1.kv.model.GetValue; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.collect.Lists; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.protobuf.StringValue; +import com.google.protobuf.UInt32Value; +import com.tencent.polaris.api.exception.ErrorCode; +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.api.exception.ServerCodes; +import com.tencent.polaris.api.exception.ServerErrorResponseException; +import com.tencent.polaris.api.plugin.server.ServerEvent; +import com.tencent.polaris.api.utils.CollectionUtils; +import com.tencent.polaris.api.utils.StringUtils; +import com.tencent.polaris.logging.LoggerFactory; +import com.tencent.polaris.metadata.core.MessageMetadataContainer; +import com.tencent.polaris.metadata.core.constant.TsfMetadataConstants; +import com.tencent.polaris.plugins.connector.common.ServiceUpdateTask; +import com.tencent.polaris.plugins.connector.consul.ConsulContext; +import com.tencent.polaris.plugins.connector.consul.service.ConsulService; +import com.tencent.polaris.plugins.connector.consul.service.authority.entity.AuthRule; +import com.tencent.polaris.plugins.connector.consul.service.authority.entity.AuthRuleGroup; +import com.tencent.polaris.plugins.connector.consul.service.authority.entity.AuthTag; +import com.tencent.polaris.plugins.connector.consul.service.authority.entity.TsfAuthConstant; +import com.tencent.polaris.plugins.connector.consul.service.common.TagConstant; +import com.tencent.polaris.specification.api.v1.model.ModelProto; +import com.tencent.polaris.specification.api.v1.security.BlockAllowListProto; +import com.tencent.polaris.specification.api.v1.service.manage.ResponseProto; +import com.tencent.polaris.specification.api.v1.service.manage.ServiceProto; +import org.slf4j.Logger; +import org.yaml.snakeyaml.Yaml; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; + +import static com.tencent.polaris.api.config.plugin.DefaultPlugins.SERVER_CONNECTOR_CONSUL; +import static com.tencent.polaris.plugins.connector.consul.service.common.TagConditionUtil.parseMatchStringType; + +/** + * @author Haotian Zhang + */ +public class AuthorityService extends ConsulService { + + private static final Logger LOG = LoggerFactory.getLogger(AuthorityService.class); + + private final Gson gson = new GsonBuilder().disableHtmlEscaping().create(); + + private final Map authorityConsulIndexMap = new ConcurrentHashMap<>(); + + public AuthorityService(ConsulClient consulClient, ConsulRawClient consulRawClient, ConsulContext consulContext, + String threadName, ObjectMapper mapper) { + super(consulClient, consulRawClient, consulContext, threadName, mapper); + } + + @Override + public void sendRequest(ServiceUpdateTask serviceUpdateTask) { + String authorityRuleKey = String.format("authority/%s/%s/data", consulContext.getNamespace(), consulContext.getServiceName()); + AuthorityKey authorityKey = new AuthorityKey(); + authorityKey.setNamespace(consulContext.getNamespace()); + authorityKey.setService(consulContext.getServiceName()); + Long currentIndex = getAuthorityConsulIndex(authorityKey); + QueryParams queryParams = new QueryParams(consulContext.getWaitTime(), currentIndex); + int code = ServerCodes.DATA_NO_CHANGE; + try { + LOG.debug("Begin get authority rules of {} sync", authorityRuleKey); + Response response = consulClient.getKVValue(authorityRuleKey, consulContext.getAclToken(), queryParams); + if (response != null) { + if (LOG.isDebugEnabled()) { + String responseStr = "Response{" + + "value='" + response.getValue() + '\'' + + ", consulIndex=" + response.getConsulIndex() + '\'' + + ", consulKnownLeader=" + response.isConsulKnownLeader() + '\'' + + ", consulLastContact=" + response.getConsulLastContact() + + '}'; + LOG.debug("tsf authority rule, consul kv namespace, response: {}", responseStr); + } + + Long newIndex = response.getConsulIndex(); + // create service. + ServiceProto.Service.Builder newServiceBuilder = ServiceProto.Service.newBuilder(); + newServiceBuilder.setNamespace(StringValue.of(consulContext.getNamespace())); + newServiceBuilder.setName(StringValue.of(consulContext.getServiceName())); + newServiceBuilder.setRevision(StringValue.of(String.valueOf(newIndex))); + // create discover response. + ResponseProto.DiscoverResponse.Builder newDiscoverResponseBuilder = ResponseProto.DiscoverResponse.newBuilder(); + newDiscoverResponseBuilder.setService(newServiceBuilder); + // 重写index + List ruleList = new ArrayList<>(); + if (Objects.nonNull(newIndex)) { + if (!Objects.equals(currentIndex, newIndex)) { + code = ServerCodes.EXECUTE_SUCCESS; + GetValue getValue = response.getValue(); + if (Objects.nonNull(getValue)) { + String decodedValue = getValue.getDecodedValue(); + LOG.info("[TSF Auth] New consul config: {}", decodedValue); + if (!StringUtils.isEmpty(decodedValue)) { + ruleList = parseResponse(decodedValue, consulContext.getNamespace(), consulContext.getServiceName(), newIndex); + } + } else { + LOG.info("empty authority rule: {}", response); + } + } else { + LOG.debug("[TSF Auth] Consul data is not changed"); + } + } else { + LOG.warn("[TSF Auth] Consul data is abnormal. {}", response); + } + if (CollectionUtils.isNotEmpty(ruleList)) { + newDiscoverResponseBuilder.addAllBlockAllowListRule(ruleList); + } + newDiscoverResponseBuilder.setCode(UInt32Value.of(code)); + ServerEvent serverEvent = new ServerEvent(serviceUpdateTask.getServiceEventKey(), newDiscoverResponseBuilder.build(), null, SERVER_CONNECTOR_CONSUL); + boolean svcDeleted = serviceUpdateTask.notifyServerEvent(serverEvent); + if (newIndex != null) { + setAuthorityConsulIndex(authorityKey, currentIndex, newIndex); + } + if (!svcDeleted) { + serviceUpdateTask.addUpdateTaskSet(); + } + } + } catch (Throwable e) { + LOG.error("[TSF Auth] tsf authority rule load error. Will sleep for {} ms. Key path:{}", + consulContext.getConsulErrorSleep(), authorityRuleKey, e); + try { + Thread.sleep(consulContext.getConsulErrorSleep()); + } catch (Exception e1) { + LOG.error("error in sleep, msg: {}", e1.getMessage()); + } + PolarisException error = ServerErrorResponseException.build(ErrorCode.NETWORK_ERROR.getCode(), + "Get authority sync failed."); + ServerEvent serverEvent = new ServerEvent(serviceUpdateTask.getServiceEventKey(), null, error, SERVER_CONNECTOR_CONSUL); + serviceUpdateTask.notifyServerEvent(serverEvent); + } + } + + private List parseResponse(String decodedValue, String namespace, String service, Long index) { + List ruleList = Lists.newArrayList(); + + // yaml -> List + Yaml yaml = new Yaml(); + List authRuleGroupList; + try { + String authJsonString = mapper.writeValueAsString(yaml.load(decodedValue)); + authRuleGroupList = mapper.readValue(authJsonString, new TypeReference>() { + }); + } catch (Exception ex) { + LOG.error("tsf authority rule load error.", ex); + throw new PolarisException(ErrorCode.INVALID_RESPONSE, "tsf authority rule load error.", ex); + } + + // List -> List + if (CollectionUtils.isNotEmpty(authRuleGroupList)) { + for (AuthRuleGroup authRuleGroup : authRuleGroupList) { + for (AuthRule authRule : authRuleGroup.getRules()) { + BlockAllowListProto.BlockAllowListRule.Builder ruleBuilder = BlockAllowListProto.BlockAllowListRule.newBuilder(); + ruleBuilder.setId(authRule.getRuleId()); + ruleBuilder.setNamespace(namespace); + ruleBuilder.setService(service); + ruleBuilder.setEnable(true); + ruleBuilder.setName(authRule.getRuleName()); + BlockAllowListProto.BlockAllowConfig.Builder blockAllowConfigBuilder = BlockAllowListProto.BlockAllowConfig.newBuilder(); + blockAllowConfigBuilder.setBlockAllowPolicy(parseBlockAllowPolicy(authRuleGroup.getType())); + if (CollectionUtils.isNotEmpty(authRule.getTags())) { + List list = new ArrayList<>(); + for (AuthTag authTag : authRule.getTags()) { + // build MatchArgument + BlockAllowListProto.BlockAllowConfig.MatchArgument.Builder matchArgumentBuilder = BlockAllowListProto.BlockAllowConfig.MatchArgument.newBuilder(); + if (StringUtils.equals(authTag.getTagField(), TagConstant.SYSTEM_FIELD.SOURCE_SERVICE_NAME)) { + matchArgumentBuilder.setType(BlockAllowListProto.BlockAllowConfig.MatchArgument.Type.CALLER_SERVICE); + matchArgumentBuilder.setKey("*"); + } else if (StringUtils.equals(authTag.getTagField(), TagConstant.SYSTEM_FIELD.SOURCE_NAMESPACE_SERVICE_NAME)) { + matchArgumentBuilder.setType(BlockAllowListProto.BlockAllowConfig.MatchArgument.Type.CALLER_SERVICE); + matchArgumentBuilder.setKey("*"); + String[] split = authTag.getTagValue().split("/"); + if (split.length == 2) { + matchArgumentBuilder.setKey(split[0]); + authTag.setTagValue(split[1]); + } + } else if (StringUtils.equals(authTag.getTagField(), TagConstant.SYSTEM_FIELD.SOURCE_APPLICATION_ID)) { + matchArgumentBuilder.setType(BlockAllowListProto.BlockAllowConfig.MatchArgument.Type.CALLER_METADATA); + matchArgumentBuilder.setKey(TsfMetadataConstants.TSF_APPLICATION_ID); + } else if (StringUtils.equals(authTag.getTagField(), TagConstant.SYSTEM_FIELD.SOURCE_APPLICATION_VERSION)) { + matchArgumentBuilder.setType(BlockAllowListProto.BlockAllowConfig.MatchArgument.Type.CALLER_METADATA); + matchArgumentBuilder.setKey(TsfMetadataConstants.TSF_PROG_VERSION); + } else if (StringUtils.equals(authTag.getTagField(), TagConstant.SYSTEM_FIELD.SOURCE_GROUP_ID)) { + matchArgumentBuilder.setType(BlockAllowListProto.BlockAllowConfig.MatchArgument.Type.CALLER_METADATA); + matchArgumentBuilder.setKey(TsfMetadataConstants.TSF_GROUP_ID); + } else if (StringUtils.equals(authTag.getTagField(), TagConstant.SYSTEM_FIELD.SOURCE_CONNECTION_IP)) { + matchArgumentBuilder.setType(BlockAllowListProto.BlockAllowConfig.MatchArgument.Type.CALLER_IP); + matchArgumentBuilder.setKey(MessageMetadataContainer.LABEL_KEY_CALLER_IP); + } else if (StringUtils.equals(authTag.getTagField(), TagConstant.SYSTEM_FIELD.DESTINATION_APPLICATION_VERSION)) { + matchArgumentBuilder.setType(BlockAllowListProto.BlockAllowConfig.MatchArgument.Type.CUSTOM); + matchArgumentBuilder.setKey(TsfMetadataConstants.TSF_PROG_VERSION); + } else if (StringUtils.equals(authTag.getTagField(), TagConstant.SYSTEM_FIELD.DESTINATION_GROUP_ID)) { + matchArgumentBuilder.setType(BlockAllowListProto.BlockAllowConfig.MatchArgument.Type.CUSTOM); + matchArgumentBuilder.setKey(TsfMetadataConstants.TSF_GROUP_ID); + } else if (StringUtils.equals(authTag.getTagField(), TagConstant.SYSTEM_FIELD.DESTINATION_INTERFACE)) { + ModelProto.MatchString.Builder matchStringBuilder = ModelProto.MatchString.newBuilder(); + matchStringBuilder.setType(parseMatchStringType(authTag.getTagOperator())); + matchStringBuilder.setValue(StringValue.of(authTag.getTagValue())); + ModelProto.API.Builder apiBuilder = ModelProto.API.newBuilder(); + apiBuilder.setPath(matchStringBuilder); + apiBuilder.setProtocol("*"); + apiBuilder.setMethod("*"); + blockAllowConfigBuilder.setApi(apiBuilder.build()); + continue; + } else if (StringUtils.equals(authTag.getTagField(), TagConstant.SYSTEM_FIELD.REQUEST_HTTP_METHOD)) { + ModelProto.MatchString.Builder matchStringBuilder = ModelProto.MatchString.newBuilder(); + matchStringBuilder.setType(ModelProto.MatchString.MatchStringType.EXACT); + matchStringBuilder.setValue(StringValue.of("*")); + ModelProto.API.Builder apiBuilder = ModelProto.API.newBuilder(); + apiBuilder.setPath(matchStringBuilder); + apiBuilder.setProtocol("*"); + apiBuilder.setMethod(authTag.getTagValue()); + blockAllowConfigBuilder.setApi(apiBuilder.build()); + continue; + } else { + matchArgumentBuilder.setType(BlockAllowListProto.BlockAllowConfig.MatchArgument.Type.CUSTOM); + matchArgumentBuilder.setKey(authTag.getTagField()); + } + ModelProto.MatchString.Builder matchStringBuilder = ModelProto.MatchString.newBuilder(); + matchStringBuilder.setType(parseMatchStringType(authTag.getTagOperator())); + matchStringBuilder.setValue(StringValue.of(authTag.getTagValue())); + matchArgumentBuilder.setValue(matchStringBuilder); + list.add(matchArgumentBuilder.build()); + } + blockAllowConfigBuilder.addAllArguments(list); + } + ruleBuilder.addBlockAllowConfig(blockAllowConfigBuilder); + ruleList.add(ruleBuilder.build()); + } + } + } + return ruleList; + } + + private BlockAllowListProto.BlockAllowConfig.BlockAllowPolicy parseBlockAllowPolicy(String type) { + switch (type) { + case TsfAuthConstant.TYPE.BLACK_LIST: + return BlockAllowListProto.BlockAllowConfig.BlockAllowPolicy.BLOCK_LIST; + case TsfAuthConstant.TYPE.WHITE_LIST: + default: + return BlockAllowListProto.BlockAllowConfig.BlockAllowPolicy.ALLOW_LIST; + } + } + + private Long getAuthorityConsulIndex(AuthorityKey authorityKey) { + Long index = authorityConsulIndexMap.get(authorityKey); + if (index != null) { + return index; + } + setAuthorityConsulIndex(authorityKey, null, -1L); + return -1L; + } + + private void setAuthorityConsulIndex(AuthorityKey authorityKey, Long lastIndex, Long newIndex) { + LOG.debug("AuthorityKey: {}; lastIndex: {}; newIndex: {}", authorityKey, lastIndex, newIndex); + authorityConsulIndexMap.put(authorityKey, newIndex); + } + + static class AuthorityKey { + private String namespace = ""; + private String service = ""; + + public String getNamespace() { + return namespace; + } + + public void setNamespace(String namespace) { + this.namespace = namespace; + } + + public String getService() { + return service; + } + + public void setService(String service) { + this.service = service; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + AuthorityKey that = (AuthorityKey) o; + return Objects.equals(getNamespace(), that.getNamespace()) && Objects.equals(getService(), that.getService()); + } + + @Override + public int hashCode() { + return Objects.hash(getNamespace(), getService()); + } + } +} diff --git a/polaris-plugins/polaris-plugins-connector/connector-consul/src/main/java/com/tencent/polaris/plugins/connector/consul/service/authority/entity/AuthRule.java b/polaris-plugins/polaris-plugins-connector/connector-consul/src/main/java/com/tencent/polaris/plugins/connector/consul/service/authority/entity/AuthRule.java new file mode 100644 index 000000000..951543de9 --- /dev/null +++ b/polaris-plugins/polaris-plugins-connector/connector-consul/src/main/java/com/tencent/polaris/plugins/connector/consul/service/authority/entity/AuthRule.java @@ -0,0 +1,153 @@ +package com.tencent.polaris.plugins.connector.consul.service.authority.entity; + +import java.io.Serializable; +import java.util.List; + +/** + * TSF微服务鉴权规则 + * + * @author hongweizhu + */ +public class AuthRule implements Serializable { + + /** + * serialVersionUID + */ + private static final long serialVersionUID = -4319194985653367847L; + /** + * 规则ID + */ + private String ruleId; + /** + * 规则名称 + */ + private String ruleName; + /** + * 是否启用:0:不启用;1:启用 + */ + private String isEnabled; + /** + * 创建时间 + */ + private String createTime; + /** + * 更新时间 + */ + private String updateTime; + /** + * 微服务于ID + */ + private String microserviceId; + /** + * 命名空间ID + */ + private String namespaceId; + /** + * 标签(Tag)列表 + */ + private List tags; + + /** + * 标签(Tag)计算规则 + */ + private String tagProgram; + + public String getRuleId() { + return ruleId; + } + + public void setRuleId(String ruleId) { + this.ruleId = ruleId; + } + + public String getRuleName() { + return ruleName; + } + + public void setRuleName(String ruleName) { + this.ruleName = ruleName; + } + + public String getIsEnabled() { + return isEnabled; + } + + public void setIsEnabled(String isEnabled) { + this.isEnabled = isEnabled; + } + + public String getCreateTime() { + return createTime; + } + + public void setCreateTime(String createTime) { + this.createTime = createTime; + } + + public String getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(String updateTime) { + this.updateTime = updateTime; + } + + public String getMicroserviceId() { + return microserviceId; + } + + public void setMicroserviceId(String microserviceId) { + this.microserviceId = microserviceId; + } + + public String getNamespaceId() { + return namespaceId; + } + + public void setNamespaceId(String namespaceId) { + this.namespaceId = namespaceId; + } + + public List getTags() { + return tags; + } + + public void setTags(List tags) { + this.tags = tags; + } + + public String getTagProgram() { + return tagProgram; + } + + public void setTagProgram(String tagProgram) { + this.tagProgram = tagProgram; + } + + /** + * 推送Consul前清理rule + */ + public void clearAuthRule() { + this.microserviceId = null; + this.namespaceId = null; + this.createTime = null; + this.updateTime = null; + this.isEnabled = null; + } + + @Override + public String toString() { + final StringBuffer sb = new StringBuffer("AuthRule{"); + sb.append("ruleId='").append(ruleId).append('\''); + sb.append(", ruleName='").append(ruleName).append('\''); + sb.append(", isEnabled='").append(isEnabled).append('\''); + sb.append(", createTime='").append(createTime).append('\''); + sb.append(", updateTime='").append(updateTime).append('\''); + sb.append(", microserviceId='").append(microserviceId).append('\''); + sb.append(", namespaceId='").append(namespaceId).append('\''); + sb.append(", tags=").append(tags); + sb.append(", tagProgram='").append(tagProgram).append('\''); + sb.append('}'); + return sb.toString(); + } +} diff --git a/polaris-plugins/polaris-plugins-connector/connector-consul/src/main/java/com/tencent/polaris/plugins/connector/consul/service/authority/entity/AuthRuleGroup.java b/polaris-plugins/polaris-plugins-connector/connector-consul/src/main/java/com/tencent/polaris/plugins/connector/consul/service/authority/entity/AuthRuleGroup.java new file mode 100644 index 000000000..9fa0f0cf0 --- /dev/null +++ b/polaris-plugins/polaris-plugins-connector/connector-consul/src/main/java/com/tencent/polaris/plugins/connector/consul/service/authority/entity/AuthRuleGroup.java @@ -0,0 +1,59 @@ +package com.tencent.polaris.plugins.connector.consul.service.authority.entity; + +import java.io.Serializable; +import java.util.List; + +public class AuthRuleGroup implements Serializable { + + /** + * serialVersionUID + */ + private static final long serialVersionUID = 1643183648126834802L; + /** + * 规则列表 + */ + private List rules; + /** + * 规则计算规则 + */ + private String ruleProgram; + + /** + * 鉴权类型 + */ + private String type; + + public List getRules() { + return rules; + } + + public void setRules(List rules) { + this.rules = rules; + } + + public String getRuleProgram() { + return ruleProgram; + } + + public void setRuleProgram(String ruleProgram) { + this.ruleProgram = ruleProgram; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + @Override + public String toString() { + final StringBuffer sb = new StringBuffer("AuthRuleGroup{"); + sb.append("rules=").append(rules); + sb.append(", ruleProgram='").append(ruleProgram).append('\''); + sb.append(", type='").append(type).append('\''); + sb.append('}'); + return sb.toString(); + } +} diff --git a/polaris-plugins/polaris-plugins-connector/connector-consul/src/main/java/com/tencent/polaris/plugins/connector/consul/service/authority/entity/AuthTag.java b/polaris-plugins/polaris-plugins-connector/connector-consul/src/main/java/com/tencent/polaris/plugins/connector/consul/service/authority/entity/AuthTag.java new file mode 100644 index 000000000..ba0410919 --- /dev/null +++ b/polaris-plugins/polaris-plugins-connector/connector-consul/src/main/java/com/tencent/polaris/plugins/connector/consul/service/authority/entity/AuthTag.java @@ -0,0 +1,14 @@ +package com.tencent.polaris.plugins.connector.consul.service.authority.entity; + + +import com.tencent.polaris.plugins.connector.consul.service.common.TagCondition; + +/** + * TSF微服务于权限标签实体 + * + * @author hongweizhu + */ +public class AuthTag extends TagCondition { + + private static final long serialVersionUID = 5850710577425618376L; +} diff --git a/polaris-plugins/polaris-plugins-connector/connector-consul/src/main/java/com/tencent/polaris/plugins/connector/consul/service/authority/entity/TsfAuthConstant.java b/polaris-plugins/polaris-plugins-connector/connector-consul/src/main/java/com/tencent/polaris/plugins/connector/consul/service/authority/entity/TsfAuthConstant.java new file mode 100644 index 000000000..dff9251d6 --- /dev/null +++ b/polaris-plugins/polaris-plugins-connector/connector-consul/src/main/java/com/tencent/polaris/plugins/connector/consul/service/authority/entity/TsfAuthConstant.java @@ -0,0 +1,24 @@ +package com.tencent.polaris.plugins.connector.consul.service.authority.entity; + +public class TsfAuthConstant { + + /** + * 鉴权类型(微服务级别) + * + * @author hongweizhu + */ + public static class TYPE { + /** + * 黑名单模式 + */ + public static final String BLACK_LIST = "B"; + /** + * 白名单模式 + */ + public static final String WHITE_LIST = "W"; + /** + * 不启用 + */ + public static final String DISABLED = "D"; + } +} diff --git a/polaris-plugins/polaris-plugins-connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/GrpcUtil.java b/polaris-plugins/polaris-plugins-connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/GrpcUtil.java index 4f793152a..e55320204 100644 --- a/polaris-plugins/polaris-plugins-connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/GrpcUtil.java +++ b/polaris-plugins/polaris-plugins-connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/GrpcUtil.java @@ -239,6 +239,8 @@ public static DiscoverRequestType buildDiscoverRequestType( return DiscoverRequestType.NEARBY_ROUTE_RULE; case LOSSLESS: return DiscoverRequestType.LOSSLESS; + case BLOCK_ALLOW_RULE: + return DiscoverRequestType.BLOCK_ALLOW_RULE; default: return DiscoverRequestType.UNKNOWN; } @@ -265,6 +267,8 @@ public static DiscoverResponseType buildDiscoverResponseType( return DiscoverResponseType.NEARBY_ROUTE_RULE; case LOSSLESS: return DiscoverResponseType.LOSSLESS; + case BLOCK_ALLOW_RULE: + return DiscoverResponseType.BLOCK_ALLOW_RULE; default: return DiscoverResponseType.UNKNOWN; } @@ -290,6 +294,8 @@ public static EventType buildEventType(DiscoverResponseType responseType) { return EventType.NEARBY_ROUTE_RULE; case LOSSLESS: return EventType.LOSSLESS; + case BLOCK_ALLOW_RULE: + return EventType.BLOCK_ALLOW_RULE; default: return EventType.UNKNOWN; } diff --git a/polaris-plugins/polaris-plugins-connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/codec/BlockAllowListRuleCacheHandler.java b/polaris-plugins/polaris-plugins-connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/codec/BlockAllowListRuleCacheHandler.java new file mode 100644 index 000000000..26c3e6ff9 --- /dev/null +++ b/polaris-plugins/polaris-plugins-connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/codec/BlockAllowListRuleCacheHandler.java @@ -0,0 +1,92 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.polaris.plugins.connector.grpc.codec; + +import com.tencent.polaris.api.plugin.cache.FlowCache; +import com.tencent.polaris.api.plugin.registry.AbstractCacheHandler; +import com.tencent.polaris.api.pojo.RegistryCacheValue; +import com.tencent.polaris.api.pojo.ServiceEventKey; +import com.tencent.polaris.api.utils.CompareUtils; +import com.tencent.polaris.client.pojo.ServiceRuleByProto; +import com.tencent.polaris.logging.LoggerFactory; +import com.tencent.polaris.specification.api.v1.security.BlockAllowListProto; +import com.tencent.polaris.specification.api.v1.service.manage.ResponseProto; +import com.tencent.polaris.specification.api.v1.service.manage.ServiceProto; +import org.slf4j.Logger; + +import java.util.ArrayList; +import java.util.List; + +public class BlockAllowListRuleCacheHandler extends AbstractCacheHandler { + + private static final Logger LOG = LoggerFactory.getLogger(BlockAllowListRuleCacheHandler.class); + + @Override + protected String getRevision(ResponseProto.DiscoverResponse discoverResponse) { + ServiceProto.Service service = discoverResponse.getService(); + return service.getRevision().getValue(); + } + + @Override + public ServiceEventKey.EventType getTargetEventType() { + return ServiceEventKey.EventType.BLOCK_ALLOW_RULE; + } + + @Override + public RegistryCacheValue messageToCacheValue(RegistryCacheValue oldValue, Object newValue, boolean isCacheLoaded, FlowCache flowCache) { + ResponseProto.DiscoverResponse discoverResponse = (ResponseProto.DiscoverResponse) newValue; + String revision = discoverResponse.getService().getRevision().getValue(); + + // 排序规则 + ResponseProto.DiscoverResponse.Builder newDiscoverResponseBuilder = ResponseProto.DiscoverResponse.newBuilder() + .mergeFrom(discoverResponse); + List unmodifiableList = discoverResponse.getBlockAllowListRuleList(); + List blockAllowListRuleList = sortNearbyRouteRules(unmodifiableList); + newDiscoverResponseBuilder.clearBlockAllowListRule(); + newDiscoverResponseBuilder.addAllBlockAllowListRule(blockAllowListRuleList); + + return new ServiceRuleByProto(newDiscoverResponseBuilder.build(), revision, isCacheLoaded, getTargetEventType()); + } + + private List sortNearbyRouteRules(List rules) { + List sorted = new ArrayList<>(rules); + sorted.sort((o1, o2) -> { + // 比较优先级,数字越小,规则优先级越大 + int priorityResult = o1.getPriority() - o2.getPriority(); + if (priorityResult != 0) { + return priorityResult; + } + + // 比较目标服务 + String destNamespace1 = o1.getNamespace(); + String destService1 = o1.getService(); + String destNamespace2 = o2.getNamespace(); + String destService2 = o2.getService(); + int serviceKeyResult = CompareUtils.compareService(destNamespace1, destService1, destNamespace2, destService2); + if (serviceKeyResult != 0) { + return serviceKeyResult; + } + + // 比较规则ID + String ruleId1 = o1.getId(); + String ruleId2 = o1.getId(); + return CompareUtils.compareSingleValue(ruleId1, ruleId2); + }); + return sorted; + } +} diff --git a/polaris-plugins/polaris-plugins-connector/connector-polaris-grpc/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.registry.CacheHandler b/polaris-plugins/polaris-plugins-connector/connector-polaris-grpc/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.registry.CacheHandler index c4bef124d..a394ebc0d 100644 --- a/polaris-plugins/polaris-plugins-connector/connector-polaris-grpc/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.registry.CacheHandler +++ b/polaris-plugins/polaris-plugins-connector/connector-polaris-grpc/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.registry.CacheHandler @@ -5,4 +5,5 @@ com.tencent.polaris.plugins.connector.grpc.codec.ServicesCacheHandler com.tencent.polaris.plugins.connector.grpc.codec.FaultDetectCacheHandler com.tencent.polaris.plugins.connector.grpc.codec.LaneRuleCacheHandler com.tencent.polaris.plugins.connector.grpc.codec.NearByRouteRuleCacheHandler -com.tencent.polaris.plugins.connector.grpc.codec.LosslessCacheHandler \ No newline at end of file +com.tencent.polaris.plugins.connector.grpc.codec.LosslessCacheHandler +com.tencent.polaris.plugins.connector.grpc.codec.BlockAllowListRuleCacheHandler diff --git a/polaris-plugins/pom.xml b/polaris-plugins/pom.xml index 2d127bfdd..e67a8a523 100644 --- a/polaris-plugins/pom.xml +++ b/polaris-plugins/pom.xml @@ -30,5 +30,6 @@ polaris-plugins-location polaris-plugins-configfilefilter polaris-plugins-lossless + polaris-plugins-auth diff --git a/polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/flow/QuotaFlow.java b/polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/flow/QuotaFlow.java index 605a3a394..fdce3e2a0 100644 --- a/polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/flow/QuotaFlow.java +++ b/polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/flow/QuotaFlow.java @@ -383,11 +383,8 @@ private List lookupRules(ServiceRule serviceRule, String method, MetadataC labelValue = getLabelValue(matchArgument, stringStringMap); } - if (null == labelValue) { - matched = false; - } else { - matched = RuleUtils.matchStringValue(matchArgument.getValue(), labelValue, function); - } + matched = RuleUtils.matchStringValue(matchArgument.getValue(), labelValue, function); + if (!matched) { break; } diff --git a/pom.xml b/pom.xml index e380e22d8..ed83d299d 100644 --- a/pom.xml +++ b/pom.xml @@ -60,6 +60,7 @@ polaris-distribution polaris-examples polaris-configuration + polaris-auth 4.0.0