Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor update password api auth check and add unit test. #12757

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import com.alibaba.nacos.plugin.auth.spi.server.AuthPluginService;

import java.util.Optional;
import java.util.Properties;

/**
* Abstract protocol auth service.
Expand Down Expand Up @@ -84,7 +85,11 @@ public boolean validateAuthority(IdentityContext identityContext, Permission per
* @return resource
*/
protected Resource parseSpecifiedResource(Secured secured) {
return new Resource(null, null, secured.resource(), SignType.SPECIFIED, null);
Properties properties = new Properties();
for (String each : secured.tags()) {
properties.put(each, each);
}
return new Resource(null, null, secured.resource(), SignType.SPECIFIED, properties);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import com.alibaba.nacos.auth.annotation.Secured;
import com.alibaba.nacos.auth.config.AuthConfigs;
import com.alibaba.nacos.auth.mock.MockAuthPluginService;
import com.alibaba.nacos.auth.mock.MockResourceParser;
import com.alibaba.nacos.plugin.auth.api.IdentityContext;
import com.alibaba.nacos.plugin.auth.api.Permission;
import com.alibaba.nacos.plugin.auth.api.Resource;
Expand Down Expand Up @@ -85,7 +86,8 @@ void testParseResourceWithSpecifiedResource() throws NoSuchMethodException {
assertEquals(SignType.SPECIFIED, actual.getType());
assertNull(actual.getNamespaceId());
assertNull(actual.getGroup());
assertNull(actual.getProperties());
assertNotNull(actual.getProperties());
assertTrue(actual.getProperties().isEmpty());
}

@Test
Expand All @@ -96,6 +98,14 @@ void testParseResourceWithNonExistType() throws NoSuchMethodException {
assertEquals(Resource.EMPTY_RESOURCE, actual);
}

@Test
@Secured(signType = "non-exist", parser = MockResourceParser.class)
void testParseResourceWithNonExistTypeException() throws NoSuchMethodException {
Secured secured = getMethodSecure("testParseResourceWithNonExistTypeException");
Resource actual = protocolAuthService.parseResource(namingRequest, secured);
assertEquals(Resource.EMPTY_RESOURCE, actual);
}

@Test
@Secured()
void testParseResourceWithNamingType() throws NoSuchMethodException {
Expand Down Expand Up @@ -152,6 +162,22 @@ void testValidateAuthorityWithPlugin() throws AccessException {
new Permission(Resource.EMPTY_RESOURCE, "")));
}

@Test
@Secured(signType = SignType.CONFIG)
void testEnabledAuthWithPlugin() throws NoSuchMethodException {
Mockito.when(authConfigs.getNacosAuthSystemType()).thenReturn(MockAuthPluginService.TEST_PLUGIN);
Secured secured = getMethodSecure("testEnabledAuthWithPlugin");
assertTrue(protocolAuthService.enableAuth(secured));
}

@Test
@Secured(signType = SignType.CONFIG)
void testEnabledAuthWithoutPlugin() throws NoSuchMethodException {
Mockito.when(authConfigs.getNacosAuthSystemType()).thenReturn("non-exist-plugin");
Secured secured = getMethodSecure("testEnabledAuthWithoutPlugin");
assertFalse(protocolAuthService.enableAuth(secured));
}

