Skip to content
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
8e0c80a
feat: migrate remaining portal APIs to OpenAPI
nobodyiam May 24, 2026
4be0b89
docs: update portal OpenAPI spec to v0.3.4
nobodyiam May 25, 2026
fb4216c
docs: add portal OpenAPI migration changelog
nobodyiam May 25, 2026
1bf846f
fix: harden portal management OpenAPI guards
nobodyiam May 25, 2026
bd74b15
fix: address portal OpenAPI review feedback
nobodyiam May 25, 2026
bd5b25a
fix: address app OpenAPI review feedback
nobodyiam May 30, 2026
4ba535b
fix: preserve app create fallback
nobodyiam May 30, 2026
99142ab
fix: align portal OpenAPI controller permissions
nobodyiam May 30, 2026
7852220
fix: address portal OpenAPI review comments
nobodyiam May 30, 2026
937983f
fix: harden portal management OpenAPI edge cases
nobodyiam May 30, 2026
8f7a871
fix: restore portal OpenAPI validation parity
nobodyiam May 30, 2026
0d49fc2
fix: preserve authorized permission init tokens
nobodyiam May 30, 2026
cb6d18f
chore: remove portal OpenAPI migration tracking
nobodyiam May 30, 2026
ef0c5a6
ci: drop removed frontend inventory test
nobodyiam May 30, 2026
5cb28dc
fix: preserve default import conflict action
nobodyiam May 30, 2026
de3acdc
fix: preserve OpenAPI audit display names
nobodyiam May 31, 2026
804bc06
fix: use updated OpenAPI audit schema
nobodyiam May 31, 2026
8e71935
fix: preserve OpenAPI item page metadata
nobodyiam May 31, 2026
8b940eb
fix: preserve OpenAPI token permission compatibility
nobodyiam May 31, 2026
d38be3b
chore: use released OpenAPI spec
nobodyiam May 31, 2026
35a12b5
fix: align openapi portal permission checks
nobodyiam May 31, 2026
2e88df2
chore: deprecate legacy portal webapi controllers
nobodyiam May 31, 2026
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 @@ -16,6 +16,7 @@ Apollo 3.0.0
* [Change: migrate Apollo Portal namespace core UI operations to OpenAPI](https://github.com/apolloconfig/apollo/pull/5612)
* [Change: migrate Apollo Portal release, branch, and instance UI operations to OpenAPI](https://github.com/apolloconfig/apollo/pull/5616)
* [Change: migrate Apollo Portal permission and AccessKey UI operations to OpenAPI](https://github.com/apolloconfig/apollo/pull/5617)
* [Change: complete Apollo Portal UI management OpenAPI migration](https://github.com/apolloconfig/apollo/pull/5618)

------------------
All issues and pull requests are [here](https://github.com/apolloconfig/apollo/milestone/18?closed=1)
2 changes: 1 addition & 1 deletion apollo-portal/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
<artifactId>apollo-portal</artifactId>
<name>Apollo Portal</name>
<properties>
<apollo.openapi.spec.url>https://raw.githubusercontent.com/apolloconfig/apollo-openapi/refs/tags/v0.3.3/apollo-openapi.yaml</apollo.openapi.spec.url>
<apollo.openapi.spec.url>https://raw.githubusercontent.com/apolloconfig/apollo-openapi/refs/tags/v0.3.4/apollo-openapi.yaml</apollo.openapi.spec.url>
<github.path>${project.artifactId}</github.path>
<maven.build.timestamp.format>yyyyMMddHHmmss</maven.build.timestamp.format>
</properties>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,11 @@
import com.ctrip.framework.apollo.openapi.model.OpenMissEnvDTO;
import com.ctrip.framework.apollo.openapi.util.OpenApiModelConverters;
import com.ctrip.framework.apollo.portal.component.PortalSettings;
import com.ctrip.framework.apollo.portal.entity.model.AppModel;
import com.ctrip.framework.apollo.portal.enricher.adapter.OpenAppDtoUserInfoEnrichedAdapter;
import com.ctrip.framework.apollo.portal.environment.Env;
import com.ctrip.framework.apollo.portal.listener.AppDeletionEvent;
import com.ctrip.framework.apollo.portal.listener.AppInfoChangedEvent;
import com.ctrip.framework.apollo.portal.service.AdditionalUserInfoEnrichService;
import com.ctrip.framework.apollo.portal.service.AppService;
import com.ctrip.framework.apollo.portal.service.ClusterService;
import com.ctrip.framework.apollo.portal.service.RoleInitializationService;
Expand Down Expand Up @@ -61,16 +62,19 @@ public class ServerAppOpenApiService implements AppOpenApiService {
private final AppService appService;
private final ApplicationEventPublisher publisher;
private final RoleInitializationService roleInitializationService;
private final AdditionalUserInfoEnrichService additionalUserInfoEnrichService;
private static final Logger logger = LoggerFactory.getLogger(ServerAppOpenApiService.class);

public ServerAppOpenApiService(PortalSettings portalSettings, ClusterService clusterService,
AppService appService, ApplicationEventPublisher publisher,
RoleInitializationService roleInitializationService) {
RoleInitializationService roleInitializationService,
AdditionalUserInfoEnrichService additionalUserInfoEnrichService) {
this.portalSettings = portalSettings;
this.clusterService = clusterService;
this.appService = appService;
this.publisher = publisher;
this.roleInitializationService = roleInitializationService;
this.additionalUserInfoEnrichService = additionalUserInfoEnrichService;
}

private App convert(OpenAppDTO dto) {
Expand Down Expand Up @@ -137,7 +141,7 @@ public List<OpenEnvClusterInfo> getEnvClusterInfo(String appId) {
@Override
public List<OpenAppDTO> getAllApps() {
final List<App> apps = this.appService.findAll();
return OpenApiModelConverters.fromApps(apps);
return enrichApps(OpenApiModelConverters.fromApps(apps));
}

@Override
Expand All @@ -146,7 +150,7 @@ public List<OpenAppDTO> getAppsInfo(List<String> appIds) {
return Collections.emptyList();
}
final List<App> apps = this.appService.findByAppIds(new HashSet<>(appIds));
return OpenApiModelConverters.fromApps(apps);
return enrichApps(OpenApiModelConverters.fromApps(apps));
}

@Override
Expand Down Expand Up @@ -180,7 +184,7 @@ public List<OpenAppDTO> getAppsBySelf(Set<String> appIds, Integer page, Integer
return Collections.emptyList();
}
List<App> apps = appService.findByAppIds(targetAppIds, pageable);
return OpenApiModelConverters.fromApps(apps);
return enrichApps(OpenApiModelConverters.fromApps(apps));
}

/**
Expand Down Expand Up @@ -245,4 +249,10 @@ public List<OpenMissEnvDTO> findMissEnvs(String appId) {
}
return response;
}

private List<OpenAppDTO> enrichApps(List<OpenAppDTO> apps) {
additionalUserInfoEnrichService.enrichAdditionalUserInfo(apps,
OpenAppDtoUserInfoEnrichedAdapter::new);
return apps;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ public void addCreateApplicationRoleToUsers(List<String> userIds, String operato
@Override
public void deleteCreateApplicationRoleFromUser(String userId, String operator) {
RequestPrecondition.checkArgumentsNotEmpty(userId);
checkUserExists(userId);
rolePermissionService.removeRoleFromUsers(SystemRoleManagerService.CREATE_APPLICATION_ROLE_NAME,
Sets.newHashSet(userId), operator);
}
Expand All @@ -265,6 +266,7 @@ public void addManageAppMasterRoleToUser(String appId, String userId, String ope
@Override
public void removeManageAppMasterRoleFromUser(String appId, String userId, String operator) {
RequestPrecondition.checkArgumentsNotEmpty(userId);
checkUserExists(userId);
roleInitializationService.initManageAppMasterRole(appId, operator);
rolePermissionService.removeRoleFromUsers(
RoleUtils.buildAppRoleName(appId, PermissionType.MANAGE_APP_MASTER),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,14 @@
import com.ctrip.framework.apollo.openapi.model.OpenReleaseChangeDTO;
import com.ctrip.framework.apollo.openapi.model.OpenReleaseDTO;
import com.ctrip.framework.apollo.openapi.model.OpenReleaseDiffDTO;
import com.ctrip.framework.apollo.openapi.model.OpenUserDTO;
import com.ctrip.framework.apollo.openapi.model.OpenUserInfoDTO;
import com.ctrip.framework.apollo.portal.entity.bo.ItemBO;
import com.ctrip.framework.apollo.portal.entity.bo.KVEntity;
import com.ctrip.framework.apollo.portal.entity.bo.NamespaceBO;
import com.ctrip.framework.apollo.portal.entity.bo.UserInfo;
import com.ctrip.framework.apollo.portal.entity.model.NamespaceTextModel;
import com.ctrip.framework.apollo.portal.entity.po.UserPO;
import com.ctrip.framework.apollo.portal.entity.vo.AppRolesAssignedUsers;
import com.ctrip.framework.apollo.portal.entity.vo.ClusterNamespaceRolesAssignedUsers;
import com.ctrip.framework.apollo.portal.entity.vo.EnvClusterInfo;
Expand Down Expand Up @@ -560,6 +562,14 @@ public static OpenUserInfoDTO fromUserInfo(final UserInfo userInfo) {
return BeanUtils.transform(OpenUserInfoDTO.class, userInfo);
}

public static List<OpenUserInfoDTO> fromUserInfos(final List<UserInfo> userInfos) {
if (CollectionUtils.isEmpty(userInfos)) {
return Collections.emptyList();
}
return userInfos.stream().map(OpenApiModelConverters::fromUserInfo)
.collect(Collectors.toList());
}

public static List<OpenUserInfoDTO> fromUserInfos(final Set<UserInfo> userInfos) {
if (CollectionUtils.isEmpty(userInfos)) {
return Collections.emptyList();
Expand All @@ -569,6 +579,17 @@ public static List<OpenUserInfoDTO> fromUserInfos(final Set<UserInfo> userInfos)
.map(OpenApiModelConverters::fromUserInfo).collect(Collectors.toList());
}

public static UserPO toUserPO(final OpenUserDTO user) {
Preconditions.checkArgument(user != null);
UserPO result = new UserPO();
result.setUsername(user.getUsername());
result.setUserDisplayName(user.getUserDisplayName());
result.setPassword(user.getPassword());
result.setEmail(user.getEmail());
result.setEnabled(user.getEnabled() == null ? 0 : user.getEnabled());
return result;
}

public static OpenAppRoleUserDTO fromAppRolesAssignedUsers(
final AppRolesAssignedUsers assignedUsers) {
Preconditions.checkArgument(assignedUsers != null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import com.ctrip.framework.apollo.audit.annotation.ApolloAuditLog;
import com.ctrip.framework.apollo.audit.annotation.OpType;
import com.ctrip.framework.apollo.common.exception.BadRequestException;
import com.ctrip.framework.apollo.common.utils.InputValidator;
import com.ctrip.framework.apollo.openapi.api.AppManagementApi;
import com.ctrip.framework.apollo.openapi.model.OpenAppDTO;
import com.ctrip.framework.apollo.openapi.model.OpenCreateAppDTO;
Expand Down Expand Up @@ -78,6 +79,7 @@ public AppController(final ConsumerAuthUtil consumerAuthUtil,
*/
@Transactional
@PreAuthorize(value = "@unifiedPermissionValidator.hasCreateApplicationPermission()")
@ApolloAuditLog(type = OpType.CREATE, name = "App.create")
@Override
public ResponseEntity<Void> createApp(OpenCreateAppDTO req) {
if (null == req.getApp()) {
Expand All @@ -87,6 +89,7 @@ public ResponseEntity<Void> createApp(OpenCreateAppDTO req) {
if (!StringUtils.hasText(app.getAppId())) {
throw new BadRequestException("AppId is null or blank");
}
validatePortalCreateApp(app);
String resolvedOperator = resolveOperator(app.getDataChangeCreatedBy());
app.setDataChangeCreatedBy(resolvedOperator);
app.setDataChangeLastModifiedBy(resolvedOperator);
Expand Down Expand Up @@ -172,7 +175,8 @@ public ResponseEntity<List<OpenAppDTO>> getAppsBySelf(Integer page, Integer size
* POST /openapi/v1/apps/envs/{env}
*/
@Override
@PreAuthorize(value = "@unifiedPermissionValidator.hasCreateApplicationPermission()")
@PreAuthorize(value = "@unifiedPermissionValidator.hasCreateApplicationPermission()"
+ " || (#app != null && @unifiedPermissionValidator.isAppAdmin(#app.appId))")
Comment thread
nobodyiam marked this conversation as resolved.
Outdated
@ApolloAuditLog(type = OpType.CREATE, name = "App.create.forEnv")
public ResponseEntity<Void> createAppInEnv(String env, OpenAppDTO app, String operator) {
if (app == null) {
Expand All @@ -190,8 +194,8 @@ public ResponseEntity<Void> createAppInEnv(String env, OpenAppDTO app, String op
* Delete App (new added)
*/
@Override
@PreAuthorize(value = "@unifiedPermissionValidator.isAppAdmin(#appId)")
@ApolloAuditLog(type = OpType.DELETE, name = "App.delete")
@PreAuthorize(value = "@unifiedPermissionValidator.isSuperAdmin()")
@ApolloAuditLog(type = OpType.RPC, name = "App.delete")
public ResponseEntity<Void> deleteApp(String appId, String operator) {
String resolvedOperator = resolveOperator(operator);
appOpenApiService.deleteApp(appId, resolvedOperator);
Expand Down Expand Up @@ -229,6 +233,28 @@ private String resolveOperator(String operator) {
UserIdentityContextHolder.getAuthType());
}

private void validatePortalCreateApp(OpenAppDTO app) {
if (!UserIdentityConstants.USER.equals(UserIdentityContextHolder.getAuthType())) {
return;
}
if (!StringUtils.hasText(app.getName())) {
throw BadRequestException.appNameIsBlank();
}
if (!InputValidator.isValidClusterNamespace(app.getAppId())) {
throw new BadRequestException("Invalid AppId format: %s",
InputValidator.INVALID_CLUSTER_NAMESPACE_MESSAGE);
}
if (!StringUtils.hasText(app.getOrgId())) {
throw BadRequestException.orgIdIsBlank();
}
if (!StringUtils.hasText(app.getOrgName())) {
throw new BadRequestException("orgName can not be blank");
}
if (!StringUtils.hasText(app.getOwnerName())) {
throw BadRequestException.ownerNameIsBlank();
}
}

private Set<String> findAppIdsAuthorizedByCurrentIdentity() {
if (UserIdentityConstants.USER.equals(UserIdentityContextHolder.getAuthType())) {
UserInfo loginUser = userInfoHolder.getUser();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ public ResponseEntity<OpenClusterDTO> getCluster(String appId, String clusterNam
}

@PreAuthorize(value = "@unifiedPermissionValidator.hasCreateClusterPermission(#appId)")
@ApolloAuditLog(type = OpType.CREATE, name = "Cluster.create")
@Override
public ResponseEntity<OpenClusterDTO> createCluster(String appId, String env,
OpenClusterDTO cluster) {
Expand Down Expand Up @@ -83,7 +84,7 @@ public ResponseEntity<OpenClusterDTO> createCluster(String appId, String env,
/**
* Delete Clusters
*/
@PreAuthorize(value = "@unifiedPermissionValidator.isAppAdmin(#appId)")
@PreAuthorize(value = "@unifiedPermissionValidator.isSuperAdmin()")
Comment thread
nobodyiam marked this conversation as resolved.
Outdated
@ApolloAuditLog(type = OpType.DELETE, name = "Cluster.delete")
@Override
public ResponseEntity<Void> deleteCluster(String env, String appId, String clusterName,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
*/
package com.ctrip.framework.apollo.openapi.v1.controller;

import com.ctrip.framework.apollo.audit.annotation.ApolloAuditLog;
import com.ctrip.framework.apollo.audit.annotation.OpType;
import com.ctrip.framework.apollo.common.dto.GrayReleaseRuleDTO;
import com.ctrip.framework.apollo.common.dto.NamespaceDTO;
import com.ctrip.framework.apollo.common.dto.ReleaseDTO;
Expand Down Expand Up @@ -74,6 +76,7 @@ public NamespaceBranchController(final UnifiedPermissionValidator unifiedPermiss

@PreAuthorize(
value = "@openapiNamespaceBranchController.canCreateBranch(#appId, #env, #clusterName, #namespaceName)")
@ApolloAuditLog(type = OpType.CREATE, name = "NamespaceBranch.create")
@Override
public ResponseEntity<OpenNamespaceDTO> createBranch(String appId, String env, String clusterName,
String namespaceName, String operator) {
Expand All @@ -85,6 +88,7 @@ public ResponseEntity<OpenNamespaceDTO> createBranch(String appId, String env, S
}

@Override
@ApolloAuditLog(type = OpType.DELETE, name = "NamespaceBranch.delete")
public ResponseEntity<Void> deleteBranch(String env, String appId, String clusterName,
String namespaceName, String branchName, String operator) {
String resolvedOperator = resolveOperator(operator, null);
Expand Down Expand Up @@ -124,6 +128,7 @@ public ResponseEntity<OpenGrayReleaseRuleDTO> getBranchGrayRules(String appId, S

@PreAuthorize(
value = "@openapiNamespaceBranchController.canMergeBranch(#appId, #env, #clusterName, #namespaceName)")
@ApolloAuditLog(type = OpType.UPDATE, name = "NamespaceBranch.merge")
@Override
public ResponseEntity<OpenReleaseDTO> merge(String appId, String env, String clusterName,
String namespaceName, String branchName, Boolean deleteBranch,
Expand All @@ -134,6 +139,7 @@ public ResponseEntity<OpenReleaseDTO> merge(String appId, String env, String clu

@PreAuthorize(
value = "@openapiNamespaceBranchController.canMergeBranch(#appId, #env, #clusterName, #namespaceName)")
@ApolloAuditLog(type = OpType.UPDATE, name = "NamespaceBranch.merge")
@Override
public ResponseEntity<OpenReleaseDTO> mergeBranch(String env, String appId, String clusterName,
String namespaceName, String branchName, Boolean deleteBranch,
Expand All @@ -144,6 +150,7 @@ public ResponseEntity<OpenReleaseDTO> mergeBranch(String env, String appId, Stri

@PreAuthorize(
value = "@openapiNamespaceBranchController.canUpdateBranchRules(#appId, #env, #clusterName, #namespaceName)")
@ApolloAuditLog(type = OpType.UPDATE, name = "NamespaceBranch.updateBranchRules")
@Override
public ResponseEntity<Void> updateBranchRules(String appId, String env, String clusterName,
String namespaceName, String branchName, OpenGrayReleaseRuleDTO openGrayReleaseRuleDTO,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,6 @@ public ResponseEntity<OpenPermissionConditionDTO> hasRootPermission(String userI
}

@Override
@PreAuthorize(value = "@unifiedPermissionValidator.hasAssignRolePermission(#appId)")
@ApolloAuditLog(type = OpType.CREATE, name = "Auth.initAppPermission")
public ResponseEntity<Void> initAppPermission(String appId, String namespaceName,
String operator) {
Comment thread
nobodyiam marked this conversation as resolved.
Comment thread
nobodyiam marked this conversation as resolved.
Expand All @@ -195,7 +194,6 @@ public ResponseEntity<Void> initAppPermission(String appId, String namespaceName
}

@Override
@PreAuthorize(value = "@unifiedPermissionValidator.hasAssignRolePermission(#appId)")
@ApolloAuditLog(type = OpType.CREATE, name = "Auth.initClusterNamespacePermission")
public ResponseEntity<Void> initClusterNamespacePermission(String appId, String env,
String clusterName, String operator) {
Comment thread
nobodyiam marked this conversation as resolved.
Expand Down
Loading
Loading