Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
bd653bf
optimize: Implement unified permission verification logic: complete t…
wjwang00 Aug 19, 2025
12d1ef0
optimize: Implement unified permission verification logic: reduce sel…
wjwang00 Aug 22, 2025
85f1bdb
test: Added some unit tests and fixed several methods.
wjwang00 Aug 28, 2025
9935b7f
add changes and license
wjwang00 Aug 28, 2025
53c8a73
add license
wjwang00 Aug 28, 2025
e1eb838
fix potential NPE
wjwang00 Aug 29, 2025
bd6eef4
fix some error UT
wjwang00 Aug 30, 2025
a9fce98
fix error UT
wjwang00 Aug 30, 2025
f2baaa7
format code
wjwang00 Aug 30, 2025
59af01f
Code Optimization
wjwang00 Sep 1, 2025
1314e57
bugfix
wjwang00 Sep 1, 2025
6040dd3
Code Optimization
wjwang00 Sep 2, 2025
e2dd28b
code fix
wjwang00 Sep 10, 2025
9aa0879
format code style
wjwang00 Sep 22, 2025
283b253
change name
wjwang00 Sep 22, 2025
f6ccb5b
format code style by google style
wjwang00 Oct 3, 2025
7eb4afc
format code only myself
wjwang00 Oct 4, 2025
3498dc4
tab format
wjwang00 Oct 4, 2025
a8a48c1
tab format
wjwang00 Oct 4, 2025
ea5f146
tab format
wjwang00 Oct 4, 2025
9c5627d
Update format
wjwang00 Oct 4, 2025
2b97489
Update UserPermissionValidator format
wjwang00 Oct 5, 2025
12cb2f9
fix NPE and remove unnecessary function
wjwang00 Oct 5, 2025
16d4e41
fix validator use
wjwang00 Oct 5, 2025
d9a034a
remove uneccery change
wjwang00 Oct 10, 2025
ec3be28
remove unnecessary change
wjwang00 Oct 10, 2025
649180f
code fix
wjwang00 Oct 10, 2025
aedf09b
rebase
wjwang00 Oct 20, 2025
c8c919c
bug fix
wjwang00 Oct 18, 2025
a6ceb45
fix ut
wjwang00 Oct 18, 2025
4593871
fix ut
wjwang00 Oct 18, 2025
823d8dc
fix ut
wjwang00 Oct 18, 2025
e506d38
fix compatibility issue.
wjwang00 Oct 19, 2025
e02ffc4
remove duplicate
wjwang00 Oct 20, 2025
e85e957
fix ut
wjwang00 Oct 20, 2025
af2d2bc
compatibility issue
wjwang00 Oct 23, 2025
72456e2
remove duplicate string
wjwang00 Oct 24, 2025
d1ac242
ut
wjwang00 Oct 24, 2025
838f264
ut name
wjwang00 Oct 24, 2025
bf00cb3
ut fix
wjwang00 Oct 24, 2025
afacd1c
simply code
wjwang00 Oct 25, 2025
72e0c31
bugfix
wjwang00 Oct 25, 2025
d90efc4
bugfix
wjwang00 Oct 25, 2025
f370f0b
not static
wjwang00 Oct 25, 2025
3439b62
format code
wjwang00 Oct 25, 2025
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
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,6 @@ Apollo 2.5.0
* [Security: Hide password when registering or modifying users](https://github.com/apolloconfig/apollo/pull/5414)
* [Fix: the logical judgment for configuration addition, deletion, and modification.](https://github.com/apolloconfig/apollo/pull/5432)
* [Feature support incremental configuration synchronization client](https://github.com/apolloconfig/apollo/pull/5288)
* [optimize: Implement unified permission verification logic and Optimize the implementation of permission verification](https://github.com/apolloconfig/apollo/pull/5456)
------------------
All issues and pull requests are [here](https://github.com/apolloconfig/apollo/milestone/16?closed=1)
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,17 @@
import com.ctrip.framework.apollo.common.entity.AppNamespace;
import com.ctrip.framework.apollo.openapi.service.ConsumerRolePermissionService;
import com.ctrip.framework.apollo.openapi.util.ConsumerAuthUtil;
import com.ctrip.framework.apollo.portal.component.AbstractPermissionValidator;
import com.ctrip.framework.apollo.portal.component.PermissionValidator;
import com.ctrip.framework.apollo.portal.constant.PermissionType;
import com.ctrip.framework.apollo.portal.util.RoleUtils;
import com.ctrip.framework.apollo.portal.entity.po.Permission;
import org.springframework.stereotype.Component;

import java.util.List;

@Component("consumerPermissionValidator")
public class ConsumerPermissionValidator implements PermissionValidator {
public class ConsumerPermissionValidator extends AbstractPermissionValidator implements
PermissionValidator {

private final ConsumerRolePermissionService permissionService;
private final ConsumerAuthUtil consumerAuthUtil;
Expand All @@ -39,54 +43,26 @@ public ConsumerPermissionValidator(final ConsumerRolePermissionService permissio
}

@Override
public boolean hasModifyNamespacePermission(String appId, String env, String clusterName,
String namespaceName) {
if (hasCreateNamespacePermission(appId)) {
public boolean hasModifyNamespacePermission(String appId, String env, String clusterName, String namespaceName) {
if (hasCreateNamespacePermission(appId)){
return true;
}
return permissionService.consumerHasPermission(consumerAuthUtil.retrieveConsumerIdFromCtx(),
PermissionType.MODIFY_NAMESPACE, RoleUtils.buildNamespaceTargetId(appId, namespaceName))
|| permissionService.consumerHasPermission(consumerAuthUtil.retrieveConsumerIdFromCtx(),
PermissionType.MODIFY_NAMESPACE,
RoleUtils.buildNamespaceTargetId(appId, namespaceName, env));
return super.hasModifyNamespacePermission(appId, env, clusterName, namespaceName);
}

@Override
public boolean hasReleaseNamespacePermission(String appId, String env, String clusterName,
String namespaceName) {
if (hasCreateNamespacePermission(appId)) {
public boolean hasReleaseNamespacePermission(String appId, String env, String clusterName, String namespaceName) {
if (hasCreateNamespacePermission(appId)){
return true;
}
return permissionService.consumerHasPermission(consumerAuthUtil.retrieveConsumerIdFromCtx(),
PermissionType.RELEASE_NAMESPACE, RoleUtils.buildNamespaceTargetId(appId, namespaceName))
|| permissionService.consumerHasPermission(consumerAuthUtil.retrieveConsumerIdFromCtx(),
PermissionType.RELEASE_NAMESPACE,
RoleUtils.buildNamespaceTargetId(appId, namespaceName, env));
}

@Override
public boolean hasAssignRolePermission(String appId) {
return permissionService.consumerHasPermission(consumerAuthUtil.retrieveConsumerIdFromCtx(),
PermissionType.ASSIGN_ROLE, appId);
}

@Override
public boolean hasCreateNamespacePermission(String appId) {
return permissionService.consumerHasPermission(consumerAuthUtil.retrieveConsumerIdFromCtx(),
PermissionType.CREATE_NAMESPACE, appId);
return super.hasReleaseNamespacePermission(appId, env, clusterName, namespaceName);
}

@Override
public boolean hasCreateAppNamespacePermission(String appId, AppNamespace appNamespace) {
throw new UnsupportedOperationException("Not supported operation");
}

@Override
public boolean hasCreateClusterPermission(String appId) {
return permissionService.consumerHasPermission(consumerAuthUtil.retrieveConsumerIdFromCtx(),
PermissionType.CREATE_CLUSTER, appId);
}

@Override
public boolean isSuperAdmin() {
// openapi shouldn't be
Expand All @@ -105,8 +81,22 @@ public boolean hasCreateApplicationPermission() {
return permissionService.consumerHasPermission(consumerId, PermissionType.CREATE_APPLICATION, SYSTEM_PERMISSION_TARGET_ID);
}

@Override
public boolean hasCreateApplicationPermission(String userId) {
return false;
}

@Override
public boolean hasManageAppMasterPermission(String appId) {
throw new UnsupportedOperationException("Not supported operation");
}

@Override
protected boolean hasPermissions(List<Permission> requiredPerms) {
if (requiredPerms == null || requiredPerms.isEmpty()) {
return false;
}
long consumerId = consumerAuthUtil.retrieveConsumerIdFromCtx();
return permissionService.hasAnyPermission(consumerId, requiredPerms);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import com.ctrip.framework.apollo.portal.entity.po.RolePermission;
import com.ctrip.framework.apollo.portal.repository.PermissionRepository;
import com.ctrip.framework.apollo.portal.repository.RolePermissionRepository;
import com.google.common.collect.Sets;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

Expand Down Expand Up @@ -77,4 +78,19 @@ public boolean consumerHasPermission(long consumerId, String permissionType, Str

return false;
}

public boolean hasAnyPermission(long consumerId, List<Permission> permissions) {
if (CollectionUtils.isEmpty(permissions)) {
return false;
}
List<Permission> consumerPermissions = permissionRepository.findConsumerPermissions(consumerId);

if (CollectionUtils.isEmpty(consumerPermissions)) {
return false;
}

Set<Permission> userPermissionSet = Sets.newHashSet(consumerPermissions);

return permissions.stream().anyMatch(userPermissionSet::contains);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
*/
@Service
public class ConsumerAuthUtil {
static final String CONSUMER_ID = "ApolloConsumerId";
public static final String CONSUMER_ID = "ApolloConsumerId";
private final ConsumerService consumerService;

public ConsumerAuthUtil(final ConsumerService consumerService) {
Expand Down Expand Up @@ -67,4 +67,4 @@ public long retrieveConsumerIdFromCtx() {
HttpServletRequest request = attributes.getRequest();
return retrieveConsumerId(request);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public AppController(
* @see com.ctrip.framework.apollo.portal.controller.AppController#create(AppModel)
*/
@Transactional
@PreAuthorize(value = "@consumerPermissionValidator.hasCreateApplicationPermission()")
@PreAuthorize(value = "@unifiedPermissionValidator.hasCreateApplicationPermission()")
@Override
public ResponseEntity<Object> createApp(OpenCreateAppDTO req) {
if (null == req.getApp()) {
Expand Down Expand Up @@ -126,7 +126,7 @@ public ResponseEntity<OpenAppDTO> getApp(String appId) {
* update app (new added)
*/
@Override
@PreAuthorize(value = "@consumerPermissionValidator.isAppAdmin(#appId)")
@PreAuthorize(value = "@unifiedPermissionValidator.isAppAdmin(#appId)")
@ApolloAuditLog(type = OpType.UPDATE, name = "App.update")
public ResponseEntity<OpenAppDTO> updateApp(String appId, String operator, OpenAppDTO dto) {
if (!Objects.equals(appId, dto.getAppId())) {
Expand Down Expand Up @@ -156,7 +156,7 @@ public ResponseEntity<List<OpenAppDTO>> getAppsBySelf(Integer page, Integer size
* POST /openapi/v1/apps/envs/{env}
*/
@Override
@PreAuthorize(value = "@consumerPermissionValidator.hasCreateApplicationPermission()")
@PreAuthorize(value = "@unifiedPermissionValidator.hasCreateApplicationPermission()")
@ApolloAuditLog(type = OpType.CREATE, name = "App.create.forEnv")
public ResponseEntity<Object> createAppInEnv(String env, String operator, OpenAppDTO app) {
if (userService.findByUserId(operator) == null) {
Expand All @@ -171,7 +171,7 @@ public ResponseEntity<Object> createAppInEnv(String env, String operator, OpenAp
* Delete App (new added)
*/
@Override
@PreAuthorize(value = "@consumerPermissionValidator.isAppAdmin(#appId)")
@PreAuthorize(value = "@unifiedPermissionValidator.isAppAdmin(#appId)")
@ApolloAuditLog(type = OpType.DELETE, name = "App.delete")
public ResponseEntity<Object> deleteApp(String appId, String operator) {
if (userService.findByUserId(operator) == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public ResponseEntity<OpenClusterDTO> getCluster(String appId, String clusterNam
return ResponseEntity.ok(this.clusterOpenApiService.getCluster(appId, env, clusterName));
}

@PreAuthorize(value = "@consumerPermissionValidator.hasCreateClusterPermission(#appId)")
@PreAuthorize(value = "@unifiedPermissionValidator.hasCreateClusterPermission(#appId)")
@Override
public ResponseEntity<OpenClusterDTO> createCluster(String appId, String env, OpenClusterDTO cluster) {

Expand Down Expand Up @@ -78,7 +78,7 @@ public ResponseEntity<OpenClusterDTO> createCluster(String appId, String env, Op
/**
* Delete Clusters
*/
@PreAuthorize(value = "@consumerPermissionValidator.isAppAdmin(#appId)")
@PreAuthorize(value = "@unifiedPermissionValidator.isAppAdmin(#appId)")
@ApolloAuditLog(type = OpType.DELETE, name = "Cluster.delete")
@Override
public ResponseEntity<Object> deleteCluster(String env, String appId, String clusterName, String operator) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ public OpenItemDTO getItemByEncodedKey(@PathVariable String appId, @PathVariable
new String(Base64.getDecoder().decode(key.getBytes(StandardCharsets.UTF_8))));
}

@PreAuthorize(value = "@consumerPermissionValidator.hasModifyNamespacePermission(#appId, #env, #clusterName, #namespaceName)")
@PreAuthorize(value = "@unifiedPermissionValidator.hasModifyNamespacePermission(#appId, #env, #clusterName, #namespaceName)")
@PostMapping(value = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/items")
public OpenItemDTO createItem(@PathVariable String appId, @PathVariable String env,
@PathVariable String clusterName, @PathVariable String namespaceName,
Expand All @@ -101,7 +101,7 @@ public OpenItemDTO createItem(@PathVariable String appId, @PathVariable String e
return this.itemOpenApiService.createItem(appId, env, clusterName, namespaceName, item);
}

@PreAuthorize(value = "@consumerPermissionValidator.hasModifyNamespacePermission(#appId, #env, #clusterName, #namespaceName)")
@PreAuthorize(value = "@unifiedPermissionValidator.hasModifyNamespacePermission(#appId, #env, #clusterName, #namespaceName)")
@PutMapping(value = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/items/{key:.+}")
public void updateItem(@PathVariable String appId, @PathVariable String env,
@PathVariable String clusterName, @PathVariable String namespaceName,
Expand Down Expand Up @@ -135,7 +135,7 @@ public void updateItem(@PathVariable String appId, @PathVariable String env,
}
}

@PreAuthorize(value = "@consumerPermissionValidator.hasModifyNamespacePermission(#appId, #env, #clusterName, #namespaceName)")
@PreAuthorize(value = "@unifiedPermissionValidator.hasModifyNamespacePermission(#appId, #env, #clusterName, #namespaceName)")
@PutMapping(value = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/encodedItems/{key:.+}")
public void updateItemByEncodedKey(@PathVariable String appId, @PathVariable String env,
@PathVariable String clusterName, @PathVariable String namespaceName,
Expand All @@ -146,7 +146,7 @@ public void updateItemByEncodedKey(@PathVariable String appId, @PathVariable Str
createIfNotExists);
}

@PreAuthorize(value = "@consumerPermissionValidator.hasModifyNamespacePermission(#appId, #env, #clusterName, #namespaceName)")
@PreAuthorize(value = "@unifiedPermissionValidator.hasModifyNamespacePermission(#appId, #env, #clusterName, #namespaceName)")
@DeleteMapping(value = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/items/{key:.+}")
public void deleteItem(@PathVariable String appId, @PathVariable String env,
@PathVariable String clusterName, @PathVariable String namespaceName,
Expand All @@ -164,7 +164,7 @@ public void deleteItem(@PathVariable String appId, @PathVariable String env,
this.itemOpenApiService.removeItem(appId, env, clusterName, namespaceName, key, operator);
}

@PreAuthorize(value = "@consumerPermissionValidator.hasModifyNamespacePermission(#appId, #env, #clusterName, #namespaceName)")
@PreAuthorize(value = "@unifiedPermissionValidator.hasModifyNamespacePermission(#appId, #env, #clusterName, #namespaceName)")
@DeleteMapping(value = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/encodedItems/{key:.+}")
public void deleteItemByEncodedKey(@PathVariable String appId, @PathVariable String env,
@PathVariable String clusterName, @PathVariable String namespaceName,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@
import com.ctrip.framework.apollo.common.exception.BadRequestException;
import com.ctrip.framework.apollo.common.utils.BeanUtils;
import com.ctrip.framework.apollo.common.utils.RequestPrecondition;
import com.ctrip.framework.apollo.portal.component.UnifiedPermissionValidator;
import com.ctrip.framework.apollo.portal.environment.Env;
import com.ctrip.framework.apollo.core.utils.StringUtils;
import com.ctrip.framework.apollo.openapi.auth.ConsumerPermissionValidator;
import com.ctrip.framework.apollo.openapi.dto.OpenGrayReleaseRuleDTO;
import com.ctrip.framework.apollo.openapi.dto.OpenNamespaceDTO;
import com.ctrip.framework.apollo.openapi.util.OpenApiBeanUtils;
Expand All @@ -43,23 +43,21 @@
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;

@RestController("openapiNamespaceBranchController")
@RequestMapping("/openapi/v1/envs/{env}")
public class NamespaceBranchController {

private final ConsumerPermissionValidator consumerPermissionValidator;
private final UnifiedPermissionValidator unifiedPermissionValidator;
private final ReleaseService releaseService;
private final NamespaceBranchService namespaceBranchService;
private final UserService userService;

public NamespaceBranchController(
final ConsumerPermissionValidator consumerPermissionValidator,
final UnifiedPermissionValidator unifiedPermissionValidator,
final ReleaseService releaseService,
final NamespaceBranchService namespaceBranchService,
final UserService userService) {
this.consumerPermissionValidator = consumerPermissionValidator;
this.unifiedPermissionValidator = unifiedPermissionValidator;
this.releaseService = releaseService;
this.namespaceBranchService = namespaceBranchService;
this.userService = userService;
Expand All @@ -77,7 +75,7 @@ public OpenNamespaceDTO findBranch(@PathVariable String appId,
return OpenApiBeanUtils.transformFromNamespaceBO(namespaceBO);
}

@PreAuthorize(value = "@consumerPermissionValidator.hasCreateNamespacePermission(#appId)")
@PreAuthorize(value = "@unifiedPermissionValidator.hasCreateNamespacePermission(#appId)")
@PostMapping(value = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/branches")
public OpenNamespaceDTO createBranch(@PathVariable String appId,
@PathVariable String env,
Expand All @@ -97,7 +95,7 @@ public OpenNamespaceDTO createBranch(@PathVariable String appId,
return BeanUtils.transform(OpenNamespaceDTO.class, namespaceDTO);
}

@PreAuthorize(value = "@consumerPermissionValidator.hasModifyNamespacePermission(#appId, #env, #clusterName, #namespaceName)")
@PreAuthorize(value = "@unifiedPermissionValidator.hasModifyNamespacePermission(#appId, #env, #clusterName, #namespaceName)")
@DeleteMapping(value = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/branches/{branchName}")
public void deleteBranch(@PathVariable String appId,
@PathVariable String env,
Expand All @@ -111,8 +109,8 @@ public void deleteBranch(@PathVariable String appId,
throw BadRequestException.userNotExists(operator);
}

boolean canDelete = consumerPermissionValidator.hasReleaseNamespacePermission(appId, env, clusterName, namespaceName) ||
(consumerPermissionValidator.hasModifyNamespacePermission(appId, env, clusterName, namespaceName) &&
boolean canDelete = unifiedPermissionValidator.hasReleaseNamespacePermission(appId, env, clusterName, namespaceName) ||
(unifiedPermissionValidator.hasModifyNamespacePermission(appId, env, clusterName, namespaceName) &&
releaseService.loadLatestRelease(appId, Env.valueOf(env), branchName, namespaceName) == null);

if (!canDelete) {
Expand All @@ -137,7 +135,7 @@ public OpenGrayReleaseRuleDTO getBranchGrayRules(@PathVariable String appId, @Pa
return OpenApiBeanUtils.transformFromGrayReleaseRuleDTO(grayReleaseRuleDTO);
}

@PreAuthorize(value = "@consumerPermissionValidator.hasModifyNamespacePermission(#appId, #env, #clusterName, #namespaceName)")
@PreAuthorize(value = "@unifiedPermissionValidator.hasModifyNamespacePermission(#appId, #env, #clusterName, #namespaceName)")
@PutMapping(value = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/branches/{branchName}/rules")
public void updateBranchRules(@PathVariable String appId, @PathVariable String env,
@PathVariable String clusterName, @PathVariable String namespaceName,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public NamespaceController(
this.namespaceOpenApiService = namespaceOpenApiService;
}

@PreAuthorize(value = "@consumerPermissionValidator.hasCreateNamespacePermission(#appId)")
@PreAuthorize(value = "@unifiedPermissionValidator.hasCreateNamespacePermission(#appId)")
@PostMapping(value = "/openapi/v1/apps/{appId}/appnamespaces")
public OpenAppNamespaceDTO createNamespace(@PathVariable String appId,
@RequestBody OpenAppNamespaceDTO appNamespaceDTO) {
Expand Down
Loading