diff --git a/auth/src/main/java/com/alibaba/nacos/auth/AbstractProtocolAuthService.java b/auth/src/main/java/com/alibaba/nacos/auth/AbstractProtocolAuthService.java index bc7d13e18..014096a27 100644 --- a/auth/src/main/java/com/alibaba/nacos/auth/AbstractProtocolAuthService.java +++ b/auth/src/main/java/com/alibaba/nacos/auth/AbstractProtocolAuthService.java @@ -22,6 +22,7 @@ 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.constant.SignType; import com.alibaba.nacos.auth.exception.AccessException; import com.alibaba.nacos.auth.util.Loggers; @@ -79,6 +80,6 @@ public abstract class AbstractProtocolAuthService implements ProtocolAuthServ * @return resource */ protected Resource parseSpecifiedResource(Secured secured) { - return new Resource(null, null, secured.resource(), secured.signType(), null); + return new Resource(null, null, secured.resource(), SignType.SPECIFIED, null); } } diff --git a/auth/src/main/java/com/alibaba/nacos/auth/constant/SignType.java b/auth/src/main/java/com/alibaba/nacos/auth/constant/SignType.java index bd9302e7d..2f9dcf057 100644 --- a/auth/src/main/java/com/alibaba/nacos/auth/constant/SignType.java +++ b/auth/src/main/java/com/alibaba/nacos/auth/constant/SignType.java @@ -28,4 +28,6 @@ public class SignType { public static final String CONFIG = "config"; public static final String CONSOLE = "console"; + + public static final String SPECIFIED = "specified"; } diff --git a/auth/src/test/java/com/alibaba/nacos/auth/GrpcProtocolAuthServiceTest.java b/auth/src/test/java/com/alibaba/nacos/auth/GrpcProtocolAuthServiceTest.java index 93753715b..43f2e3298 100644 --- a/auth/src/test/java/com/alibaba/nacos/auth/GrpcProtocolAuthServiceTest.java +++ b/auth/src/test/java/com/alibaba/nacos/auth/GrpcProtocolAuthServiceTest.java @@ -83,7 +83,7 @@ public class GrpcProtocolAuthServiceTest { Secured secured = getMethodSecure("testParseResourceWithSpecifiedResource"); Resource actual = protocolAuthService.parseResource(namingRequest, secured); assertEquals("testResource", actual.getName()); - assertEquals(SignType.NAMING, actual.getType()); + assertEquals(SignType.SPECIFIED, actual.getType()); assertNull(actual.getNamespaceId()); assertNull(actual.getGroup()); assertNull(actual.getProperties()); @@ -141,13 +141,13 @@ public class GrpcProtocolAuthServiceTest { } @Test - public void testValidateAuthorityWithoutPlugin() { + public void testValidateAuthorityWithoutPlugin() throws AccessException { assertTrue(protocolAuthService .validateAuthority(new IdentityContext(), new Permission(Resource.EMPTY_RESOURCE, ""))); } @Test - public void testValidateAuthorityWithPlugin() { + public void testValidateAuthorityWithPlugin() throws AccessException { Mockito.when(authConfigs.getNacosAuthSystemType()).thenReturn(MockAuthPluginService.TEST_PLUGIN); assertFalse(protocolAuthService .validateAuthority(new IdentityContext(), new Permission(Resource.EMPTY_RESOURCE, ""))); diff --git a/auth/src/test/java/com/alibaba/nacos/auth/HttpProtocolAuthServiceTest.java b/auth/src/test/java/com/alibaba/nacos/auth/HttpProtocolAuthServiceTest.java index 0715f485b..275b16513 100644 --- a/auth/src/test/java/com/alibaba/nacos/auth/HttpProtocolAuthServiceTest.java +++ b/auth/src/test/java/com/alibaba/nacos/auth/HttpProtocolAuthServiceTest.java @@ -73,7 +73,7 @@ public class HttpProtocolAuthServiceTest { Secured secured = getMethodSecure("testParseResourceWithSpecifiedResource"); Resource actual = httpProtocolAuthService.parseResource(request, secured); assertEquals("testResource", actual.getName()); - assertEquals(SignType.NAMING, actual.getType()); + assertEquals(SignType.SPECIFIED, actual.getType()); assertNull(actual.getNamespaceId()); assertNull(actual.getGroup()); assertNull(actual.getProperties()); @@ -131,13 +131,13 @@ public class HttpProtocolAuthServiceTest { } @Test - public void testValidateAuthorityWithoutPlugin() { + public void testValidateAuthorityWithoutPlugin() throws AccessException { assertTrue(httpProtocolAuthService .validateAuthority(new IdentityContext(), new Permission(Resource.EMPTY_RESOURCE, ""))); } @Test - public void testValidateAuthorityWithPlugin() { + public void testValidateAuthorityWithPlugin() throws AccessException { Mockito.when(authConfigs.getNacosAuthSystemType()).thenReturn(MockAuthPluginService.TEST_PLUGIN); assertFalse(httpProtocolAuthService .validateAuthority(new IdentityContext(), new Permission(Resource.EMPTY_RESOURCE, ""))); diff --git a/console/src/main/java/com/alibaba/nacos/console/security/nacos/NacosAuthManager.java b/console/src/main/java/com/alibaba/nacos/console/security/nacos/NacosAuthManager.java index 344569f37..9e5b0f299 100644 --- a/console/src/main/java/com/alibaba/nacos/console/security/nacos/NacosAuthManager.java +++ b/console/src/main/java/com/alibaba/nacos/console/security/nacos/NacosAuthManager.java @@ -19,7 +19,6 @@ package com.alibaba.nacos.console.security.nacos; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.remote.request.Request; import com.alibaba.nacos.auth.AuthManager; -import com.alibaba.nacos.auth.AuthPluginService; import com.alibaba.nacos.auth.api.IdentityContext; import com.alibaba.nacos.auth.api.Permission; import com.alibaba.nacos.auth.exception.AccessException; @@ -41,8 +40,6 @@ import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; -import java.util.Collection; -import java.util.LinkedList; import java.util.List; /** @@ -52,21 +49,7 @@ import java.util.List; * @since 1.2.0 */ @Component -@SuppressWarnings("PMD.ServiceOrDaoClassShouldEndWithImplRule") -public class NacosAuthManager implements AuthManager, AuthPluginService { - - private static final String AUTH_PLUGIN_TYPE = "nacos"; - - private static final String USER_IDENTITY_PARAM_KEY = "user"; - - private static final List IDENTITY_NAMES = new LinkedList() { - { - add(AuthConstants.AUTHORIZATION_HEADER); - add(Constants.ACCESS_TOKEN); - add(AuthConstants.PARAM_USERNAME); - add(AuthConstants.PARAM_PASSWORD); - } - }; +public class NacosAuthManager implements AuthManager { @Autowired private JwtTokenManager tokenManager; @@ -87,6 +70,12 @@ public class NacosAuthManager implements AuthManager, AuthPluginService { return user; } + User login(IdentityContext identityContext) throws AccessException { + String token = resolveToken(identityContext); + validate0(token); + return getNacosUser(token); + } + @Override public User loginRemote(Object request) throws AccessException { Request req = (Request) request; @@ -97,33 +86,13 @@ public class NacosAuthManager implements AuthManager, AuthPluginService { @Override public void auth(Permission permission, User user) throws AccessException { - auth0(permission, user); - } - - @Override - public Collection identityNames() { - return IDENTITY_NAMES; - } - - @Override - public boolean validateIdentity(IdentityContext identityContext) throws AccessException { - String token = resolveToken(identityContext); - validate0(token); - NacosUser user = getNacosUser(token); - identityContext.setParameter(USER_IDENTITY_PARAM_KEY, user); - return true; - } - - @Override - public Boolean validateAuthority(IdentityContext identityContext, Permission permission) throws AccessException { - NacosUser user = (NacosUser) identityContext.getParameter(USER_IDENTITY_PARAM_KEY); - auth0(permission, user); - return true; - } - - @Override - public String getAuthServiceName() { - return AUTH_PLUGIN_TYPE; + if (Loggers.AUTH.isDebugEnabled()) { + Loggers.AUTH.debug("auth permission: {}, user: {}", permission, user); + } + + if (!roleService.hasPermission(user.getUserName(), permission)) { + throw new AccessException("authorization failed!"); + } } /** @@ -228,14 +197,4 @@ public class NacosAuthManager implements AuthManager, AuthPluginService { } return user; } - - private void auth0(Permission permission, User user) throws AccessException { - if (Loggers.AUTH.isDebugEnabled()) { - Loggers.AUTH.debug("auth permission: {}, user: {}", permission, user); - } - - if (!roleService.hasPermission(user.getUserName(), permission)) { - throw new AccessException("authorization failed!"); - } - } } diff --git a/console/src/main/java/com/alibaba/nacos/console/security/nacos/NacosAuthPluginService.java b/console/src/main/java/com/alibaba/nacos/console/security/nacos/NacosAuthPluginService.java new file mode 100644 index 000000000..4046aaa58 --- /dev/null +++ b/console/src/main/java/com/alibaba/nacos/console/security/nacos/NacosAuthPluginService.java @@ -0,0 +1,86 @@ +/* + * 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.console.security.nacos; + +import com.alibaba.nacos.api.common.Constants; +import com.alibaba.nacos.auth.AuthPluginService; +import com.alibaba.nacos.auth.api.IdentityContext; +import com.alibaba.nacos.auth.api.Permission; +import com.alibaba.nacos.auth.exception.AccessException; +import com.alibaba.nacos.auth.model.User; +import com.alibaba.nacos.console.security.nacos.constant.AuthConstants; +import com.alibaba.nacos.console.security.nacos.users.NacosUser; +import com.alibaba.nacos.sys.utils.ApplicationUtils; + +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; + +/** + * Nacos default auth plugin service implementation. + * + * @author xiweng.yy + */ +@SuppressWarnings("PMD.ServiceOrDaoClassShouldEndWithImplRule") +public class NacosAuthPluginService implements AuthPluginService { + + private static final String AUTH_PLUGIN_TYPE = "nacos"; + + private static final String USER_IDENTITY_PARAM_KEY = "user"; + + private static final List IDENTITY_NAMES = new LinkedList() { + { + add(AuthConstants.AUTHORIZATION_HEADER); + add(Constants.ACCESS_TOKEN); + add(AuthConstants.PARAM_USERNAME); + add(AuthConstants.PARAM_PASSWORD); + } + }; + + private NacosAuthManager nacosAuthManager; + + @Override + public Collection identityNames() { + return IDENTITY_NAMES; + } + + @Override + public boolean validateIdentity(IdentityContext identityContext) throws AccessException { + checkNacosAuthManager(); + User user = nacosAuthManager.login(identityContext); + identityContext.setParameter(USER_IDENTITY_PARAM_KEY, user); + return true; + } + + @Override + public Boolean validateAuthority(IdentityContext identityContext, Permission permission) throws AccessException { + NacosUser user = (NacosUser) identityContext.getParameter(USER_IDENTITY_PARAM_KEY); + nacosAuthManager.auth(permission, user); + return true; + } + + @Override + public String getAuthServiceName() { + return AUTH_PLUGIN_TYPE; + } + + private void checkNacosAuthManager() { + if (null == nacosAuthManager) { + nacosAuthManager = ApplicationUtils.getBean(NacosAuthManager.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 1eddb5776..88310a349 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 @@ -16,8 +16,11 @@ package com.alibaba.nacos.console.security.nacos.roles; -import com.alibaba.nacos.auth.config.AuthConfigs; 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.constant.SignType; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.auth.PermissionInfo; import com.alibaba.nacos.config.server.auth.PermissionPersistService; @@ -115,7 +118,7 @@ public class NacosRoleServiceImpl { */ public boolean hasPermission(String username, Permission permission) { //update password - if (AuthConstants.UPDATE_PASSWORD_ENTRY_POINT.equals(permission.getResource())) { + if (AuthConstants.UPDATE_PASSWORD_ENTRY_POINT.equals(permission.getResource().getName())) { return true; } @@ -146,7 +149,7 @@ public class NacosRoleServiceImpl { String permissionResource = permissionInfo.getResource().replaceAll("\\*", ".*"); String permissionAction = permissionInfo.getAction(); if (permissionAction.contains(permission.getAction()) && Pattern - .matches(permissionResource, permission.getResource().getName())) { + .matches(permissionResource, joinResource(permission.getResource()))) { return true; } } @@ -251,4 +254,29 @@ public class NacosRoleServiceImpl { public List findRolesLikeRoleName(String role) { return rolePersistService.findRolesLikeRoleName(role); } + + private String joinResource(Resource resource) { + if (SignType.SPECIFIED.equals(resource.getType())) { + return resource.getName(); + } + StringBuilder result = new StringBuilder(); + String namespaceId = resource.getNamespaceId(); + if (StringUtils.isNotBlank(namespaceId)) { + result.append(namespaceId); + } + String group = resource.getGroup(); + if (StringUtils.isBlank(group)) { + result.append(Constants.Resource.SPLITTER).append('*'); + } else { + result.append(Constants.Resource.SPLITTER).append(group); + } + String resourceName = resource.getName(); + if (StringUtils.isBlank(resourceName)) { + result.append(Constants.Resource.SPLITTER).append(resource.getType().toLowerCase()).append("/*"); + } else { + result.append(Constants.Resource.SPLITTER).append(resource.getType().toLowerCase()).append('/') + .append(resourceName); + } + return result.toString(); + } } diff --git a/console/src/main/resources/META-INF/services/com.alibaba.nacos.auth.AuthPluginService b/console/src/main/resources/META-INF/services/com.alibaba.nacos.auth.AuthPluginService new file mode 100644 index 000000000..820e6addb --- /dev/null +++ b/console/src/main/resources/META-INF/services/com.alibaba.nacos.auth.AuthPluginService @@ -0,0 +1,17 @@ +# +# 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. +# + +com.alibaba.nacos.console.security.nacos.NacosAuthPluginService diff --git a/core/src/test/java/com/alibaba/nacos/core/auth/AuthConfigTest.java b/core/src/test/java/com/alibaba/nacos/core/auth/AuthConfigTest.java index c2fb2f5d2..bb72f9307 100644 --- a/core/src/test/java/com/alibaba/nacos/core/auth/AuthConfigTest.java +++ b/core/src/test/java/com/alibaba/nacos/core/auth/AuthConfigTest.java @@ -19,6 +19,9 @@ package com.alibaba.nacos.core.auth; import org.junit.Assert; import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; import org.springframework.boot.web.servlet.FilterRegistrationBean; /** @@ -27,12 +30,16 @@ import org.springframework.boot.web.servlet.FilterRegistrationBean; * @author chenglu * @date 2021-07-06 13:36 */ +@RunWith(MockitoJUnitRunner.class) public class AuthConfigTest { + @Mock + private AuthFilter authFilter; + @Test public void testAuthFilterRegistration() { AuthConfig config = new AuthConfig(); - FilterRegistrationBean filter = config.authFilterRegistration(); + FilterRegistrationBean filter = config.authFilterRegistration(authFilter); Assert.assertTrue(filter.getFilter() instanceof AuthFilter); Assert.assertTrue(filter.getUrlPatterns().contains("/*"));