private Secured getMethodSecure(String methodName) throws NoSuchMethodException {
Method method = GrpcProtocolAuthServiceTest.class.getDeclaredMethod(methodName);
return method.getAnnotation(Secured.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import com.alibaba.nacos.auth.annotation.Secured;
import com.alibaba.nacos.auth.config.AuthConfigs;
import com.alibaba.nacos.auth.mock.MockAuthPluginService;
import com.alibaba.nacos.auth.mock.MockResourceParser;
import com.alibaba.nacos.plugin.auth.api.IdentityContext;
import com.alibaba.nacos.plugin.auth.api.Permission;
import com.alibaba.nacos.plugin.auth.api.Resource;
Expand Down Expand Up @@ -56,12 +57,12 @@ class HttpProtocolAuthServiceTest {
@Mock
private HttpServletRequest request;

private HttpProtocolAuthService httpProtocolAuthService;
private HttpProtocolAuthService protocolAuthService;

@BeforeEach
void setUp() throws Exception {
httpProtocolAuthService = new HttpProtocolAuthService(authConfigs);
httpProtocolAuthService.initialize();
protocolAuthService = new HttpProtocolAuthService(authConfigs);
protocolAuthService.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");
Expand All @@ -71,30 +72,40 @@ void setUp() throws Exception {
}

@Test
@Secured(resource = "testResource")
@Secured(resource = "testResource", tags = {"testTag"})
void testParseResourceWithSpecifiedResource() throws NoSuchMethodException {
Secured secured = getMethodSecure("testParseResourceWithSpecifiedResource");
Resource actual = httpProtocolAuthService.parseResource(request, secured);
Resource actual = protocolAuthService.parseResource(request, secured);
assertEquals("testResource", actual.getName());
assertEquals(SignType.SPECIFIED, actual.getType());
assertNull(actual.getNamespaceId());
assertNull(actual.getGroup());
assertNull(actual.getProperties());
assertNotNull(actual.getProperties());
assertEquals(1, actual.getProperties().size());
assertEquals("testTag", actual.getProperties().get("testTag"));
}

@Test
@Secured(signType = "non-exist")
void testParseResourceWithNonExistType() throws NoSuchMethodException {
Secured secured = getMethodSecure("testParseResourceWithNonExistType");
Resource actual = httpProtocolAuthService.parseResource(request, secured);
Resource actual = protocolAuthService.parseResource(request, secured);
assertEquals(Resource.EMPTY_RESOURCE, actual);
}

@Test
@Secured(signType = "non-exist", parser = MockResourceParser.class)
void testParseResourceWithNonExistTypeException() throws NoSuchMethodException {
Secured secured = getMethodSecure("testParseResourceWithNonExistTypeException");
Resource actual = protocolAuthService.parseResource(request, secured);
assertEquals(Resource.EMPTY_RESOURCE, actual);
}

@Test
@Secured()
void testParseResourceWithNamingType() throws NoSuchMethodException {
Secured secured = getMethodSecure("testParseResourceWithNamingType");
Resource actual = httpProtocolAuthService.parseResource(request, secured);
Resource actual = protocolAuthService.parseResource(request, secured);
assertEquals(SignType.NAMING, actual.getType());
assertEquals("testS", actual.getName());
assertEquals("testNNs", actual.getNamespaceId());
Expand All @@ -106,7 +117,7 @@ void testParseResourceWithNamingType() throws NoSuchMethodException {
@Secured(signType = SignType.CONFIG)
void testParseResourceWithConfigType() throws NoSuchMethodException {
Secured secured = getMethodSecure("testParseResourceWithConfigType");
Resource actual = httpProtocolAuthService.parseResource(request, secured);
Resource actual = protocolAuthService.parseResource(request, secured);
assertEquals(SignType.CONFIG, actual.getType());
assertEquals("testD", actual.getName());
assertEquals("testNNs", actual.getNamespaceId());
Expand All @@ -116,36 +127,52 @@ void testParseResourceWithConfigType() throws NoSuchMethodException {

@Test
void testParseIdentity() {
IdentityContext actual = httpProtocolAuthService.parseIdentity(request);
IdentityContext actual = protocolAuthService.parseIdentity(request);
assertNotNull(actual);
}

@Test
void testValidateIdentityWithoutPlugin() throws AccessException {
IdentityContext identityContext = new IdentityContext();
assertTrue(httpProtocolAuthService.validateIdentity(identityContext, Resource.EMPTY_RESOURCE));
assertTrue(protocolAuthService.validateIdentity(identityContext, Resource.EMPTY_RESOURCE));
}

@Test
void testValidateIdentityWithPlugin() throws AccessException {
Mockito.when(authConfigs.getNacosAuthSystemType()).thenReturn(MockAuthPluginService.TEST_PLUGIN);
IdentityContext identityContext = new IdentityContext();
assertFalse(httpProtocolAuthService.validateIdentity(identityContext, Resource.EMPTY_RESOURCE));
assertFalse(protocolAuthService.validateIdentity(identityContext, Resource.EMPTY_RESOURCE));
}

@Test
void testValidateAuthorityWithoutPlugin() throws AccessException {
assertTrue(httpProtocolAuthService.validateAuthority(new IdentityContext(),
assertTrue(protocolAuthService.validateAuthority(new IdentityContext(),
new Permission(Resource.EMPTY_RESOURCE, "")));
}

@Test
void testValidateAuthorityWithPlugin() throws AccessException {
Mockito.when(authConfigs.getNacosAuthSystemType()).thenReturn(MockAuthPluginService.TEST_PLUGIN);
assertFalse(httpProtocolAuthService.validateAuthority(new IdentityContext(),
assertFalse(protocolAuthService.validateAuthority(new IdentityContext(),
new Permission(Resource.EMPTY_RESOURCE, "")));
}

@Test
@Secured(signType = SignType.CONFIG)
void testEnabledAuthWithPlugin() throws NoSuchMethodException {
Mockito.when(authConfigs.getNacosAuthSystemType()).thenReturn(MockAuthPluginService.TEST_PLUGIN);
Secured secured = getMethodSecure("testEnabledAuthWithPlugin");
assertTrue(protocolAuthService.enableAuth(secured));
}

@Test
@Secured(signType = SignType.CONFIG)
void testEnabledAuthWithoutPlugin() throws NoSuchMethodException {
Mockito.when(authConfigs.getNacosAuthSystemType()).thenReturn("non-exist-plugin");
Secured secured = getMethodSecure("testEnabledAuthWithoutPlugin");
assertFalse(protocolAuthService.enableAuth(secured));
}

private Secured getMethodSecure(String methodName) throws NoSuchMethodException {
Method method = HttpProtocolAuthServiceTest.class.getDeclaredMethod(methodName);
return method.getAnnotation(Secured.class);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* 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.mock;

import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException;
import com.alibaba.nacos.auth.annotation.Secured;
import com.alibaba.nacos.auth.parser.ResourceParser;
import com.alibaba.nacos.plugin.auth.api.Resource;

public class MockResourceParser implements ResourceParser<Object> {

@Override
public Resource parse(Object request, Secured secured) {
throw new NacosRuntimeException(500);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import com.alibaba.nacos.api.common.Constants;
import com.alibaba.nacos.api.config.remote.request.ConfigBatchListenRequest;
import com.alibaba.nacos.api.config.remote.request.ConfigChangeNotifyRequest;
import com.alibaba.nacos.api.config.remote.request.ConfigPublishRequest;
import com.alibaba.nacos.api.remote.request.Request;
import com.alibaba.nacos.auth.annotation.Secured;
Expand Down Expand Up @@ -98,6 +99,23 @@ void testParseWithConfigBatchListenRequest() throws NoSuchMethodException {
assertEquals(StringUtils.EMPTY, actual.getGroup());
assertEquals(StringUtils.EMPTY, actual.getName());
assertEquals(Constants.Config.CONFIG_MODULE, actual.getType());
request.getConfigListenContexts().clear();
actual = resourceParser.parse(request, secured);
assertEquals(StringUtils.EMPTY, actual.getNamespaceId());
assertEquals(StringUtils.EMPTY, actual.getGroup());
assertEquals(StringUtils.EMPTY, actual.getName());
assertEquals(Constants.Config.CONFIG_MODULE, actual.getType());
}

@Test
@Secured(signType = Constants.Config.CONFIG_MODULE)
void testParseWithReflectionRequest() throws NoSuchMethodException {
Secured secured = getMethodSecure();
Request request = ConfigChangeNotifyRequest.build("rTestD", "rTestG", "rTestNs");
Resource actual = resourceParser.parse(request, secured);
assertEquals("rTestNs", actual.getNamespaceId());
assertEquals("rTestG", actual.getGroup());
assertEquals("rTestD", actual.getName());
}

private Request mockConfigRequest(String tenant, String group, String dataId) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,6 @@ private String resolveToken(IdentityContext identityContext) {
public Boolean validateAuthority(IdentityContext identityContext, Permission permission) throws AccessException {
NacosUser user = (NacosUser) identityContext.getParameter(AuthConstants.NACOS_USER_KEY);
authenticationManager.authorize(permission, user);

return true;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,8 @@ public Object deleteUser(@RequestParam String username) {
* @since 1.2.0
*/
@PutMapping
@Secured(resource = AuthConstants.UPDATE_PASSWORD_ENTRY_POINT, action = ActionTypes.WRITE)
@Secured(resource = AuthConstants.UPDATE_PASSWORD_ENTRY_POINT, action = ActionTypes.WRITE, tags = {
AuthConstants.UPDATE_PASSWORD_ENTRY_POINT})
public Object updateUser(@RequestParam String username, @RequestParam String newPassword,
HttpServletResponse response, HttpServletRequest request) throws IOException {
// admin or same user
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;
Expand Down Expand Up @@ -120,8 +121,7 @@ private void reload() {
* @return true if granted, false otherwise
*/
public boolean hasPermission(NacosUser nacosUser, Permission permission) {
//update password
if (AuthConstants.UPDATE_PASSWORD_ENTRY_POINT.equals(permission.getResource().getName())) {
if (isUpdatePasswordPermission(permission)) {
return true;
}

Expand Down Expand Up @@ -161,6 +161,14 @@ public boolean hasPermission(NacosUser nacosUser, Permission permission) {
return false;
}

/**
* If API is update user password, don't do permission check, because there is permission check in API logic.
*/
private boolean isUpdatePasswordPermission(Permission permission) {
Properties properties = permission.getResource().getProperties();
return null != properties && properties.contains(AuthConstants.UPDATE_PASSWORD_ENTRY_POINT);
}

public List<RoleInfo> getRoles(String username) {
List<RoleInfo> roleInfoList = roleInfoMap.get(username);
if (!authConfigs.isCachingEnabled() || roleInfoList == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;
import java.util.Properties;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
Expand Down Expand Up @@ -113,10 +114,14 @@ void hasPermission() {

Permission permission2 = new Permission();
permission2.setAction("rw");
Resource resource = new Resource("public", "group", AuthConstants.UPDATE_PASSWORD_ENTRY_POINT, "rw", null);
Resource resource = new Resource("public", "group", AuthConstants.UPDATE_PASSWORD_ENTRY_POINT, "rw",
new Properties());
permission2.setResource(resource);
boolean res2 = nacosRoleService.hasPermission(nacosUser, permission2);
assertTrue(res2);
assertFalse(res2);
resource.getProperties().put(AuthConstants.UPDATE_PASSWORD_ENTRY_POINT, AuthConstants.UPDATE_PASSWORD_ENTRY_POINT);
boolean res3 = nacosRoleService.hasPermission(nacosUser, permission2);
assertTrue(res3);
}

@Test
Expand All @@ -127,7 +132,8 @@ void getRoles() {

@Test
void getRolesFromDatabase() {
Page<RoleInfo> roleInfoPage = nacosRoleService.getRolesFromDatabase("nacos", "ROLE_ADMIN", 1, Integer.MAX_VALUE);
Page<RoleInfo> roleInfoPage = nacosRoleService.getRolesFromDatabase("nacos", "ROLE_ADMIN", 1,
Integer.MAX_VALUE);
assertEquals(0, roleInfoPage.getTotalCount());
}

Expand All @@ -141,8 +147,8 @@ void getPermissions() {

@Test
void getPermissionsByRoleFromDatabase() {
Page<PermissionInfo> permissionsByRoleFromDatabase = nacosRoleService.getPermissionsByRoleFromDatabase("role-admin", 1,
Integer.MAX_VALUE);
Page<PermissionInfo> permissionsByRoleFromDatabase = nacosRoleService.getPermissionsByRoleFromDatabase(
"role-admin", 1, Integer.MAX_VALUE);
assertNull(permissionsByRoleFromDatabase);
}

Expand All @@ -169,7 +175,8 @@ void deleteRole() {

@Test
void getPermissionsFromDatabase() {
Page<PermissionInfo> permissionsFromDatabase = nacosRoleService.getPermissionsFromDatabase("role-admin", 1, Integer.MAX_VALUE);
Page<PermissionInfo> permissionsFromDatabase = nacosRoleService.getPermissionsFromDatabase("role-admin", 1,
Integer.MAX_VALUE);
assertEquals(0, permissionsFromDatabase.getTotalCount());
}

Expand Down
Loading