【Fix issue #13600】 auth error when non-admin users clone config (#13674)

* Fix #13600 auth error when non-admin users clone config

* Fix #13600 auth error when non-admin users clone config

* Fix #13600 auth error when non-admin users clone config

* Fix #13600 auth error when non-admin users clone config
This commit is contained in:
icarus 2025-08-05 19:16:04 +08:00 committed by GitHub
parent 5536ceb683
commit 7f0e9310fb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 95 additions and 4 deletions

View File

@ -52,9 +52,11 @@ public class Constants {
public static final String GROUP = "group";
public static final String GROUP_NAME = "groupName";
public static final String NAMESPACE_ID = "namespaceId";
public static final String TARGET_NAMESPACE_ID = "targetNamespaceId";
public static final String LAST_MODIFIED = "Last-Modified";
public static final String ACCEPT_ENCODING = "Accept-Encoding";

View File

@ -32,7 +32,7 @@ public abstract class AbstractResourceParser<R> implements ResourceParser<R> {
@Override
public Resource parse(R request, Secured secured) {
String namespaceId = getNamespaceId(request);
String namespaceId = getNamespaceId(request, secured);
String group = getGroup(request);
String name = getResourceName(request);
Properties properties = getProperties(request);
@ -50,6 +50,18 @@ public abstract class AbstractResourceParser<R> implements ResourceParser<R> {
*/
protected abstract String getNamespaceId(R request);
/**
* Get namespaceId from request and secured. No implementation is required by default,this method can be rewrited
* with special processing.
*
* @param request request
* @param secured secured
* @return namespaceId
*/
protected String getNamespaceId(R request, Secured secured) {
return getNamespaceId(request);
}
/**
* Get group name from request.
*

View File

@ -17,10 +17,12 @@
package com.alibaba.nacos.auth.parser.http;
import com.alibaba.nacos.api.common.Constants;
import com.alibaba.nacos.auth.annotation.Secured;
import com.alibaba.nacos.common.utils.NamespaceUtil;
import com.alibaba.nacos.common.utils.StringUtils;
import jakarta.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.Properties;
/**
@ -39,6 +41,14 @@ public class ConfigHttpResourceParser extends AbstractHttpResourceParser {
return NamespaceUtil.processNamespaceParameter(namespaceId);
}
@Override
protected String getNamespaceId(HttpServletRequest request, Secured secured) {
return Arrays.stream(secured.tags()).filter(tag -> tag.startsWith(Constants.NAMESPACE_ID))
.map(tag -> tag.split(com.alibaba.nacos.plugin.auth.constant.Constants.Resource.SPLITTER))
.filter(splitTags -> splitTags.length >= 2).map(splitTags -> request.getParameter(splitTags[1]))
.filter(StringUtils::isNotBlank).findFirst().orElseGet(() -> getNamespaceId(request));
}
@Override
protected String getGroup(HttpServletRequest request) {
String groupName = request.getParameter(Constants.GROUP_NAME);

View File

@ -34,6 +34,7 @@ import java.lang.reflect.Method;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
// todo remove this
@ -139,4 +140,65 @@ class ConfigHttpResourceParserTest {
Method method = this.getClass().getDeclaredMethod(methodName);
return method.getAnnotation(Secured.class);
}
@Test
@Secured(tags = {"namespaceId:customNsParam"})
void testParseWithSecuredTags() throws NoSuchMethodException {
Secured secured = getMethodSecure();
when(request.getParameter("customNsParam")).thenReturn("tagNs");
String actualNamespaceId = resourceParser.getNamespaceId(request, secured);
assertEquals("tagNs", actualNamespaceId);
}
@Test
@Secured(tags = {"namespaceId:emptyParam"})
void testParseWithSecuredTagsButEmptyParam() throws NoSuchMethodException {
Secured secured = getMethodSecure();
when(request.getParameter("emptyParam")).thenReturn(StringUtils.EMPTY);
when(request.getParameter(Constants.NAMESPACE_ID)).thenReturn("defaultNs");
String actualNamespaceId = resourceParser.getNamespaceId(request, secured);
assertEquals("defaultNs", actualNamespaceId);
}
@Test
@Secured(tags = {"invalidTag:param", "namespaceId:multiTag"})
void testParseWithMultipleTags() throws NoSuchMethodException {
Secured secured = getMethodSecure();
when(request.getParameter("multiTag")).thenReturn("multiNs");
String actualNamespaceId = resourceParser.getNamespaceId(request, secured);
assertEquals("multiNs", actualNamespaceId);
}
@Test
@Secured(tags = {"namespaceId:splitTag:extra"})
void testParseWithInvalidSplitTag() throws NoSuchMethodException {
Secured secured = getMethodSecure();
when(request.getParameter("splitTag")).thenReturn("splitNs");
String actualNamespaceId = resourceParser.getNamespaceId(request, secured);
assertEquals("splitNs", actualNamespaceId);
}
@Test
@Secured(tags = {"normalTag"})
void testParseWithNoNamespaceTag() throws NoSuchMethodException {
Secured secured = getMethodSecure();
when(request.getParameter(Constants.NAMESPACE_ID)).thenReturn("defaultNs");
String actualNamespaceId = resourceParser.getNamespaceId(request, secured);
assertEquals("defaultNs", actualNamespaceId);
}
@Test
@Secured(tags = {"namespaceId"})
void testParseWithInvalidSplitTag1() throws NoSuchMethodException {
Secured secured = getMethodSecure();
when(request.getParameter(Constants.NAMESPACE_ID)).thenReturn("defaultNs");
String actualNamespaceId = resourceParser.getNamespaceId(request, secured);
assertEquals("defaultNs", actualNamespaceId);
}
}

View File

@ -593,7 +593,7 @@ public class ConfigControllerV3 {
* Execute clone config operation.
*/
@PostMapping("/clone")
@Secured(action = ActionTypes.WRITE, signType = SignType.CONFIG, apiType = ApiType.ADMIN_API)
@Secured(action = ActionTypes.WRITE, signType = SignType.CONFIG, apiType = ApiType.CONSOLE_API)
public Result<Map<String, Object>> cloneConfig(HttpServletRequest request,
@RequestParam(value = "src_user", required = false) String srcUser,
@RequestParam(value = "namespaceId") String namespaceId, @RequestBody List<ConfigCloneInfo> cloneInfos,

View File

@ -357,7 +357,8 @@ public class ConsoleConfigController {
* @throws NacosException If a Nacos-specific error occurs.
*/
@PostMapping("/clone")
@Secured(action = ActionTypes.WRITE, signType = SignType.CONFIG, apiType = ApiType.CONSOLE_API)
@Secured(action = ActionTypes.WRITE, signType = SignType.CONFIG, apiType = ApiType.CONSOLE_API, tags = {
com.alibaba.nacos.plugin.auth.constant.Constants.Tag.SECURED_SPECIAL_TAGS})
public Result<Map<String, Object>> cloneConfig(HttpServletRequest request,
@RequestParam(required = false) String srcUser,
@RequestParam(value = "targetNamespaceId") String namespaceId,

View File

@ -66,5 +66,9 @@ public class Constants {
public static class Tag {
public static final String ONLY_IDENTITY = "only_identity";
public static final String SECURED_SPECIAL_TAGS =
com.alibaba.nacos.api.common.Constants.NAMESPACE_ID + Resource.SPLITTER
+ com.alibaba.nacos.api.common.Constants.TARGET_NAMESPACE_ID;
}
}