From d2e11a3de77ad01291f9dd0377e77db664156736 Mon Sep 17 00:00:00 2001 From: KomachiSion Date: Thu, 20 Jan 2022 14:22:43 +0800 Subject: [PATCH] Add new ProtocolAuthService to replace AuthManager. --- .../auth/AbstractProtocolAuthService.java | 84 +++++++++ .../alibaba/nacos/auth/AuthPluginService.java | 4 +- .../nacos/auth/GrpcProtocolAuthService.java | 75 ++++++++ .../nacos/auth/HttpProtocolAuthService.java | 75 ++++++++ .../nacos/auth/ProtocolAuthService.java | 72 ++++++++ .../alibaba/nacos/auth/api/Permission.java | 9 +- .../com/alibaba/nacos/auth/api/Resource.java | 5 + .../com/alibaba/nacos/auth/util/Loggers.java | 41 +++++ .../auth/GrpcProtocolAuthServiceTest.java | 160 ++++++++++++++++++ .../auth/HttpProtocolAuthServiceTest.java | 150 ++++++++++++++++ .../nacos/roles/NacosRoleServiceImpl.java | 4 +- .../alibaba/nacos/core/auth/AuthFilter.java | 6 +- .../core/auth/RemoteRequestAuthFilter.java | 6 +- 13 files changed, 679 insertions(+), 12 deletions(-) create mode 100644 auth/src/main/java/com/alibaba/nacos/auth/AbstractProtocolAuthService.java create mode 100644 auth/src/main/java/com/alibaba/nacos/auth/GrpcProtocolAuthService.java create mode 100644 auth/src/main/java/com/alibaba/nacos/auth/HttpProtocolAuthService.java create mode 100644 auth/src/main/java/com/alibaba/nacos/auth/ProtocolAuthService.java create mode 100644 auth/src/main/java/com/alibaba/nacos/auth/util/Loggers.java create mode 100644 auth/src/test/java/com/alibaba/nacos/auth/GrpcProtocolAuthServiceTest.java create mode 100644 auth/src/test/java/com/alibaba/nacos/auth/HttpProtocolAuthServiceTest.java diff --git a/auth/src/main/java/com/alibaba/nacos/auth/AbstractProtocolAuthService.java b/auth/src/main/java/com/alibaba/nacos/auth/AbstractProtocolAuthService.java new file mode 100644 index 000000000..0731fe630 --- /dev/null +++ b/auth/src/main/java/com/alibaba/nacos/auth/AbstractProtocolAuthService.java @@ -0,0 +1,84 @@ +/* + * Copyright 1999-2021 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.alibaba.nacos.auth; + +import com.alibaba.nacos.auth.annotation.Secured; +import com.alibaba.nacos.auth.api.IdentityContext; +import com.alibaba.nacos.auth.api.Permission; +import com.alibaba.nacos.auth.api.Resource; +import com.alibaba.nacos.auth.config.AuthConfigs; +import com.alibaba.nacos.auth.constant.Constants; +import com.alibaba.nacos.auth.exception.AccessException; +import com.alibaba.nacos.auth.util.Loggers; + +import java.util.Optional; + +/** + * Abstract protocol auth service. + * + *

Implement #validateIdentity and #validateAuthority method template. + * + * @author xiweng.yy + */ +public abstract class AbstractProtocolAuthService implements ProtocolAuthService { + + protected final AuthConfigs authConfigs; + + protected AbstractProtocolAuthService(AuthConfigs authConfigs) { + this.authConfigs = authConfigs; + } + + @Override + public boolean validateIdentity(IdentityContext identityContext) throws AccessException { + if (!authConfigs.isAuthEnabled()) { + return true; + } + Optional authPluginService = AuthPluginManager.getInstance() + .findAuthServiceSpiImpl(authConfigs.getNacosAuthSystemType()); + if (authPluginService.isPresent()) { + return authPluginService.get().validateIdentity(identityContext); + } + Loggers.AUTH.warn("Can't find auth plugin for type {}, please add plugin to classpath or set {} as false", + authConfigs.getNacosAuthSystemType(), Constants.Auth.NACOS_CORE_AUTH_ENABLED); + return true; + } + + @Override + public boolean validateAuthority(IdentityContext identityContext, Permission permission) { + if (!authConfigs.isAuthEnabled()) { + return true; + } + Optional authPluginService = AuthPluginManager.getInstance() + .findAuthServiceSpiImpl(authConfigs.getNacosAuthSystemType()); + if (authPluginService.isPresent()) { + return authPluginService.get().validateAuthority(identityContext, permission); + } + Loggers.AUTH.warn("Can't find auth plugin for type {}, please add plugin to classpath or set {} as false", + authConfigs.getNacosAuthSystemType(), Constants.Auth.NACOS_CORE_AUTH_ENABLED); + return true; + } + + /** + * Get resource from secured annotation specified resource. + * + * @param secured secured annotation + * @return resource + */ + protected Resource parseSpecifiedResource(Secured secured) { + return new Resource(null, null, secured.resource(), secured.signType(), null); + } +} diff --git a/auth/src/main/java/com/alibaba/nacos/auth/AuthPluginService.java b/auth/src/main/java/com/alibaba/nacos/auth/AuthPluginService.java index 328d9d8db..dde7cae8a 100644 --- a/auth/src/main/java/com/alibaba/nacos/auth/AuthPluginService.java +++ b/auth/src/main/java/com/alibaba/nacos/auth/AuthPluginService.java @@ -40,10 +40,10 @@ public interface AuthPluginService { * To validate whether the identity context from request is legal or illegal. * * @param identityContext where we can find the user information - * @return IdentityContext user auth result + * @return {@code true} if legal, otherwise {@code false} * @throws AccessException if authentication is failed */ - IdentityContext validateIdentity(IdentityContext identityContext) throws AccessException; + boolean validateIdentity(IdentityContext identityContext) throws AccessException; /** * Validate the identity whether has the resource authority. diff --git a/auth/src/main/java/com/alibaba/nacos/auth/GrpcProtocolAuthService.java b/auth/src/main/java/com/alibaba/nacos/auth/GrpcProtocolAuthService.java new file mode 100644 index 000000000..9c7ea5601 --- /dev/null +++ b/auth/src/main/java/com/alibaba/nacos/auth/GrpcProtocolAuthService.java @@ -0,0 +1,75 @@ +/* + * Copyright 1999-2021 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.alibaba.nacos.auth; + +import com.alibaba.nacos.api.remote.request.Request; +import com.alibaba.nacos.auth.annotation.Secured; +import com.alibaba.nacos.auth.api.IdentityContext; +import com.alibaba.nacos.auth.api.Resource; +import com.alibaba.nacos.auth.config.AuthConfigs; +import com.alibaba.nacos.auth.constant.SignType; +import com.alibaba.nacos.auth.context.GrpcIdentityContextBuilder; +import com.alibaba.nacos.auth.parser.grpc.AbstractGrpcResourceParser; +import com.alibaba.nacos.auth.parser.grpc.ConfigGrpcResourceParser; +import com.alibaba.nacos.auth.parser.grpc.NamingGrpcResourceParser; +import com.alibaba.nacos.auth.util.Loggers; +import com.alibaba.nacos.common.utils.StringUtils; + +import java.util.HashMap; +import java.util.Map; + +/** + * Auth Service for Http protocol. + * + * @author xiweng.yy + */ +public class GrpcProtocolAuthService extends AbstractProtocolAuthService { + + private final Map resourceParserMap; + + private final GrpcIdentityContextBuilder identityContextBuilder; + + protected GrpcProtocolAuthService(AuthConfigs authConfigs) { + super(authConfigs); + resourceParserMap = new HashMap<>(2); + identityContextBuilder = new GrpcIdentityContextBuilder(authConfigs); + } + + @Override + public void initialize() { + resourceParserMap.put(SignType.NAMING, new NamingGrpcResourceParser()); + resourceParserMap.put(SignType.CONFIG, new ConfigGrpcResourceParser()); + } + + @Override + public Resource parseResource(Request request, Secured secured) { + if (StringUtils.isNotBlank(secured.resource())) { + return parseSpecifiedResource(secured); + } + String type = secured.signType(); + if (!resourceParserMap.containsKey(type)) { + Loggers.AUTH.warn("Can't find Grpc request resourceParser for type {}", type); + return Resource.EMPTY_RESOURCE; + } + return resourceParserMap.get(type).parse(request, type); + } + + @Override + public IdentityContext parseIdentity(Request request) { + return identityContextBuilder.build(request); + } +} diff --git a/auth/src/main/java/com/alibaba/nacos/auth/HttpProtocolAuthService.java b/auth/src/main/java/com/alibaba/nacos/auth/HttpProtocolAuthService.java new file mode 100644 index 000000000..3088c38e1 --- /dev/null +++ b/auth/src/main/java/com/alibaba/nacos/auth/HttpProtocolAuthService.java @@ -0,0 +1,75 @@ +/* + * Copyright 1999-2021 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.alibaba.nacos.auth; + +import com.alibaba.nacos.auth.annotation.Secured; +import com.alibaba.nacos.auth.api.IdentityContext; +import com.alibaba.nacos.auth.api.Resource; +import com.alibaba.nacos.auth.config.AuthConfigs; +import com.alibaba.nacos.auth.constant.SignType; +import com.alibaba.nacos.auth.context.HttpIdentityContextBuilder; +import com.alibaba.nacos.auth.parser.http.AbstractHttpResourceParser; +import com.alibaba.nacos.auth.parser.http.ConfigHttpResourceParser; +import com.alibaba.nacos.auth.parser.http.NamingHttpResourceParser; +import com.alibaba.nacos.auth.util.Loggers; +import com.alibaba.nacos.common.utils.StringUtils; + +import javax.servlet.http.HttpServletRequest; +import java.util.HashMap; +import java.util.Map; + +/** + * Auth Service for Http protocol. + * + * @author xiweng.yy + */ +public class HttpProtocolAuthService extends AbstractProtocolAuthService { + + private final Map resourceParserMap; + + private final HttpIdentityContextBuilder identityContextBuilder; + + protected HttpProtocolAuthService(AuthConfigs authConfigs) { + super(authConfigs); + resourceParserMap = new HashMap<>(2); + identityContextBuilder = new HttpIdentityContextBuilder(authConfigs); + } + + @Override + public void initialize() { + resourceParserMap.put(SignType.NAMING, new NamingHttpResourceParser()); + resourceParserMap.put(SignType.CONFIG, new ConfigHttpResourceParser()); + } + + @Override + public Resource parseResource(HttpServletRequest request, Secured secured) { + if (StringUtils.isNotBlank(secured.resource())) { + return parseSpecifiedResource(secured); + } + String type = secured.signType(); + if (!resourceParserMap.containsKey(type)) { + Loggers.AUTH.warn("Can't find Http request resourceParser for type {}", type); + return Resource.EMPTY_RESOURCE; + } + return resourceParserMap.get(type).parse(request, type); + } + + @Override + public IdentityContext parseIdentity(HttpServletRequest request) { + return identityContextBuilder.build(request); + } +} diff --git a/auth/src/main/java/com/alibaba/nacos/auth/ProtocolAuthService.java b/auth/src/main/java/com/alibaba/nacos/auth/ProtocolAuthService.java new file mode 100644 index 000000000..95565fd21 --- /dev/null +++ b/auth/src/main/java/com/alibaba/nacos/auth/ProtocolAuthService.java @@ -0,0 +1,72 @@ +/* + * Copyright 1999-2021 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.alibaba.nacos.auth; + +import com.alibaba.nacos.auth.annotation.Secured; +import com.alibaba.nacos.auth.api.IdentityContext; +import com.alibaba.nacos.auth.api.Permission; +import com.alibaba.nacos.auth.api.Resource; +import com.alibaba.nacos.auth.exception.AccessException; + +/** + * Protocol auth service. + * + * @author xiweng.yy + */ +public interface ProtocolAuthService { + + /** + * Init protocol auth service. + */ + void initialize(); + + /** + * Parse resource from protocol request and secured annotation. + * + * @param request protocol request + * @param secured api secured annotation + * @return resource + */ + Resource parseResource(R request, Secured secured); + + /** + * Parse identity context from protocol request. + * + * @param request protocol request + * @return identity context + */ + IdentityContext parseIdentity(R request); + + /** + * Validate identity whether is legal. + * + * @param identityContext identity context + * @return {@code true} if legal, otherwise {@code false} + * @throws AccessException exception during validating + */ + boolean validateIdentity(IdentityContext identityContext) throws AccessException; + + /** + * Validate identity whether had permission for the resource and action. + * + * @param identityContext identity context + * @param permission permssion include resource and action + * @return {@code true} if legal, otherwise {@code false} + * @throws AccessException exception during validating + */ + boolean validateAuthority(IdentityContext identityContext, Permission permission) throws AccessException; +} diff --git a/auth/src/main/java/com/alibaba/nacos/auth/api/Permission.java b/auth/src/main/java/com/alibaba/nacos/auth/api/Permission.java index e0c6a77c8..c62537f2d 100644 --- a/auth/src/main/java/com/alibaba/nacos/auth/api/Permission.java +++ b/auth/src/main/java/com/alibaba/nacos/auth/api/Permission.java @@ -23,6 +23,7 @@ import java.io.Serializable; * * @author nkorange * @author mai.jh + * @author xiweng.yy * @since 1.2.0 */ public class Permission implements Serializable { @@ -32,7 +33,7 @@ public class Permission implements Serializable { /** * An unique key of resource. */ - private String resource; + private Resource resource; /** * Action on resource, refer to class ActionTypes. @@ -43,16 +44,16 @@ public class Permission implements Serializable { } - public Permission(String resource, String action) { + public Permission(Resource resource, String action) { this.resource = resource; this.action = action; } - public String getResource() { + public Resource getResource() { return resource; } - public void setResource(String resource) { + public void setResource(Resource resource) { this.resource = resource; } diff --git a/auth/src/main/java/com/alibaba/nacos/auth/api/Resource.java b/auth/src/main/java/com/alibaba/nacos/auth/api/Resource.java index f38de4d74..cbf43dddf 100644 --- a/auth/src/main/java/com/alibaba/nacos/auth/api/Resource.java +++ b/auth/src/main/java/com/alibaba/nacos/auth/api/Resource.java @@ -16,6 +16,8 @@ package com.alibaba.nacos.auth.api; +import com.alibaba.nacos.common.utils.StringUtils; + import java.io.Serializable; import java.util.Properties; @@ -30,6 +32,9 @@ public class Resource implements Serializable { private static final long serialVersionUID = 925971662931204553L; + public static final Resource EMPTY_RESOURCE = new Resource(StringUtils.EMPTY, StringUtils.EMPTY, StringUtils.EMPTY, + StringUtils.EMPTY, null); + private final String namespaceId; private final String group; diff --git a/auth/src/main/java/com/alibaba/nacos/auth/util/Loggers.java b/auth/src/main/java/com/alibaba/nacos/auth/util/Loggers.java new file mode 100644 index 000000000..bb6cbe651 --- /dev/null +++ b/auth/src/main/java/com/alibaba/nacos/auth/util/Loggers.java @@ -0,0 +1,41 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.alibaba.nacos.auth.util; + +import ch.qos.logback.classic.Level; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Loggers for core. + * + * @author nkorange + * @since 1.2.0 + */ +public class Loggers { + + private static final String AUTH_LOG_NAME = "auth"; + + public static final Logger AUTH = LoggerFactory.getLogger("com.alibaba.nacos.auth"); + + public static void setLogLevel(String logName, String level) { + + if (AUTH_LOG_NAME.equals(logName)) { + ((ch.qos.logback.classic.Logger) AUTH).setLevel(Level.valueOf(level)); + } + } +} diff --git a/auth/src/test/java/com/alibaba/nacos/auth/GrpcProtocolAuthServiceTest.java b/auth/src/test/java/com/alibaba/nacos/auth/GrpcProtocolAuthServiceTest.java new file mode 100644 index 000000000..93753715b --- /dev/null +++ b/auth/src/test/java/com/alibaba/nacos/auth/GrpcProtocolAuthServiceTest.java @@ -0,0 +1,160 @@ +/* + * Copyright 1999-2021 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.alibaba.nacos.auth; + +import com.alibaba.nacos.api.config.remote.request.ConfigPublishRequest; +import com.alibaba.nacos.api.naming.remote.request.AbstractNamingRequest; +import com.alibaba.nacos.auth.annotation.Secured; +import com.alibaba.nacos.auth.api.IdentityContext; +import com.alibaba.nacos.auth.api.Permission; +import com.alibaba.nacos.auth.api.Resource; +import com.alibaba.nacos.auth.config.AuthConfigs; +import com.alibaba.nacos.auth.constant.SignType; +import com.alibaba.nacos.auth.exception.AccessException; +import com.alibaba.nacos.auth.mock.MockAuthPluginService; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; + +import java.lang.reflect.Method; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +@RunWith(MockitoJUnitRunner.class) +public class GrpcProtocolAuthServiceTest { + + @Mock + private AuthConfigs authConfigs; + + private ConfigPublishRequest configRequest; + + private AbstractNamingRequest namingRequest; + + private GrpcProtocolAuthService protocolAuthService; + + @Before + public void setUp() throws Exception { + protocolAuthService = new GrpcProtocolAuthService(authConfigs); + protocolAuthService.initialize(); + mockConfigRequest(); + mockNamingRequest(); + Mockito.when(authConfigs.isAuthEnabled()).thenReturn(true); + } + + private void mockConfigRequest() { + configRequest = new ConfigPublishRequest(); + configRequest.setTenant("testCNs"); + configRequest.setGroup("testCG"); + configRequest.setDataId("testD"); + } + + private void mockNamingRequest() { + namingRequest = new AbstractNamingRequest() { + }; + namingRequest.setNamespace("testNNs"); + namingRequest.setGroupName("testNG"); + namingRequest.setServiceName("testS"); + } + + @Test + @Secured(resource = "testResource") + public void testParseResourceWithSpecifiedResource() throws NoSuchMethodException { + Secured secured = getMethodSecure("testParseResourceWithSpecifiedResource"); + Resource actual = protocolAuthService.parseResource(namingRequest, secured); + assertEquals("testResource", actual.getName()); + assertEquals(SignType.NAMING, actual.getType()); + assertNull(actual.getNamespaceId()); + assertNull(actual.getGroup()); + assertNull(actual.getProperties()); + } + + @Test + @Secured(signType = "non-exist") + public void testParseResourceWithNonExistType() throws NoSuchMethodException { + Secured secured = getMethodSecure("testParseResourceWithNonExistType"); + Resource actual = protocolAuthService.parseResource(namingRequest, secured); + assertEquals(Resource.EMPTY_RESOURCE, actual); + } + + @Test + @Secured() + public void testParseResourceWithNamingType() throws NoSuchMethodException { + Secured secured = getMethodSecure("testParseResourceWithNamingType"); + Resource actual = protocolAuthService.parseResource(namingRequest, secured); + assertEquals(SignType.NAMING, actual.getType()); + assertEquals("testS", actual.getName()); + assertEquals("testNNs", actual.getNamespaceId()); + assertEquals("testNG", actual.getGroup()); + assertNotNull(actual.getProperties()); + } + + @Test + @Secured(signType = SignType.CONFIG) + public void testParseResourceWithConfigType() throws NoSuchMethodException { + Secured secured = getMethodSecure("testParseResourceWithConfigType"); + Resource actual = protocolAuthService.parseResource(configRequest, secured); + assertEquals(SignType.CONFIG, actual.getType()); + assertEquals("testD", actual.getName()); + assertEquals("testCNs", actual.getNamespaceId()); + assertEquals("testCG", actual.getGroup()); + assertNotNull(actual.getProperties()); + } + + @Test + public void testParseIdentity() { + IdentityContext actual = protocolAuthService.parseIdentity(namingRequest); + assertNotNull(actual); + } + + @Test + public void testValidateIdentityWithoutPlugin() throws AccessException { + IdentityContext identityContext = new IdentityContext(); + assertTrue(protocolAuthService.validateIdentity(identityContext)); + } + + @Test + public void testValidateIdentityWithPlugin() throws AccessException { + Mockito.when(authConfigs.getNacosAuthSystemType()).thenReturn(MockAuthPluginService.TEST_PLUGIN); + IdentityContext identityContext = new IdentityContext(); + assertFalse(protocolAuthService.validateIdentity(identityContext)); + } + + @Test + public void testValidateAuthorityWithoutPlugin() { + assertTrue(protocolAuthService + .validateAuthority(new IdentityContext(), new Permission(Resource.EMPTY_RESOURCE, ""))); + } + + @Test + public void testValidateAuthorityWithPlugin() { + Mockito.when(authConfigs.getNacosAuthSystemType()).thenReturn(MockAuthPluginService.TEST_PLUGIN); + assertFalse(protocolAuthService + .validateAuthority(new IdentityContext(), new Permission(Resource.EMPTY_RESOURCE, ""))); + } + + private Secured getMethodSecure(String methodName) throws NoSuchMethodException { + Method method = GrpcProtocolAuthServiceTest.class.getMethod(methodName); + return method.getAnnotation(Secured.class); + } +} diff --git a/auth/src/test/java/com/alibaba/nacos/auth/HttpProtocolAuthServiceTest.java b/auth/src/test/java/com/alibaba/nacos/auth/HttpProtocolAuthServiceTest.java new file mode 100644 index 000000000..0715f485b --- /dev/null +++ b/auth/src/test/java/com/alibaba/nacos/auth/HttpProtocolAuthServiceTest.java @@ -0,0 +1,150 @@ +/* + * Copyright 1999-2021 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.alibaba.nacos.auth; + +import com.alibaba.nacos.api.common.Constants; +import com.alibaba.nacos.api.naming.CommonParams; +import com.alibaba.nacos.auth.annotation.Secured; +import com.alibaba.nacos.auth.api.IdentityContext; +import com.alibaba.nacos.auth.api.Permission; +import com.alibaba.nacos.auth.api.Resource; +import com.alibaba.nacos.auth.config.AuthConfigs; +import com.alibaba.nacos.auth.constant.SignType; +import com.alibaba.nacos.auth.exception.AccessException; +import com.alibaba.nacos.auth.mock.MockAuthPluginService; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; + +import javax.servlet.http.HttpServletRequest; +import java.lang.reflect.Method; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.eq; + +@RunWith(MockitoJUnitRunner.class) +public class HttpProtocolAuthServiceTest { + + @Mock + private AuthConfigs authConfigs; + + @Mock + private HttpServletRequest request; + + private HttpProtocolAuthService httpProtocolAuthService; + + @Before + public void setUp() throws Exception { + httpProtocolAuthService = new HttpProtocolAuthService(authConfigs); + httpProtocolAuthService.initialize(); + Mockito.when(request.getParameter(eq(CommonParams.NAMESPACE_ID))).thenReturn("testNNs"); + Mockito.when(request.getParameter(eq(CommonParams.GROUP_NAME))).thenReturn("testNG"); + Mockito.when(request.getParameter(eq(CommonParams.SERVICE_NAME))).thenReturn("testS"); + Mockito.when(request.getParameter(eq("tenant"))).thenReturn("testCNs"); + Mockito.when(request.getParameter(eq(Constants.GROUP))).thenReturn("testCG"); + Mockito.when(request.getParameter(eq(Constants.DATAID))).thenReturn("testD"); + Mockito.when(authConfigs.isAuthEnabled()).thenReturn(true); + } + + @Test + @Secured(resource = "testResource") + public void testParseResourceWithSpecifiedResource() throws NoSuchMethodException { + Secured secured = getMethodSecure("testParseResourceWithSpecifiedResource"); + Resource actual = httpProtocolAuthService.parseResource(request, secured); + assertEquals("testResource", actual.getName()); + assertEquals(SignType.NAMING, actual.getType()); + assertNull(actual.getNamespaceId()); + assertNull(actual.getGroup()); + assertNull(actual.getProperties()); + } + + @Test + @Secured(signType = "non-exist") + public void testParseResourceWithNonExistType() throws NoSuchMethodException { + Secured secured = getMethodSecure("testParseResourceWithNonExistType"); + Resource actual = httpProtocolAuthService.parseResource(request, secured); + assertEquals(Resource.EMPTY_RESOURCE, actual); + } + + @Test + @Secured() + public void testParseResourceWithNamingType() throws NoSuchMethodException { + Secured secured = getMethodSecure("testParseResourceWithNamingType"); + Resource actual = httpProtocolAuthService.parseResource(request, secured); + assertEquals(SignType.NAMING, actual.getType()); + assertEquals("testS", actual.getName()); + assertEquals("testNNs", actual.getNamespaceId()); + assertEquals("testNG", actual.getGroup()); + assertNotNull(actual.getProperties()); + } + + @Test + @Secured(signType = SignType.CONFIG) + public void testParseResourceWithConfigType() throws NoSuchMethodException { + Secured secured = getMethodSecure("testParseResourceWithConfigType"); + Resource actual = httpProtocolAuthService.parseResource(request, secured); + assertEquals(SignType.CONFIG, actual.getType()); + assertEquals("testD", actual.getName()); + assertEquals("testCNs", actual.getNamespaceId()); + assertEquals("testCG", actual.getGroup()); + assertNotNull(actual.getProperties()); + } + + @Test + public void testParseIdentity() { + IdentityContext actual = httpProtocolAuthService.parseIdentity(request); + assertNotNull(actual); + } + + @Test + public void testValidateIdentityWithoutPlugin() throws AccessException { + IdentityContext identityContext = new IdentityContext(); + assertTrue(httpProtocolAuthService.validateIdentity(identityContext)); + } + + @Test + public void testValidateIdentityWithPlugin() throws AccessException { + Mockito.when(authConfigs.getNacosAuthSystemType()).thenReturn(MockAuthPluginService.TEST_PLUGIN); + IdentityContext identityContext = new IdentityContext(); + assertFalse(httpProtocolAuthService.validateIdentity(identityContext)); + } + + @Test + public void testValidateAuthorityWithoutPlugin() { + assertTrue(httpProtocolAuthService + .validateAuthority(new IdentityContext(), new Permission(Resource.EMPTY_RESOURCE, ""))); + } + + @Test + public void testValidateAuthorityWithPlugin() { + Mockito.when(authConfigs.getNacosAuthSystemType()).thenReturn(MockAuthPluginService.TEST_PLUGIN); + assertFalse(httpProtocolAuthService + .validateAuthority(new IdentityContext(), new Permission(Resource.EMPTY_RESOURCE, ""))); + } + + private Secured getMethodSecure(String methodName) throws NoSuchMethodException { + Method method = HttpProtocolAuthServiceTest.class.getMethod(methodName); + return method.getAnnotation(Secured.class); + } +} diff --git a/console/src/main/java/com/alibaba/nacos/console/security/nacos/roles/NacosRoleServiceImpl.java b/console/src/main/java/com/alibaba/nacos/console/security/nacos/roles/NacosRoleServiceImpl.java index 7a56abecc..1eddb5776 100644 --- a/console/src/main/java/com/alibaba/nacos/console/security/nacos/roles/NacosRoleServiceImpl.java +++ b/console/src/main/java/com/alibaba/nacos/console/security/nacos/roles/NacosRoleServiceImpl.java @@ -132,7 +132,7 @@ public class NacosRoleServiceImpl { } // Old global admin can pass resource 'console/': - if (permission.getResource().startsWith(AuthConstants.CONSOLE_RESOURCE_NAME_PREFIX)) { + if (permission.getResource().getName().startsWith(AuthConstants.CONSOLE_RESOURCE_NAME_PREFIX)) { return false; } @@ -146,7 +146,7 @@ public class NacosRoleServiceImpl { String permissionResource = permissionInfo.getResource().replaceAll("\\*", ".*"); String permissionAction = permissionInfo.getAction(); if (permissionAction.contains(permission.getAction()) && Pattern - .matches(permissionResource, permission.getResource())) { + .matches(permissionResource, permission.getResource().getName())) { return true; } } diff --git a/core/src/main/java/com/alibaba/nacos/core/auth/AuthFilter.java b/core/src/main/java/com/alibaba/nacos/core/auth/AuthFilter.java index 255dd7d40..d2316dd81 100644 --- a/core/src/main/java/com/alibaba/nacos/core/auth/AuthFilter.java +++ b/core/src/main/java/com/alibaba/nacos/core/auth/AuthFilter.java @@ -18,6 +18,7 @@ package com.alibaba.nacos.core.auth; import com.alibaba.nacos.auth.AuthManager; import com.alibaba.nacos.auth.annotation.Secured; +import com.alibaba.nacos.auth.api.Resource; import com.alibaba.nacos.auth.config.AuthConfigs; import com.alibaba.nacos.auth.exception.AccessException; import com.alibaba.nacos.auth.api.Permission; @@ -125,8 +126,9 @@ public class AuthFilter implements Filter { // deny if we don't find any resource: throw new AccessException("resource name invalid!"); } - - authManager.auth(new Permission(resource, action), authManager.login(req)); + + Resource resourceObj = new Resource(null, null, resource, secured.signType(), null); + authManager.auth(new Permission(resourceObj, action), authManager.login(req)); } chain.doFilter(request, response); diff --git a/core/src/main/java/com/alibaba/nacos/core/auth/RemoteRequestAuthFilter.java b/core/src/main/java/com/alibaba/nacos/core/auth/RemoteRequestAuthFilter.java index e2be6f3af..ecb0d4eb1 100644 --- a/core/src/main/java/com/alibaba/nacos/core/auth/RemoteRequestAuthFilter.java +++ b/core/src/main/java/com/alibaba/nacos/core/auth/RemoteRequestAuthFilter.java @@ -22,6 +22,7 @@ import com.alibaba.nacos.api.remote.request.RequestMeta; import com.alibaba.nacos.api.remote.response.Response; import com.alibaba.nacos.auth.AuthManager; import com.alibaba.nacos.auth.annotation.Secured; +import com.alibaba.nacos.auth.api.Resource; import com.alibaba.nacos.auth.config.AuthConfigs; import com.alibaba.nacos.auth.exception.AccessException; import com.alibaba.nacos.auth.api.Permission; @@ -75,8 +76,9 @@ public class RemoteRequestAuthFilter extends AbstractRequestFilter { // deny if we don't find any resource: throw new AccessException("resource name invalid!"); } - - authManager.auth(new Permission(resource, action), authManager.loginRemote(request)); + + Resource resourceObj = new Resource(null, null, resource, secured.signType(), null); + authManager.auth(new Permission(resourceObj, action), authManager.loginRemote(request)); } } catch (AccessException e) {