From 618e3e145fea0df83382ceda519bad6e1b641b80 Mon Sep 17 00:00:00 2001 From: greysonfang Date: Mon, 28 Oct 2024 14:53:44 +0800 Subject: [PATCH 01/33] =?UTF-8?q?feat=EF=BC=9A=E7=94=A8=E6=88=B7=E4=B8=AA?= =?UTF-8?q?=E4=BA=BA=E8=A7=86=E8=A7=92=20=E6=9D=83=E9=99=90=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E4=BC=98=E5=8C=96=20#11138?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/service/ServiceProjectAuthResource.kt | 11 + .../api/user/UserAuthAuthorizationResource.kt | 15 + .../auth/api/user/UserAuthHandoverResource.kt | 112 + .../api/user/UserAuthResourceGroupResource.kt | 25 + .../user/UserAuthResourceMemberResource.kt | 73 +- .../auth/api/user/UserAuthResourceResource.kt | 2 +- .../devops/auth/constant/AuthI18nConstants.kt | 5 + .../devops/auth/constant/AuthMessageCode.kt | 8 + .../devops/auth/pojo/dto/HandoverDetailDTO.kt | 20 + .../pojo/dto/HandoverOverviewCreateDTO.kt | 28 + .../auth/pojo/dto/InvalidAuthorizationsDTO.kt | 15 + .../auth/pojo/dto/MemberGroupJoinedDTO.kt | 11 + .../devops/auth/pojo/enum/CollationType.kt | 7 + .../devops/auth/pojo/enum/HandoverAction.kt | 21 + .../auth/pojo/enum/HandoverQueryChannel.kt | 34 + .../devops/auth/pojo/enum/HandoverStatus.kt | 24 + .../devops/auth/pojo/enum/HandoverType.kt | 18 + .../devops/auth/pojo/enum/JoinedType.kt | 5 +- .../devops/auth/pojo/enum/OperateChannel.kt | 37 + .../pojo/enum/RemoveMemberButtonControl.kt | 3 + .../tencent/devops/auth/pojo/enum/SortType.kt | 7 + .../request/GroupMemberCommonConditionReq.kt | 13 +- .../GroupMemberHandoverConditionReq.kt | 10 +- .../request/GroupMemberRemoveConditionReq.kt | 65 + .../request/GroupMemberRenewalConditionReq.kt | 10 +- .../pojo/request/HandoverDetailsQueryReq.kt | 38 + .../request/HandoverOverviewBatchUpdateReq.kt | 18 + .../pojo/request/HandoverOverviewQueryReq.kt | 40 + .../pojo/request/HandoverOverviewUpdateReq.kt | 18 + .../ResourceType2CountOfHandoverQuery.kt | 34 + .../devops/auth/pojo/vo/AuthProjectVO.kt | 11 + .../pojo/vo/BatchOperateGroupMemberCheckVo.kt | 16 +- .../devops/auth/pojo/vo/GroupDetailsInfoVo.kt | 9 +- .../pojo/vo/HandoverAuthorizationDetailVo.kt | 16 + .../auth/pojo/vo/HandoverGroupDetailVo.kt | 19 + .../devops/auth/pojo/vo/HandoverOverviewVo.kt | 39 + ...rmissionsVo.kt => ResourceType2CountVo.kt} | 7 +- .../devops/auth/dao/AuthAuthorizationDao.kt | 21 +- .../devops/auth/dao/AuthHandoverDetailDao.kt | 128 + .../auth/dao/AuthHandoverOverviewDao.kt | 175 ++ .../devops/auth/dao/AuthResourceDao.kt | 13 + .../auth/dao/AuthResourceGroupConfigDao.kt | 2 + .../devops/auth/dao/AuthResourceGroupDao.kt | 25 + .../auth/dao/AuthResourceGroupMemberDao.kt | 114 +- .../dao/AuthResourceGroupPermissionDao.kt | 22 + .../tencent/devops/auth/dao/StrategyDao.kt | 10 +- .../rbac/config/RbacAuthConfiguration.kt | 131 +- .../rbac/config/RbacServiceConfiguration.kt | 4 +- ...acCacheService.kt => RbacCommonService.kt} | 50 +- .../service/RbacPermissionApplyService.kt | 16 +- ...bacPermissionHandoverApplicationService.kt | 365 +++ .../RbacPermissionManageFacadeServiceImpl.kt | 2333 +++++++++++++++++ .../service/RbacPermissionProjectService.kt | 14 +- ...ResourceGroupAndMemberFacadeServiceImpl.kt | 423 --- ...ermissionResourceGroupPermissionService.kt | 33 +- .../RbacPermissionResourceGroupService.kt | 6 +- .../RbacPermissionResourceGroupSyncService.kt | 7 +- .../RbacPermissionResourceMemberService.kt | 746 +----- .../RbacPermissionResourceValidateService.kt | 63 +- .../rbac/service/RbacPermissionService.kt | 14 +- .../service/migrate/AbMigratePolicyService.kt | 10 +- .../MigratePermissionHandoverService.kt | 6 +- .../service/migrate/MigrateResourceService.kt | 6 +- .../service/migrate/MigrateResultService.kt | 6 +- .../service/migrate/MigrateV0PolicyService.kt | 10 +- .../service/migrate/MigrateV3PolicyService.kt | 6 +- .../migrate/RbacPermissionMigrateService.kt | 4 +- .../sample/config/MockAuthConfiguration.kt | 14 +- ...plePermissionHandoverApplicationService.kt | 69 + .../SamplePermissionManageFacadeService.kt | 173 ++ ...sionResourceGroupAndMemberFacadeService.kt | 45 - ...ermissionResourceGroupPermissionService.kt | 6 + .../SamplePermissionResourceMemberService.kt | 85 +- ...SamplePermissionResourceValidateService.kt | 10 + .../service/ServiceProjectAuthResourceImpl.kt | 13 +- .../ServiceResourceGroupResourceImpl.kt | 6 +- .../ServiceResourceMemberResourceImpl.kt | 8 +- .../user/UserAuthAuthorizationResourceImpl.kt | 21 +- .../user/UserAuthHandoverResourceImpl.kt | 101 + .../user/UserAuthResourceGroupResourceImpl.kt | 70 +- .../UserAuthResourceMemberResourceImpl.kt | 123 +- .../service/PermissionAuthorizationService.kt | 40 +- .../PermissionAuthorizationServiceImpl.kt | 190 +- .../PermissionHandoverApplicationService.kt | 78 + .../iam/PermissionManageFacadeService.kt | 250 ++ ...sionResourceGroupAndMemberFacadeService.kt | 56 - ...ermissionResourceGroupPermissionService.kt | 9 + .../iam/PermissionResourceMemberService.kt | 76 - .../iam/PermissionResourceValidateService.kt | 11 + .../lock/HandleHandoverApplicationLock.kt | 45 + .../migrate/AbMigratePolicyServiceTest.kt | 4 +- .../migrate/MigrateV0PolicyServiceTest.kt | 2 +- .../migrate/MigrateV3PolicyServiceTest.kt | 2 +- .../devops/common/auth/api/ActionId.kt | 1 + .../ResourceAuthorizationConditionRequest.kt | 6 + ...ceAuthorizationHandoverConditionRequest.kt | 6 + .../api/pojo/ResourceAuthorizationResponse.kt | 8 +- .../service/impl/AbsProjectServiceImpl.kt | 1 + .../quality/dao/v2/QualityMetadataDao.kt | 2 + .../service/RepositoryAuthService.kt | 2 +- .../i18n/auth/message_en_US.properties | 12 + .../i18n/auth/message_zh_CN.properties | 12 +- support-files/sql/1001_ci_auth_ddl_mysql.sql | 35 + .../2030_ci_auth-update_v3.0_mysql.sql | 7 + 104 files changed, 5566 insertions(+), 1640 deletions(-) create mode 100644 src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/user/UserAuthHandoverResource.kt create mode 100644 src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/dto/HandoverDetailDTO.kt create mode 100644 src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/dto/HandoverOverviewCreateDTO.kt create mode 100644 src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/dto/InvalidAuthorizationsDTO.kt create mode 100644 src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/dto/MemberGroupJoinedDTO.kt create mode 100644 src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/CollationType.kt create mode 100644 src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/HandoverAction.kt create mode 100644 src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/HandoverQueryChannel.kt create mode 100644 src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/HandoverStatus.kt create mode 100644 src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/HandoverType.kt create mode 100644 src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/OperateChannel.kt create mode 100644 src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/SortType.kt create mode 100644 src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/request/GroupMemberRemoveConditionReq.kt create mode 100644 src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/request/HandoverDetailsQueryReq.kt create mode 100644 src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/request/HandoverOverviewBatchUpdateReq.kt create mode 100644 src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/request/HandoverOverviewQueryReq.kt create mode 100644 src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/request/HandoverOverviewUpdateReq.kt create mode 100644 src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/request/ResourceType2CountOfHandoverQuery.kt create mode 100644 src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/vo/AuthProjectVO.kt create mode 100644 src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/vo/HandoverAuthorizationDetailVo.kt create mode 100644 src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/vo/HandoverGroupDetailVo.kt create mode 100644 src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/vo/HandoverOverviewVo.kt rename src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/vo/{MemberGroupCountWithPermissionsVo.kt => ResourceType2CountVo.kt} (63%) create mode 100644 src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/AuthHandoverDetailDao.kt create mode 100644 src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/AuthHandoverOverviewDao.kt rename src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/{RbacCacheService.kt => RbacCommonService.kt} (80%) create mode 100644 src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionHandoverApplicationService.kt create mode 100644 src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionManageFacadeServiceImpl.kt delete mode 100644 src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionResourceGroupAndMemberFacadeServiceImpl.kt create mode 100644 src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/service/SamplePermissionHandoverApplicationService.kt create mode 100644 src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/service/SamplePermissionManageFacadeService.kt delete mode 100644 src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/service/SamplePermissionResourceGroupAndMemberFacadeService.kt create mode 100644 src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/user/UserAuthHandoverResourceImpl.kt create mode 100644 src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/iam/PermissionHandoverApplicationService.kt create mode 100644 src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/iam/PermissionManageFacadeService.kt delete mode 100644 src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/iam/PermissionResourceGroupAndMemberFacadeService.kt create mode 100644 src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/lock/HandleHandoverApplicationLock.kt diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/service/ServiceProjectAuthResource.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/service/ServiceProjectAuthResource.kt index e51091c9ab47..57b9c63191dc 100644 --- a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/service/ServiceProjectAuthResource.kt +++ b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/service/ServiceProjectAuthResource.kt @@ -27,8 +27,10 @@ package com.tencent.devops.auth.api.service +import com.tencent.devops.auth.pojo.vo.AuthProjectVO import com.tencent.devops.auth.pojo.vo.ProjectPermissionInfoVO import com.tencent.devops.common.api.auth.AUTH_HEADER_DEVOPS_BK_TOKEN +import com.tencent.devops.common.api.auth.AUTH_HEADER_DEVOPS_USER_ID import com.tencent.devops.common.api.auth.AUTH_HEADER_GIT_TYPE import com.tencent.devops.common.api.auth.AUTH_HEADER_USER_ID import com.tencent.devops.common.api.pojo.Result @@ -259,4 +261,13 @@ interface ServiceProjectAuthResource { @Parameter(description = "项目Code", required = true) projectCode: String ): Result + + @GET + @Path("/listUserProjectsWithAuthorization") + @Operation(summary = "获取用户授权相关的项目") + fun listUserProjectsWithAuthorization( + @HeaderParam(AUTH_HEADER_DEVOPS_USER_ID) + @Parameter(description = "用户ID", required = true) + userId: String + ): Result> } diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/user/UserAuthAuthorizationResource.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/user/UserAuthAuthorizationResource.kt index 0f672983e720..1a57ff934aaa 100644 --- a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/user/UserAuthAuthorizationResource.kt +++ b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/user/UserAuthAuthorizationResource.kt @@ -28,7 +28,10 @@ package com.tencent.devops.auth.api.user +import com.tencent.devops.auth.pojo.enum.OperateChannel +import com.tencent.devops.auth.pojo.vo.AuthProjectVO import com.tencent.devops.auth.pojo.vo.ResourceTypeInfoVo +import com.tencent.devops.common.api.auth.AUTH_HEADER_DEVOPS_USER_ID import com.tencent.devops.common.api.auth.AUTH_HEADER_USER_ID import com.tencent.devops.common.api.auth.AUTH_HEADER_USER_ID_DEFAULT_VALUE import com.tencent.devops.common.api.model.SQLPage @@ -68,6 +71,9 @@ interface UserAuthAuthorizationResource { @Parameter(description = "项目ID", required = true) @PathParam("projectId") projectId: String, + @Parameter(description = "操作渠道", required = true) + @QueryParam("operateChannel") + operateChannel: OperateChannel?, @Parameter(description = "查询条件", required = true) condition: ResourceAuthorizationConditionRequest ): Result> @@ -138,4 +144,13 @@ interface UserAuthAuthorizationResource { @Parameter(description = "资源授权交接条件实体", required = true) condition: ResetAllResourceAuthorizationReq ): Result> + + @GET + @Path("/listUserProjectsWithAuthorization") + @Operation(summary = "获取用户授权相关的项目") + fun listUserProjectsWithAuthorization( + @HeaderParam(AUTH_HEADER_DEVOPS_USER_ID) + @Parameter(description = "用户ID", required = true) + userId: String + ): Result> } diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/user/UserAuthHandoverResource.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/user/UserAuthHandoverResource.kt new file mode 100644 index 000000000000..cb24bde8d7c6 --- /dev/null +++ b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/user/UserAuthHandoverResource.kt @@ -0,0 +1,112 @@ +package com.tencent.devops.auth.api.user + +import com.tencent.devops.auth.pojo.request.HandoverDetailsQueryReq +import com.tencent.devops.auth.pojo.request.HandoverOverviewBatchUpdateReq +import com.tencent.devops.auth.pojo.request.HandoverOverviewQueryReq +import com.tencent.devops.auth.pojo.request.HandoverOverviewUpdateReq +import com.tencent.devops.auth.pojo.request.ResourceType2CountOfHandoverQuery +import com.tencent.devops.auth.pojo.vo.HandoverAuthorizationDetailVo +import com.tencent.devops.auth.pojo.vo.HandoverGroupDetailVo +import com.tencent.devops.auth.pojo.vo.HandoverOverviewVo +import com.tencent.devops.auth.pojo.vo.ResourceType2CountVo +import com.tencent.devops.common.api.auth.AUTH_HEADER_USER_ID +import com.tencent.devops.common.api.auth.AUTH_HEADER_USER_ID_DEFAULT_VALUE +import com.tencent.devops.common.api.model.SQLPage +import com.tencent.devops.common.api.pojo.Result +import com.tencent.devops.common.auth.api.pojo.ResourceAuthorizationHandoverConditionRequest +import io.swagger.v3.oas.annotations.Operation +import io.swagger.v3.oas.annotations.Parameter +import io.swagger.v3.oas.annotations.tags.Tag +import javax.ws.rs.Consumes +import javax.ws.rs.HeaderParam +import javax.ws.rs.POST +import javax.ws.rs.Path +import javax.ws.rs.PathParam +import javax.ws.rs.Produces +import javax.ws.rs.core.MediaType + +@Tag(name = "USER_RESOURCE_AUTHORIZATION", description = "用户-权限-交接相关") +@Path("/user/auth/handover/") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +interface UserAuthHandoverResource { + @POST + @Path("/{projectId}/handoverAuthorizationsApplication") + @Operation(summary = "交接授权申请") + fun handoverAuthorizationsApplication( + @Parameter(description = "用户ID", required = true, example = AUTH_HEADER_USER_ID_DEFAULT_VALUE) + @HeaderParam(AUTH_HEADER_USER_ID) + userId: String, + @Parameter(description = "项目ID", required = true) + @PathParam("projectId") + projectId: String, + @Parameter(description = "资源授权交接条件实体", required = true) + condition: ResourceAuthorizationHandoverConditionRequest + ): Result + + @POST + @Path("/listHandoverOverviews") + @Operation(summary = "权限交接总览列表") + fun listHandoverOverviews( + @Parameter(description = "用户名", required = true) + @HeaderParam(AUTH_HEADER_USER_ID) + userId: String, + @Parameter(description = "权限交接总览查询", required = true) + queryRequest: HandoverOverviewQueryReq + ): Result> + + @POST + @Path("/getResourceType2CountOfHandover") + @Operation(summary = "获取资源授权管理数量") + fun getResourceType2CountOfHandover( + @Parameter(description = "用户名", required = true) + @HeaderParam(AUTH_HEADER_USER_ID) + userId: String, + @Parameter(description = "查询请求体", required = true) + queryReq: ResourceType2CountOfHandoverQuery + ): Result> + + @POST + @Path("/listAuthorizationsOfHandover") + @Operation(summary = "获取交接单中授权相关") + fun listAuthorizationsOfHandover( + @Parameter(description = "用户名", required = true) + @HeaderParam(AUTH_HEADER_USER_ID) + userId: String, + @Parameter(description = "权限交接详细查询请求体", required = true) + queryReq: HandoverDetailsQueryReq + ): Result> + + @POST + @Path("/listGroupsOfHandover") + @Operation(summary = "获取交接单中用户组相关") + fun listGroupsOfHandover( + @Parameter(description = "用户名", required = true) + @HeaderParam(AUTH_HEADER_USER_ID) + userId: String, + @Parameter(description = "权限交接详细查询请求体", required = true) + queryReq: HandoverDetailsQueryReq + ): Result> + + @POST + @Path("/handleHanoverApplication") + @Operation(summary = "处理交接审批单") + fun handleHanoverApplication( + @Parameter(description = "用户名", required = true) + @HeaderParam(AUTH_HEADER_USER_ID) + userId: String, + @Parameter(description = "更新权限交接总览请求体", required = true) + request: HandoverOverviewUpdateReq + ): Result + + @POST + @Path("/batchHandleHanoverApplications") + @Operation(summary = "批量处理交接审批单") + fun batchHandleHanoverApplications( + @Parameter(description = "用户名", required = true) + @HeaderParam(AUTH_HEADER_USER_ID) + userId: String, + @Parameter(description = "批量更新权限交接总览请求体", required = true) + request: HandoverOverviewBatchUpdateReq + ): Result +} diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/user/UserAuthResourceGroupResource.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/user/UserAuthResourceGroupResource.kt index 5dc4c0342674..be36c7f632ce 100644 --- a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/user/UserAuthResourceGroupResource.kt +++ b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/user/UserAuthResourceGroupResource.kt @@ -30,6 +30,7 @@ package com.tencent.devops.auth.api.user import com.tencent.devops.auth.pojo.dto.GroupMemberRenewalDTO import com.tencent.devops.auth.pojo.dto.RenameGroupDTO +import com.tencent.devops.auth.pojo.enum.OperateChannel import com.tencent.devops.auth.pojo.vo.GroupDetailsInfoVo import com.tencent.devops.auth.pojo.vo.IamGroupPoliciesVo import com.tencent.devops.common.api.annotation.BkInterfaceI18n @@ -110,6 +111,9 @@ interface UserAuthResourceGroupResource { @QueryParam("action") @Parameter(description = "操作") action: String?, + @QueryParam("operateChannel") + @Parameter(description = "操作渠道") + operateChannel: OperateChannel?, @Parameter(description = "起始位置,从0开始") @QueryParam("start") start: Int, @@ -118,6 +122,27 @@ interface UserAuthResourceGroupResource { limit: Int ): Result> + @GET + @Path("{groupId}/getMemberGroupDetails/") + @Operation(summary = "获取用户加入单个组的详情") + fun getMemberGroupDetails( + @Parameter(description = "用户名", required = true) + @HeaderParam(AUTH_HEADER_USER_ID) + userId: String, + @Parameter(description = "项目ID", required = true) + @PathParam("projectId") + projectId: String, + @Parameter(description = "资源类型", required = true) + @PathParam("resourceType") + resourceType: String, + @Parameter(description = "用户组Id") + @PathParam("groupId") + groupId: Int, + @QueryParam("memberId") + @Parameter(description = "组织ID/成员ID") + memberId: String + ): Result + @PUT @Path("{groupId}/member/renewal") @Operation(summary = "用户续期") diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/user/UserAuthResourceMemberResource.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/user/UserAuthResourceMemberResource.kt index 26a74ba5b87c..06664d8bb50d 100644 --- a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/user/UserAuthResourceMemberResource.kt +++ b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/user/UserAuthResourceMemberResource.kt @@ -2,15 +2,17 @@ package com.tencent.devops.auth.api.user import com.tencent.devops.auth.pojo.ResourceMemberInfo import com.tencent.devops.auth.pojo.enum.BatchOperateType +import com.tencent.devops.auth.pojo.enum.OperateChannel import com.tencent.devops.auth.pojo.request.GroupMemberCommonConditionReq import com.tencent.devops.auth.pojo.request.GroupMemberHandoverConditionReq +import com.tencent.devops.auth.pojo.request.GroupMemberRemoveConditionReq import com.tencent.devops.auth.pojo.request.GroupMemberRenewalConditionReq import com.tencent.devops.auth.pojo.request.GroupMemberSingleRenewalReq import com.tencent.devops.auth.pojo.request.ProjectMembersQueryConditionReq import com.tencent.devops.auth.pojo.request.RemoveMemberFromProjectReq import com.tencent.devops.auth.pojo.vo.BatchOperateGroupMemberCheckVo import com.tencent.devops.auth.pojo.vo.GroupDetailsInfoVo -import com.tencent.devops.auth.pojo.vo.MemberGroupCountWithPermissionsVo +import com.tencent.devops.auth.pojo.vo.ResourceType2CountVo import com.tencent.devops.common.api.auth.AUTH_HEADER_USER_ID import com.tencent.devops.common.api.model.SQLPage import com.tencent.devops.common.api.pojo.Result @@ -96,8 +98,8 @@ interface UserAuthResourceMemberResource { @PUT @Path("/batch/renewal") - @Operation(summary = "批量续期组成员权限--无需进行审批") - fun batchRenewalGroupMembers( + @Operation(summary = "批量续期组成员权限--管理员视角") + fun batchRenewalGroupMembersFromManager( @Parameter(description = "用户名", required = true) @HeaderParam(AUTH_HEADER_USER_ID) userId: String, @@ -110,8 +112,8 @@ interface UserAuthResourceMemberResource { @DELETE @Path("/batch/remove") - @Operation(summary = "批量移除用户组成员") - fun batchRemoveGroupMembers( + @Operation(summary = "批量移除用户组成员--管理员视角") + fun batchRemoveGroupMembersFromManager( @Parameter(description = "用户名", required = true) @HeaderParam(AUTH_HEADER_USER_ID) userId: String, @@ -119,13 +121,47 @@ interface UserAuthResourceMemberResource { @PathParam("projectId") projectId: String, @Parameter(description = "批量移除成员请求实体") - removeMemberDTO: GroupMemberCommonConditionReq + removeMemberDTO: GroupMemberRemoveConditionReq + ): Result + + @DELETE + @Path("/batch/personal/remove") + @Operation(summary = "批量退出用户组成员--个人视角") + fun batchRemoveGroupMembersFromPersonal( + @Parameter(description = "用户名", required = true) + @HeaderParam(AUTH_HEADER_USER_ID) + userId: String, + @Parameter(description = "项目ID", required = true) + @PathParam("projectId") + projectId: String, + @Parameter(description = "批量移除成员请求实体") + removeMemberDTO: GroupMemberRemoveConditionReq + ): Result + + @DELETE + @Path("/single/{groupId}/{operateChannel}/remove") + @Operation(summary = "退出单个组") + fun deleteResourceGroupMembers( + @Parameter(description = "用户名", required = true) + @HeaderParam(AUTH_HEADER_USER_ID) + userId: String, + @Parameter(description = "项目ID", required = true) + @PathParam("projectId") + projectId: String, + @Parameter(description = "组ID", required = true) + @PathParam("groupId") + groupId: Int, + @Parameter(description = "操作渠道", required = true) + @PathParam("operateChannel") + operateChannel: OperateChannel, + @Parameter(description = "操作对象", required = true) + targetMember: ResourceMemberInfo ): Result @PUT @Path("/batch/handover") - @Operation(summary = "批量交接用户组成员") - fun batchHandoverGroupMembers( + @Operation(summary = "批量交接用户组成员--管理员视角") + fun batchHandoverGroupMembersFromManager( @Parameter(description = "用户名", required = true) @HeaderParam(AUTH_HEADER_USER_ID) userId: String, @@ -136,6 +172,20 @@ interface UserAuthResourceMemberResource { handoverMemberDTO: GroupMemberHandoverConditionReq ): Result + @PUT + @Path("/batch/personal/handover") + @Operation(summary = "批量交接用户组成员--个人视角") + fun batchHandoverApplicationFromPersonal( + @Parameter(description = "用户名", required = true) + @HeaderParam(AUTH_HEADER_USER_ID) + userId: String, + @Parameter(description = "项目ID", required = true) + @PathParam("projectId") + projectId: String, + @Parameter(description = "批量交接成员请求实体") + handoverMemberDTO: GroupMemberHandoverConditionReq + ): Result + @POST @Path("/batch/{batchOperateType}/check/") @Operation(summary = "批量操作用户组检查") @@ -211,6 +261,9 @@ interface UserAuthResourceMemberResource { relatedResourceCode: String?, @QueryParam("action") @Parameter(description = "操作") - action: String? - ): Result> + action: String?, + @QueryParam("operateChannel") + @Parameter(description = "操作渠道") + operateChannel: OperateChannel? + ): Result> } diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/user/UserAuthResourceResource.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/user/UserAuthResourceResource.kt index aa0f259134df..0f648870d3d2 100644 --- a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/user/UserAuthResourceResource.kt +++ b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/user/UserAuthResourceResource.kt @@ -34,9 +34,9 @@ import com.tencent.devops.auth.pojo.vo.IamGroupMemberInfoVo import com.tencent.devops.common.api.auth.AUTH_HEADER_USER_ID import com.tencent.devops.common.api.pojo.Pagination import com.tencent.devops.common.api.pojo.Result -import io.swagger.v3.oas.annotations.tags.Tag import io.swagger.v3.oas.annotations.Operation import io.swagger.v3.oas.annotations.Parameter +import io.swagger.v3.oas.annotations.tags.Tag import javax.ws.rs.Consumes import javax.ws.rs.GET import javax.ws.rs.HeaderParam diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/constant/AuthI18nConstants.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/constant/AuthI18nConstants.kt index 4be2b229dfc4..a6ecd7673092 100644 --- a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/constant/AuthI18nConstants.kt +++ b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/constant/AuthI18nConstants.kt @@ -47,4 +47,9 @@ object AuthI18nConstants { const val BK_MEMBER_EXPIRED_AT_DISPLAY_EXPIRED = "bkMemberExpiredAtDisplayExpired" // 有效期: 已过期 const val BK_MEMBER_EXPIRED_AT_DISPLAY_NORMAL = "bkMemberExpiredAtDisplayNormal" // 有效期: {0}天 const val BK_MEMBER_EXPIRED_AT_DISPLAY_PERMANENT = "bkMemberExpiredAtDisplayPermanent" // 有效期: 永久 + + const val BK_APPLY_TO_HANDOVER = "bkApplyToHandover" // 申请移交 + const val BK_HANDOVER_GROUPS = "bkHandoverGroups" // 个权限用户组 + const val BK_HANDOVER_AUTHORIZATIONS = "bkHandoverAuthorizations" // 个授权 + const val BK_PROJECT = "bk_project" // 蓝盾项目 } diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/constant/AuthMessageCode.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/constant/AuthMessageCode.kt index 4b66ce68fbbb..fcc1f10c5761 100644 --- a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/constant/AuthMessageCode.kt +++ b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/constant/AuthMessageCode.kt @@ -142,4 +142,12 @@ object AuthMessageCode { const val INVALID_EXPIRED_PERM_NOT_ALLOW_TO_HANDOVER = "2121089" // 已过期的权限不允许交接 const val ERROR_USER_INFORMATION_NOT_SYNCED = "2121090" // 请等待第二天用户信息同步后再尝试操作,因为新入职用户的信息尚未同步完成。 + + const val ERROR_HANDOVER_OVERVIEW_NOT_EXIST = "2121091" // 权限交接记录不存在 + const val ERROR_HANDOVER_FINISH = "2121092" // 该交接申请单已被处理,不允许重复操作 + const val ERROR_HANDOVER_REVOKE = "2121093" // 由于您不是该交接申请单的发起人,无法进行撤销操作 + const val ERROR_HANDOVER_APPROVAL = "2121094" // 由于您不是该交接申请单的审批人,无法进行任何操作 + const val ERROR_HANDOVER_HANDLE = "2121095" // 该交接申请单正在被处理中,请耐心等待 + const val ERROR_REPERTORY_HANDOVER_AUTHORIZATION = "2121096" // 交接操作不合法,用户没有对应代码库授权的权限 + const val ERROR_SINGLE_GROUP_REMOVE = "2121098" // 由于直接退出用户组,会导致授权失效,必须进行用户组移交 } diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/dto/HandoverDetailDTO.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/dto/HandoverDetailDTO.kt new file mode 100644 index 000000000000..a5fec007c00d --- /dev/null +++ b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/dto/HandoverDetailDTO.kt @@ -0,0 +1,20 @@ +package com.tencent.devops.auth.pojo.dto + +import com.tencent.devops.auth.pojo.enum.HandoverType +import io.swagger.v3.oas.annotations.media.Schema + +@Schema(title = "权限交接详细表") +data class HandoverDetailDTO( + @get:Schema(title = "项目ID") + val projectCode: String, + @get:Schema(title = "流程单号") + var flowNo: String? = null, + @get:Schema(title = "授权/组ID") + val itemId: String, + @get:Schema(title = "组/授权资源关联的资源类型") + val resourceType: String, + @get:Schema(title = "交接类型") + val handoverType: HandoverType, + @get:Schema(title = "审批人") + var approver: String? = null +) diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/dto/HandoverOverviewCreateDTO.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/dto/HandoverOverviewCreateDTO.kt new file mode 100644 index 000000000000..18681ce365a6 --- /dev/null +++ b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/dto/HandoverOverviewCreateDTO.kt @@ -0,0 +1,28 @@ +package com.tencent.devops.auth.pojo.dto + +import com.tencent.devops.auth.pojo.enum.HandoverStatus +import io.swagger.v3.oas.annotations.media.Schema + +@Schema(title = "创建权限交接总览DTO") +data class HandoverOverviewCreateDTO( + @get:Schema(title = "项目ID") + val projectCode: String, + @get:Schema(title = "项目名称") + val projectName: String, + @get:Schema(title = "项目ID") + var title: String? = null, + @get:Schema(title = "流程单号") + var flowNo: String? = null, + @get:Schema(title = "申请人") + val applicant: String, + @get:Schema(title = "审批人") + val approver: String, + @get:Schema(title = "审批结果") + val handoverStatus: HandoverStatus, + @get:Schema(title = "用户组个数") + val groupCount: Int, + @get:Schema(title = "授权个数") + val authorizationCount: Int, + @get:Schema(title = "备注") + val remark: String? = null +) diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/dto/InvalidAuthorizationsDTO.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/dto/InvalidAuthorizationsDTO.kt new file mode 100644 index 000000000000..c532f60faa2d --- /dev/null +++ b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/dto/InvalidAuthorizationsDTO.kt @@ -0,0 +1,15 @@ +package com.tencent.devops.auth.pojo.dto + +import io.swagger.v3.oas.annotations.media.Schema + +@Schema(title = "移除/移交用户组成员导致的无效授权") +data class InvalidAuthorizationsDTO( + @get:Schema(title = "引起代持人权限失效的用户组") + val invalidGroupIds: List, + @get:Schema(title = "引起代持人权限失效的流水线") + val invalidPipelineIds: List, + @get:Schema(title = "引起oauth失效的代码库") + val invalidRepertoryIds: List = emptyList(), + @get:Schema(title = "失效的CMDB环境节点") + val invalidEnvNodeIds: List = emptyList() +) diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/dto/MemberGroupJoinedDTO.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/dto/MemberGroupJoinedDTO.kt new file mode 100644 index 000000000000..0d25b99c8495 --- /dev/null +++ b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/dto/MemberGroupJoinedDTO.kt @@ -0,0 +1,11 @@ +package com.tencent.devops.auth.pojo.dto + +import com.tencent.devops.auth.pojo.enum.MemberType +import io.swagger.v3.oas.annotations.media.Schema + +data class MemberGroupJoinedDTO( + @get:Schema(title = "组id") + val id: Int, + @get:Schema(title = "组成员类型") + val memberType: MemberType +) diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/CollationType.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/CollationType.kt new file mode 100644 index 000000000000..bd3ff41bc50a --- /dev/null +++ b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/CollationType.kt @@ -0,0 +1,7 @@ +package com.tencent.devops.auth.pojo.enum + +enum class CollationType { + ASC, + + DESC; +} diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/HandoverAction.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/HandoverAction.kt new file mode 100644 index 000000000000..7360046c9efd --- /dev/null +++ b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/HandoverAction.kt @@ -0,0 +1,21 @@ +package com.tencent.devops.auth.pojo.enum + +enum class HandoverAction(val value: Int, val alias: String) { + // 审批成功 + AGREE(1, "已通过"), + + // 审批驳回 + REJECT(2, "已被拒绝"), + + // 撤销 + REVOKE(3, "撤销"); + + companion object { + fun get(value: Int): HandoverAction { + HandoverAction.values().forEach { + if (value == it.value) return it + } + throw IllegalArgumentException("No enum for constant $value") + } + } +} diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/HandoverQueryChannel.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/HandoverQueryChannel.kt new file mode 100644 index 000000000000..352a6c6bbe9c --- /dev/null +++ b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/HandoverQueryChannel.kt @@ -0,0 +1,34 @@ +/* + * Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-CI 蓝鲸持续集成平台 is licensed under the MIT license. + * + * A copy of the MIT License is included in this file. + * + * + * Terms of the MIT License: + * --------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.devops.auth.pojo.enum + +enum class HandoverQueryChannel { + PREVIEW, + + HANDOVER_APPLICATION +} diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/HandoverStatus.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/HandoverStatus.kt new file mode 100644 index 000000000000..aaf82e6e9acb --- /dev/null +++ b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/HandoverStatus.kt @@ -0,0 +1,24 @@ +package com.tencent.devops.auth.pojo.enum + +enum class HandoverStatus(val value: Int) { + // 审批中 + PENDING(0), + + // 审批成功 + SUCCEED(1), + + // 审批驳回 + REJECT(2), + + // 撤销 + REVOKE(3); + + companion object { + fun get(value: Int): HandoverStatus { + HandoverStatus.values().forEach { + if (value == it.value) return it + } + throw IllegalArgumentException("No enum for constant $value") + } + } +} diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/HandoverType.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/HandoverType.kt new file mode 100644 index 000000000000..5d3dce177853 --- /dev/null +++ b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/HandoverType.kt @@ -0,0 +1,18 @@ +package com.tencent.devops.auth.pojo.enum + +enum class HandoverType(val value: String, val alias: String) { + // 用户组 + GROUP("group", "用户组"), + + // 授权 + AUTHORIZATION("authorization", "授权管理"); + + companion object { + fun get(value: String): HandoverType { + HandoverType.values().forEach { + if (value == it.value) return it + } + throw IllegalArgumentException("No enum for constant $value") + } + } +} diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/JoinedType.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/JoinedType.kt index 06b700c88bdb..8bff11d48701 100644 --- a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/JoinedType.kt +++ b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/JoinedType.kt @@ -32,5 +32,8 @@ enum class JoinedType { DIRECT, // 通过模板加入 - TEMPLATE + TEMPLATE, + + // 通过组织加入 + DEPARTMENT } diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/OperateChannel.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/OperateChannel.kt new file mode 100644 index 000000000000..5c08753de64d --- /dev/null +++ b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/OperateChannel.kt @@ -0,0 +1,37 @@ +/* + * Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-CI 蓝鲸持续集成平台 is licensed under the MIT license. + * + * A copy of the MIT License is included in this file. + * + * + * Terms of the MIT License: + * --------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package com.tencent.devops.auth.pojo.enum + +enum class OperateChannel(val value: String) { + // 个人视角 + PERSONAL("personal"), + + // 管理员视角 + MANAGER("manager"); +} diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/RemoveMemberButtonControl.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/RemoveMemberButtonControl.kt index fc06012edd14..55064743eb0f 100644 --- a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/RemoveMemberButtonControl.kt +++ b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/RemoveMemberButtonControl.kt @@ -37,6 +37,9 @@ enum class RemoveMemberButtonControl { // 通过模板加入,不允许移出组 TEMPLATE, + // 用户通过组织 间接加入,不允许移出组 + DEPARTMENT, + // 其他,允许移出组 OTHER } diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/SortType.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/SortType.kt new file mode 100644 index 000000000000..1ae8dc933fe1 --- /dev/null +++ b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/SortType.kt @@ -0,0 +1,7 @@ +package com.tencent.devops.auth.pojo.enum + +enum class SortType { + FLOW_NO, + + CREATE_TIME; +} diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/request/GroupMemberCommonConditionReq.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/request/GroupMemberCommonConditionReq.kt index db943b94c74d..651969f3bd00 100644 --- a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/request/GroupMemberCommonConditionReq.kt +++ b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/request/GroupMemberCommonConditionReq.kt @@ -1,24 +1,25 @@ package com.tencent.devops.auth.pojo.request import com.tencent.devops.auth.pojo.ResourceMemberInfo +import com.tencent.devops.auth.pojo.dto.MemberGroupJoinedDTO +import com.tencent.devops.auth.pojo.enum.OperateChannel import io.swagger.v3.oas.annotations.media.Schema @Schema(title = "用户组成员处理公共请求体") open class GroupMemberCommonConditionReq( @get:Schema(title = "组IDs") - open val groupIds: List = emptyList(), + open val groupIds: List = emptyList(), @get:Schema(title = "全选的资源类型") open val resourceTypes: List = emptyList(), @get:Schema(title = "全量选择") open val allSelection: Boolean = false, - @get:Schema(title = "是否排除唯一管理员组") - open var excludedUniqueManagerGroup: Boolean = false, @get:Schema(title = "目标对象") - open val targetMember: ResourceMemberInfo + open val targetMember: ResourceMemberInfo, + @get:Schema(title = "操作渠道") + open val operateChannel: OperateChannel = OperateChannel.MANAGER ) { override fun toString(): String { return "GroupMemberCommonConditionReq(groupIds=$groupIds,resourceTypes=$resourceTypes," + - "allSelection=$allSelection,excludedUniqueManagerGroup=$excludedUniqueManagerGroup," + - "targetMember=$targetMember)" + "allSelection=$allSelection,targetMember=$targetMember,operateChannel=$operateChannel)" } } diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/request/GroupMemberHandoverConditionReq.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/request/GroupMemberHandoverConditionReq.kt index dc0f85b0daa7..bd53c502ae31 100644 --- a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/request/GroupMemberHandoverConditionReq.kt +++ b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/request/GroupMemberHandoverConditionReq.kt @@ -29,28 +29,30 @@ package com.tencent.devops.auth.pojo.request import com.tencent.devops.auth.constant.AuthMessageCode.INVALID_HANDOVER_TO import com.tencent.devops.auth.pojo.ResourceMemberInfo +import com.tencent.devops.auth.pojo.dto.MemberGroupJoinedDTO +import com.tencent.devops.auth.pojo.enum.OperateChannel import com.tencent.devops.common.api.exception.ErrorCodeException import io.swagger.v3.oas.annotations.media.Schema @Schema(title = "用户组成员交接条件请求体") data class GroupMemberHandoverConditionReq( @get:Schema(title = "组IDs") - override val groupIds: List = emptyList(), + override val groupIds: List = emptyList(), @get:Schema(title = "全选的资源类型") override val resourceTypes: List = emptyList(), @get:Schema(title = "全量选择") override val allSelection: Boolean = false, - @get:Schema(title = "是否排除唯一管理员组") - override var excludedUniqueManagerGroup: Boolean = false, @get:Schema(title = "目标对象") override val targetMember: ResourceMemberInfo, + @get:Schema(title = "操作渠道") + override val operateChannel: OperateChannel = OperateChannel.MANAGER, @get:Schema(title = "授予人") val handoverTo: ResourceMemberInfo ) : GroupMemberCommonConditionReq( groupIds = groupIds, resourceTypes = resourceTypes, allSelection = allSelection, - excludedUniqueManagerGroup = excludedUniqueManagerGroup, + operateChannel = operateChannel, targetMember = targetMember ) { fun checkHandoverTo() { diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/request/GroupMemberRemoveConditionReq.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/request/GroupMemberRemoveConditionReq.kt new file mode 100644 index 000000000000..c801a2941342 --- /dev/null +++ b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/request/GroupMemberRemoveConditionReq.kt @@ -0,0 +1,65 @@ +/* + * Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-CI 蓝鲸持续集成平台 is licensed under the MIT license. + * + * A copy of the MIT License is included in this file. + * + * + * Terms of the MIT License: + * --------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.devops.auth.pojo.request + +import com.tencent.devops.auth.constant.AuthMessageCode.INVALID_HANDOVER_TO +import com.tencent.devops.auth.pojo.ResourceMemberInfo +import com.tencent.devops.auth.pojo.dto.MemberGroupJoinedDTO +import com.tencent.devops.auth.pojo.enum.OperateChannel +import com.tencent.devops.common.api.exception.ErrorCodeException +import io.swagger.v3.oas.annotations.media.Schema + +@Schema(title = "用户组成员移除条件请求体") +data class GroupMemberRemoveConditionReq( + @get:Schema(title = "组IDs") + override val groupIds: List = emptyList(), + @get:Schema(title = "全选的资源类型") + override val resourceTypes: List = emptyList(), + @get:Schema(title = "全量选择") + override val allSelection: Boolean = false, + @get:Schema(title = "目标对象") + override val targetMember: ResourceMemberInfo, + @get:Schema(title = "操作渠道") + override val operateChannel: OperateChannel = OperateChannel.MANAGER, + @get:Schema(title = "授予人") + val handoverTo: ResourceMemberInfo? = null +) : GroupMemberCommonConditionReq( + groupIds = groupIds, + resourceTypes = resourceTypes, + allSelection = allSelection, + operateChannel = operateChannel, + targetMember = targetMember +) { + fun checkHandoverTo() { + if (handoverTo == null || handoverTo.id == targetMember.id) { + throw ErrorCodeException( + errorCode = INVALID_HANDOVER_TO + ) + } + } +} diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/request/GroupMemberRenewalConditionReq.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/request/GroupMemberRenewalConditionReq.kt index b8b6ce8598c9..37c085a607c2 100644 --- a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/request/GroupMemberRenewalConditionReq.kt +++ b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/request/GroupMemberRenewalConditionReq.kt @@ -28,26 +28,28 @@ package com.tencent.devops.auth.pojo.request import com.tencent.devops.auth.pojo.ResourceMemberInfo +import com.tencent.devops.auth.pojo.dto.MemberGroupJoinedDTO +import com.tencent.devops.auth.pojo.enum.OperateChannel import io.swagger.v3.oas.annotations.media.Schema @Schema(title = "用户组成员续期") data class GroupMemberRenewalConditionReq( @get:Schema(title = "组IDs") - override val groupIds: List, + override val groupIds: List, @get:Schema(title = "全选某种资源类型下的用户组") override val resourceTypes: List = emptyList(), @get:Schema(title = "全量选择") override val allSelection: Boolean = false, - @get:Schema(title = "是否排除唯一管理员组") - override var excludedUniqueManagerGroup: Boolean = false, @get:Schema(title = "目标对象") override val targetMember: ResourceMemberInfo, + @get:Schema(title = "操作渠道") + override val operateChannel: OperateChannel = OperateChannel.MANAGER, @get:Schema(title = "续期时长(天)") val renewalDuration: Int ) : GroupMemberCommonConditionReq( groupIds = groupIds, resourceTypes = resourceTypes, allSelection = allSelection, - excludedUniqueManagerGroup = excludedUniqueManagerGroup, + operateChannel = operateChannel, targetMember = targetMember ) diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/request/HandoverDetailsQueryReq.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/request/HandoverDetailsQueryReq.kt new file mode 100644 index 000000000000..81065ed94d44 --- /dev/null +++ b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/request/HandoverDetailsQueryReq.kt @@ -0,0 +1,38 @@ +package com.tencent.devops.auth.pojo.request + +import com.tencent.devops.auth.pojo.enum.HandoverQueryChannel +import io.swagger.v3.oas.annotations.media.Schema + +@Schema(title = "权限交接详细查询请求体") +data class HandoverDetailsQueryReq( + @get:Schema(title = "项目ID") + val projectCode: String, + @get:Schema(title = "组/授权资源关联的资源类型") + val resourceType: String, + @get:Schema(title = "流程单号") + val flowNo: String?, + @get:Schema(title = "交接预览请求条件") + val previewConditionReq: GroupMemberCommonConditionReq?, + @get:Schema(title = "渠道") + val queryChannel: HandoverQueryChannel, + @get:Schema(title = "第几页") + val page: Int, + @get:Schema(title = "每页大小") + val pageSize: Int +) { + fun check() { + when (queryChannel) { + HandoverQueryChannel.HANDOVER_APPLICATION -> { + if (flowNo == null) { + throw IllegalArgumentException("flowNo cannot be null!") + } + } + + else -> { + if (previewConditionReq == null) { + throw IllegalArgumentException("previewConditionReq can not be null!") + } + } + } + } +} diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/request/HandoverOverviewBatchUpdateReq.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/request/HandoverOverviewBatchUpdateReq.kt new file mode 100644 index 000000000000..bc15c9e8e2c7 --- /dev/null +++ b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/request/HandoverOverviewBatchUpdateReq.kt @@ -0,0 +1,18 @@ +package com.tencent.devops.auth.pojo.request + +import com.tencent.devops.auth.pojo.enum.HandoverAction +import io.swagger.v3.oas.annotations.media.Schema + +@Schema(title = "批量更新权限交接请求体") +data class HandoverOverviewBatchUpdateReq( + @get:Schema(title = "流程单号") + val flowNos: List = emptyList(), + @get:Schema(title = "是否全选") + val allSelection: Boolean = false, + @get:Schema(title = "操作人") + val operator: String, + @get:Schema(title = "审批操作") + val handoverAction: HandoverAction, + @get:Schema(title = "备注") + val remark: String? = null +) diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/request/HandoverOverviewQueryReq.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/request/HandoverOverviewQueryReq.kt new file mode 100644 index 000000000000..0b5bbcdf76c1 --- /dev/null +++ b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/request/HandoverOverviewQueryReq.kt @@ -0,0 +1,40 @@ +package com.tencent.devops.auth.pojo.request + +import com.tencent.devops.auth.pojo.enum.CollationType +import com.tencent.devops.auth.pojo.enum.HandoverStatus +import com.tencent.devops.auth.pojo.enum.SortType +import io.swagger.v3.oas.annotations.media.Schema + +@Schema(title = "权限交接总览查询") +data class HandoverOverviewQueryReq( + @get:Schema(title = "成员ID") + val memberId: String, + @get:Schema(title = "项目ID") + val projectCode: String? = null, + @get:Schema(title = "项目名称") + val projectName: String? = null, + @get:Schema(title = "项目ID") + val title: String? = null, + @get:Schema(title = "流程单号") + val flowNo: String? = null, + @get:Schema(title = "流程单号列表") + val flowNos: List? = null, + @get:Schema(title = "申请人") + val applicant: String? = null, + @get:Schema(title = "审批人") + val approver: String? = null, + @get:Schema(title = "审批结果") + val handoverStatus: HandoverStatus? = null, + @get:Schema(title = "最小提单时间") + val minCreatedTime: Long? = null, + @get:Schema(title = "最打提单时间") + val maxCreatedTime: Long? = null, + @get:Schema(title = "排序类型") + val sortType: SortType? = null, + @get:Schema(title = "排序类型") + val collationType: CollationType? = null, + @get:Schema(title = "页数") + val page: Int? = null, + @get:Schema(title = "页大小") + val pageSize: Int? = null +) diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/request/HandoverOverviewUpdateReq.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/request/HandoverOverviewUpdateReq.kt new file mode 100644 index 000000000000..cd0c27b2b21e --- /dev/null +++ b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/request/HandoverOverviewUpdateReq.kt @@ -0,0 +1,18 @@ +package com.tencent.devops.auth.pojo.request + +import com.tencent.devops.auth.pojo.enum.HandoverAction +import io.swagger.v3.oas.annotations.media.Schema + +@Schema(title = "更新权限交接总览请求体") +data class HandoverOverviewUpdateReq( + @get:Schema(title = "项目ID") + val projectCode: String, + @get:Schema(title = "流程单号") + val flowNo: String, + @get:Schema(title = "操作人") + val operator: String, + @get:Schema(title = "审批操作") + val handoverAction: HandoverAction, + @get:Schema(title = "备注") + val remark: String? = null +) diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/request/ResourceType2CountOfHandoverQuery.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/request/ResourceType2CountOfHandoverQuery.kt new file mode 100644 index 000000000000..a3c0cd1c298a --- /dev/null +++ b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/request/ResourceType2CountOfHandoverQuery.kt @@ -0,0 +1,34 @@ +package com.tencent.devops.auth.pojo.request + +import com.tencent.devops.auth.pojo.enum.BatchOperateType +import com.tencent.devops.auth.pojo.enum.HandoverQueryChannel +import io.swagger.v3.oas.annotations.media.Schema + +data class ResourceType2CountOfHandoverQuery( + @get:Schema(title = "项目ID") + val projectCode: String, + @get:Schema(title = "渠道") + val queryChannel: HandoverQueryChannel, + @get:Schema(title = "流程单号") + val flowNo: String?, + @get:Schema(title = "交接预览请求条件") + val previewConditionReq: GroupMemberCommonConditionReq?, + @get:Schema(title = "批量操作动作") + val batchOperateType: BatchOperateType? +) { + fun check() { + when (queryChannel) { + HandoverQueryChannel.HANDOVER_APPLICATION -> { + if (flowNo == null) { + throw IllegalArgumentException("flowNo cannot be null!") + } + } + + else -> { + if (previewConditionReq == null || batchOperateType == null) { + throw IllegalArgumentException("previewConditionReq or batchOperateType can not be null!") + } + } + } + } +} diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/vo/AuthProjectVO.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/vo/AuthProjectVO.kt new file mode 100644 index 000000000000..7aa6eac5485e --- /dev/null +++ b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/vo/AuthProjectVO.kt @@ -0,0 +1,11 @@ +package com.tencent.devops.auth.pojo.vo + +import io.swagger.v3.oas.annotations.media.Schema + +@Schema(title = "项目返回体") +data class AuthProjectVO( + @get:Schema(title = "数量") + val projectCode: String, + @get:Schema(title = "项目") + val projectName: String +) diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/vo/BatchOperateGroupMemberCheckVo.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/vo/BatchOperateGroupMemberCheckVo.kt index 501eb12299c5..e208d27c74fc 100644 --- a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/vo/BatchOperateGroupMemberCheckVo.kt +++ b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/vo/BatchOperateGroupMemberCheckVo.kt @@ -6,6 +6,20 @@ import io.swagger.v3.oas.annotations.media.Schema data class BatchOperateGroupMemberCheckVo( @get:Schema(title = "总数") val totalCount: Int, + @get:Schema(title = "可操作的数量") + val operableCount: Int? = 0, @get:Schema(title = "无法操作的数量") - val inoperableCount: Int? = null + val inoperableCount: Int? = 0, + @get:Schema(title = "唯一管理员组的数量") + val uniqueManagerCount: Int? = 0, + @get:Schema(title = "导致代持人失效的用户组") + val invalidGroupCount: Int? = 0, + @get:Schema(title = "无效的流水线授权数量") + val invalidPipelineAuthorizationCount: Int? = 0, + @get:Schema(title = "无效的代码库授权数量") + val invalidRepositoryAuthorizationCount: Int? = 0, + @get:Schema(title = "无效的环境节点授权数量") + val invalidEnvNodeAuthorizationCount: Int? = 0, + @get:Schema(title = "可交接的组数量") + val canHandoverCount: Int? = 0 ) diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/vo/GroupDetailsInfoVo.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/vo/GroupDetailsInfoVo.kt index 6193dda5c6ef..846dd81c6817 100644 --- a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/vo/GroupDetailsInfoVo.kt +++ b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/vo/GroupDetailsInfoVo.kt @@ -1,6 +1,7 @@ package com.tencent.devops.auth.pojo.vo import com.tencent.devops.auth.pojo.enum.JoinedType +import com.tencent.devops.auth.pojo.enum.MemberType import com.tencent.devops.auth.pojo.enum.RemoveMemberButtonControl import io.swagger.v3.oas.annotations.media.Schema @@ -29,5 +30,11 @@ data class GroupDetailsInfoVo( @get:Schema(title = "加入方式") val joinedType: JoinedType, @get:Schema(title = "操作人") - val operator: String + val operator: String, + @get:Schema(title = "是否正在交接") + val beingHandedOver: Boolean? = null, + @get:Schema(title = "交接单号") + val flowNo: String? = null, + @get:Schema(title = "组成员类型") + val memberType: MemberType? = null ) diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/vo/HandoverAuthorizationDetailVo.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/vo/HandoverAuthorizationDetailVo.kt new file mode 100644 index 000000000000..0828981a8d45 --- /dev/null +++ b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/vo/HandoverAuthorizationDetailVo.kt @@ -0,0 +1,16 @@ +package com.tencent.devops.auth.pojo.vo + +import com.tencent.devops.auth.pojo.enum.HandoverType +import io.swagger.v3.oas.annotations.media.Schema + +@Schema(title = "授权交接详细表") +data class HandoverAuthorizationDetailVo( + @get:Schema(title = "授权资源ID") + val resourceCode: String, + @get:Schema(title = "授权资源名称") + val resourceName: String, + @get:Schema(title = "交接类型") + val handoverType: HandoverType, + @get:Schema(title = "授权人") + val handoverFrom: String +) diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/vo/HandoverGroupDetailVo.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/vo/HandoverGroupDetailVo.kt new file mode 100644 index 000000000000..6a6e3e40e82b --- /dev/null +++ b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/vo/HandoverGroupDetailVo.kt @@ -0,0 +1,19 @@ +package com.tencent.devops.auth.pojo.vo + +import io.swagger.v3.oas.annotations.media.Schema + +@Schema(title = "用户组交接详细返回体") +data class HandoverGroupDetailVo( + @get:Schema(title = "项目ID") + val projectCode: String, + @get:Schema(title = "组ID") + val iamGroupId: Int, + @get:Schema(title = "组名称") + val groupName: String, + @get:Schema(title = "组描述") + val groupDesc: String? = null, + @get:Schema(title = "关联的资源ID") + val resourceCode: String, + @get:Schema(title = "关联的资源名称") + val resourceName: String +) diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/vo/HandoverOverviewVo.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/vo/HandoverOverviewVo.kt new file mode 100644 index 000000000000..7069614b4a67 --- /dev/null +++ b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/vo/HandoverOverviewVo.kt @@ -0,0 +1,39 @@ +package com.tencent.devops.auth.pojo.vo + +import com.tencent.devops.auth.pojo.enum.HandoverStatus +import io.swagger.v3.oas.annotations.media.Schema +import java.time.LocalDateTime + +@Schema(title = "权限交接总览返回体") +data class HandoverOverviewVo( + @get:Schema(title = "ID") + val id: Long, + @get:Schema(title = "项目ID") + val projectCode: String, + @get:Schema(title = "项目名称") + val projectName: String, + @get:Schema(title = "标题") + val title: String, + @get:Schema(title = "流程单号") + val flowNo: String, + @get:Schema(title = "申请人") + val applicant: String, + @get:Schema(title = "审批人") + val approver: String, + @get:Schema(title = "审批结果") + val handoverStatus: HandoverStatus, + @get:Schema(title = "用户组个数") + val groupCount: Int, + @get:Schema(title = "授权个数") + val authorizationCount: Int, + @get:Schema(title = "创建时间") + val createTime: LocalDateTime, + @get:Schema(title = "最后修改人") + val lastOperator: String? = null, + @get:Schema(title = "是否可以撤销,提单为当前用户并且单据处于审批中") + val canRevoke: Boolean? = null, + @get:Schema(title = "是否可以审批,审批人为当前用户并且单据处于审批中") + val canApproval: Boolean? = null, + @get:Schema(title = "备注") + val remark: String? = null +) diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/vo/MemberGroupCountWithPermissionsVo.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/vo/ResourceType2CountVo.kt similarity index 63% rename from src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/vo/MemberGroupCountWithPermissionsVo.kt rename to src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/vo/ResourceType2CountVo.kt index 293d3646046c..49d6a24ff37e 100644 --- a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/vo/MemberGroupCountWithPermissionsVo.kt +++ b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/vo/ResourceType2CountVo.kt @@ -1,13 +1,16 @@ package com.tencent.devops.auth.pojo.vo +import com.tencent.devops.auth.pojo.enum.HandoverType import io.swagger.v3.oas.annotations.media.Schema @Schema(title = "用户有权限的用户组数量") -data class MemberGroupCountWithPermissionsVo( +data class ResourceType2CountVo( @get:Schema(title = "资源类型") val resourceType: String, @get:Schema(title = "资源类型名") val resourceTypeName: String, @get:Schema(title = "数量") - val count: Long + val count: Long, + @get:Schema(title = "类型") + val type: HandoverType = HandoverType.GROUP ) diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/AuthAuthorizationDao.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/AuthAuthorizationDao.kt index dca6f34e966d..6d2be47ba955 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/AuthAuthorizationDao.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/AuthAuthorizationDao.kt @@ -73,7 +73,6 @@ class AuthAuthorizationDao { .set(HANDOVER_FROM_CN_NAME, resourceAuthorizationDto.handoverFromCnName) .set(RESOURCE_NAME, resourceAuthorizationDto.resourceName) .set(HANDOVER_TIME, handoverDateTime) - .where(CREATE_TIME.eq(UPDATE_TIME)) .execute() } } @@ -191,6 +190,12 @@ class AuthAuthorizationDao { if (resourceName != null) { conditions.add(RESOURCE_NAME.like("%$resourceName%")) } + if (!filterResourceCodes.isNullOrEmpty()) { + conditions.add(RESOURCE_CODE.`in`(filterResourceCodes)) + } + if (!excludeResourceCodes.isNullOrEmpty()) { + conditions.add(RESOURCE_CODE.notIn(excludeResourceCodes)) + } if (handoverFrom != null) { conditions.add(HANDOVER_FROM.eq(handoverFrom)) } @@ -202,8 +207,22 @@ class AuthAuthorizationDao { return conditions } + fun listUserProjects( + dslContext: DSLContext, + userId: String + ): List { + return with(TAuthResourceAuthorization.T_AUTH_RESOURCE_AUTHORIZATION) { + dslContext.select(PROJECT_CODE) + .from(this) + .where(HANDOVER_FROM.eq(userId)) + .groupBy(PROJECT_CODE) + .fetch().map { it.value1() } + } + } + fun TAuthResourceAuthorizationRecord.convert(): ResourceAuthorizationResponse { return ResourceAuthorizationResponse( + id = id, projectCode = projectCode, resourceType = resourceType, resourceName = resourceName, diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/AuthHandoverDetailDao.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/AuthHandoverDetailDao.kt new file mode 100644 index 000000000000..73a5ac000dc9 --- /dev/null +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/AuthHandoverDetailDao.kt @@ -0,0 +1,128 @@ +package com.tencent.devops.auth.dao + +import com.tencent.devops.auth.pojo.dto.HandoverDetailDTO +import com.tencent.devops.auth.pojo.enum.HandoverType +import com.tencent.devops.model.auth.tables.TAuthHandoverDetail +import com.tencent.devops.model.auth.tables.records.TAuthHandoverDetailRecord +import org.jooq.Condition +import org.jooq.DSLContext +import org.jooq.impl.DSL.count +import org.springframework.stereotype.Repository + +@Repository +class AuthHandoverDetailDao { + fun batchCreate( + dslContext: DSLContext, + handoverDetailDTOs: List + ) { + with(TAuthHandoverDetail.T_AUTH_HANDOVER_DETAIL) { + handoverDetailDTOs.forEach { + dslContext.insertInto( + this, + PROJECT_CODE, + FLOW_NO, + ITEM_ID, + RESOURCE_TYPE, + HANDOVER_TYPE + ).values( + it.projectCode, + it.flowNo, + it.itemId, + it.resourceType, + it.handoverType.value + ).execute() + } + } + } + + fun list( + dslContext: DSLContext, + projectCode: String, + flowNos: List, + resourceType: String?, + handoverType: HandoverType? + ): List { + with(TAuthHandoverDetail.T_AUTH_HANDOVER_DETAIL) { + return dslContext.selectFrom(this) + .where( + buildQueryConditions( + projectCode = projectCode, + flowNos = flowNos, + resourceType = resourceType, + handoverType = handoverType + ) + ).fetch().map { it.convert() } + } + } + + fun count( + dslContext: DSLContext, + projectCode: String, + flowNos: List, + resourceType: String?, + handoverType: HandoverType? + ): Long { + with(TAuthHandoverDetail.T_AUTH_HANDOVER_DETAIL) { + return dslContext.selectCount().from(this) + .where( + buildQueryConditions( + projectCode = projectCode, + flowNos = flowNos, + resourceType = resourceType, + handoverType = handoverType + ) + ).fetchOne(0, Long::class.java)!! + } + } + + fun countWithResourceType( + dslContext: DSLContext, + projectCode: String, + flowNo: String, + handoverType: HandoverType? + ): Map { + with(TAuthHandoverDetail.T_AUTH_HANDOVER_DETAIL) { + return dslContext.select(RESOURCE_TYPE, count()) + .from(this) + .where( + buildQueryConditions( + projectCode = projectCode, + flowNos = listOf(flowNo), + resourceType = null, + handoverType = handoverType + ) + ).groupBy(RESOURCE_TYPE) + .fetch().map { Pair(it.value1(), it.value2().toLong()) }.toMap() + } + } + + private fun buildQueryConditions( + projectCode: String, + flowNos: List, + resourceType: String?, + handoverType: HandoverType? + ): List { + with(TAuthHandoverDetail.T_AUTH_HANDOVER_DETAIL) { + val conditions = mutableListOf() + conditions.add(PROJECT_CODE.eq(projectCode)) + conditions.add(FLOW_NO.`in`(flowNos)) + resourceType?.let { + conditions.add(RESOURCE_TYPE.eq(resourceType)) + } + handoverType?.let { + conditions.add(HANDOVER_TYPE.eq(handoverType.value)) + } + return conditions + } + } + + private fun TAuthHandoverDetailRecord.convert(): HandoverDetailDTO { + return HandoverDetailDTO( + projectCode = projectCode, + flowNo = flowNo, + itemId = itemId, + resourceType = resourceType, + handoverType = HandoverType.get(handoverType) + ) + } +} diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/AuthHandoverOverviewDao.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/AuthHandoverOverviewDao.kt new file mode 100644 index 000000000000..e4e837b84572 --- /dev/null +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/AuthHandoverOverviewDao.kt @@ -0,0 +1,175 @@ +package com.tencent.devops.auth.dao + +import com.tencent.devops.auth.pojo.dto.HandoverOverviewCreateDTO +import com.tencent.devops.auth.pojo.enum.CollationType +import com.tencent.devops.auth.pojo.enum.HandoverStatus +import com.tencent.devops.auth.pojo.enum.SortType +import com.tencent.devops.auth.pojo.request.HandoverOverviewQueryReq +import com.tencent.devops.auth.pojo.request.HandoverOverviewUpdateReq +import com.tencent.devops.auth.pojo.vo.HandoverOverviewVo +import com.tencent.devops.common.api.util.DateTimeUtil +import com.tencent.devops.common.api.util.PageUtil +import com.tencent.devops.common.db.utils.skipCheck +import com.tencent.devops.model.auth.tables.TAuthHandoverOverview +import com.tencent.devops.model.auth.tables.records.TAuthHandoverOverviewRecord +import org.jooq.Condition +import org.jooq.DSLContext +import org.springframework.stereotype.Repository +import java.time.LocalDateTime + +@Repository +class AuthHandoverOverviewDao { + fun create( + dslContext: DSLContext, + overviewDTO: HandoverOverviewCreateDTO + ) { + with(TAuthHandoverOverview.T_AUTH_HANDOVER_OVERVIEW) { + dslContext.insertInto( + this, + PROJECT_CODE, + PROJECT_NAME, + TITLE, + FLOW_NO, + APPLICANT, + APPROVER, + STATUS, + GROUP_COUNT, + AUTHORIZATION_COUNT, + REMARK + ).values( + overviewDTO.projectCode, + overviewDTO.projectName, + overviewDTO.title, + overviewDTO.flowNo, + overviewDTO.applicant, + overviewDTO.approver, + overviewDTO.handoverStatus.value, + overviewDTO.groupCount, + overviewDTO.authorizationCount, + overviewDTO.remark + ).execute() + } + } + + fun update( + dslContext: DSLContext, + overviewDTO: HandoverOverviewUpdateReq + ) { + with(TAuthHandoverOverview.T_AUTH_HANDOVER_OVERVIEW) { + dslContext.update(this) + .set(STATUS, overviewDTO.handoverAction.value) + .let { if (overviewDTO.remark != null) it.set(REMARK, overviewDTO.remark) else it } + .set(UPDATE_TIME, LocalDateTime.now()) + .set(LAST_OPERATOR, overviewDTO.operator) + .where(FLOW_NO.eq(overviewDTO.flowNo)) + .and(PROJECT_CODE.eq(overviewDTO.projectCode)) + .execute() + } + } + + fun get( + dslContext: DSLContext, + flowNo: String + ): HandoverOverviewVo? { + return with(TAuthHandoverOverview.T_AUTH_HANDOVER_OVERVIEW) { + dslContext.selectFrom(this) + .where(FLOW_NO.eq(flowNo)) + .fetchAny()?.convert() + } + } + + fun list( + dslContext: DSLContext, + queryRequest: HandoverOverviewQueryReq + ): List { + return with(TAuthHandoverOverview.T_AUTH_HANDOVER_OVERVIEW) { + dslContext.selectFrom(this) + .where(buildQueryConditions(queryRequest)) + .let { + when { + queryRequest.sortType == SortType.FLOW_NO && queryRequest.collationType == CollationType.ASC -> { + it.orderBy(FLOW_NO.asc()) + } + + queryRequest.sortType == SortType.FLOW_NO && queryRequest.collationType == CollationType.DESC -> { + it.orderBy(FLOW_NO.desc()) + } + + queryRequest.sortType == SortType.CREATE_TIME && queryRequest.collationType == CollationType.ASC -> { + it.orderBy(CREATE_TIME.asc()) + } + + queryRequest.sortType == SortType.CREATE_TIME && queryRequest.collationType == CollationType.DESC -> { + it.orderBy(CREATE_TIME.desc()) + } + + else -> { + it.orderBy(FLOW_NO.desc()) + } + } + } + .let { + if (queryRequest.page != null && queryRequest.pageSize != null) { + val sqlLimit = PageUtil.convertPageSizeToSQLLimit(queryRequest.page, queryRequest.pageSize) + it.limit(sqlLimit.limit).offset(sqlLimit.offset) + } else { + it + } + } + .skipCheck() + .fetch() + .map { it.convert(queryRequest.memberId) } + } + } + + fun count( + dslContext: DSLContext, + queryRequest: HandoverOverviewQueryReq + ): Long { + return with(TAuthHandoverOverview.T_AUTH_HANDOVER_OVERVIEW) { + dslContext.selectCount().from(this) + .where(buildQueryConditions(queryRequest)) + .fetchOne(0, Long::class.java)!! + } + } + + private fun buildQueryConditions( + queryRequest: HandoverOverviewQueryReq + ): List { + with(TAuthHandoverOverview.T_AUTH_HANDOVER_OVERVIEW) { + val conditions = mutableListOf() + conditions.add(APPROVER.eq(queryRequest.memberId).or(APPLICANT.eq(queryRequest.memberId))) + queryRequest.projectCode?.let { conditions.add(PROJECT_CODE.eq(queryRequest.projectCode)) } + queryRequest.projectName?.let { conditions.add(PROJECT_NAME.like("%${queryRequest.projectName}%")) } + queryRequest.title?.let { conditions.add(TITLE.like("%${queryRequest.title}%")) } + queryRequest.flowNo?.let { conditions.add(FLOW_NO.eq(queryRequest.flowNo)) } + queryRequest.flowNos?.let { conditions.add(FLOW_NO.`in`(queryRequest.flowNos)) } + queryRequest.applicant?.let { conditions.add(APPLICANT.like("%${queryRequest.applicant}%")) } + queryRequest.approver?.let { conditions.add(APPROVER.like("%${queryRequest.approver}%")) } + queryRequest.handoverStatus?.let { conditions.add(STATUS.eq(queryRequest.handoverStatus!!.value)) } + queryRequest.minCreatedTime?.let { conditions.add(CREATE_TIME.ge(DateTimeUtil.convertTimestampToLocalDateTime(it / 1000))) } + queryRequest.maxCreatedTime?.let { conditions.add(CREATE_TIME.le(DateTimeUtil.convertTimestampToLocalDateTime(it / 1000))) } + return conditions + } + } + + fun TAuthHandoverOverviewRecord.convert(memberId: String? = null): HandoverOverviewVo { + return HandoverOverviewVo( + id = id, + projectCode = projectCode, + projectName = projectName, + title = title, + flowNo = flowNo, + applicant = applicant, + approver = approver, + handoverStatus = HandoverStatus.get(status), + groupCount = groupCount, + authorizationCount = authorizationCount, + lastOperator = lastOperator, + createTime = createTime, + canRevoke = memberId?.let { memberId == applicant && status == HandoverStatus.PENDING.value }, + canApproval = memberId?.let { memberId == approver && status == HandoverStatus.PENDING.value }, + remark = remark + ) + } +} diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/AuthResourceDao.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/AuthResourceDao.kt index 82ce971c7a30..f07e40207bdb 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/AuthResourceDao.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/AuthResourceDao.kt @@ -314,6 +314,19 @@ class AuthResourceDao { } } + fun listByResourceCodes( + dslContext: DSLContext, + resourceType: String, + resourceCodes: List + ): List { + return with(TAuthResource.T_AUTH_RESOURCE) { + dslContext.selectFrom(this) + .where(RESOURCE_TYPE.eq(resourceType)) + .and(RESOURCE_CODE.`in`(resourceCodes)) + .fetch().map { convert(it) } + } + } + fun getResourceCodeByType( dslContext: DSLContext, projectCode: String, diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/AuthResourceGroupConfigDao.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/AuthResourceGroupConfigDao.kt index 73a0268d9c85..24e2d69f5196 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/AuthResourceGroupConfigDao.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/AuthResourceGroupConfigDao.kt @@ -28,6 +28,7 @@ package com.tencent.devops.auth.dao +import com.tencent.devops.common.db.utils.skipCheck import com.tencent.devops.model.auth.tables.TAuthResourceGroupConfig import com.tencent.devops.model.auth.tables.records.TAuthResourceGroupConfigRecord import org.jooq.DSLContext @@ -110,6 +111,7 @@ class AuthResourceGroupConfigDao { dslContext.selectFrom(this) .orderBy(CREATE_TIME.desc(), RESOURCE_TYPE, GROUP_CODE) .limit(pageSize).offset((page - 1) * pageSize) + .skipCheck() .fetch() } } diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/AuthResourceGroupDao.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/AuthResourceGroupDao.kt index e18db590fd82..f4a0c58ccbcd 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/AuthResourceGroupDao.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/AuthResourceGroupDao.kt @@ -30,6 +30,7 @@ package com.tencent.devops.auth.dao import com.tencent.devops.auth.pojo.AuthResourceGroup import com.tencent.devops.common.auth.api.AuthResourceType +import org.jooq.impl.DSL.count import com.tencent.devops.model.auth.tables.TAuthResourceGroup import com.tencent.devops.model.auth.tables.records.TAuthResourceGroupRecord import org.jooq.DSLContext @@ -204,6 +205,21 @@ class AuthResourceGroupDao { } } + fun getResourceType2Count( + dslContext: DSLContext, + projectCode: String, + iamGroupIds: List + ): Map { + return with(TAuthResourceGroup.T_AUTH_RESOURCE_GROUP) { + dslContext.select(RESOURCE_TYPE, count()) + .from(this) + .where(PROJECT_CODE.eq(projectCode)) + .and(RELATION_ID.`in`(iamGroupIds)) + .groupBy(RESOURCE_TYPE) + .fetch().map { Pair(it.value1(), it.value2().toLong()) }.toMap() + } + } + fun delete( dslContext: DSLContext, projectCode: String, @@ -368,6 +384,7 @@ class AuthResourceGroupDao { dslContext: DSLContext, projectCode: String, resourceType: String, + iamGroupIds: List? = null, offset: Int, limit: Int ): List { @@ -375,6 +392,14 @@ class AuthResourceGroupDao { val records = dslContext.selectFrom(this) .where(PROJECT_CODE.eq(projectCode)) .and(RESOURCE_TYPE.eq(resourceType)) + .let { + if (!iamGroupIds.isNullOrEmpty()) { + it.and(RELATION_ID.`in`(iamGroupIds)) + } else { + it + } + } + .orderBy(CREATE_TIME) .offset(offset) .limit(limit) .fetch() diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/AuthResourceGroupMemberDao.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/AuthResourceGroupMemberDao.kt index c18017d08a2f..e8fbeab28835 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/AuthResourceGroupMemberDao.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/AuthResourceGroupMemberDao.kt @@ -27,7 +27,6 @@ package com.tencent.devops.auth.dao -import com.tencent.bk.sdk.iam.constants.ManagerScopesEnum import com.tencent.devops.auth.pojo.AuthResourceGroupMember import com.tencent.devops.auth.pojo.ResourceMemberInfo import com.tencent.devops.auth.pojo.dto.ProjectMembersQueryConditionDTO @@ -444,17 +443,17 @@ class AuthResourceGroupMemberDao { with(projectMembersQueryConditionDTO) { conditions.add(PROJECT_CODE.eq(projectCode)) if (queryTemplate == false) { - conditions.add(MEMBER_TYPE.notEqual(ManagerScopesEnum.getType(ManagerScopesEnum.TEMPLATE))) + conditions.add(MEMBER_TYPE.notEqual(MemberType.TEMPLATE.type)) } else { - conditions.add(MEMBER_TYPE.eq(ManagerScopesEnum.getType(ManagerScopesEnum.TEMPLATE))) + conditions.add(MEMBER_TYPE.eq(MemberType.TEMPLATE.type)) } memberType?.let { type -> conditions.add(MEMBER_TYPE.eq(type)) } userName?.let { name -> - conditions.add(MEMBER_TYPE.eq(ManagerScopesEnum.getType(ManagerScopesEnum.USER))) + conditions.add(MEMBER_TYPE.eq(MemberType.USER.type)) conditions.add(MEMBER_ID.like("%$name%").or(MEMBER_NAME.like("%$name%"))) } deptName?.let { name -> - conditions.add(MEMBER_TYPE.eq(ManagerScopesEnum.getType(ManagerScopesEnum.DEPARTMENT))) + conditions.add(MEMBER_TYPE.eq(MemberType.DEPARTMENT.type)) conditions.add(MEMBER_NAME.like("%$name%")) } minExpiredTime?.let { minTime -> conditions.add(EXPIRED_TIME.ge(minTime)) } @@ -522,7 +521,7 @@ class AuthResourceGroupMemberDao { ) .from(tResourceGroupMember) .where(tResourceGroupMember.PROJECT_CODE.eq(projectCode)) - .and(tResourceGroupMember.MEMBER_TYPE.notEqual(ManagerScopesEnum.getType(ManagerScopesEnum.TEMPLATE))) + .and(tResourceGroupMember.MEMBER_TYPE.notEqual(MemberType.TEMPLATE.type)) .groupBy(tResourceGroupMember.MEMBER_ID) .unionAll( dslContext.select( @@ -551,11 +550,11 @@ class AuthResourceGroupMemberDao { conditions.add(memberTypeField.eq(memberType)) } if (userName != null) { - conditions.add(memberTypeField.eq(ManagerScopesEnum.getType(ManagerScopesEnum.USER))) + conditions.add(memberTypeField.eq(MemberType.USER.type)) conditions.add(memberId.like("%$userName%").or(memberName.like("%$userName%"))) } if (deptName != null) { - conditions.add(memberTypeField.eq(ManagerScopesEnum.getType(ManagerScopesEnum.DEPARTMENT))) + conditions.add(memberTypeField.eq(MemberType.DEPARTMENT.type)) conditions.add(memberName.like("%$deptName%")) } return conditions @@ -564,7 +563,7 @@ class AuthResourceGroupMemberDao { /** * 获取成员按资源类型分组数量 */ - fun countMemberGroup( + fun countMemberGroupOfResourceType( dslContext: DSLContext, projectCode: String, memberId: String, @@ -572,7 +571,8 @@ class AuthResourceGroupMemberDao { resourceType: String? = null, iamGroupIds: List? = null, minExpiredAt: LocalDateTime? = null, - maxExpiredAt: LocalDateTime? = null + maxExpiredAt: LocalDateTime? = null, + memberDeptInfos: List? = null ): Map { val conditions = buildMemberGroupCondition( projectCode = projectCode, @@ -581,7 +581,8 @@ class AuthResourceGroupMemberDao { resourceType = resourceType, iamGroupIds = iamGroupIds, minExpiredAt = minExpiredAt, - maxExpiredAt = maxExpiredAt + maxExpiredAt = maxExpiredAt, + memberDeptInfos = memberDeptInfos ) return with(TAuthResourceGroupMember.T_AUTH_RESOURCE_GROUP_MEMBER) { val select = dslContext.select(RESOURCE_TYPE, count()) @@ -592,6 +593,69 @@ class AuthResourceGroupMemberDao { } } + fun countMemberGroup( + dslContext: DSLContext, + projectCode: String, + memberId: String, + iamTemplateIds: List, + resourceType: String? = null, + iamGroupIds: List? = null, + excludeIamGroupIds: List? = null, + minExpiredAt: LocalDateTime? = null, + maxExpiredAt: LocalDateTime? = null, + memberDeptInfos: List? = null, + filterMemberType: MemberType? = null, + onlyExcludeUserDirectlyJoined: Boolean? = false + ): Long { + val conditions = buildMemberGroupCondition( + projectCode = projectCode, + memberId = memberId, + iamTemplateIds = iamTemplateIds, + resourceType = resourceType, + iamGroupIds = iamGroupIds, + minExpiredAt = minExpiredAt, + maxExpiredAt = maxExpiredAt, + memberDeptInfos = memberDeptInfos, + filterMemberType = filterMemberType + ) + val excludeConditions = buildExcludeMemberGroupCondition( + excludeIamGroupIds = excludeIamGroupIds, + onlyExcludeUserDirectlyJoined = onlyExcludeUserDirectlyJoined + ) + return with(TAuthResourceGroupMember.T_AUTH_RESOURCE_GROUP_MEMBER) { + dslContext.select(count()) + .from(this) + .where(conditions) + .let { + excludeConditions.forEach { excludeCondition -> + it.andNot(excludeCondition) + } + it + } + .fetchOne(0, Long::class.java) ?: 0L + } + } + + fun buildExcludeMemberGroupCondition( + excludeIamGroupIds: List?, + // 仅排除用户直接加入的组 + onlyExcludeUserDirectlyJoined: Boolean? + ): MutableList { + val conditions = mutableListOf() + with(TAuthResourceGroupMember.T_AUTH_RESOURCE_GROUP_MEMBER) { + if (!excludeIamGroupIds.isNullOrEmpty()) { + // 仅排除用户直接加入的用户组 + if (onlyExcludeUserDirectlyJoined == true) { + conditions.add(IAM_GROUP_ID.`in`(excludeIamGroupIds).and(MEMBER_TYPE.eq(MemberType.USER.type))) + } else { + // 会把组织/用户/模板加入的組都排除 + conditions.add(IAM_GROUP_ID.`in`(excludeIamGroupIds)) + } + } + } + return conditions + } + fun listMemberGroupIdsInProject( dslContext: DSLContext, projectCode: String, @@ -617,11 +681,15 @@ class AuthResourceGroupMemberDao { dslContext: DSLContext, projectCode: String, memberId: String, - iamTemplateIds: List, + iamTemplateIds: List? = emptyList(), resourceType: String? = null, iamGroupIds: List? = null, + excludeIamGroupIds: List? = null, minExpiredAt: LocalDateTime? = null, maxExpiredAt: LocalDateTime? = null, + memberDeptInfos: List? = null, + filterMemberType: MemberType? = null, + onlyExcludeUserDirectlyJoined: Boolean? = false, offset: Int? = null, limit: Int? = null ): List { @@ -632,11 +700,23 @@ class AuthResourceGroupMemberDao { resourceType = resourceType, iamGroupIds = iamGroupIds, minExpiredAt = minExpiredAt, - maxExpiredAt = maxExpiredAt + maxExpiredAt = maxExpiredAt, + memberDeptInfos = memberDeptInfos, + filterMemberType = filterMemberType + ) + val excludeConditions = buildExcludeMemberGroupCondition( + excludeIamGroupIds = excludeIamGroupIds, + onlyExcludeUserDirectlyJoined = onlyExcludeUserDirectlyJoined ) return with(TAuthResourceGroupMember.T_AUTH_RESOURCE_GROUP_MEMBER) { dslContext.selectFrom(this) .where(conditions) + .let { + excludeConditions.forEach { excludeCondition -> + it.andNot(excludeCondition) + } + it + } .orderBy(IAM_GROUP_ID.desc()) .let { if (offset != null && limit != null) it.offset(offset).limit(limit) else it } .fetch() @@ -647,12 +727,13 @@ class AuthResourceGroupMemberDao { private fun buildMemberGroupCondition( projectCode: String, memberId: String, - iamTemplateIds: List, + iamTemplateIds: List? = emptyList(), resourceType: String? = null, iamGroupIds: List? = null, minExpiredAt: LocalDateTime? = null, maxExpiredAt: LocalDateTime? = null, - memberDeptInfos: List? = null + memberDeptInfos: List? = null, + filterMemberType: MemberType? = null ): MutableList { val conditions = mutableListOf() with(TAuthResourceGroupMember.T_AUTH_RESOURCE_GROUP_MEMBER) { @@ -663,7 +744,7 @@ class AuthResourceGroupMemberDao { MEMBER_TYPE.`in`(listOf(MemberType.USER.type, MemberType.DEPARTMENT.type)) )).let { // 获取模板加入 - if (iamTemplateIds.isNotEmpty()) { + if (!iamTemplateIds.isNullOrEmpty()) { it.or(MEMBER_ID.`in`(iamTemplateIds).and(MEMBER_TYPE.eq(MemberType.TEMPLATE.type))) } else { it @@ -676,6 +757,7 @@ class AuthResourceGroupMemberDao { it } }) + filterMemberType?.let { conditions.add(MEMBER_TYPE.eq(filterMemberType.type)) } resourceType?.let { conditions.add(RESOURCE_TYPE.eq(resourceType)) } minExpiredAt?.let { conditions.add(EXPIRED_TIME.ge(minExpiredAt)) } maxExpiredAt?.let { conditions.add(EXPIRED_TIME.le(maxExpiredAt)) } diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/AuthResourceGroupPermissionDao.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/AuthResourceGroupPermissionDao.kt index 4eb94a340c0b..4ca56fad06d3 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/AuthResourceGroupPermissionDao.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/AuthResourceGroupPermissionDao.kt @@ -2,6 +2,7 @@ package com.tencent.devops.auth.dao import com.tencent.devops.auth.pojo.dto.ResourceGroupPermissionDTO import com.tencent.devops.common.auth.api.AuthResourceType +import com.tencent.devops.common.auth.api.ResourceTypeId import com.tencent.devops.model.auth.tables.TAuthResourceGroupPermission import com.tencent.devops.model.auth.tables.records.TAuthResourceGroupPermissionRecord import org.jooq.Condition @@ -183,6 +184,26 @@ class AuthResourceGroupPermissionDao { } } + fun isGroupsHasProjectLevelPermission( + dslContext: DSLContext, + projectCode: String, + filterIamGroupIds: List, + actionRelatedResourceType: String, + action: String + ): Boolean { + return with(TAuthResourceGroupPermission.T_AUTH_RESOURCE_GROUP_PERMISSION) { + dslContext.selectCount() + .from(this) + .where(PROJECT_CODE.eq(projectCode)) + .and(IAM_GROUP_ID.`in`(filterIamGroupIds)) + .and(ACTION_RELATED_RESOURCE_TYPE.eq(actionRelatedResourceType)) + .and(ACTION.eq(action)) + .and(RELATED_RESOURCE_TYPE.eq(ResourceTypeId.PROJECT)) + .and(RELATED_RESOURCE_CODE.eq(projectCode)) + .fetchOne(0, Int::class.java)!! > 0 + } + } + fun listGroupResourcesWithPermission( dslContext: DSLContext, projectCode: String, @@ -194,6 +215,7 @@ class AuthResourceGroupPermissionDao { dslContext.select(RELATED_RESOURCE_TYPE, RELATED_RESOURCE_CODE) .from(this) .where(PROJECT_CODE.eq(projectCode)) + .and(IAM_GROUP_ID.`in`(filterIamGroupIds)) .and(ACTION_RELATED_RESOURCE_TYPE.eq(resourceType)) .and(ACTION.eq(action)) .groupBy(RELATED_RESOURCE_TYPE, RELATED_RESOURCE_CODE) diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/StrategyDao.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/StrategyDao.kt index 6f1303ef2cd1..f3a715df452a 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/StrategyDao.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/StrategyDao.kt @@ -28,6 +28,7 @@ package com.tencent.devops.auth.dao import com.tencent.devops.auth.entity.StrategyInfo +import com.tencent.devops.common.db.utils.skipCheck import com.tencent.devops.model.auth.tables.TAuthStrategy import com.tencent.devops.model.auth.tables.records.TAuthStrategyRecord import org.jooq.DSLContext @@ -39,7 +40,8 @@ class StrategyDao { fun create(dslContext: DSLContext, userId: String, strategyInfo: StrategyInfo): Int { with(TAuthStrategy.T_AUTH_STRATEGY) { - return dslContext.insertInto(this, + return dslContext.insertInto( + this, STRATEGY_NAME, STRATEGY_BODY, IS_DELETE, @@ -86,7 +88,11 @@ class StrategyDao { fun list(dslContext: DSLContext): Result? { with(TAuthStrategy.T_AUTH_STRATEGY) { - return dslContext.selectFrom(this).where((IS_DELETE.eq(0))).orderBy(CREATE_TIME.desc()).fetch() + return dslContext.selectFrom(this) + .where((IS_DELETE.eq(0))) + .orderBy(CREATE_TIME.desc()) + .skipCheck() + .fetch() } } diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/config/RbacAuthConfiguration.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/config/RbacAuthConfiguration.kt index 6c1fedc8b3ec..8f0b51098939 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/config/RbacAuthConfiguration.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/config/RbacAuthConfiguration.kt @@ -43,6 +43,9 @@ import com.tencent.bk.sdk.iam.service.v2.impl.V2GrantServiceImpl import com.tencent.bk.sdk.iam.service.v2.impl.V2ManagerServiceImpl import com.tencent.bk.sdk.iam.service.v2.impl.V2PolicyServiceImpl import com.tencent.devops.auth.dao.AuthActionDao +import com.tencent.devops.auth.dao.AuthAuthorizationDao +import com.tencent.devops.auth.dao.AuthHandoverDetailDao +import com.tencent.devops.auth.dao.AuthHandoverOverviewDao import com.tencent.devops.auth.dao.AuthMigrationDao import com.tencent.devops.auth.dao.AuthMonitorSpaceDao import com.tencent.devops.auth.dao.AuthResourceDao @@ -57,15 +60,16 @@ import com.tencent.devops.auth.provider.rbac.service.AuthResourceService import com.tencent.devops.auth.provider.rbac.service.ItsmService import com.tencent.devops.auth.provider.rbac.service.PermissionGradeManagerService import com.tencent.devops.auth.provider.rbac.service.PermissionSubsetManagerService -import com.tencent.devops.auth.provider.rbac.service.RbacCacheService +import com.tencent.devops.auth.provider.rbac.service.RbacCommonService import com.tencent.devops.auth.provider.rbac.service.RbacPermissionApplyService import com.tencent.devops.auth.provider.rbac.service.RbacPermissionAuthMonitorSpaceService import com.tencent.devops.auth.provider.rbac.service.RbacPermissionAuthorizationScopesService import com.tencent.devops.auth.provider.rbac.service.RbacPermissionExtService +import com.tencent.devops.auth.provider.rbac.service.RbacPermissionHandoverApplicationService import com.tencent.devops.auth.provider.rbac.service.RbacPermissionItsmCallbackService +import com.tencent.devops.auth.provider.rbac.service.RbacPermissionManageFacadeServiceImpl import com.tencent.devops.auth.provider.rbac.service.RbacPermissionProjectService import com.tencent.devops.auth.provider.rbac.service.RbacPermissionResourceCallbackService -import com.tencent.devops.auth.provider.rbac.service.RbacPermissionResourceGroupAndMemberFacadeServiceImpl import com.tencent.devops.auth.provider.rbac.service.RbacPermissionResourceGroupPermissionService import com.tencent.devops.auth.provider.rbac.service.RbacPermissionResourceGroupService import com.tencent.devops.auth.provider.rbac.service.RbacPermissionResourceGroupSyncService @@ -93,7 +97,8 @@ import com.tencent.devops.auth.service.PermissionAuthorizationService import com.tencent.devops.auth.service.ResourceService import com.tencent.devops.auth.service.SuperManagerService import com.tencent.devops.auth.service.iam.MigrateCreatorFixService -import com.tencent.devops.auth.service.iam.PermissionResourceGroupAndMemberFacadeService +import com.tencent.devops.auth.service.iam.PermissionHandoverApplicationService +import com.tencent.devops.auth.service.iam.PermissionManageFacadeService import com.tencent.devops.auth.service.iam.PermissionResourceGroupPermissionService import com.tencent.devops.auth.service.iam.PermissionResourceGroupService import com.tencent.devops.auth.service.iam.PermissionResourceGroupSyncService @@ -198,7 +203,7 @@ class RbacAuthConfiguration { ) @Bean - fun permissionResourceGroupAndMemberFacadeService( + fun permissionFacadeService( permissionResourceGroupService: PermissionResourceGroupService, groupPermissionService: PermissionResourceGroupPermissionService, permissionResourceMemberService: PermissionResourceMemberService, @@ -207,8 +212,17 @@ class RbacAuthConfiguration { dslContext: DSLContext, deptService: DeptService, iamV2ManagerService: V2ManagerService, - rbacCacheService: RbacCacheService - ) = RbacPermissionResourceGroupAndMemberFacadeServiceImpl( + permissionAuthorizationService: PermissionAuthorizationService, + syncIamGroupMemberService: PermissionResourceGroupSyncService, + authAuthorizationDao: AuthAuthorizationDao, + permissionHandoverApplicationService: PermissionHandoverApplicationService, + rbacCommonService: RbacCommonService, + redisOperation: RedisOperation, + authorizationDao: AuthAuthorizationDao, + authResourceService: AuthResourceService, + client: Client, + config: CommonConfig + ) = RbacPermissionManageFacadeServiceImpl( permissionResourceGroupService = permissionResourceGroupService, groupPermissionService = groupPermissionService, permissionResourceMemberService = permissionResourceMemberService, @@ -217,13 +231,22 @@ class RbacAuthConfiguration { dslContext = dslContext, deptService = deptService, iamV2ManagerService = iamV2ManagerService, - rbacCacheService = rbacCacheService + permissionAuthorizationService = permissionAuthorizationService, + syncIamGroupMemberService = syncIamGroupMemberService, + authAuthorizationDao = authAuthorizationDao, + permissionHandoverApplicationService = permissionHandoverApplicationService, + rbacCommonService = rbacCommonService, + redisOperation = redisOperation, + authorizationDao = authorizationDao, + authResourceService = authResourceService, + client = client, + config = config ) @Bean fun permissionResourceGroupPermissionService( v2ManagerService: V2ManagerService, - rbacCacheService: RbacCacheService, + rbacCommonService: RbacCommonService, monitorSpaceService: AuthMonitorSpaceService, authResourceGroupDao: AuthResourceGroupDao, dslContext: DSLContext, @@ -237,7 +260,7 @@ class RbacAuthConfiguration { objectMapper: ObjectMapper ) = RbacPermissionResourceGroupPermissionService( v2ManagerService = v2ManagerService, - rbacCacheService = rbacCacheService, + rbacCommonService = rbacCommonService, monitorSpaceService = monitorSpaceService, authResourceGroupDao = authResourceGroupDao, dslContext = dslContext, @@ -259,19 +282,14 @@ class RbacAuthConfiguration { authResourceGroupMemberDao: AuthResourceGroupMemberDao, dslContext: DSLContext, deptService: DeptService, - rbacCacheService: RbacCacheService, - permissionAuthorizationService: PermissionAuthorizationService, - syncIamGroupMemberService: PermissionResourceGroupSyncService + rbacCommonService: RbacCommonService ) = RbacPermissionResourceMemberService( authResourceService = authResourceService, iamV2ManagerService = iamV2ManagerService, authResourceGroupDao = authResourceGroupDao, authResourceGroupMemberDao = authResourceGroupMemberDao, dslContext = dslContext, - deptService = deptService, - permissionAuthorizationService = permissionAuthorizationService, - syncIamGroupMemberService = syncIamGroupMemberService, - rbacCacheService = rbacCacheService + deptService = deptService ) @Bean @@ -301,7 +319,7 @@ class RbacAuthConfiguration { iamV2PolicyService: PolicyService, authResourceCodeConverter: AuthResourceCodeConverter, superManagerService: SuperManagerService, - rbacCacheService: RbacCacheService, + rbacCommonService: RbacCommonService, client: Client, authProjectUserMetricsService: AuthProjectUserMetricsService ) = RbacPermissionService( @@ -311,7 +329,7 @@ class RbacAuthConfiguration { policyService = iamV2PolicyService, authResourceCodeConverter = authResourceCodeConverter, superManagerService = superManagerService, - rbacCacheService = rbacCacheService, + rbacCommonService = rbacCommonService, client = client, authProjectUserMetricsService = authProjectUserMetricsService ) @@ -323,19 +341,21 @@ class RbacAuthConfiguration { authResourceService: AuthResourceService, authResourceGroupDao: AuthResourceGroupDao, dslContext: DSLContext, - rbacCacheService: RbacCacheService, + rbacCommonService: RbacCommonService, resourceGroupMemberService: RbacPermissionResourceMemberService, client: Client, - resourceMemberService: PermissionResourceMemberService + resourceMemberService: PermissionResourceMemberService, + permissionManageFacadeService: PermissionManageFacadeService ) = RbacPermissionProjectService( authHelper = authHelper, authResourceService = authResourceService, authResourceGroupDao = authResourceGroupDao, dslContext = dslContext, - rbacCacheService = rbacCacheService, + rbacCommonService = rbacCommonService, resourceGroupMemberService = resourceGroupMemberService, client = client, - resourceMemberService = resourceMemberService + resourceMemberService = resourceMemberService, + permissionManageFacadeService = permissionManageFacadeService ) @Bean @@ -356,7 +376,7 @@ class RbacAuthConfiguration { authResourceService: AuthResourceService, authResourceGroupConfigDao: AuthResourceGroupConfigDao, authResourceGroupDao: AuthResourceGroupDao, - rbacCacheService: RbacCacheService, + rbacCommonService: RbacCommonService, config: CommonConfig, client: Client, authResourceCodeConverter: AuthResourceCodeConverter, @@ -370,7 +390,7 @@ class RbacAuthConfiguration { authResourceService = authResourceService, authResourceGroupConfigDao = authResourceGroupConfigDao, authResourceGroupDao = authResourceGroupDao, - rbacCacheService = rbacCacheService, + rbacCommonService = rbacCommonService, config = config, client = client, authResourceCodeConverter = authResourceCodeConverter, @@ -384,12 +404,16 @@ class RbacAuthConfiguration { @Primary fun rbacPermissionResourceValidateService( permissionService: PermissionService, - rbacCacheService: RbacCacheService, - client: Client + rbacCommonService: RbacCommonService, + client: Client, + authAuthorizationDao: AuthAuthorizationDao, + dslContext: DSLContext ) = RbacPermissionResourceValidateService( permissionService = permissionService, - rbacCacheService = rbacCacheService, - client = client + rbacCommonService = rbacCommonService, + client = client, + authAuthorizationDao = authAuthorizationDao, + dslContext = dslContext ) @Bean @@ -402,7 +426,7 @@ class RbacAuthConfiguration { @Bean fun migrateResourceService( resourceService: ResourceService, - rbacCacheService: RbacCacheService, + rbacCommonService: RbacCommonService, rbacPermissionResourceService: RbacPermissionResourceService, migrateCreatorFixService: MigrateCreatorFixService, authResourceService: AuthResourceService, @@ -417,7 +441,7 @@ class RbacAuthConfiguration { authResourceGroupDao: AuthResourceGroupDao ) = MigrateResourceService( resourceService = resourceService, - rbacCacheService = rbacCacheService, + rbacCommonService = rbacCommonService, rbacPermissionResourceService = rbacPermissionResourceService, migrateCreatorFixService = migrateCreatorFixService, authResourceService = authResourceService, @@ -451,7 +475,7 @@ class RbacAuthConfiguration { @Bean fun migrateResultService( permissionService: PermissionService, - rbacCacheService: RbacCacheService, + rbacCommonService: RbacCommonService, migrateResourceCodeConverter: MigrateResourceCodeConverter, authVerifyRecordService: AuthVerifyRecordService, migrateResourceService: MigrateResourceService, @@ -463,7 +487,7 @@ class RbacAuthConfiguration { redisOperation: RedisOperation ) = MigrateResultService( permissionService = permissionService, - rbacCacheService = rbacCacheService, + rbacCommonService = rbacCommonService, migrateResourceCodeConverter = migrateResourceCodeConverter, authVerifyRecordService = authVerifyRecordService, migrateResourceService = migrateResourceService, @@ -486,7 +510,7 @@ class RbacAuthConfiguration { migrateIamApiService: MigrateIamApiService, authResourceCodeConverter: AuthResourceCodeConverter, permissionService: PermissionService, - rbacCacheService: RbacCacheService, + rbacCommonService: RbacCommonService, authMigrationDao: AuthMigrationDao, deptService: DeptService, permissionResourceGroupPermissionService: PermissionResourceGroupPermissionService, @@ -501,7 +525,7 @@ class RbacAuthConfiguration { migrateResourceCodeConverter = migrateResourceCodeConverter, authResourceCodeConverter = authResourceCodeConverter, permissionService = permissionService, - rbacCacheService = rbacCacheService, + rbacCommonService = rbacCommonService, authMigrationDao = authMigrationDao, deptService = deptService, permissionResourceGroupPermissionService = permissionResourceGroupPermissionService, @@ -519,7 +543,7 @@ class RbacAuthConfiguration { migrateIamApiService: MigrateIamApiService, authResourceCodeConverter: AuthResourceCodeConverter, permissionService: PermissionService, - rbacCacheService: RbacCacheService, + rbacCommonService: RbacCommonService, authMigrationDao: AuthMigrationDao, deptService: DeptService, permissionResourceGroupPermissionService: PermissionResourceGroupPermissionService, @@ -534,7 +558,7 @@ class RbacAuthConfiguration { migrateResourceCodeConverter = migrateResourceCodeConverter, authResourceCodeConverter = authResourceCodeConverter, permissionService = permissionService, - rbacCacheService = rbacCacheService, + rbacCommonService = rbacCommonService, authMigrationDao = authMigrationDao, deptService = deptService, permissionResourceGroupPermissionService = permissionResourceGroupPermissionService, @@ -557,7 +581,6 @@ class RbacAuthConfiguration { dslContext: DSLContext, authMigrationDao: AuthMigrationDao, authMonitorSpaceDao: AuthMonitorSpaceDao, - cacheService: RbacCacheService, permissionResourceMemberService: RbacPermissionResourceMemberService, migrateResourceAuthorizationService: MigrateResourceAuthorizationService, migrateResourceGroupService: MigrateResourceGroupService @@ -575,7 +598,6 @@ class RbacAuthConfiguration { dslContext = dslContext, authMigrationDao = authMigrationDao, authMonitorSpaceDao = authMonitorSpaceDao, - cacheService = cacheService, permissionResourceMemberService = permissionResourceMemberService, migrateResourceAuthorizationService = migrateResourceAuthorizationService, migrateResourceGroupService = migrateResourceGroupService @@ -591,13 +613,38 @@ class RbacAuthConfiguration { authResourceService: AuthResourceService, authResourceGroupMemberService: PermissionResourceMemberService, dslContext: DSLContext, - resourceGroupAndMemberFacadeService: PermissionResourceGroupAndMemberFacadeService + permissionManageFacadeService: PermissionManageFacadeService ) = MigratePermissionHandoverService( permissionResourceMemberService = permissionResourceMemberService, authResourceGroupDao = authResourceGroupDao, authResourceService = authResourceService, dslContext = dslContext, - resourceGroupAndMemberFacadeService = resourceGroupAndMemberFacadeService + permissionManageFacadeService = permissionManageFacadeService + ) + + @Bean + fun permissionHandoverService( + dslContext: DSLContext, + handoverOverviewDao: AuthHandoverOverviewDao, + handoverDetailDao: AuthHandoverDetailDao, + authorizationDao: AuthAuthorizationDao, + authResourceGroupDao: AuthResourceGroupDao, + rbacCommonService: RbacCommonService, + redisOperation: RedisOperation, + client: Client, + config: CommonConfig, + deptService: DeptService + ) = RbacPermissionHandoverApplicationService( + dslContext = dslContext, + handoverOverviewDao = handoverOverviewDao, + handoverDetailDao = handoverDetailDao, + authorizationDao = authorizationDao, + authResourceGroupDao = authResourceGroupDao, + rbacCommonService = rbacCommonService, + redisOperation = redisOperation, + client = client, + config = config, + deptService = deptService ) @Bean @@ -636,7 +683,7 @@ class RbacAuthConfiguration { authResourceGroupDao: AuthResourceGroupDao, iamV2ManagerService: V2ManagerService, authResourceGroupMemberDao: AuthResourceGroupMemberDao, - rbacCacheService: RbacCacheService, + rbacCommonService: RbacCommonService, redisOperation: RedisOperation, authResourceSyncDao: AuthResourceSyncDao, authResourceGroupApplyDao: AuthResourceGroupApplyDao, @@ -649,7 +696,7 @@ class RbacAuthConfiguration { authResourceGroupDao = authResourceGroupDao, iamV2ManagerService = iamV2ManagerService, authResourceGroupMemberDao = authResourceGroupMemberDao, - rbacCacheService = rbacCacheService, + rbacCommonService = rbacCommonService, redisOperation = redisOperation, authResourceSyncDao = authResourceSyncDao, authResourceGroupApplyDao = authResourceGroupApplyDao, diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/config/RbacServiceConfiguration.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/config/RbacServiceConfiguration.kt index 47d4f23c856b..647ab1149e8a 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/config/RbacServiceConfiguration.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/config/RbacServiceConfiguration.kt @@ -45,7 +45,7 @@ import com.tencent.devops.auth.provider.rbac.service.AuthResourceService import com.tencent.devops.auth.provider.rbac.service.ItsmService import com.tencent.devops.auth.provider.rbac.service.PermissionGradeManagerService import com.tencent.devops.auth.provider.rbac.service.PermissionSubsetManagerService -import com.tencent.devops.auth.provider.rbac.service.RbacCacheService +import com.tencent.devops.auth.provider.rbac.service.RbacCommonService import com.tencent.devops.auth.service.AuthAuthorizationScopesService import com.tencent.devops.auth.service.AuthProjectUserMetricsService import com.tencent.devops.auth.service.BkHttpRequestService @@ -72,7 +72,7 @@ class RbacServiceConfiguration { iamConfiguration: IamConfiguration, authResourceGroupConfigDao: AuthResourceGroupConfigDao, authProjectUserMetricsService: AuthProjectUserMetricsService - ) = RbacCacheService( + ) = RbacCommonService( dslContext = dslContext, authResourceTypeDao = authResourceTypeDao, authActionDao = authActionDao, diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacCacheService.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacCommonService.kt similarity index 80% rename from src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacCacheService.kt rename to src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacCommonService.kt index 1a9f467e7b7d..e3126357fbcd 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacCacheService.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacCommonService.kt @@ -3,18 +3,21 @@ package com.tencent.devops.auth.provider.rbac.service import com.fasterxml.jackson.core.type.TypeReference import com.github.benmanes.caffeine.cache.Caffeine import com.tencent.bk.sdk.iam.config.IamConfiguration -import com.tencent.bk.sdk.iam.constants.ManagerScopesEnum import com.tencent.bk.sdk.iam.dto.SubjectDTO import com.tencent.bk.sdk.iam.dto.V2QueryPolicyDTO import com.tencent.bk.sdk.iam.dto.action.ActionDTO import com.tencent.bk.sdk.iam.dto.resource.V2ResourceNode import com.tencent.bk.sdk.iam.service.PolicyService +import com.tencent.devops.auth.constant.AuthI18nConstants import com.tencent.devops.auth.constant.AuthMessageCode import com.tencent.devops.auth.dao.AuthActionDao import com.tencent.devops.auth.dao.AuthResourceGroupConfigDao import com.tencent.devops.auth.dao.AuthResourceTypeDao import com.tencent.devops.auth.pojo.AuthGroupConfigAction +import com.tencent.devops.auth.pojo.enum.HandoverType +import com.tencent.devops.auth.pojo.enum.MemberType import com.tencent.devops.auth.pojo.vo.ActionInfoVo +import com.tencent.devops.auth.pojo.vo.ResourceType2CountVo import com.tencent.devops.auth.pojo.vo.ResourceTypeInfoVo import com.tencent.devops.auth.service.AuthProjectUserMetricsService import com.tencent.devops.common.api.exception.ErrorCodeException @@ -22,12 +25,13 @@ import com.tencent.devops.common.api.util.JsonUtil import com.tencent.devops.common.auth.api.AuthPermission import com.tencent.devops.common.auth.api.AuthResourceType import com.tencent.devops.common.auth.rbac.utils.RbacAuthUtils +import com.tencent.devops.common.web.utils.I18nUtil import org.jooq.DSLContext import org.slf4j.LoggerFactory import java.util.concurrent.TimeUnit @Suppress("MagicNumber", "LongParameterList") -class RbacCacheService constructor( +class RbacCommonService( private val dslContext: DSLContext, private val authResourceTypeDao: AuthResourceTypeDao, private val authActionDao: AuthActionDao, @@ -38,7 +42,7 @@ class RbacCacheService constructor( ) { companion object { - private val logger = LoggerFactory.getLogger(RbacCacheService::class.java) + private val logger = LoggerFactory.getLogger(RbacCommonService::class.java) } /*获取资源类型下的动作*/ @@ -190,7 +194,7 @@ class RbacCacheService constructor( val subject = SubjectDTO.builder() .id(userId) - .type(ManagerScopesEnum.getType(ManagerScopesEnum.USER)) + .type(MemberType.USER.type) .build() val queryPolicyDTO = V2QueryPolicyDTO.builder().system(iamConfiguration.systemId) .subject(subject) @@ -213,4 +217,42 @@ class RbacCacheService constructor( ) } } + + fun convertResourceType2Count( + resourceType2Count: Map, + type: HandoverType = HandoverType.GROUP + ): List { + val memberGroupCountList = mutableListOf() + // 项目排在第一位 + resourceType2Count[AuthResourceType.PROJECT.value]?.let { projectCount -> + memberGroupCountList.add( + ResourceType2CountVo( + resourceType = AuthResourceType.PROJECT.value, + resourceTypeName = I18nUtil.getCodeLanMessage( + messageCode = AuthResourceType.PROJECT.value + AuthI18nConstants.RESOURCE_TYPE_NAME_SUFFIX + ), + count = projectCount, + type = type + ) + ) + } + + listResourceTypes() + .filter { it.resourceType != AuthResourceType.PROJECT.value } + .forEach { resourceTypeInfoVo -> + resourceType2Count[resourceTypeInfoVo.resourceType]?.let { count -> + val memberGroupCount = ResourceType2CountVo( + resourceType = resourceTypeInfoVo.resourceType, + resourceTypeName = I18nUtil.getCodeLanMessage( + messageCode = resourceTypeInfoVo.resourceType + AuthI18nConstants.RESOURCE_TYPE_NAME_SUFFIX, + defaultMessage = resourceTypeInfoVo.name + ), + count = count, + type = type + ) + memberGroupCountList.add(memberGroupCount) + } + } + return memberGroupCountList + } } diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionApplyService.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionApplyService.kt index 63e94d7445bb..7be37a4a987a 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionApplyService.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionApplyService.kt @@ -60,7 +60,7 @@ class RbacPermissionApplyService @Autowired constructor( val authResourceService: AuthResourceService, val authResourceGroupConfigDao: AuthResourceGroupConfigDao, val authResourceGroupDao: AuthResourceGroupDao, - val rbacCacheService: RbacCacheService, + val rbacCommonService: RbacCommonService, val config: CommonConfig, val client: Client, val authResourceCodeConverter: AuthResourceCodeConverter, @@ -80,11 +80,11 @@ class RbacPermissionApplyService @Autowired constructor( private val codeccTaskDetailRedirectUri = "${config.devopsHostGateway}/console/codecc/%s/task/%s/detail?buildNum=latest" private val groupPermissionDetailRedirectUri = "${config.devopsHostGateway}/permission/group/detail?group_id=%s&x-devops-project-id=%s" override fun listResourceTypes(userId: String): List { - return rbacCacheService.listResourceTypes() + return rbacCommonService.listResourceTypes() } override fun listActions(userId: String, resourceType: String): List { - return rbacCacheService.listResourceType2Action(resourceType) + return rbacCommonService.listResourceType2Action(resourceType) } override fun listGroupsForApply( @@ -263,7 +263,7 @@ class RbacPermissionApplyService @Autowired constructor( return managerRoleGroupInfoList.map { gInfo -> val dbGroupRecord = dbGroupRecords.find { record -> record.relationId == gInfo.id.toString() } val resourceType = dbGroupRecord?.resourceType ?: AuthResourceType.PROJECT.value - val resourceTypeName = rbacCacheService.getResourceTypeInfo(resourceType).name + val resourceTypeName = rbacCommonService.getResourceTypeInfo(resourceType).name val resourceName = dbGroupRecord?.resourceName ?: projectName val resourceCode = dbGroupRecord?.resourceCode ?: projectId val memberJoinedResult = verifyMemberJoinedResult[gInfo.id.toInt()] @@ -326,7 +326,7 @@ class RbacPermissionApplyService @Autowired constructor( itsmService.buildGroupApplyItsmValue( ApplyJoinGroupFormDataInfo( projectName = projectInfo.projectName, - resourceTypeName = rbacCacheService.getResourceTypeInfo(resourceGroupInfo.resourceType).name, + resourceTypeName = rbacCommonService.getResourceTypeInfo(resourceGroupInfo.resourceType).name, resourceName = resourceGroupInfo.resourceName, groupName = resourceGroupInfo.groupName, validityPeriod = generateValidityPeriod(applyJoinGroupInfo.expiredAt.toLong()), @@ -487,11 +487,11 @@ class RbacPermissionApplyService @Autowired constructor( ) val groupInfoList: MutableList = mutableListOf() // 判断action是否为空 - val actionInfo = if (action != null) rbacCacheService.getActionInfo(action) else null + val actionInfo = if (action != null) rbacCommonService.getActionInfo(action) else null val iamRelatedResourceType = actionInfo?.relatedResourceType ?: resourceType val resourceTypeName = I18nUtil.getCodeLanMessage( messageCode = resourceType + AuthI18nConstants.RESOURCE_TYPE_NAME_SUFFIX, - defaultMessage = rbacCacheService.getResourceTypeInfo(resourceType).name + defaultMessage = rbacCommonService.getResourceTypeInfo(resourceType).name ) val projectInfo = authResourceService.get( @@ -578,7 +578,7 @@ class RbacPermissionApplyService @Autowired constructor( ) } else { if (isEnablePermission) { - rbacCacheService.getGroupConfigAction(finalResourceType).forEach { + rbacCommonService.getGroupConfigAction(finalResourceType).forEach { if (it.actions.contains(action)) { buildRedirectGroupInfo( groupInfoList = groupInfoList, diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionHandoverApplicationService.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionHandoverApplicationService.kt new file mode 100644 index 000000000000..ac33d5892ab1 --- /dev/null +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionHandoverApplicationService.kt @@ -0,0 +1,365 @@ +package com.tencent.devops.auth.provider.rbac.service + +import com.tencent.bk.sdk.iam.constants.ManagerScopesEnum +import com.tencent.devops.auth.constant.AuthI18nConstants.BK_APPLY_TO_HANDOVER +import com.tencent.devops.auth.constant.AuthI18nConstants.BK_HANDOVER_AUTHORIZATIONS +import com.tencent.devops.auth.constant.AuthI18nConstants.BK_HANDOVER_GROUPS +import com.tencent.devops.auth.constant.AuthMessageCode +import com.tencent.devops.auth.dao.AuthAuthorizationDao +import com.tencent.devops.auth.dao.AuthHandoverDetailDao +import com.tencent.devops.auth.dao.AuthHandoverOverviewDao +import com.tencent.devops.auth.dao.AuthResourceGroupDao +import com.tencent.devops.auth.pojo.dto.HandoverDetailDTO +import com.tencent.devops.auth.pojo.dto.HandoverOverviewCreateDTO +import com.tencent.devops.auth.pojo.enum.HandoverStatus +import com.tencent.devops.auth.pojo.enum.HandoverType +import com.tencent.devops.auth.pojo.request.HandoverDetailsQueryReq +import com.tencent.devops.auth.pojo.request.HandoverOverviewQueryReq +import com.tencent.devops.auth.pojo.request.HandoverOverviewUpdateReq +import com.tencent.devops.auth.pojo.vo.HandoverAuthorizationDetailVo +import com.tencent.devops.auth.pojo.vo.HandoverGroupDetailVo +import com.tencent.devops.auth.pojo.vo.HandoverOverviewVo +import com.tencent.devops.auth.pojo.vo.ResourceType2CountVo +import com.tencent.devops.auth.service.DeptService +import com.tencent.devops.auth.service.iam.PermissionHandoverApplicationService +import com.tencent.devops.common.api.exception.ErrorCodeException +import com.tencent.devops.common.api.model.SQLPage +import com.tencent.devops.common.api.util.DateTimeUtil +import com.tencent.devops.common.api.util.PageUtil +import com.tencent.devops.common.auth.api.pojo.ResourceAuthorizationConditionRequest +import com.tencent.devops.common.client.Client +import com.tencent.devops.common.notify.enums.NotifyType +import com.tencent.devops.common.redis.RedisOperation +import com.tencent.devops.common.service.config.CommonConfig +import com.tencent.devops.common.web.utils.I18nUtil +import com.tencent.devops.notify.api.service.ServiceNotifyMessageTemplateResource +import com.tencent.devops.notify.pojo.SendNotifyMessageTemplateRequest +import org.jooq.DSLContext +import org.jooq.impl.DSL +import org.slf4j.LoggerFactory +import java.time.LocalDateTime + +class RbacPermissionHandoverApplicationService( + private val dslContext: DSLContext, + private val handoverOverviewDao: AuthHandoverOverviewDao, + private val handoverDetailDao: AuthHandoverDetailDao, + private val authorizationDao: AuthAuthorizationDao, + private val authResourceGroupDao: AuthResourceGroupDao, + private val rbacCommonService: RbacCommonService, + private val redisOperation: RedisOperation, + private val client: Client, + private val config: CommonConfig, + private val deptService: DeptService +) : PermissionHandoverApplicationService { + override fun createHandoverApplication( + overview: HandoverOverviewCreateDTO, + details: List + ): String { + logger.info("create handover application:{}|{}", overview, details) + val flowNo = generateFlowNo() + val (title, handoverOverviewContentOfEmail, handoverOverviewContentOfRtx) = + generateOverviewContent( + groupCount = overview.groupCount, + authorizationCount = overview.authorizationCount + ) + dslContext.transaction { configuration -> + val transactionContext = DSL.using(configuration) + handoverOverviewDao.create( + dslContext = transactionContext, + overviewDTO = overview.copy( + flowNo = flowNo, + title = title + ) + ) + handoverDetailDao.batchCreate( + dslContext = transactionContext, + handoverDetailDTOs = details.map { it.copy(flowNo = flowNo) } + ) + } + val handoverFromCnName = deptService.getMemberInfo(overview.applicant, ManagerScopesEnum.USER).displayName + val handoverToCnName = deptService.getMemberInfo(overview.approver, ManagerScopesEnum.USER).displayName + val resourceType2CountOfHandover = getResourceType2CountOfHandoverApplication(flowNo) + val handoverOverviewTableBuilder = StringBuilder() + resourceType2CountOfHandover.forEach { + handoverOverviewTableBuilder.append( + String.format( + HANDOVER_APPLICATION_TABLE_OF_EMAIL, it.type.alias, it.resourceTypeName, it.count + ) + ) + } + val handoverOverviewTable = handoverOverviewTableBuilder.toString() + val bodyParams = mapOf( + "handoverFrom" to overview.applicant.plus("($handoverFromCnName)"), + "handoverTo" to overview.approver.plus("($handoverToCnName)"), + "projectName" to overview.projectName, + "handoverOverviews" to handoverOverviewContentOfEmail, + "handoverOverviewContentOfRtx" to handoverOverviewContentOfRtx, + "table" to handoverOverviewTable, + "url" to String.format(handoverApplicationUrl, flowNo) + ) + logger.info("send handover application email:{} ", bodyParams) + val request = SendNotifyMessageTemplateRequest( + templateCode = HANDOVER_APPLICATION_TEMPLATE_CODE, + bodyParams = bodyParams, + titleParams = bodyParams, + notifyType = mutableSetOf(NotifyType.RTX.name, NotifyType.EMAIL.name), + receivers = mutableSetOf(overview.approver) + ) + kotlin.runCatching { + client.get(ServiceNotifyMessageTemplateResource::class).sendNotifyMessageByTemplate(request) + }.onFailure { + logger.warn("notify email fail ${it.message}|$bodyParams|${overview.approver}") + } + return flowNo + } + + private fun generateOverviewContent( + groupCount: Int, + authorizationCount: Int + ): Triple { + val bkHandoverGroups = I18nUtil.getCodeLanMessage(BK_HANDOVER_GROUPS) + val bkHandoverAuthorizations = I18nUtil.getCodeLanMessage(BK_HANDOVER_AUTHORIZATIONS) + var titleOfApplication = I18nUtil.getCodeLanMessage(BK_APPLY_TO_HANDOVER) + var handoverOverviewContentOfEmail = "" + var handoverOverviewContentOfRtx = "" + when { + groupCount > 0 && authorizationCount > 0 -> { + titleOfApplication = titleOfApplication.plus(groupCount).plus( + bkHandoverGroups.plus(",").plus(authorizationCount).plus(bkHandoverAuthorizations) + ) + handoverOverviewContentOfEmail = """$groupCount$bkHandoverGroups,$authorizationCount$bkHandoverAuthorizations""".trimMargin() + handoverOverviewContentOfRtx = handoverOverviewContentOfRtx.plus(groupCount).plus( + bkHandoverGroups.plus(",").plus(authorizationCount).plus(bkHandoverAuthorizations) + ) + } + + groupCount > 0 -> { + titleOfApplication = titleOfApplication.plus(groupCount).plus(bkHandoverGroups) + handoverOverviewContentOfEmail = """$groupCount$bkHandoverGroups""".trimMargin() + handoverOverviewContentOfRtx = handoverOverviewContentOfRtx.plus(groupCount).plus(bkHandoverGroups) + } + + else -> { + titleOfApplication = titleOfApplication.plus(authorizationCount).plus(bkHandoverAuthorizations) + handoverOverviewContentOfEmail = """$authorizationCount$bkHandoverAuthorizations""".trimMargin() + handoverOverviewContentOfRtx = handoverOverviewContentOfRtx.plus(authorizationCount).plus(bkHandoverAuthorizations) + } + } + return Triple(titleOfApplication, handoverOverviewContentOfEmail, handoverOverviewContentOfRtx) + } + + /** + * 生成格式如 REQ2024111300001 + * REQ 固定前缀 + * 20241113 表示日期 + * 00001 表示当天第几个单号 + * */ + override fun generateFlowNo(): String { + val currentTime = DateTimeUtil.toDateTime(LocalDateTime.now(), DateTimeUtil.YYYYMMDD) + val incrementedValue = redisOperation.increment(String.format(FLOW_NO_KEY, currentTime), 1) + val formattedIncrementedValue = String.format("%05d", incrementedValue) + return FLOW_NO_PREFIX + currentTime + formattedIncrementedValue + } + + override fun updateHandoverApplication(overview: HandoverOverviewUpdateReq) { + logger.info("update handover application:{}", overview) + handoverOverviewDao.update( + dslContext = dslContext, + overviewDTO = overview + ) + } + + override fun getHandoverOverview(flowNo: String): HandoverOverviewVo { + return handoverOverviewDao.get( + dslContext = dslContext, + flowNo = flowNo + ) ?: throw ErrorCodeException( + errorCode = AuthMessageCode.ERROR_HANDOVER_OVERVIEW_NOT_EXIST + ) + } + + override fun listHandoverOverviews( + queryRequest: HandoverOverviewQueryReq + ): SQLPage { + logger.info("list handover overviews :$queryRequest") + val records = handoverOverviewDao.list( + dslContext = dslContext, + queryRequest = queryRequest + ) + val count = handoverOverviewDao.count( + dslContext = dslContext, + queryRequest = queryRequest + ) + return SQLPage( + records = records, + count = count + ) + } + + override fun listAuthorizationsOfHandoverApplication( + queryReq: HandoverDetailsQueryReq + ): SQLPage { + logger.info("list authorizations of handover application :$queryReq") + val flowNo = queryReq.flowNo!! + val overview = getHandoverOverview(flowNo) + val resourceCodes = handoverDetailDao.list( + dslContext = dslContext, + projectCode = overview.projectCode, + flowNos = listOf(flowNo), + resourceType = queryReq.resourceType, + handoverType = HandoverType.AUTHORIZATION + ).map { it.itemId } + val count = handoverDetailDao.count( + dslContext = dslContext, + projectCode = overview.projectCode, + flowNos = listOf(flowNo), + resourceType = queryReq.resourceType, + handoverType = HandoverType.AUTHORIZATION + ) + val records = authorizationDao.list( + dslContext = dslContext, + condition = ResourceAuthorizationConditionRequest( + projectCode = overview.projectCode, + resourceType = queryReq.resourceType, + filterResourceCodes = resourceCodes, + page = queryReq.page, + pageSize = queryReq.pageSize + ) + ).map { + HandoverAuthorizationDetailVo( + resourceCode = it.resourceCode, + resourceName = it.resourceName, + handoverType = HandoverType.AUTHORIZATION, + handoverFrom = overview.applicant + ) + } + return SQLPage(records = records, count = count) + } + + override fun listGroupsOfHandoverApplication( + queryReq: HandoverDetailsQueryReq + ): SQLPage { + logger.info("list groups of handover application :$queryReq") + val flowNo = queryReq.flowNo!! + val handoverOverview = getHandoverOverview(flowNo) + val iamGroupIdsByHandover = listHandoverDetails( + projectCode = handoverOverview.projectCode, + flowNo = flowNo, + resourceType = queryReq.resourceType, + handoverType = HandoverType.GROUP + ).map { it.itemId } + if (iamGroupIdsByHandover.isEmpty()) + return SQLPage(0, emptyList()) + val convertPageSizeToSQLLimit = PageUtil.convertPageSizeToSQLLimit( + page = queryReq.page, + pageSize = queryReq.pageSize + ) + val records = authResourceGroupDao.listGroupByResourceType( + dslContext = dslContext, + projectCode = handoverOverview.projectCode, + resourceType = queryReq.resourceType, + iamGroupIds = iamGroupIdsByHandover, + offset = convertPageSizeToSQLLimit.offset, + limit = convertPageSizeToSQLLimit.limit + ).map { + HandoverGroupDetailVo( + projectCode = it.projectCode, + iamGroupId = it.relationId, + groupName = it.groupName, + groupDesc = it.description, + resourceCode = it.resourceCode, + resourceName = it.resourceName + ) + } + return SQLPage( + count = iamGroupIdsByHandover.size.toLong(), + records = records + ) + } + + override fun getResourceType2CountOfHandoverApplication(flowNo: String): List { + logger.info("get resource type count of handover application:$flowNo") + val handoverOverview = getHandoverOverview(flowNo) + val resourceType2CountWithGroup = handoverDetailDao.countWithResourceType( + dslContext = dslContext, + projectCode = handoverOverview.projectCode, + flowNo = flowNo, + handoverType = HandoverType.GROUP + ) + val resourceType2CountWithAuthorization = handoverDetailDao.countWithResourceType( + dslContext = dslContext, + projectCode = handoverOverview.projectCode, + flowNo = flowNo, + handoverType = HandoverType.AUTHORIZATION + ) + val result = mutableListOf() + if (resourceType2CountWithGroup.isNotEmpty()) { + result.addAll( + rbacCommonService.convertResourceType2Count( + resourceType2Count = resourceType2CountWithGroup, + type = HandoverType.GROUP + ) + ) + } + if (resourceType2CountWithAuthorization.isNotEmpty()) { + result.addAll( + rbacCommonService.convertResourceType2Count( + resourceType2Count = resourceType2CountWithAuthorization, + type = HandoverType.AUTHORIZATION + ) + ) + } + return result + } + + override fun listHandoverDetails( + projectCode: String, + flowNo: String, + resourceType: String?, + handoverType: HandoverType? + ): List { + return handoverDetailDao.list( + dslContext = dslContext, + projectCode = projectCode, + flowNos = listOf(flowNo), + resourceType = resourceType, + handoverType = handoverType + ) + } + + override fun listMemberHandoverDetails( + projectCode: String, + memberId: String, + handoverType: HandoverType, + resourceType: String? + ): List { + logger.info("list member handover details:$projectCode|$memberId|$handoverType|$resourceType") + val handoverOverviews = listHandoverOverviews( + queryRequest = HandoverOverviewQueryReq( + memberId = memberId, + projectCode = projectCode, + applicant = memberId, + handoverStatus = HandoverStatus.PENDING + ) + ).records + val flowNos = handoverOverviews.map { it.flowNo } + val flowNo2Approver = handoverOverviews.associate { Pair(it.flowNo, it.approver) } + return handoverDetailDao.list( + dslContext = dslContext, + projectCode = projectCode, + flowNos = flowNos, + resourceType = resourceType, + handoverType = handoverType + ).map { it.copy(approver = flowNo2Approver[it.flowNo]) } + } + + private val handoverApplicationUrl = "${config.devopsHostGateway}/console/permission/my-handover?type=handoverToMe&flowNo=%s" + + companion object { + private val logger = LoggerFactory.getLogger(RbacPermissionHandoverApplicationService::class.java) + private const val FLOW_NO_PREFIX = "REQ" + private const val FLOW_NO_KEY = "AUTH:HANDOVER:FLOW:NO:%s" + private const val HANDOVER_APPLICATION_TABLE_OF_EMAIL = "%s%s%s" + private const val HANDOVER_APPLICATION_TEMPLATE_CODE = "BK_PERMISSIONS_HANDOVER_APPLICATION" + } +} diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionManageFacadeServiceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionManageFacadeServiceImpl.kt new file mode 100644 index 000000000000..c94142d45788 --- /dev/null +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionManageFacadeServiceImpl.kt @@ -0,0 +1,2333 @@ +package com.tencent.devops.auth.provider.rbac.service + +import com.tencent.bk.sdk.iam.constants.ManagerScopesEnum +import com.tencent.bk.sdk.iam.dto.manager.ManagerMember +import com.tencent.bk.sdk.iam.dto.response.MemberGroupDetailsResponse +import com.tencent.bk.sdk.iam.service.v2.V2ManagerService +import com.tencent.devops.auth.constant.AuthI18nConstants +import com.tencent.devops.auth.constant.AuthMessageCode +import com.tencent.devops.auth.constant.AuthMessageCode.ERROR_HANDOVER_APPROVAL +import com.tencent.devops.auth.constant.AuthMessageCode.ERROR_HANDOVER_FINISH +import com.tencent.devops.auth.constant.AuthMessageCode.ERROR_HANDOVER_HANDLE +import com.tencent.devops.auth.constant.AuthMessageCode.ERROR_HANDOVER_REVOKE +import com.tencent.devops.auth.constant.AuthMessageCode.ERROR_SINGLE_GROUP_REMOVE +import com.tencent.devops.auth.dao.AuthAuthorizationDao +import com.tencent.devops.auth.dao.AuthResourceGroupDao +import com.tencent.devops.auth.dao.AuthResourceGroupMemberDao +import com.tencent.devops.auth.pojo.AuthResourceGroupMember +import com.tencent.devops.auth.pojo.ResourceMemberInfo +import com.tencent.devops.auth.pojo.dto.HandoverDetailDTO +import com.tencent.devops.auth.pojo.dto.HandoverOverviewCreateDTO +import com.tencent.devops.auth.pojo.dto.IamGroupIdsQueryConditionDTO +import com.tencent.devops.auth.pojo.dto.InvalidAuthorizationsDTO +import com.tencent.devops.auth.pojo.dto.MemberGroupJoinedDTO +import com.tencent.devops.auth.pojo.dto.ProjectMembersQueryConditionDTO +import com.tencent.devops.auth.pojo.enum.BatchOperateType +import com.tencent.devops.auth.pojo.enum.HandoverAction +import com.tencent.devops.auth.pojo.enum.HandoverQueryChannel +import com.tencent.devops.auth.pojo.enum.HandoverStatus +import com.tencent.devops.auth.pojo.enum.HandoverType +import com.tencent.devops.auth.pojo.enum.JoinedType +import com.tencent.devops.auth.pojo.enum.MemberType +import com.tencent.devops.auth.pojo.enum.OperateChannel +import com.tencent.devops.auth.pojo.enum.RemoveMemberButtonControl +import com.tencent.devops.auth.pojo.request.GroupMemberCommonConditionReq +import com.tencent.devops.auth.pojo.request.GroupMemberHandoverConditionReq +import com.tencent.devops.auth.pojo.request.GroupMemberRemoveConditionReq +import com.tencent.devops.auth.pojo.request.GroupMemberRenewalConditionReq +import com.tencent.devops.auth.pojo.request.GroupMemberSingleRenewalReq +import com.tencent.devops.auth.pojo.request.HandoverDetailsQueryReq +import com.tencent.devops.auth.pojo.request.HandoverOverviewBatchUpdateReq +import com.tencent.devops.auth.pojo.request.HandoverOverviewQueryReq +import com.tencent.devops.auth.pojo.request.HandoverOverviewUpdateReq +import com.tencent.devops.auth.pojo.request.ProjectMembersQueryConditionReq +import com.tencent.devops.auth.pojo.request.RemoveMemberFromProjectReq +import com.tencent.devops.auth.pojo.request.ResourceType2CountOfHandoverQuery +import com.tencent.devops.auth.pojo.vo.BatchOperateGroupMemberCheckVo +import com.tencent.devops.auth.pojo.vo.GroupDetailsInfoVo +import com.tencent.devops.auth.pojo.vo.HandoverAuthorizationDetailVo +import com.tencent.devops.auth.pojo.vo.HandoverGroupDetailVo +import com.tencent.devops.auth.pojo.vo.HandoverOverviewVo +import com.tencent.devops.auth.pojo.vo.ResourceType2CountVo +import com.tencent.devops.auth.service.DeptService +import com.tencent.devops.auth.service.PermissionAuthorizationService +import com.tencent.devops.auth.service.iam.PermissionHandoverApplicationService +import com.tencent.devops.auth.service.iam.PermissionManageFacadeService +import com.tencent.devops.auth.service.iam.PermissionResourceGroupPermissionService +import com.tencent.devops.auth.service.iam.PermissionResourceGroupService +import com.tencent.devops.auth.service.iam.PermissionResourceGroupSyncService +import com.tencent.devops.auth.service.iam.PermissionResourceMemberService +import com.tencent.devops.auth.service.lock.HandleHandoverApplicationLock +import com.tencent.devops.common.api.exception.ErrorCodeException +import com.tencent.devops.common.api.model.SQLPage +import com.tencent.devops.common.api.util.DateTimeUtil +import com.tencent.devops.common.api.util.PageUtil +import com.tencent.devops.common.api.util.timestamp +import com.tencent.devops.common.api.util.timestampmilli +import com.tencent.devops.common.auth.api.ActionId +import com.tencent.devops.common.auth.api.AuthPermission +import com.tencent.devops.common.auth.api.AuthResourceType +import com.tencent.devops.common.auth.api.ResourceTypeId +import com.tencent.devops.common.auth.api.pojo.ResetAllResourceAuthorizationReq +import com.tencent.devops.common.auth.api.pojo.ResourceAuthorizationConditionRequest +import com.tencent.devops.common.auth.api.pojo.ResourceAuthorizationHandoverConditionRequest +import com.tencent.devops.common.auth.enums.HandoverChannelCode +import com.tencent.devops.common.client.Client +import com.tencent.devops.common.notify.enums.NotifyType +import com.tencent.devops.common.redis.RedisOperation +import com.tencent.devops.common.service.config.CommonConfig +import com.tencent.devops.common.service.utils.RetryUtils +import com.tencent.devops.common.web.utils.I18nUtil +import com.tencent.devops.model.auth.tables.records.TAuthResourceGroupRecord +import com.tencent.devops.notify.api.service.ServiceNotifyMessageTemplateResource +import com.tencent.devops.notify.pojo.SendNotifyMessageTemplateRequest +import org.jooq.DSLContext +import org.slf4j.LoggerFactory +import java.time.LocalDateTime +import java.util.concurrent.CompletableFuture +import java.util.concurrent.Executors +import java.util.concurrent.TimeUnit + +@Suppress("ComplexCondition") +class RbacPermissionManageFacadeServiceImpl( + private val permissionResourceGroupService: PermissionResourceGroupService, + private val groupPermissionService: PermissionResourceGroupPermissionService, + private val permissionResourceMemberService: PermissionResourceMemberService, + private val authResourceGroupDao: AuthResourceGroupDao, + private val authResourceGroupMemberDao: AuthResourceGroupMemberDao, + private val dslContext: DSLContext, + private val deptService: DeptService, + private val iamV2ManagerService: V2ManagerService, + private val authAuthorizationDao: AuthAuthorizationDao, + private val syncIamGroupMemberService: PermissionResourceGroupSyncService, + private val permissionAuthorizationService: PermissionAuthorizationService, + private val permissionHandoverApplicationService: PermissionHandoverApplicationService, + private val rbacCommonService: RbacCommonService, + private val redisOperation: RedisOperation, + private val authorizationDao: AuthAuthorizationDao, + private val authResourceService: AuthResourceService, + private val client: Client, + private val config: CommonConfig +) : PermissionManageFacadeService { + override fun getMemberGroupsDetails( + projectId: String, + memberId: String, + resourceType: String?, + iamGroupIds: List?, + groupName: String?, + minExpiredAt: Long?, + maxExpiredAt: Long?, + relatedResourceType: String?, + relatedResourceCode: String?, + action: String?, + operateChannel: OperateChannel?, + start: Int?, + limit: Int? + ): SQLPage { + // 根据查询条件查询得到iam组id + val iamGroupIdsByConditions = listIamGroupIdsByConditions( + condition = IamGroupIdsQueryConditionDTO( + projectCode = projectId, + groupName = groupName, + iamGroupIds = iamGroupIds, + relatedResourceType = relatedResourceType, + relatedResourceCode = relatedResourceCode, + action = action + ) + ) + // 查询成员所在资源用户组列表 + val (count, resourceGroupMembers) = listResourceGroupMembers( + projectCode = projectId, + memberId = memberId, + resourceType = resourceType, + iamGroupIds = iamGroupIdsByConditions, + minExpiredAt = minExpiredAt, + maxExpiredAt = maxExpiredAt, + operateChannel = operateChannel, + start = start, + limit = limit + ) + // 用户组对应的资源信息 + val resourceGroupMap = authResourceGroupDao.listByRelationId( + dslContext = dslContext, + projectCode = projectId, + iamGroupIds = resourceGroupMembers.map { it.iamGroupId.toString() } + ).associateBy { it.relationId } + // 只有一个成员的管理员组 + val uniqueManagerGroups = authResourceGroupMemberDao.listProjectUniqueManagerGroups( + dslContext = dslContext, + projectCode = projectId, + iamGroupIds = resourceGroupMembers.map { it.iamGroupId } + ) + // 用户组成员详情 + val groupMemberDetailMap = getGroupMemberDetailMap( + memberId = memberId, + resourceGroupMembers = resourceGroupMembers, + operateChannel = operateChannel + ) + // 获取用户正在交接的用户组,仅用于个人视角 + val groupsBeingHandover = if (operateChannel == OperateChannel.PERSONAL) { + permissionHandoverApplicationService.listMemberHandoverDetails( + projectCode = projectId, + memberId = memberId, + handoverType = HandoverType.GROUP + ) + } else { + emptyList() + } + val records = mutableListOf() + resourceGroupMembers.forEach { + val resourceGroup = resourceGroupMap[it.iamGroupId.toString()]!! + val groupMemberDetail = groupMemberDetailMap["${it.iamGroupId}_${it.memberId}"] + records.add( + convertGroupDetailsInfoVo( + resourceGroup = resourceGroup, + groupMemberDetail = groupMemberDetail, + uniqueManagerGroups = uniqueManagerGroups, + authResourceGroupMember = it, + operateChannel = operateChannel, + groupsBeingHandover = groupsBeingHandover + ) + ) + } + return SQLPage(count = count, records = records) + } + + private fun getGroupMemberDetailMap( + memberId: String, + resourceGroupMembers: List, + operateChannel: OperateChannel? + ): Map { + // 如果用户离职,查询权限中心接口会报错 + if (deptService.isUserDeparted(memberId)) { + return emptyMap() + } + // 用户组成员详情 + val groupMemberDetailMap = mutableMapOf() + // 直接加入的用户 + val userGroupIds = resourceGroupMembers + .filter { it.memberType == MemberType.USER.type } + .map { it.iamGroupId } + if (userGroupIds.isNotEmpty()) { + iamV2ManagerService.listMemberGroupsDetails( + MemberType.USER.type, + memberId, + userGroupIds.joinToString(",") + ).forEach { + groupMemberDetailMap["${it.id}_$memberId"] = it + } + } + val deptGroups = resourceGroupMembers + .filter { it.memberType == MemberType.DEPARTMENT.type } + when { + deptGroups.isEmpty() -> {} + operateChannel == OperateChannel.PERSONAL -> { + // 个人视角,会获取用户通过组织间接加入的组 + deptGroups.groupBy({ it.memberId }, { it.iamGroupId.toString() }) + .forEach { (deptId, iamGroupIds) -> + if (iamGroupIds.isEmpty()) return@forEach + iamV2ManagerService.listMemberGroupsDetails( + MemberType.DEPARTMENT.type, + deptId, + iamGroupIds.joinToString(",") + ).forEach { + groupMemberDetailMap["${it.id}_$deptId"] = it + } + } + } + + else -> { + // 管理员视角,获取组织直接加入的用户组 + val deptGroupIds = deptGroups.map { it.iamGroupId } + iamV2ManagerService.listMemberGroupsDetails( + MemberType.DEPARTMENT.type, + memberId, + deptGroupIds.joinToString(",") + ).forEach { + groupMemberDetailMap["${it.id}_$memberId"] = it + } + } + } + // 人员模板加入的组 + resourceGroupMembers.filter { it.memberType == MemberType.TEMPLATE.type } + .groupBy({ it.memberId }, { it.iamGroupId.toString() }) + .forEach { (iamTemplateId, iamGroupIds) -> + if (iamGroupIds.isEmpty()) return@forEach + iamV2ManagerService.listMemberGroupsDetails( + MemberType.TEMPLATE.type, + iamTemplateId, + iamGroupIds.joinToString(",") + ).forEach { + groupMemberDetailMap["${it.id}_$iamTemplateId"] = it + } + } + return groupMemberDetailMap + } + + private fun convertGroupDetailsInfoVo( + resourceGroup: TAuthResourceGroupRecord, + groupMemberDetail: MemberGroupDetailsResponse?, + uniqueManagerGroups: List, + authResourceGroupMember: AuthResourceGroupMember, + operateChannel: OperateChannel?, + groupsBeingHandover: List + ): GroupDetailsInfoVo { + // 如果用户离职,查询权限中心接口会报错,因此从数据库直接取数据,而不去调用权限中心接口。 + val (expiredAt, joinedTime) = if (groupMemberDetail != null) { + Pair( + TimeUnit.SECONDS.toMillis(groupMemberDetail.expiredAt), + TimeUnit.SECONDS.toMillis(groupMemberDetail.createdAt) + ) + } else { + Pair( + authResourceGroupMember.expiredTime.timestampmilli(), + 0L + ) + } + val between = expiredAt - System.currentTimeMillis() + val groupId = resourceGroup.relationId.toInt() + return GroupDetailsInfoVo( + resourceCode = resourceGroup.resourceCode, + resourceName = resourceGroup.resourceName, + resourceType = resourceGroup.resourceType, + groupId = groupId, + groupName = resourceGroup.groupName, + groupDesc = resourceGroup.description, + expiredAtDisplay = when { + expiredAt == PERMANENT_EXPIRED_TIME -> + I18nUtil.getCodeLanMessage(messageCode = AuthI18nConstants.BK_MEMBER_EXPIRED_AT_DISPLAY_PERMANENT) + + between >= 0 -> I18nUtil.getCodeLanMessage( + messageCode = AuthI18nConstants.BK_MEMBER_EXPIRED_AT_DISPLAY_NORMAL, + params = arrayOf(DateTimeUtil.formatDay(between)) + ) + + else -> I18nUtil.getCodeLanMessage( + messageCode = AuthI18nConstants.BK_MEMBER_EXPIRED_AT_DISPLAY_EXPIRED + ) + }, + expiredAt = expiredAt, + joinedTime = joinedTime, + removeMemberButtonControl = when { + authResourceGroupMember.memberType == MemberType.TEMPLATE.type -> + RemoveMemberButtonControl.TEMPLATE + + operateChannel == OperateChannel.PERSONAL && + authResourceGroupMember.memberType == MemberType.DEPARTMENT.type -> + RemoveMemberButtonControl.DEPARTMENT + + resourceGroup.resourceType == AuthResourceType.PROJECT.value && + uniqueManagerGroups.contains(authResourceGroupMember.iamGroupId) -> + RemoveMemberButtonControl.UNIQUE_MANAGER + + uniqueManagerGroups.contains(authResourceGroupMember.iamGroupId) -> + RemoveMemberButtonControl.UNIQUE_OWNER + + else -> + RemoveMemberButtonControl.OTHER + }, + joinedType = when { + authResourceGroupMember.memberType == MemberType.TEMPLATE.type -> JoinedType.TEMPLATE + authResourceGroupMember.memberType == MemberType.DEPARTMENT.type && + operateChannel == OperateChannel.PERSONAL -> JoinedType.DEPARTMENT + + else -> JoinedType.DIRECT + }, + operator = "", + beingHandedOver = groupsBeingHandover.map { it.itemId.toInt() }.contains(groupId), + flowNo = groupsBeingHandover.firstOrNull { it.itemId.toInt() == groupId }?.flowNo, + memberType = MemberType.get(authResourceGroupMember.memberType) + ) + } + + override fun getMemberGroupsCount( + projectCode: String, + memberId: String, + groupName: String?, + minExpiredAt: Long?, + maxExpiredAt: Long?, + relatedResourceType: String?, + relatedResourceCode: String?, + action: String?, + operateChannel: OperateChannel? + ): List { + val (iamTemplateIds, memberDeptInfos) = getMemberTemplateIdsAndDeptInfos( + projectCode = projectCode, + memberId = memberId, + operateChannel = operateChannel + ) + val iamGroupIdsByConditions = listIamGroupIdsByConditions( + condition = IamGroupIdsQueryConditionDTO( + projectCode = projectCode, + groupName = groupName, + relatedResourceType = relatedResourceType, + relatedResourceCode = relatedResourceCode, + action = action + ) + ) + // 获取成员加入的用户组 + val memberGroupCountMap = authResourceGroupMemberDao.countMemberGroupOfResourceType( + dslContext = dslContext, + projectCode = projectCode, + memberId = memberId, + iamTemplateIds = iamTemplateIds, + iamGroupIds = iamGroupIdsByConditions, + minExpiredAt = minExpiredAt?.let { DateTimeUtil.convertTimestampToLocalDateTime(it / 1000) }, + maxExpiredAt = maxExpiredAt?.let { DateTimeUtil.convertTimestampToLocalDateTime(it / 1000) }, + memberDeptInfos = memberDeptInfos + ) + return rbacCommonService.convertResourceType2Count(memberGroupCountMap) + } + + private fun getMemberTemplateIdsAndDeptInfos( + projectCode: String, + memberId: String, + operateChannel: OperateChannel? + ): Pair, List> { + // 获取用户加入的项目级用户组模板ID + val iamTemplateIds = listProjectMemberGroupTemplateIds( + projectCode = projectCode, + memberId = memberId + ) + // 获取用户部门信息 + val memberDeptInfos = if (operateChannel == OperateChannel.PERSONAL) { + getMemberDeptInfos(memberId) + } else { + emptyList() + } + return Pair(iamTemplateIds, memberDeptInfos) + } + + override fun listIamGroupIdsByConditions(condition: IamGroupIdsQueryConditionDTO): List { + return with(condition) { + val filterGroupsByGroupName = if (isQueryByGroupName()) { + permissionResourceGroupService.listIamGroupIdsByGroupName( + projectId = projectCode, + groupName = groupName!! + ) + } else { + emptyList() + } + val finalGroupIds = if (isQueryByGroupPermissions()) { + groupPermissionService.listGroupsByPermissionConditions( + projectCode = projectCode, + filterIamGroupIds = filterGroupsByGroupName, + relatedResourceType = relatedResourceType!!, + relatedResourceCode = relatedResourceCode, + action = action + ) + } else { + filterGroupsByGroupName + }.toMutableList() + iamGroupIds?.let { finalGroupIds.addAll(it) } + finalGroupIds + } + } + + override fun listMemberGroupIdsInProject( + projectCode: String, + memberId: String + ): List { + // 获取用户加入的项目级用户组模板ID + val iamTemplateIds = listProjectMemberGroupTemplateIds( + projectCode = projectCode, + memberId = memberId + ) + return authResourceGroupMemberDao.listMemberGroupIdsInProject( + dslContext = dslContext, + projectCode = projectCode, + memberId = memberId, + iamTemplateIds = iamTemplateIds + ) + } + + @Suppress("LongParameterList") + override fun listResourceGroupMembers( + projectCode: String, + memberId: String, + resourceType: String?, + iamGroupIds: List?, + minExpiredAt: Long?, + maxExpiredAt: Long?, + operateChannel: OperateChannel?, + filterMemberType: MemberType?, + excludeIamGroupIds: List?, + onlyExcludeUserDirectlyJoined: Boolean?, + start: Int?, + limit: Int? + ): Pair> { + // 获取用户的部门信息以及加入的项目级别用户组模板ID + val (iamTemplateIds, memberDeptInfos) = getMemberTemplateIdsAndDeptInfos( + projectCode = projectCode, + memberId = memberId, + operateChannel = operateChannel + ) + + val minExpiredTime = minExpiredAt?.let { DateTimeUtil.convertTimestampToLocalDateTime(it / 1000) } + val maxExpiredTime = maxExpiredAt?.let { DateTimeUtil.convertTimestampToLocalDateTime(it / 1000) } + val count = authResourceGroupMemberDao.countMemberGroup( + dslContext = dslContext, + projectCode = projectCode, + memberId = memberId, + iamTemplateIds = iamTemplateIds, + resourceType = resourceType, + iamGroupIds = iamGroupIds, + minExpiredAt = minExpiredTime, + maxExpiredAt = maxExpiredTime, + memberDeptInfos = memberDeptInfos, + filterMemberType = filterMemberType, + excludeIamGroupIds = excludeIamGroupIds, + onlyExcludeUserDirectlyJoined = onlyExcludeUserDirectlyJoined + ) + val resourceGroupMembers = authResourceGroupMemberDao.listMemberGroupDetail( + dslContext = dslContext, + projectCode = projectCode, + memberId = memberId, + iamTemplateIds = iamTemplateIds, + resourceType = resourceType, + iamGroupIds = iamGroupIds, + minExpiredAt = minExpiredTime, + maxExpiredAt = maxExpiredTime, + memberDeptInfos = memberDeptInfos, + filterMemberType = filterMemberType, + excludeIamGroupIds = excludeIamGroupIds, + onlyExcludeUserDirectlyJoined = onlyExcludeUserDirectlyJoined, + offset = start, + limit = limit + ) + return Pair(count, resourceGroupMembers) + } + + // 获取用户加入的项目级用户组模板ID + private fun listProjectMemberGroupTemplateIds( + projectCode: String, + memberId: String + ): List { + // 查询项目下包含该成员的组列表 + val projectGroupIds = authResourceGroupMemberDao.listResourceGroupMember( + dslContext = dslContext, + projectCode = projectCode, + resourceType = AuthResourceType.PROJECT.value, + memberId = memberId + ).map { it.iamGroupId.toString() } + // 通过项目组ID获取人员模板ID + return authResourceGroupDao.listByRelationId( + dslContext = dslContext, + projectCode = projectCode, + iamGroupIds = projectGroupIds + ).filter { it.iamTemplateId != null } + .map { it.iamTemplateId.toString() } + } + + private fun getMemberDeptInfos( + memberId: String + ): List { + deptService.getUserInfo( + userId = "admin", + name = memberId + )?.deptInfo ?: return emptyList() + return deptService.getUserDeptInfo(memberId).toList() + } + + private fun getGroupIdsByGroupMemberCondition( + projectCode: String, + commonCondition: GroupMemberCommonConditionReq + ): Map> { + val finalMemberGroups = mutableListOf() + + val resourceGroupMembersByCondition = when { + commonCondition.allSelection -> { + listResourceGroupMembers( + projectCode = projectCode, + memberId = commonCondition.targetMember.id, + operateChannel = commonCondition.operateChannel + ).second + } + + commonCondition.resourceTypes.isNotEmpty() -> { + commonCondition.resourceTypes.flatMap { resourceType -> + listResourceGroupMembers( + projectCode = projectCode, + memberId = commonCondition.targetMember.id, + resourceType = resourceType, + operateChannel = commonCondition.operateChannel + ).second + } + } + + else -> emptyList() + } + + finalMemberGroups.addAll(resourceGroupMembersByCondition) + if (commonCondition.groupIds.isNotEmpty()) { + val memberType2groupIds = commonCondition.groupIds.groupBy { it.memberType } + memberType2groupIds.forEach { (memberType, groupIds) -> + val groupsOfSelect = listResourceGroupMembers( + projectCode = projectCode, + memberId = commonCondition.targetMember.id, + iamGroupIds = groupIds.map { it.id }, + operateChannel = commonCondition.operateChannel, + filterMemberType = memberType + ).second + finalMemberGroups.addAll(groupsOfSelect) + } + } + // 分类 + val result = mutableMapOf>() + finalMemberGroups.groupBy { it.memberType }.forEach { (memberType, groups) -> + result[MemberType.get(memberType)] = groups.map { it.iamGroupId } + } + return result + } + + override fun listProjectMembersByComplexConditions( + conditionReq: ProjectMembersQueryConditionReq + ): SQLPage { + logger.info("list project members by complex conditions: $conditionReq") + // 不允许同时查询部门名称和用户名称 + if (conditionReq.userName != null && conditionReq.deptName != null) { + return SQLPage(count = 0, records = emptyList()) + } + + // 简单查询直接返回结果 + if (!conditionReq.isComplexQuery()) { + return permissionResourceMemberService.listProjectMembers( + projectCode = conditionReq.projectCode, + memberType = conditionReq.memberType, + userName = conditionReq.userName, + deptName = conditionReq.deptName, + departedFlag = conditionReq.departedFlag, + page = conditionReq.page, + pageSize = conditionReq.pageSize + ) + } + + // 处理复杂查询条件 + val iamGroupIdsByCondition = if (conditionReq.isNeedToQueryIamGroups()) { + listIamGroupIdsByConditions( + condition = IamGroupIdsQueryConditionDTO( + projectCode = conditionReq.projectCode, + groupName = conditionReq.groupName, + relatedResourceType = conditionReq.relatedResourceType, + relatedResourceCode = conditionReq.relatedResourceCode, + action = conditionReq.action + ) + ) + } else { + emptyList() + }.toMutableList() + + // 查询不到用户组,直接返回空 + if (conditionReq.isNeedToQueryIamGroups() && iamGroupIdsByCondition.isEmpty()) { + return SQLPage(0, emptyList()) + } + + val conditionDTO = ProjectMembersQueryConditionDTO.build(conditionReq, iamGroupIdsByCondition) + + if (iamGroupIdsByCondition.isNotEmpty()) { + logger.debug("iamGroupIdsByCondition :{}", iamGroupIdsByCondition) + // 根据用户组Id查询出对应用户组中的人员模板成员 + val iamTemplateIds = authResourceGroupMemberDao.listProjectMembersByComplexConditions( + dslContext = dslContext, + conditionDTO = ProjectMembersQueryConditionDTO( + projectCode = conditionDTO.projectCode, + queryTemplate = true, + iamGroupIds = conditionDTO.iamGroupIds + ) + ) + if (iamTemplateIds.isNotEmpty()) { + // 根据查询出的人员模板ID,查询出对应的组ID + val iamGroupIdsFromTemplate = authResourceGroupDao.listIamGroupIdsByConditions( + dslContext = dslContext, + projectCode = conditionDTO.projectCode, + iamTemplateIds = iamTemplateIds.map { it.id.toInt() } + ) + iamGroupIdsByCondition.addAll(iamGroupIdsFromTemplate) + logger.debug("iamGroupIdsByCondition and template :{}", iamGroupIdsByCondition) + } + } + + val records = authResourceGroupMemberDao.listProjectMembersByComplexConditions( + dslContext = dslContext, + conditionDTO = conditionDTO + ) + logger.debug("listProjectMembersByComplexConditions :{}", records) + + val count = authResourceGroupMemberDao.countProjectMembersByComplexConditions( + dslContext = dslContext, + conditionDTO = conditionDTO + ) + logger.debug("listProjectMembersByComplexConditions :$count") + // 添加离职标志 + return if (conditionDTO.departedFlag == false) { + SQLPage(count, records) + } else { + SQLPage(count, permissionResourceMemberService.addDepartedFlagToMembers(records)) + } + } + + override fun listInvalidAuthorizationsAfterOperatedGroups( + projectCode: String, + iamGroupIds: List, + memberId: String + ): InvalidAuthorizationsDTO { + val startEpoch = System.currentTimeMillis() + try { + // 筛选出本次操作中未过期的用户组 + val iamGroupIdsOfNotExpired = getNotExpiredIamGroupIds( + projectCode = projectCode, + memberId = memberId, + iamGroupIds = iamGroupIds + ) + // 获取用户退出/交接以上用户组后,还未退出的用户组 + val (count, userGroupsJoinedAfterOperatedGroups) = listResourceGroupMembers( + projectCode = projectCode, + memberId = memberId, + excludeIamGroupIds = iamGroupIds, + onlyExcludeUserDirectlyJoined = true, + operateChannel = OperateChannel.PERSONAL, + minExpiredAt = LocalDateTime.now().timestampmilli() + ) + logger.debug("list all user groups joined after operated groups: {}, {}", count, userGroupsJoinedAfterOperatedGroups) + + val isHasProjectVisitPermAfterOperatedGroups = checkProjectVisitPermission( + projectCode = projectCode, + iamGroupIds = userGroupsJoinedAfterOperatedGroups.map { it.iamGroupId } + ) + logger.debug("whether the user has project visit perm after operated groups: {}", isHasProjectVisitPermAfterOperatedGroups) + + if (count == 0L || !isHasProjectVisitPermAfterOperatedGroups) { + return getInvalidAuthorizationsAfterAllGroupsRemoved(projectCode, memberId, iamGroupIdsOfNotExpired) + } + val (invalidGroups, invalidPipelines) = listInvalidPipelinesAfterOperatedGroups( + projectCode = projectCode, + iamGroupIds = iamGroupIds, + memberId = memberId + ) + val invalidAuthorizationsDTO = InvalidAuthorizationsDTO( + invalidGroupIds = invalidGroups, + invalidPipelineIds = invalidPipelines + ) + logger.info( + "invalid authorizations after operated groups|$projectCode|$iamGroupIds|$memberId|$invalidAuthorizationsDTO" + ) + return invalidAuthorizationsDTO + } finally { + logger.info( + "It take(${System.currentTimeMillis() - startEpoch})ms to check invalid authorizations after operated groups" + + "|$projectCode|$iamGroupIds|$memberId" + ) + } + } + + private fun getNotExpiredIamGroupIds( + projectCode: String, + memberId: String, + iamGroupIds: List + ): List { + return authResourceGroupMemberDao.listMemberGroupDetail( + dslContext = dslContext, + projectCode = projectCode, + memberId = memberId, + iamGroupIds = iamGroupIds, + minExpiredAt = LocalDateTime.now() + ).map { it.iamGroupId } + } + + private fun checkProjectVisitPermission( + projectCode: String, + iamGroupIds: List + ): Boolean { + return groupPermissionService.isGroupsHasPermission( + projectCode = projectCode, + filterIamGroupIds = iamGroupIds, + relatedResourceType = ResourceTypeId.PROJECT, + relatedResourceCode = projectCode, + action = ActionId.PROJECT_VISIT + ) + } + + private fun getInvalidAuthorizationsAfterAllGroupsRemoved( + projectCode: String, + memberId: String, + iamGroupIdsOfNotExpired: List + ): InvalidAuthorizationsDTO { + val invalidAuthorizations = authAuthorizationDao.list( + dslContext = dslContext, + condition = ResourceAuthorizationConditionRequest( + projectCode = projectCode, + handoverFrom = memberId + ) + ).groupBy({ it.resourceType }, { it.resourceCode }) + + val operatedGroupsWithExecutePerm = groupPermissionService.listGroupsByPermissionConditions( + projectCode = projectCode, + relatedResourceType = AuthResourceType.PIPELINE_DEFAULT.value, + action = ActionId.PIPELINE_EXECUTE, + filterIamGroupIds = iamGroupIdsOfNotExpired + ) + + return InvalidAuthorizationsDTO( + invalidGroupIds = operatedGroupsWithExecutePerm, + invalidPipelineIds = invalidAuthorizations[ResourceTypeId.PIPELINE] ?: emptyList(), + invalidRepertoryIds = invalidAuthorizations[ResourceTypeId.REPERTORY] ?: emptyList(), + invalidEnvNodeIds = invalidAuthorizations[ResourceTypeId.ENV_NODE] ?: emptyList() + ) + } + + private fun listInvalidPipelinesAfterOperatedGroups( + projectCode: String, + iamGroupIds: List, + memberId: String + ): InvalidAuthorizationsDTO { + logger.info("list invalid authorizations after operated groups:$projectCode|$iamGroupIds|$memberId") + val now = LocalDateTime.now() + // 0.筛选出本次操作中的用户未过期用户组ID + val iamGroupIdsOfNotExpired = getNotExpiredIamGroupIds( + projectCode = projectCode, + memberId = memberId, + iamGroupIds = iamGroupIds + ) + logger.debug("list iam group ids of not expired:{}", iamGroupIdsOfNotExpired) + // 1.筛选出本次退出/交接中包含流水线执行权限的用户组 + val operatedGroupsWithExecutePerm = groupPermissionService.listGroupsByPermissionConditions( + projectCode = projectCode, + relatedResourceType = AuthResourceType.PIPELINE_DEFAULT.value, + action = ActionId.PIPELINE_EXECUTE, + filterIamGroupIds = iamGroupIdsOfNotExpired + ) + logger.debug("list operated groups with execute perm:{}", operatedGroupsWithExecutePerm) + if (operatedGroupsWithExecutePerm.isEmpty()) { + return InvalidAuthorizationsDTO(emptyList(), emptyList()) + } + + // 2.获取用户退出/交接以上操作的用户组后,还未退出并且未过期的流水线/项目级别(仅这些类型会包含流水线执行权限)的用户组。 + val userGroupsJoinedAfterOperatedGroups = listResourceGroupMembers( + projectCode = projectCode, + memberId = memberId, + resourceType = ResourceTypeId.PIPELINE, + excludeIamGroupIds = iamGroupIdsOfNotExpired, + operateChannel = OperateChannel.PERSONAL, + onlyExcludeUserDirectlyJoined = true, + minExpiredAt = now.timestampmilli() + ).second.toMutableList().apply { + addAll( + listResourceGroupMembers( + projectCode = projectCode, + memberId = memberId, + resourceType = ResourceTypeId.PROJECT, + excludeIamGroupIds = iamGroupIdsOfNotExpired, + operateChannel = OperateChannel.PERSONAL, + onlyExcludeUserDirectlyJoined = true, + minExpiredAt = now.timestampmilli() + ).second + ) + }.map { it.iamGroupId } + logger.debug("list pipeline and project groups joined after operated groups:{}", userGroupsJoinedAfterOperatedGroups) + + // 3.查询未退出的流水线/项目级别的用户组中是否包含项目级别的流水线执行权限。 + // 查询用户在未退出的用户组中否还有整个项目的流水线执行权限。若有的话,则对流水线的代持人权限未造成影响。 + val hasAllPipelineExecutePermAfterOperateGroups = groupPermissionService.isGroupsHasProjectLevelPermission( + projectCode = projectCode, + filterIamGroupIds = userGroupsJoinedAfterOperatedGroups, + action = ActionId.PIPELINE_EXECUTE + ) + logger.debug("has all pipeline execute perm after operate groups:{}", hasAllPipelineExecutePermAfterOperateGroups) + + // 3.1.若用户在未退出的组中拥有整个项目的流水线执行权限,则本次不会对任何的流水线代持人权限造成影响。 + if (hasAllPipelineExecutePermAfterOperateGroups) + return InvalidAuthorizationsDTO(emptyList(), emptyList()) + + // 3.2.若没有的话,查询本次退出/交接的用户组中是否包含项目级别的流水线执行权限。 + val hasAllPipelineExecutePermInOperateGroups = groupPermissionService.isGroupsHasProjectLevelPermission( + projectCode = projectCode, + filterIamGroupIds = operatedGroupsWithExecutePerm, + action = ActionId.PIPELINE_EXECUTE + ) + logger.debug("has all pipeline execute perm in operate groups:{}", hasAllPipelineExecutePermInOperateGroups) + + val pipelinesWithoutAuthorization = if (hasAllPipelineExecutePermInOperateGroups) { + // 3.2.1 如果本次退出/交接的用户组中包含项目级别的流水线执行权限, + // 那么查询出用户还有执行流水线权限的流水线,该项目下除了这些流水线,其他的流水线代持人权限都会失效。 + val userHasExecutePermAfterOperatedGroups = groupPermissionService.listGroupResourcesWithPermission( + projectCode = projectCode, + filterIamGroupIds = userGroupsJoinedAfterOperatedGroups, + relatedResourceType = ResourceTypeId.PIPELINE, + action = ActionId.PIPELINE_EXECUTE + )[ResourceTypeId.PIPELINE] ?: emptyList() + logger.debug("user has execute perm after operated groups:{}", userHasExecutePermAfterOperatedGroups) + // 失去代持人权限的流水线 + authAuthorizationDao.list( + dslContext = dslContext, + condition = ResourceAuthorizationConditionRequest( + projectCode = projectCode, + resourceType = ResourceTypeId.PIPELINE, + handoverFrom = memberId, + excludeResourceCodes = userHasExecutePermAfterOperatedGroups + ) + ).map { it.resourceCode } + } else { + // 3.2.2 如果本次退出/交接的用户组中不包含整个项目的流水线执行权限。 + // 通过计算得出,用户本次操作用户组,导致失去流水线执行权限的流水线。 + // 然后再计算失去这些流水线执行权限后,会导致哪些流水线的代持人权限失效。 + val pipelinesWithExecutePermAfterOperatedGroups = groupPermissionService.listGroupResourcesWithPermission( + projectCode = projectCode, + filterIamGroupIds = userGroupsJoinedAfterOperatedGroups, + relatedResourceType = ResourceTypeId.PIPELINE, + action = ActionId.PIPELINE_EXECUTE + )[ResourceTypeId.PIPELINE] ?: emptyList() + logger.debug("pipelines with execute perm after operate groups:{}", pipelinesWithExecutePermAfterOperatedGroups) + + val pipelinesWithExecutePermInOperateGroups = groupPermissionService.listGroupResourcesWithPermission( + projectCode = projectCode, + filterIamGroupIds = operatedGroupsWithExecutePerm, + relatedResourceType = ResourceTypeId.PIPELINE, + action = ActionId.PIPELINE_EXECUTE + )[ResourceTypeId.PIPELINE] ?: emptyList() + logger.debug("pipelines with execute perm in operate groups:{}", pipelinesWithExecutePermInOperateGroups) + + val pipelineExecutePermLostFromUser = pipelinesWithExecutePermInOperateGroups.filterNot { + pipelinesWithExecutePermAfterOperatedGroups.contains(it) + } + // 失去代持人权限的流水线 + authAuthorizationDao.list( + dslContext = dslContext, + condition = ResourceAuthorizationConditionRequest( + projectCode = projectCode, + resourceType = ResourceTypeId.PIPELINE, + handoverFrom = memberId, + filterResourceCodes = pipelineExecutePermLostFromUser + ) + ).map { it.resourceCode } + } + logger.debug("pipelines without authorization:{}", pipelinesWithoutAuthorization) + if (pipelinesWithoutAuthorization.isNotEmpty()) { + return InvalidAuthorizationsDTO( + invalidGroupIds = operatedGroupsWithExecutePerm, + invalidPipelineIds = pipelinesWithoutAuthorization + ) + } + + return InvalidAuthorizationsDTO(emptyList(), emptyList()) + } + + override fun renewalGroupMember( + userId: String, + projectCode: String, + renewalConditionReq: GroupMemberSingleRenewalReq + ): Boolean { + logger.info("renewal group member $userId|$projectCode|$renewalConditionReq") + val groupId = renewalConditionReq.groupId + batchOperateGroupMembers( + projectCode = projectCode, + type = BatchOperateType.RENEWAL, + conditionReq = GroupMemberRenewalConditionReq( + groupIds = listOf( + MemberGroupJoinedDTO( + id = groupId, + memberType = MemberType.get(renewalConditionReq.targetMember.type) + ) + ), + targetMember = renewalConditionReq.targetMember, + renewalDuration = renewalConditionReq.renewalDuration + ), + operateGroupMemberTask = ::renewalTask + ) + return true + } + + private fun renewalTask( + projectCode: String, + groupId: Int, + renewalConditionReq: GroupMemberRenewalConditionReq, + expiredAt: Long + ) { + logger.info("renewal group member ${renewalConditionReq.targetMember}|$projectCode|$groupId|$expiredAt") + val targetMember = renewalConditionReq.targetMember + if (targetMember.type == MemberType.USER.type && deptService.isUserDeparted(targetMember.id)) { + return + } + val secondsOfRenewalDuration = TimeUnit.DAYS.toSeconds(renewalConditionReq.renewalDuration.toLong()) + val secondsOfCurrentTime = System.currentTimeMillis() / 1000 + // 若权限已过期,则为当前时间+续期天数,若未过期,则为有效期+续期天数 + val finalExpiredAt = if (expiredAt < secondsOfCurrentTime) { + secondsOfCurrentTime + } else { + expiredAt + } + secondsOfRenewalDuration + if (!isNeedToRenewal(finalExpiredAt)) { + return + } + permissionResourceMemberService.renewalIamGroupMembers( + groupId = groupId, + members = listOf(ManagerMember(targetMember.type, targetMember.id)), + expiredAt = finalExpiredAt + ) + authResourceGroupMemberDao.update( + dslContext = dslContext, + projectCode = projectCode, + iamGroupId = groupId, + expiredTime = DateTimeUtil.convertTimestampToLocalDateTime(finalExpiredAt), + memberId = targetMember.id + ) + } + + private fun isNeedToRenewal(expiredAt: Long): Boolean { + return expiredAt < PERMANENT_EXPIRED_TIME + } + + override fun batchRenewalGroupMembersFromManager( + userId: String, + projectCode: String, + renewalConditionReq: GroupMemberRenewalConditionReq + ): Boolean { + logger.info("batch renewal group member $userId|$projectCode|$renewalConditionReq") + batchOperateGroupMembers( + projectCode = projectCode, + type = BatchOperateType.RENEWAL, + conditionReq = renewalConditionReq, + operateGroupMemberTask = ::renewalTask + ) + return true + } + + override fun batchHandoverGroupMembersFromManager( + userId: String, + projectCode: String, + handoverMemberDTO: GroupMemberHandoverConditionReq + ): Boolean { + logger.info("batch handover group members from manager $userId|$projectCode|$handoverMemberDTO") + handoverMemberDTO.checkHandoverTo() + // 若交接对象是部门,直接进行交接 + if (handoverMemberDTO.targetMember.type == MemberType.DEPARTMENT.type) { + batchOperateGroupMembers( + projectCode = projectCode, + type = BatchOperateType.HANDOVER, + conditionReq = handoverMemberDTO, + operateGroupMemberTask = ::handoverTask + ) + } + // 若操作对象是用户,需要将被影响流水线授权一并交接给授权人 + val groupIds = getGroupIdsByGroupMemberCondition( + projectCode = projectCode, + commonCondition = handoverMemberDTO + )[MemberType.USER] ?: return true + // 获取导致失效的流水线/代码库授权/环境节点授权,并进行交接 + val (invalidGroups, invalidPipelines, invalidRepertoryIds, invalidEnvNodeIds) = + listInvalidAuthorizationsAfterOperatedGroups( + projectCode = projectCode, + iamGroupIds = groupIds, + memberId = handoverMemberDTO.targetMember.id + ) + // 检查授予人是否有代码库oauth权限 + if (invalidRepertoryIds.isNotEmpty()) { + permissionAuthorizationService.checkRepertoryAuthorizationsHanover( + operator = userId, + projectCode = projectCode, + repertoryIds = invalidRepertoryIds, + handoverFrom = handoverMemberDTO.targetMember.id, + handoverTo = handoverMemberDTO.handoverTo.id + ) + } + // 交接用户组 + batchOperateGroupMembers( + projectCode = projectCode, + type = BatchOperateType.HANDOVER, + conditionReq = handoverMemberDTO, + operateGroupMemberTask = ::handoverTask + ) + handoverAuthorizationsWhenOperatedGroups( + userId = userId, + projectCode = projectCode, + invalidPipelines = invalidPipelines, + invalidRepertoryIds = invalidRepertoryIds, + invalidEnvNodeIds = invalidEnvNodeIds, + handoverFrom = handoverMemberDTO.targetMember.id, + handoverTo = handoverMemberDTO.handoverTo.id + ) + return true + } + + private fun handoverAuthorizationsWhenOperatedGroups( + userId: String, + projectCode: String, + invalidRepertoryIds: List, + invalidPipelines: List, + invalidEnvNodeIds: List, + handoverFrom: String, + handoverTo: String + ) { + if (invalidRepertoryIds.isNotEmpty()) { + permissionAuthorizationService.resetResourceAuthorizationByResourceType( + operator = userId, + projectCode = projectCode, + condition = ResourceAuthorizationHandoverConditionRequest( + projectCode = projectCode, + resourceType = ResourceTypeId.REPERTORY, + fullSelection = true, + filterResourceCodes = invalidRepertoryIds, + handoverChannel = HandoverChannelCode.MANAGER, + handoverFrom = handoverFrom, + handoverTo = handoverTo, + checkPermission = false + ) + ) + } + if (invalidPipelines.isNotEmpty()) { + permissionAuthorizationService.resetResourceAuthorizationByResourceType( + operator = userId, + projectCode = projectCode, + condition = ResourceAuthorizationHandoverConditionRequest( + projectCode = projectCode, + resourceType = ResourceTypeId.PIPELINE, + fullSelection = true, + filterResourceCodes = invalidPipelines, + handoverChannel = HandoverChannelCode.MANAGER, + handoverFrom = handoverFrom, + handoverTo = handoverTo, + checkPermission = false + ) + ) + } + if (invalidEnvNodeIds.isNotEmpty()) { + permissionAuthorizationService.resetResourceAuthorizationByResourceType( + operator = userId, + projectCode = projectCode, + condition = ResourceAuthorizationHandoverConditionRequest( + projectCode = projectCode, + resourceType = ResourceTypeId.ENV_NODE, + fullSelection = true, + filterResourceCodes = invalidEnvNodeIds, + handoverChannel = HandoverChannelCode.MANAGER, + handoverFrom = handoverFrom, + handoverTo = handoverTo, + checkPermission = false + ) + ) + } + } + + override fun batchHandoverApplicationFromPersonal( + userId: String, + projectCode: String, + handoverMemberDTO: GroupMemberHandoverConditionReq + ): String { + logger.info("batch handover group members from personal $userId|$projectCode|$handoverMemberDTO") + handoverMemberDTO.checkHandoverTo() + // 成员直接加入的组 + val groupIds = getGroupIdsByGroupMemberCondition( + projectCode = projectCode, + commonCondition = handoverMemberDTO + )[MemberType.get(MemberType.USER.type)]?.toMutableList() + if (groupIds.isNullOrEmpty()) { + throw ErrorCodeException( + errorCode = AuthMessageCode.GROUP_NOT_EXIST + ) + } + + // 过滤掉审核中的用户组 + val beingHandoverGroups = permissionHandoverApplicationService.listMemberHandoverDetails( + projectCode = projectCode, + memberId = handoverMemberDTO.targetMember.id, + handoverType = HandoverType.GROUP + ).map { it.itemId.toInt() } + groupIds.removeAll(beingHandoverGroups) + // 本次操作导致失效的授权 + val invalidAuthorizations = listInvalidAuthorizationsAfterOperatedGroups( + projectCode = projectCode, + iamGroupIds = groupIds, + memberId = handoverMemberDTO.targetMember.id + ) + + val invalidPipelines = invalidAuthorizations.invalidPipelineIds + val invalidRepertoryIds = invalidAuthorizations.invalidRepertoryIds + val invalidEnvNodeIds = invalidAuthorizations.invalidEnvNodeIds + if (invalidRepertoryIds.isNotEmpty()) { + permissionAuthorizationService.checkRepertoryAuthorizationsHanover( + operator = userId, + projectCode = projectCode, + repertoryIds = invalidRepertoryIds, + handoverFrom = handoverMemberDTO.targetMember.id, + handoverTo = handoverMemberDTO.handoverTo.id + ) + } + val handoverDetails = buildHandoverDetails( + projectCode = projectCode, + groupIds = groupIds.map { it.toString() }, + invalidPipelines = invalidPipelines, + invalidRepertoryIds = invalidRepertoryIds, + invalidEnvNodeIds = invalidEnvNodeIds + ) + val projectName = authResourceService.get( + projectCode = projectCode, + resourceType = ResourceTypeId.PROJECT, + resourceCode = projectCode + ).resourceName + // 创建交接单 + val flowNo = permissionHandoverApplicationService.createHandoverApplication( + overview = HandoverOverviewCreateDTO( + projectCode = projectCode, + projectName = projectName, + applicant = handoverMemberDTO.targetMember.id, + approver = handoverMemberDTO.handoverTo.id, + handoverStatus = HandoverStatus.PENDING, + groupCount = groupIds.size, + authorizationCount = invalidPipelines.size + invalidRepertoryIds.size + ), + details = handoverDetails + ) + return flowNo + } + + private fun buildHandoverDetails( + projectCode: String, + groupIds: List, + invalidPipelines: List, + invalidRepertoryIds: List, + invalidEnvNodeIds: List + ): List { + val handoverDetails = mutableListOf() + if (groupIds.isNotEmpty()) { + val resourceGroups = authResourceGroupDao.listByRelationId( + dslContext = dslContext, + projectCode = projectCode, + iamGroupIds = groupIds + ) + resourceGroups.forEach { groupInfo -> + handoverDetails.add( + HandoverDetailDTO( + projectCode = projectCode, + itemId = groupInfo.relationId, + resourceType = groupInfo.resourceType, + handoverType = HandoverType.GROUP + ) + ) + } + } + + invalidPipelines.forEach { pipelineId -> + handoverDetails.add( + HandoverDetailDTO( + projectCode = projectCode, + itemId = pipelineId, + resourceType = ResourceTypeId.PIPELINE, + handoverType = HandoverType.AUTHORIZATION + ) + ) + } + invalidRepertoryIds.forEach { repertoryId -> + handoverDetails.add( + HandoverDetailDTO( + projectCode = projectCode, + itemId = repertoryId, + resourceType = ResourceTypeId.REPERTORY, + handoverType = HandoverType.AUTHORIZATION + ) + ) + } + invalidEnvNodeIds.forEach { envNodeId -> + handoverDetails.add( + HandoverDetailDTO( + projectCode = projectCode, + itemId = envNodeId, + resourceType = ResourceTypeId.ENV_NODE, + handoverType = HandoverType.AUTHORIZATION + ) + ) + } + return handoverDetails + } + + override fun batchDeleteResourceGroupMembersFromManager( + userId: String, + projectCode: String, + removeMemberDTO: GroupMemberRemoveConditionReq + ): Boolean { + logger.info("batch delete group members $userId|$projectCode|$removeMemberDTO") + // 若操作对象是组织,则直接退出即可。 + if (removeMemberDTO.targetMember.type == MemberType.DEPARTMENT.type) { + batchOperateGroupMembers( + projectCode = projectCode, + type = BatchOperateType.REMOVE, + conditionReq = removeMemberDTO, + operateGroupMemberTask = ::deleteTask + ) + return true + } + // 以下逻辑是用户类型成员的批量移出组 + // 根据条件获取成员直接加入的用户组 + val groupIdsDirectlyJoined = getGroupIdsByGroupMemberCondition( + projectCode = projectCode, + commonCondition = removeMemberDTO + )[MemberType.USER] ?: return true + val (invalidGroups, invalidPipelines, invalidRepertoryIds, invalidEnvNodeIds) = + listInvalidAuthorizationsAfterOperatedGroups( + projectCode = projectCode, + iamGroupIds = groupIdsDirectlyJoined, + memberId = removeMemberDTO.targetMember.id + ) + if (invalidRepertoryIds.isNotEmpty()) { + permissionAuthorizationService.checkRepertoryAuthorizationsHanover( + operator = userId, + projectCode = projectCode, + repertoryIds = invalidRepertoryIds, + handoverFrom = removeMemberDTO.targetMember.id, + handoverTo = removeMemberDTO.handoverTo!!.id + ) + } + // 获取唯一管理员组 + val uniqueManagerGroups = authResourceGroupMemberDao.listProjectUniqueManagerGroups( + dslContext = dslContext, + projectCode = projectCode, + iamGroupIds = groupIdsDirectlyJoined + ) + val (toHandoverGroups, toDeleteGroups) = groupIdsDirectlyJoined.partition { + uniqueManagerGroups.contains(it) || invalidGroups.contains(it) + } + // 直接退出的用户组 + batchOperateGroupMembers( + projectCode = projectCode, + type = BatchOperateType.REMOVE, + conditionReq = GroupMemberRemoveConditionReq( + groupIds = toDeleteGroups.map { + MemberGroupJoinedDTO( + id = it, + memberType = MemberType.USER + ) + }, + targetMember = removeMemberDTO.targetMember + ), + operateGroupMemberTask = ::deleteTask + ) + // 交接唯一拥有者、影响代持人权限的用户组以及流水线/代码库授权/环境节点授权 + if (toHandoverGroups.isNotEmpty()) { + removeMemberDTO.checkHandoverTo() + batchOperateGroupMembers( + projectCode = projectCode, + type = BatchOperateType.HANDOVER, + conditionReq = GroupMemberHandoverConditionReq( + groupIds = toHandoverGroups.map { + MemberGroupJoinedDTO( + id = it, + memberType = MemberType.USER + ) + }, + targetMember = removeMemberDTO.targetMember, + handoverTo = removeMemberDTO.handoverTo!! + ), + operateGroupMemberTask = ::handoverTask + ) + } + handoverAuthorizationsWhenOperatedGroups( + userId = userId, + projectCode = projectCode, + invalidPipelines = invalidPipelines, + invalidRepertoryIds = invalidRepertoryIds, + invalidEnvNodeIds = invalidEnvNodeIds, + handoverFrom = removeMemberDTO.targetMember.id, + handoverTo = removeMemberDTO.handoverTo!!.id + ) + return true + } + + override fun batchDeleteResourceGroupMembersFromPersonal( + userId: String, + projectCode: String, + removeMemberDTO: GroupMemberRemoveConditionReq + ): String? { + logger.info("batch delete group members from personal $userId|$projectCode|$removeMemberDTO") + // 根据条件获取成员直接加入的用户组 + val groupIds = getGroupIdsByGroupMemberCondition( + projectCode = projectCode, + commonCondition = removeMemberDTO + )[MemberType.USER]?.toMutableList() ?: return null + + // 过滤掉审核中的用户组 + val beingHandoverGroups = permissionHandoverApplicationService.listMemberHandoverDetails( + projectCode = projectCode, + memberId = removeMemberDTO.targetMember.id, + handoverType = HandoverType.GROUP + ).map { it.itemId.toInt() } + groupIds.removeAll(beingHandoverGroups) + + val (invalidGroups, invalidPipelines, invalidRepertoryIds, invalidEnvNodeIds) = + listInvalidAuthorizationsAfterOperatedGroups( + projectCode = projectCode, + iamGroupIds = groupIds, + memberId = removeMemberDTO.targetMember.id + ) + + // 检查授予人是否有代码库oauth权限 + if (invalidRepertoryIds.isNotEmpty()) { + permissionAuthorizationService.checkRepertoryAuthorizationsHanover( + operator = userId, + projectCode = projectCode, + repertoryIds = invalidRepertoryIds, + handoverFrom = removeMemberDTO.targetMember.id, + handoverTo = removeMemberDTO.handoverTo!!.id + ) + } + + // 获取唯一管理员组 + val uniqueManagerGroups = authResourceGroupMemberDao.listProjectUniqueManagerGroups( + dslContext = dslContext, + projectCode = projectCode, + iamGroupIds = groupIds + ) + val (toHandoverGroups, toDeleteGroups) = groupIds.partition { + uniqueManagerGroups.contains(it) || invalidGroups.contains(it) + } + // 直接退出的用户组 + batchOperateGroupMembers( + projectCode = projectCode, + type = BatchOperateType.REMOVE, + conditionReq = GroupMemberRemoveConditionReq( + groupIds = toDeleteGroups.map { + MemberGroupJoinedDTO( + id = it, + memberType = MemberType.USER + ) + }, + targetMember = removeMemberDTO.targetMember + ), + operateGroupMemberTask = ::deleteTask + ) + if (toHandoverGroups.isEmpty() && invalidPipelines.isEmpty() && invalidRepertoryIds.isEmpty() && invalidEnvNodeIds.isEmpty()) { + return null + } + val handoverDetails = buildHandoverDetails( + projectCode = projectCode, + groupIds = toHandoverGroups.map { it.toString() }, + invalidPipelines = invalidPipelines, + invalidRepertoryIds = invalidRepertoryIds, + invalidEnvNodeIds = invalidEnvNodeIds + ) + + val projectName = authResourceService.get( + projectCode = projectCode, + resourceType = ResourceTypeId.PROJECT, + resourceCode = projectCode + ).resourceName + val flowNo = permissionHandoverApplicationService.createHandoverApplication( + overview = HandoverOverviewCreateDTO( + projectCode = projectCode, + projectName = projectName, + applicant = removeMemberDTO.targetMember.id, + approver = removeMemberDTO.handoverTo!!.id, + handoverStatus = HandoverStatus.PENDING, + groupCount = toHandoverGroups.size, + authorizationCount = invalidPipelines.size + invalidRepertoryIds.size + ), + details = handoverDetails + ) + return flowNo + } + + override fun deleteResourceGroupMembers( + userId: String, + projectCode: String, + groupId: Int, + targetMember: ResourceMemberInfo + ): Boolean { + logger.info("delete single group members from personal:$userId|$targetMember|$projectCode|$groupId") + if (targetMember.type == MemberType.USER.type) { + val (invalidGroups, invalidPipelines, invalidRepertoryIds, invalidEnvNodeIds) = + listInvalidAuthorizationsAfterOperatedGroups( + projectCode = projectCode, + iamGroupIds = listOf(groupId), + memberId = targetMember.id + ) + if (invalidGroups.isNotEmpty() || invalidPipelines.isNotEmpty() || + invalidRepertoryIds.isNotEmpty() || invalidEnvNodeIds.isNotEmpty()) { + throw ErrorCodeException(errorCode = ERROR_SINGLE_GROUP_REMOVE) + } + } + batchOperateGroupMembers( + projectCode = projectCode, + type = BatchOperateType.REMOVE, + conditionReq = GroupMemberRemoveConditionReq( + groupIds = listOf( + MemberGroupJoinedDTO( + id = groupId, + memberType = MemberType.get(targetMember.type) + ) + ), + targetMember = targetMember + ), + operateGroupMemberTask = ::deleteTask + ) + return true + } + + private fun handoverTask( + projectCode: String, + groupId: Int, + handoverMemberDTO: GroupMemberHandoverConditionReq, + expiredAt: Long + ) { + logger.info( + "handover group member $projectCode|$groupId|" + + "${handoverMemberDTO.targetMember}|${handoverMemberDTO.handoverTo}" + ) + val currentTimeSeconds = System.currentTimeMillis() / 1000 + var finalExpiredAt = expiredAt + when { + // 若权限已过期,如果是唯一管理员组,允许交接,交接人将获得半年权限;其他的直接删除。 + expiredAt < currentTimeSeconds -> { + val isUniqueManagerGroup = authResourceGroupMemberDao.listProjectUniqueManagerGroups( + dslContext = dslContext, + projectCode = projectCode, + iamGroupIds = listOf(groupId) + ).isNotEmpty() + if (isUniqueManagerGroup) { + finalExpiredAt = currentTimeSeconds + TimeUnit.DAYS.toSeconds(180) + } else { + deleteTask( + projectCode = projectCode, + groupId = groupId, + removeMemberDTO = GroupMemberRemoveConditionReq( + targetMember = handoverMemberDTO.targetMember + ), + expiredAt = finalExpiredAt + ) + return + } + } + // 若交接人已经在用户组内,无需交接。 + authResourceGroupMemberDao.isMemberInGroup( + dslContext = dslContext, + projectCode = projectCode, + iamGroupId = groupId, + memberId = handoverMemberDTO.handoverTo.id + ) -> { + deleteTask( + projectCode = projectCode, + groupId = groupId, + removeMemberDTO = GroupMemberRemoveConditionReq( + targetMember = handoverMemberDTO.targetMember + ), + expiredAt = finalExpiredAt + ) + return + } + } + + val members = listOf( + ManagerMember( + handoverMemberDTO.handoverTo.type, + handoverMemberDTO.handoverTo.id + ) + ) + if (finalExpiredAt < currentTimeSeconds) { + throw ErrorCodeException( + errorCode = AuthMessageCode.INVALID_EXPIRED_PERM_NOT_ALLOW_TO_HANDOVER + ) + } + + permissionResourceMemberService.addIamGroupMember( + groupId = groupId, + members = members, + expiredAt = finalExpiredAt + ) + permissionResourceMemberService.deleteIamGroupMembers( + groupId = groupId, + type = handoverMemberDTO.targetMember.type, + memberIds = listOf(handoverMemberDTO.targetMember.id) + ) + authResourceGroupMemberDao.handoverGroupMembers( + dslContext = dslContext, + projectCode = projectCode, + iamGroupId = groupId, + handoverFrom = handoverMemberDTO.targetMember, + handoverTo = handoverMemberDTO.handoverTo, + expiredTime = DateTimeUtil.convertTimestampToLocalDateTime(finalExpiredAt) + ) + } + + private fun deleteTask( + projectCode: String, + groupId: Int, + removeMemberDTO: GroupMemberRemoveConditionReq, + expiredAt: Long + ) { + val targetMember = removeMemberDTO.targetMember + logger.info("delete group member $projectCode|$groupId|$targetMember") + permissionResourceMemberService.deleteIamGroupMembers( + groupId = groupId, + type = targetMember.type, + memberIds = listOf(targetMember.id) + ) + authResourceGroupMemberDao.batchDeleteGroupMembers( + dslContext = dslContext, + projectCode = projectCode, + iamGroupId = groupId, + memberIds = listOf(removeMemberDTO.targetMember.id) + ) + } + + override fun batchOperateGroupMembersCheck( + userId: String, + projectCode: String, + batchOperateType: BatchOperateType, + conditionReq: GroupMemberCommonConditionReq + ): BatchOperateGroupMemberCheckVo { + logger.info("batch operate group member check|$userId|$projectCode|$batchOperateType|$conditionReq") + // 获取成员加入的用户组 + val joinedType2GroupIds = getGroupIdsByGroupMemberCondition( + projectCode = projectCode, + commonCondition = conditionReq + ) + // 通过组织或者模板加入的用户组 + val groupsOfTemplateOrDeptJoined = when (conditionReq.targetMember.type) { + MemberType.USER.type -> { + listOfNotNull( + joinedType2GroupIds[MemberType.DEPARTMENT], + joinedType2GroupIds[MemberType.TEMPLATE] + ).flatten() + } + + else -> joinedType2GroupIds[MemberType.TEMPLATE] ?: emptyList() + } + // 直接加入的组 + val groupsOfDirectlyJoined = joinedType2GroupIds[MemberType.get(conditionReq.targetMember.type)] ?: emptyList() + // 总数 + val totalCount = groupsOfTemplateOrDeptJoined.size + groupsOfDirectlyJoined.size + return when (batchOperateType) { + BatchOperateType.REMOVE -> { + if (conditionReq.targetMember.type == MemberType.DEPARTMENT.type) { + BatchOperateGroupMemberCheckVo( + totalCount = totalCount, + operableCount = groupsOfDirectlyJoined.size, + inoperableCount = groupsOfTemplateOrDeptJoined.size + ) + } else { + val groupsOfUniqueManager = authResourceGroupMemberDao.listProjectUniqueManagerGroups( + dslContext = dslContext, + projectCode = projectCode, + iamGroupIds = groupsOfDirectlyJoined + ) + // 本次操作导致流水线代持人权限受到影响的用户组及流水线/代码库oauth/环境节点 + val (invalidGroups, invalidPipelines, invalidRepositoryIds, invalidEnvNodeIds) = + listInvalidAuthorizationsAfterOperatedGroups( + projectCode = projectCode, + iamGroupIds = groupsOfDirectlyJoined, + memberId = conditionReq.targetMember.id + ) + // 当批量移出时, + // 直接加入的组中,唯一管理员组/影响流水线代持权限不允许被移出 + // 间接加入的组中,通过组织、模板加入的组不允许被移出 + val groupsOfInOperableWhenBatchRemove = groupsOfDirectlyJoined.count { + groupsOfUniqueManager.contains(it) || invalidGroups.contains(it) + } + groupsOfTemplateOrDeptJoined.size + + BatchOperateGroupMemberCheckVo( + totalCount = totalCount, + operableCount = totalCount - groupsOfInOperableWhenBatchRemove, + inoperableCount = groupsOfInOperableWhenBatchRemove, + uniqueManagerCount = groupsOfUniqueManager.size, + invalidGroupCount = invalidGroups.size, + invalidPipelineAuthorizationCount = invalidPipelines.size, + invalidRepositoryAuthorizationCount = invalidRepositoryIds.size, + invalidEnvNodeAuthorizationCount = invalidEnvNodeIds.size, + canHandoverCount = groupsOfUniqueManager.union(invalidGroups).size + ) + } + } + + BatchOperateType.RENEWAL -> { + // 部门/组织加入以及永久权限的组不允许再续期 + with(conditionReq) { + val isUserDeparted = targetMember.type == MemberType.USER.type && + deptService.isUserDeparted(targetMember.id) + // 离职用户不允许续期 + if (isUserDeparted) { + BatchOperateGroupMemberCheckVo( + totalCount = totalCount, + inoperableCount = totalCount + ) + } else { + // 永久期限 不允许再续期 + val groupCountOfPermanentExpiredTime = listMemberGroupsDetails( + projectCode = projectCode, + memberId = targetMember.id, + memberType = targetMember.type, + groupIds = groupsOfDirectlyJoined + ).filter { + // iam用的是秒级时间戳 + it.expiredAt == PERMANENT_EXPIRED_TIME / 1000 + }.size + val groupsOfInOperableWhenBatchRenewal = groupCountOfPermanentExpiredTime + groupsOfTemplateOrDeptJoined.size + BatchOperateGroupMemberCheckVo( + totalCount = totalCount, + operableCount = totalCount - groupsOfInOperableWhenBatchRenewal, + inoperableCount = groupsOfInOperableWhenBatchRenewal + ) + } + } + } + + BatchOperateType.HANDOVER -> { + // 已过期(除唯一管理员组 )或通过模板/组织加入的不允许移交 + with(conditionReq) { + val finalGroupIds = groupsOfDirectlyJoined.toMutableList() + val uniqueManagerGroupIds = authResourceGroupMemberDao.listProjectUniqueManagerGroups( + dslContext = dslContext, + projectCode = projectCode, + iamGroupIds = groupsOfDirectlyJoined + ) + // 去除唯一管理员组 + if (uniqueManagerGroupIds.isNotEmpty()) { + finalGroupIds.removeAll(uniqueManagerGroupIds) + } + val groupCountOfExpired = listMemberGroupsDetails( + projectCode = projectCode, + memberId = targetMember.id, + memberType = targetMember.type, + groupIds = finalGroupIds + ).filter { + // iam用的是秒级时间戳 + it.expiredAt < System.currentTimeMillis() / 1000 + }.size + val inoperableCount = groupsOfTemplateOrDeptJoined.size + groupCountOfExpired + // 本次操作导致流水线代持人权限受到影响的流水线 + val (invalidGroups, invalidPipelines, invalidRepositoryIds, invalidEnvNodeIds) = + listInvalidAuthorizationsAfterOperatedGroups( + projectCode = projectCode, + iamGroupIds = groupsOfDirectlyJoined, + memberId = conditionReq.targetMember.id + ) + + BatchOperateGroupMemberCheckVo( + totalCount = totalCount, + operableCount = totalCount - inoperableCount, + inoperableCount = groupsOfTemplateOrDeptJoined.size + groupCountOfExpired, + invalidPipelineAuthorizationCount = invalidPipelines.size, + invalidRepositoryAuthorizationCount = invalidRepositoryIds.size, + invalidEnvNodeAuthorizationCount = invalidEnvNodeIds.size, + canHandoverCount = totalCount - inoperableCount + ) + } + } + + else -> { + BatchOperateGroupMemberCheckVo( + totalCount = totalCount, + inoperableCount = groupsOfTemplateOrDeptJoined.size, + operableCount = groupsOfDirectlyJoined.size + ) + } + } + } + + override fun removeMemberFromProject( + userId: String, + projectCode: String, + removeMemberFromProjectReq: RemoveMemberFromProjectReq + ): List { + logger.info("remove member from project $userId|$projectCode|$removeMemberFromProjectReq") + return with(removeMemberFromProjectReq) { + val memberType = targetMember.type + val isNeedToHandover = handoverTo != null + if (memberType == MemberType.USER.type && isNeedToHandover) { + removeMemberFromProjectReq.checkHandoverTo() + val handoverMemberDTO = GroupMemberHandoverConditionReq( + allSelection = true, + targetMember = targetMember, + handoverTo = handoverTo!! + ) + batchOperateGroupMembers( + projectCode = projectCode, + type = BatchOperateType.HANDOVER, + conditionReq = handoverMemberDTO, + operateGroupMemberTask = ::handoverTask + ) + permissionAuthorizationService.resetAllResourceAuthorization( + operator = userId, + projectCode = projectCode, + condition = ResetAllResourceAuthorizationReq( + projectCode = projectCode, + handoverFrom = removeMemberFromProjectReq.targetMember.id, + handoverTo = removeMemberFromProjectReq.handoverTo!!.id, + preCheck = false, + checkPermission = false + ) + ) + } else { + val removeMemberDTO = GroupMemberRemoveConditionReq( + allSelection = true, + targetMember = targetMember + ) + batchOperateGroupMembers( + projectCode = projectCode, + type = BatchOperateType.REMOVE, + conditionReq = removeMemberDTO, + operateGroupMemberTask = ::deleteTask + ) + } + + if (memberType == MemberType.USER.type) { + // 查询用户还存在那些组织中 + val userDeptInfos = deptService.getUserInfo( + userId = "admin", + name = targetMember.id + )?.deptInfo?.map { it.name!! } + if (userDeptInfos != null) { + return authResourceGroupMemberDao.isMembersInProject( + dslContext = dslContext, + projectCode = projectCode, + memberNames = userDeptInfos, + memberType = MemberType.DEPARTMENT.type + ) + } + } + return emptyList() + } + } + + override fun removeMemberFromProjectCheck( + userId: String, + projectCode: String, + removeMemberFromProjectReq: RemoveMemberFromProjectReq + ): Boolean { + val targetMember = removeMemberFromProjectReq.targetMember + val isMemberHasNoPermission = batchOperateGroupMembersCheck( + userId = userId, + projectCode = projectCode, + batchOperateType = BatchOperateType.HANDOVER, + conditionReq = GroupMemberCommonConditionReq( + allSelection = true, + targetMember = removeMemberFromProjectReq.targetMember + ) + ).let { it.totalCount == it.inoperableCount } + + val isMemberHasNoAuthorizations = + if (targetMember.type == MemberType.USER.type) { + permissionAuthorizationService.listResourceAuthorizations( + condition = ResourceAuthorizationConditionRequest( + projectCode = projectCode, + handoverFrom = targetMember.id + ) + ).count == 0L + } else { + true + } + return isMemberHasNoPermission && isMemberHasNoAuthorizations + } + + override fun handleHanoverApplication(request: HandoverOverviewUpdateReq): Boolean { + val overview = permissionHandoverApplicationService.getHandoverOverview(request.flowNo) + logger.info("handle hanover application:{}|{} ", request, overview) + HandleHandoverApplicationLock(redisOperation, request.flowNo).use { lock -> + if (!lock.tryLock()) { + logger.warn("The handover application is being processed!$request") + throw ErrorCodeException(errorCode = ERROR_HANDOVER_HANDLE) + } + try { + handleHanoverCheck(request = request, overview = overview) + if (request.handoverAction == HandoverAction.AGREE) { + handleHandoverAgreeAction( + request = request, + overview = overview + ) + } + permissionHandoverApplicationService.updateHandoverApplication( + overview = request + ) + val projectName = authResourceService.get( + projectCode = request.projectCode, + resourceType = ResourceTypeId.PROJECT, + resourceCode = request.projectCode + ).resourceName + val bodyParams = mapOf( + "projectName" to projectName, + "result" to request.handoverAction.alias, + "url" to String.format(handoverApplicationUrl, request.flowNo) + ) + // 发邮件 + val emailRequest = SendNotifyMessageTemplateRequest( + templateCode = HANDOVER_APPLICATION_RESULT_TEMPLATE_CODE, + bodyParams = bodyParams, + titleParams = bodyParams, + notifyType = mutableSetOf(NotifyType.RTX.name, NotifyType.EMAIL.name), + receivers = mutableSetOf(overview.applicant) + ) + logger.info("send handover application result email:{}|{} ", request, emailRequest) + kotlin.runCatching { + client.get(ServiceNotifyMessageTemplateResource::class).sendNotifyMessageByTemplate(emailRequest) + }.onFailure { + logger.warn("notify email fail ${it.message}|$bodyParams|${overview.approver}") + } + } catch (e: Exception) { + logger.warn("handle hanover application error,$e|$request") + throw e + } + } + return true + } + + override fun batchHandleHanoverApplications(request: HandoverOverviewBatchUpdateReq): Boolean { + logger.info("batch handle hanover application:{} ", request) + val startEpoch = System.currentTimeMillis() + try { + val overviews = when { + request.allSelection -> permissionHandoverApplicationService.listHandoverOverviews( + queryRequest = HandoverOverviewQueryReq( + memberId = request.operator, + approver = request.operator, + handoverStatus = HandoverStatus.PENDING + ) + ) + + request.flowNos.isNotEmpty() -> permissionHandoverApplicationService.listHandoverOverviews( + queryRequest = HandoverOverviewQueryReq( + memberId = request.operator, + approver = request.operator, + handoverStatus = HandoverStatus.PENDING, + flowNos = request.flowNos + ) + ) + + else -> return true + }.records + overviews.forEach { overview -> + handleHanoverApplication( + request = HandoverOverviewUpdateReq( + projectCode = overview.projectCode, + flowNo = overview.flowNo, + operator = request.operator, + handoverAction = request.handoverAction, + remark = request.remark + ) + ) + } + } finally { + "It take(${System.currentTimeMillis() - startEpoch})ms to batch handle hanover applications" + } + return true + } + + override fun getResourceType2CountOfHandover(queryReq: ResourceType2CountOfHandoverQuery): List { + queryReq.check() + return if (queryReq.queryChannel == HandoverQueryChannel.HANDOVER_APPLICATION) { + permissionHandoverApplicationService.getResourceType2CountOfHandoverApplication(queryReq.flowNo!!) + } else { + getResourceType2CountOfHandoverPreview(queryReq) + } + } + + // 交接预览 + private fun getResourceType2CountOfHandoverPreview(queryReq: ResourceType2CountOfHandoverQuery): List { + val projectCode = queryReq.projectCode + val previewConditionReq = queryReq.previewConditionReq!! + val batchOperateType = queryReq.batchOperateType!! + val groupIdsDirectlyJoined = getGroupIdsByGroupMemberCondition( + projectCode = projectCode, + commonCondition = previewConditionReq + )[MemberType.USER] ?: return emptyList() + + val result = mutableListOf() + val (invalidGroups, invalidPipelines, invalidRepertoryIds, invalidEnvNodeIds) = + listInvalidAuthorizationsAfterOperatedGroups( + projectCode = projectCode, + iamGroupIds = groupIdsDirectlyJoined, + memberId = previewConditionReq.targetMember.id + ) + if (batchOperateType == BatchOperateType.REMOVE) { + // 只有一个成员的管理员组 + val uniqueManagerGroups = authResourceGroupMemberDao.listProjectUniqueManagerGroups( + dslContext = dslContext, + projectCode = projectCode, + iamGroupIds = groupIdsDirectlyJoined + ) + val needToHandoverGroupIds = invalidGroups.union(uniqueManagerGroups).map { it.toString() } + val resourceType2CountOfGroup = authResourceGroupDao.getResourceType2Count( + dslContext = dslContext, + projectCode = projectCode, + iamGroupIds = needToHandoverGroupIds + ) + if (resourceType2CountOfGroup.isNotEmpty()) { + result.addAll( + rbacCommonService.convertResourceType2Count( + resourceType2Count = resourceType2CountOfGroup, + type = HandoverType.GROUP + ) + ) + } + } + if (invalidPipelines.isNotEmpty()) { + result.addAll( + rbacCommonService.convertResourceType2Count( + resourceType2Count = mapOf(ResourceTypeId.PIPELINE to invalidPipelines.size.toLong()), + type = HandoverType.AUTHORIZATION + ) + ) + } + if (invalidRepertoryIds.isNotEmpty()) { + result.addAll( + rbacCommonService.convertResourceType2Count( + resourceType2Count = mapOf(ResourceTypeId.REPERTORY to invalidRepertoryIds.size.toLong()), + type = HandoverType.AUTHORIZATION + ) + ) + } + if (invalidEnvNodeIds.isNotEmpty()) { + result.addAll( + rbacCommonService.convertResourceType2Count( + resourceType2Count = mapOf(ResourceTypeId.ENV_NODE to invalidEnvNodeIds.size.toLong()), + type = HandoverType.AUTHORIZATION + ) + ) + } + return result + } + + override fun listAuthorizationsOfHandover(queryReq: HandoverDetailsQueryReq): SQLPage { + queryReq.check() + return if (queryReq.queryChannel == HandoverQueryChannel.HANDOVER_APPLICATION) { + permissionHandoverApplicationService.listAuthorizationsOfHandoverApplication(queryReq) + } else { + listAuthorizationsOfHandoverPreview(queryReq) + } + } + + private fun listAuthorizationsOfHandoverPreview(queryReq: HandoverDetailsQueryReq): SQLPage { + val projectCode = queryReq.projectCode + val previewConditionReq = queryReq.previewConditionReq!! + val groupIdsDirectlyJoined = getGroupIdsByGroupMemberCondition( + projectCode = projectCode, + commonCondition = previewConditionReq + )[MemberType.USER] ?: return SQLPage(0, emptyList()) + val (invalidGroups, invalidPipelines, invalidRepertoryIds, invalidEnvNodeIds) = + listInvalidAuthorizationsAfterOperatedGroups( + projectCode = projectCode, + iamGroupIds = groupIdsDirectlyJoined, + memberId = previewConditionReq.targetMember.id + ) + + val invalidResources = when (queryReq.resourceType) { + ResourceTypeId.PIPELINE -> invalidPipelines + ResourceTypeId.ENV_NODE -> invalidEnvNodeIds + else -> invalidRepertoryIds + } + + val records = authorizationDao.list( + dslContext = dslContext, + condition = ResourceAuthorizationConditionRequest( + projectCode = projectCode, + resourceType = queryReq.resourceType, + filterResourceCodes = invalidResources, + page = queryReq.page, + pageSize = queryReq.pageSize + ) + ).map { + HandoverAuthorizationDetailVo( + resourceCode = it.resourceCode, + resourceName = it.resourceName, + handoverType = HandoverType.AUTHORIZATION, + handoverFrom = it.handoverFrom + ) + } + return SQLPage(count = invalidResources.size.toLong(), records = records) + } + + override fun listGroupsOfHandover(queryReq: HandoverDetailsQueryReq): SQLPage { + queryReq.check() + return if (queryReq.queryChannel == HandoverQueryChannel.HANDOVER_APPLICATION) { + permissionHandoverApplicationService.listGroupsOfHandoverApplication(queryReq) + } else { + listGroupsOfHandoverPreview(queryReq) + } + } + + override fun isProjectMember( + projectCode: String, + userId: String + ): Boolean { + // 获取用户加入的项目级用户组模板ID + val iamTemplateIds = listProjectMemberGroupTemplateIds( + projectCode = projectCode, + memberId = userId + ) + val memberDeptInfos = deptService.getUserInfo( + userId = "admin", + name = userId + )?.deptInfo?.map { it.name!! } + + return authResourceGroupMemberDao.isMemberInProject( + dslContext = dslContext, + projectCode = projectCode, + userId = userId, + iamTemplateIds = iamTemplateIds, + memberDeptInfos = memberDeptInfos + ) || rbacCommonService.validateUserProjectPermission( + userId = userId, + projectCode = projectCode, + permission = AuthPermission.VISIT + ) + } + + private fun listGroupsOfHandoverPreview(queryReq: HandoverDetailsQueryReq): SQLPage { + val projectCode = queryReq.projectCode + val previewConditionReq = queryReq.previewConditionReq!! + val convertPageSizeToSQLLimit = PageUtil.convertPageSizeToSQLLimit(queryReq.page, queryReq.pageSize) + val groupIdsDirectlyJoined = getGroupIdsByGroupMemberCondition( + projectCode = projectCode, + commonCondition = previewConditionReq + )[MemberType.USER] ?: return SQLPage(0, emptyList()) + val invalidGroupIds = listInvalidAuthorizationsAfterOperatedGroups( + projectCode = projectCode, + iamGroupIds = groupIdsDirectlyJoined, + memberId = previewConditionReq.targetMember.id + ).invalidGroupIds + // 只有一个成员的管理员组 + val uniqueManagerGroups = authResourceGroupMemberDao.listProjectUniqueManagerGroups( + dslContext = dslContext, + projectCode = projectCode, + iamGroupIds = groupIdsDirectlyJoined + ) + val needToHandoverGroupIds = invalidGroupIds.union(uniqueManagerGroups).map { it.toString() } + val records = authResourceGroupDao.listGroupByResourceType( + dslContext = dslContext, + projectCode = projectCode, + resourceType = queryReq.resourceType, + iamGroupIds = needToHandoverGroupIds, + offset = convertPageSizeToSQLLimit.offset, + limit = convertPageSizeToSQLLimit.limit + ).map { + HandoverGroupDetailVo( + projectCode = it.projectCode, + iamGroupId = it.relationId, + groupName = it.groupName, + groupDesc = it.description, + resourceCode = it.resourceCode, + resourceName = it.resourceName + ) + } + return SQLPage(count = invalidGroupIds.size.toLong(), records = records) + } + + private fun handleHanoverCheck( + request: HandoverOverviewUpdateReq, + overview: HandoverOverviewVo + ) { + if (overview.handoverStatus != HandoverStatus.PENDING) { + throw ErrorCodeException(errorCode = ERROR_HANDOVER_FINISH) + } + if (request.handoverAction == HandoverAction.REVOKE && request.operator != overview.applicant) { + throw ErrorCodeException(errorCode = ERROR_HANDOVER_REVOKE) + } + if (request.handoverAction != HandoverAction.REVOKE && request.operator != overview.approver) { + throw ErrorCodeException(errorCode = ERROR_HANDOVER_APPROVAL) + } + } + + private fun handleHandoverAgreeAction( + request: HandoverOverviewUpdateReq, + overview: HandoverOverviewVo + ) { + val handoverDetails = permissionHandoverApplicationService.listHandoverDetails( + projectCode = overview.projectCode, + flowNo = overview.flowNo + ) + val handoverType2Records = handoverDetails.groupBy { it.handoverType } + + // 交接用户组 + val groupsOfHandover = handoverType2Records[HandoverType.GROUP]?.map { it.itemId.toInt() } + if (!groupsOfHandover.isNullOrEmpty()) { + val targetMember = ResourceMemberInfo( + id = overview.applicant, + name = deptService.getMemberInfo(overview.applicant, ManagerScopesEnum.USER).displayName, + type = MemberType.USER.type + ) + val handoverTo = ResourceMemberInfo( + id = overview.approver, + name = deptService.getMemberInfo(overview.approver, ManagerScopesEnum.USER).displayName, + type = MemberType.USER.type + ) + + val groupMemberHandoverConditionReq = GroupMemberHandoverConditionReq( + groupIds = groupsOfHandover.map { + MemberGroupJoinedDTO( + id = it, + memberType = MemberType.USER + ) + }, + targetMember = targetMember, + handoverTo = handoverTo + ) + batchOperateGroupMembers( + projectCode = overview.projectCode, + type = BatchOperateType.HANDOVER, + conditionReq = groupMemberHandoverConditionReq, + operateGroupMemberTask = ::handoverTask + ) + } + + // 交接授权 + val authorizationsOfHandover = handoverType2Records[HandoverType.AUTHORIZATION] + if (!authorizationsOfHandover.isNullOrEmpty()) { + val resourceType2Authorizations = authorizationsOfHandover.groupBy { it.resourceType } + resourceType2Authorizations.forEach { (resourceType, authorizations) -> + permissionAuthorizationService.resetResourceAuthorizationByResourceType( + operator = request.operator, + projectCode = overview.projectCode, + condition = ResourceAuthorizationHandoverConditionRequest( + projectCode = overview.projectCode, + resourceType = resourceType, + filterResourceCodes = authorizations.map { it.itemId }, + fullSelection = true, + handoverChannel = HandoverChannelCode.MANAGER, + handoverFrom = overview.applicant, + handoverTo = overview.approver, + checkPermission = false + ) + ) + } + } + } + + private fun batchOperateGroupMembers( + projectCode: String, + conditionReq: T, + type: BatchOperateType, + operateGroupMemberTask: ( + projectCode: String, + groupId: Int, + conditionReq: T, + expiredAt: Long + ) -> Unit + ): Boolean { + val startEpoch = System.currentTimeMillis() + try { + // 成员直接加入的组 + val groupIds = getGroupIdsByGroupMemberCondition( + projectCode = projectCode, + commonCondition = conditionReq + )[MemberType.get(conditionReq.targetMember.type)] + if (groupIds.isNullOrEmpty()) { + return true + } + + val targetMember = conditionReq.targetMember + val memberGroupsDetailsList = listMemberGroupsDetails( + projectCode = projectCode, + memberId = targetMember.id, + memberType = targetMember.type, + groupIds = groupIds + ) + val outOfSyncGroupIds = mutableListOf() + val futures = groupIds.map { groupId -> + CompletableFuture.supplyAsync( + { + val memberGroupsDetails = memberGroupsDetailsList.firstOrNull { it.id == groupId } + if (memberGroupsDetails == null) { + logger.warn("The data is out of sync, and the record no longer exists in the iam.$groupId") + outOfSyncGroupIds.add(groupId) + return@supplyAsync + } + val expiredAt = memberGroupsDetails.expiredAt + RetryUtils.retry(3) { + operateGroupMemberTask.invoke( + projectCode, + groupId, + conditionReq, + expiredAt + ) + } + }, executorService + ) + } + handleFutures( + projectCode = projectCode, + outOfSyncGroupIds = outOfSyncGroupIds, + futures = futures + ) + } finally { + "It take(${System.currentTimeMillis() - startEpoch})ms to $type group members|$projectCode|$conditionReq" + } + return true + } + + private fun listMemberGroupsDetails( + projectCode: String, + memberId: String, + memberType: String, + groupIds: List + ): List { + val memberGroupsDetailsList = mutableListOf() + val groupIdsChunk = groupIds.chunked(100) + val futures = groupIdsChunk.map { + CompletableFuture.supplyAsync( + { + memberGroupsDetailsList.addAll( + // 若离职,则从数据库获取用户加入组的过期时间,调用iam接口会报错。 + // 虽然数据库的过期时间可能不是最新的。 + if (memberType == MemberType.USER.type && deptService.isUserDeparted(memberId)) { + val records = authResourceGroupMemberDao.listMemberGroupDetail( + dslContext = dslContext, + projectCode = projectCode, + memberId = memberId, + iamTemplateIds = emptyList(), + iamGroupIds = it + ) + records.map { record -> + MemberGroupDetailsResponse().apply { + id = record.iamGroupId + expiredAt = record.expiredTime.timestamp() + } + } + } else { + iamV2ManagerService.listMemberGroupsDetails( + memberType, + memberId, + it.joinToString(",") + ) + } + ) + }, executorService + ) + } + try { + CompletableFuture.allOf(*futures.toTypedArray()).join() + } catch (ignore: Exception) { + logger.warn("list member groups details failed!$ignore") + throw ignore + } + return memberGroupsDetailsList + } + + private fun handleFutures( + projectCode: String, + outOfSyncGroupIds: List, + futures: List> + ) { + try { + CompletableFuture.allOf(*futures.toTypedArray()).join() + // 存在iam那边已经把用户组下成员删除,但蓝盾数据库未同步问题 + outOfSyncGroupIds.forEach { + syncIamGroupMemberService.syncIamGroupMember( + projectCode = projectCode, + iamGroupId = it + ) + } + } catch (ignore: Exception) { + logger.warn("batch operate group members failed", ignore) + throw ErrorCodeException( + errorCode = AuthMessageCode.ERROR_BATCH_OPERATE_GROUP_MEMBERS + ) + } + } + + private val handoverApplicationUrl = "${config.devopsHostGateway}/console/permission/my-handover?type=handoverFromMe&flowNo=%s" + + companion object { + private val logger = LoggerFactory.getLogger(RbacPermissionResourceMemberService::class.java) + + private val executorService = Executors.newFixedThreadPool(30) + + // 永久过期时间 + private const val PERMANENT_EXPIRED_TIME = 4102444800000L + + private const val HANDOVER_APPLICATION_RESULT_TEMPLATE_CODE = "BK_PERMISSIONS_HANDOVER_APPLICATION_RESULT" + } +} diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionProjectService.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionProjectService.kt index 965440da8291..bd382b7677c4 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionProjectService.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionProjectService.kt @@ -32,6 +32,7 @@ import com.tencent.bk.sdk.iam.helper.AuthHelper import com.tencent.devops.auth.constant.AuthMessageCode import com.tencent.devops.auth.dao.AuthResourceGroupDao import com.tencent.devops.auth.pojo.vo.ProjectPermissionInfoVO +import com.tencent.devops.auth.service.iam.PermissionManageFacadeService import com.tencent.devops.auth.service.iam.PermissionProjectService import com.tencent.devops.auth.service.iam.PermissionResourceMemberService import com.tencent.devops.common.api.exception.ErrorCodeException @@ -53,10 +54,11 @@ class RbacPermissionProjectService( private val authResourceService: AuthResourceService, private val authResourceGroupDao: AuthResourceGroupDao, private val dslContext: DSLContext, - private val rbacCacheService: RbacCacheService, + private val rbacCommonService: RbacCommonService, private val resourceGroupMemberService: RbacPermissionResourceMemberService, private val client: Client, - private val resourceMemberService: PermissionResourceMemberService + private val resourceMemberService: PermissionResourceMemberService, + private val permissionManageFacadeService: PermissionManageFacadeService ) : PermissionProjectService { companion object { @@ -137,7 +139,7 @@ class RbacPermissionProjectService( return managerPermission } - return rbacCacheService.validateUserProjectPermission( + return rbacCommonService.validateUserProjectPermission( userId = userId, projectCode = projectCode, permission = AuthPermission.VISIT @@ -153,7 +155,7 @@ class RbacPermissionProjectService( userId: String, projectCode: String ): Boolean { - return resourceMemberService.isProjectMember( + return permissionManageFacadeService.isProjectMember( projectCode = projectCode, userId = userId ) @@ -167,14 +169,14 @@ class RbacPermissionProjectService( // resourceCode = projectCode, // group = null // ).contains(userId) - return resourceMemberService.isProjectMember( + return permissionManageFacadeService.isProjectMember( projectCode = projectCode, userId = userId ) } override fun checkProjectManager(userId: String, projectCode: String): Boolean { - return rbacCacheService.checkProjectManager(userId, projectCode) + return rbacCommonService.checkProjectManager(userId, projectCode) } override fun createProjectUser(userId: String, projectCode: String, roleCode: String): Boolean { diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionResourceGroupAndMemberFacadeServiceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionResourceGroupAndMemberFacadeServiceImpl.kt deleted file mode 100644 index 79229b6946b4..000000000000 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionResourceGroupAndMemberFacadeServiceImpl.kt +++ /dev/null @@ -1,423 +0,0 @@ -package com.tencent.devops.auth.provider.rbac.service - -import com.tencent.bk.sdk.iam.constants.ManagerScopesEnum -import com.tencent.bk.sdk.iam.dto.response.MemberGroupDetailsResponse -import com.tencent.bk.sdk.iam.service.v2.V2ManagerService -import com.tencent.devops.auth.constant.AuthI18nConstants -import com.tencent.devops.auth.dao.AuthResourceGroupDao -import com.tencent.devops.auth.dao.AuthResourceGroupMemberDao -import com.tencent.devops.auth.pojo.AuthResourceGroupMember -import com.tencent.devops.auth.pojo.ResourceMemberInfo -import com.tencent.devops.auth.pojo.dto.IamGroupIdsQueryConditionDTO -import com.tencent.devops.auth.pojo.dto.ProjectMembersQueryConditionDTO -import com.tencent.devops.auth.pojo.enum.JoinedType -import com.tencent.devops.auth.pojo.enum.RemoveMemberButtonControl -import com.tencent.devops.auth.pojo.request.ProjectMembersQueryConditionReq -import com.tencent.devops.auth.pojo.vo.GroupDetailsInfoVo -import com.tencent.devops.auth.pojo.vo.MemberGroupCountWithPermissionsVo -import com.tencent.devops.auth.service.DeptService -import com.tencent.devops.auth.service.iam.PermissionResourceGroupAndMemberFacadeService -import com.tencent.devops.auth.service.iam.PermissionResourceGroupPermissionService -import com.tencent.devops.auth.service.iam.PermissionResourceGroupService -import com.tencent.devops.auth.service.iam.PermissionResourceMemberService -import com.tencent.devops.common.api.model.SQLPage -import com.tencent.devops.common.api.util.DateTimeUtil -import com.tencent.devops.common.api.util.timestampmilli -import com.tencent.devops.common.auth.api.AuthResourceType -import com.tencent.devops.common.web.utils.I18nUtil -import com.tencent.devops.model.auth.tables.records.TAuthResourceGroupRecord -import org.jooq.DSLContext -import org.slf4j.LoggerFactory -import java.util.concurrent.TimeUnit - -class RbacPermissionResourceGroupAndMemberFacadeServiceImpl( - private val permissionResourceGroupService: PermissionResourceGroupService, - private val groupPermissionService: PermissionResourceGroupPermissionService, - private val permissionResourceMemberService: PermissionResourceMemberService, - private val authResourceGroupDao: AuthResourceGroupDao, - private val authResourceGroupMemberDao: AuthResourceGroupMemberDao, - private val dslContext: DSLContext, - private val deptService: DeptService, - private val iamV2ManagerService: V2ManagerService, - private val rbacCacheService: RbacCacheService -) : PermissionResourceGroupAndMemberFacadeService { - override fun getMemberGroupsDetails( - projectId: String, - memberId: String, - resourceType: String?, - iamGroupIds: List?, - groupName: String?, - minExpiredAt: Long?, - maxExpiredAt: Long?, - relatedResourceType: String?, - relatedResourceCode: String?, - action: String?, - start: Int?, - limit: Int? - ): SQLPage { - // 根据查询条件查询得到iam组id - val iamGroupIdsByConditions = listIamGroupIdsByConditions( - condition = IamGroupIdsQueryConditionDTO( - projectCode = projectId, - groupName = groupName, - iamGroupIds = iamGroupIds, - relatedResourceType = relatedResourceType, - relatedResourceCode = relatedResourceCode, - action = action - ) - ) - // 查询成员所在资源用户组列表,直接加入+通过用户组(模板)加入 - val (count, resourceGroupMembers) = permissionResourceMemberService.listResourceGroupMembers( - projectCode = projectId, - memberId = memberId, - resourceType = resourceType, - iamGroupIds = iamGroupIdsByConditions, - minExpiredAt = minExpiredAt, - maxExpiredAt = maxExpiredAt, - start = start, - limit = limit - ) - // 用户组对应的资源信息 - val resourceGroupMap = authResourceGroupDao.listByRelationId( - dslContext = dslContext, - projectCode = projectId, - iamGroupIds = resourceGroupMembers.map { it.iamGroupId.toString() } - ).associateBy { it.relationId } - // 只有一个成员的管理员组 - val uniqueManagerGroups = authResourceGroupMemberDao.listProjectUniqueManagerGroups( - dslContext = dslContext, - projectCode = projectId, - iamGroupIds = resourceGroupMembers.map { it.iamGroupId } - ) - // 用户组成员详情 - val groupMemberDetailMap = getGroupMemberDetailMap( - memberId = memberId, - resourceGroupMembers = resourceGroupMembers - ) - val records = mutableListOf() - resourceGroupMembers.forEach { - val resourceGroup = resourceGroupMap[it.iamGroupId.toString()]!! - val groupMemberDetail = groupMemberDetailMap["${it.iamGroupId}_${it.memberId}"] - records.add( - convertGroupDetailsInfoVo( - resourceGroup = resourceGroup, - groupMemberDetail = groupMemberDetail, - uniqueManagerGroups = uniqueManagerGroups, - authResourceGroupMember = it - ) - ) - } - return SQLPage(count = count, records = records) - } - - private fun getGroupMemberDetailMap( - memberId: String, - resourceGroupMembers: List - ): Map { - // 如果用户离职,查询权限中心接口会报错 - if (deptService.isUserDeparted(memberId)) { - return emptyMap() - } - // 用户组成员详情 - val groupMemberDetailMap = mutableMapOf() - // 直接加入的用户 - val userGroupIds = resourceGroupMembers - .filter { it.memberType == ManagerScopesEnum.getType(ManagerScopesEnum.USER) } - .map { it.iamGroupId } - if (userGroupIds.isNotEmpty()) { - iamV2ManagerService.listMemberGroupsDetails( - ManagerScopesEnum.getType(ManagerScopesEnum.USER), - memberId, - userGroupIds.joinToString(",") - ).forEach { - groupMemberDetailMap["${it.id}_$memberId"] = it - } - } - // 直接加入的组织 - val deptGroupIds = resourceGroupMembers - .filter { it.memberType == ManagerScopesEnum.getType(ManagerScopesEnum.DEPARTMENT) } - .map { it.iamGroupId } - if (deptGroupIds.isNotEmpty()) { - iamV2ManagerService.listMemberGroupsDetails( - ManagerScopesEnum.getType(ManagerScopesEnum.DEPARTMENT), - memberId, - deptGroupIds.joinToString(",") - ).forEach { - groupMemberDetailMap["${it.id}_$memberId"] = it - } - } - // 人员模板加入的组 - resourceGroupMembers.filter { it.memberType == ManagerScopesEnum.getType(ManagerScopesEnum.TEMPLATE) } - .groupBy({ it.memberId }, { it.iamGroupId.toString() }) - .forEach { (iamTemplateId, iamGroupIds) -> - if (iamGroupIds.isEmpty()) return@forEach - iamV2ManagerService.listMemberGroupsDetails( - ManagerScopesEnum.getType(ManagerScopesEnum.TEMPLATE), - iamTemplateId, - iamGroupIds.joinToString(",") - ).forEach { - groupMemberDetailMap["${it.id}_$iamTemplateId"] = it - } - } - return groupMemberDetailMap - } - - private fun convertGroupDetailsInfoVo( - resourceGroup: TAuthResourceGroupRecord, - groupMemberDetail: MemberGroupDetailsResponse?, - uniqueManagerGroups: List, - authResourceGroupMember: AuthResourceGroupMember - ): GroupDetailsInfoVo { - // 如果用户离职,查询权限中心接口会报错,因此从数据库直接取数据,而不去调用权限中心接口。 - val (expiredAt, joinedTime) = if (groupMemberDetail != null) { - Pair( - TimeUnit.SECONDS.toMillis(groupMemberDetail.expiredAt), - TimeUnit.SECONDS.toMillis(groupMemberDetail.createdAt) - ) - } else { - Pair( - authResourceGroupMember.expiredTime.timestampmilli(), - 0L - ) - } - val between = expiredAt - System.currentTimeMillis() - return GroupDetailsInfoVo( - resourceCode = resourceGroup.resourceCode, - resourceName = resourceGroup.resourceName, - resourceType = resourceGroup.resourceType, - groupId = resourceGroup.relationId.toInt(), - groupName = resourceGroup.groupName, - groupDesc = resourceGroup.description, - expiredAtDisplay = when { - expiredAt == PERMANENT_EXPIRED_TIME -> - I18nUtil.getCodeLanMessage(messageCode = AuthI18nConstants.BK_MEMBER_EXPIRED_AT_DISPLAY_PERMANENT) - - between >= 0 -> I18nUtil.getCodeLanMessage( - messageCode = AuthI18nConstants.BK_MEMBER_EXPIRED_AT_DISPLAY_NORMAL, - params = arrayOf(DateTimeUtil.formatDay(between)) - ) - - else -> I18nUtil.getCodeLanMessage( - messageCode = AuthI18nConstants.BK_MEMBER_EXPIRED_AT_DISPLAY_EXPIRED - ) - }, - expiredAt = expiredAt, - joinedTime = joinedTime, - removeMemberButtonControl = when { - authResourceGroupMember.memberType == ManagerScopesEnum.getType(ManagerScopesEnum.TEMPLATE) -> - RemoveMemberButtonControl.TEMPLATE - - resourceGroup.resourceType == AuthResourceType.PROJECT.value && - uniqueManagerGroups.contains(authResourceGroupMember.iamGroupId) -> - RemoveMemberButtonControl.UNIQUE_MANAGER - - uniqueManagerGroups.contains(authResourceGroupMember.iamGroupId) -> - RemoveMemberButtonControl.UNIQUE_OWNER - - else -> - RemoveMemberButtonControl.OTHER - }, - joinedType = when (authResourceGroupMember.memberType) { - ManagerScopesEnum.getType(ManagerScopesEnum.TEMPLATE) -> JoinedType.TEMPLATE - else -> JoinedType.DIRECT - }, - operator = "" - ) - } - - override fun getMemberGroupsCount( - projectCode: String, - memberId: String, - groupName: String?, - minExpiredAt: Long?, - maxExpiredAt: Long?, - relatedResourceType: String?, - relatedResourceCode: String?, - action: String? - ): List { - // 查询项目下包含该成员的组列表 - val projectGroupIds = authResourceGroupMemberDao.listResourceGroupMember( - dslContext = dslContext, - projectCode = projectCode, - resourceType = AuthResourceType.PROJECT.value, - memberId = memberId - ).map { it.iamGroupId.toString() } - // 通过项目组ID获取人员模板ID - val iamTemplateId = authResourceGroupDao.listByRelationId( - dslContext = dslContext, - projectCode = projectCode, - iamGroupIds = projectGroupIds - ).filter { it.iamTemplateId != null } - .map { it.iamTemplateId.toString() } - - val iamGroupIdsByConditions = listIamGroupIdsByConditions( - condition = IamGroupIdsQueryConditionDTO( - projectCode = projectCode, - groupName = groupName, - relatedResourceType = relatedResourceType, - relatedResourceCode = relatedResourceCode, - action = action - ) - ) - // 获取成员直接加入的组和通过模板加入的组 - val memberGroupCountMap = authResourceGroupMemberDao.countMemberGroup( - dslContext = dslContext, - projectCode = projectCode, - memberId = memberId, - iamTemplateIds = iamTemplateId, - iamGroupIds = iamGroupIdsByConditions, - minExpiredAt = minExpiredAt?.let { DateTimeUtil.convertTimestampToLocalDateTime(it / 1000) }, - maxExpiredAt = maxExpiredAt?.let { DateTimeUtil.convertTimestampToLocalDateTime(it / 1000) } - ) - val memberGroupCountList = mutableListOf() - // 项目排在第一位 - memberGroupCountMap[AuthResourceType.PROJECT.value]?.let { projectCount -> - memberGroupCountList.add( - MemberGroupCountWithPermissionsVo( - resourceType = AuthResourceType.PROJECT.value, - resourceTypeName = I18nUtil.getCodeLanMessage( - messageCode = AuthResourceType.PROJECT.value + AuthI18nConstants.RESOURCE_TYPE_NAME_SUFFIX - ), - count = projectCount - ) - ) - } - - rbacCacheService.listResourceTypes() - .filter { it.resourceType != AuthResourceType.PROJECT.value } - .forEach { resourceTypeInfoVo -> - memberGroupCountMap[resourceTypeInfoVo.resourceType]?.let { count -> - val memberGroupCount = MemberGroupCountWithPermissionsVo( - resourceType = resourceTypeInfoVo.resourceType, - resourceTypeName = I18nUtil.getCodeLanMessage( - messageCode = resourceTypeInfoVo.resourceType + AuthI18nConstants.RESOURCE_TYPE_NAME_SUFFIX, - defaultMessage = resourceTypeInfoVo.name - ), - count = count - ) - memberGroupCountList.add(memberGroupCount) - } - } - - return memberGroupCountList - } - - override fun listIamGroupIdsByConditions(condition: IamGroupIdsQueryConditionDTO): List { - return with(condition) { - val filterGroupsByGroupName = if (isQueryByGroupName()) { - permissionResourceGroupService.listIamGroupIdsByGroupName( - projectId = projectCode, - groupName = groupName!! - ) - } else { - emptyList() - } - val finalGroupIds = if (isQueryByGroupPermissions()) { - groupPermissionService.listGroupsByPermissionConditions( - projectCode = projectCode, - filterIamGroupIds = filterGroupsByGroupName, - relatedResourceType = relatedResourceType!!, - relatedResourceCode = relatedResourceCode, - action = action - ) - } else { - filterGroupsByGroupName - }.toMutableList() - iamGroupIds?.let { finalGroupIds.addAll(it) } - finalGroupIds - } - } - - override fun listProjectMembersByComplexConditions( - conditionReq: ProjectMembersQueryConditionReq - ): SQLPage { - logger.info("list project members by complex conditions: $conditionReq") - // 不允许同时查询部门名称和用户名称 - if (conditionReq.userName != null && conditionReq.deptName != null) { - return SQLPage(count = 0, records = emptyList()) - } - - // 简单查询直接返回结果 - if (!conditionReq.isComplexQuery()) { - return permissionResourceMemberService.listProjectMembers( - projectCode = conditionReq.projectCode, - memberType = conditionReq.memberType, - userName = conditionReq.userName, - deptName = conditionReq.deptName, - departedFlag = conditionReq.departedFlag, - page = conditionReq.page, - pageSize = conditionReq.pageSize - ) - } - - // 处理复杂查询条件 - val iamGroupIdsByCondition = if (conditionReq.isNeedToQueryIamGroups()) { - listIamGroupIdsByConditions( - condition = IamGroupIdsQueryConditionDTO( - projectCode = conditionReq.projectCode, - groupName = conditionReq.groupName, - relatedResourceType = conditionReq.relatedResourceType, - relatedResourceCode = conditionReq.relatedResourceCode, - action = conditionReq.action - ) - ) - } else { - emptyList() - }.toMutableList() - - // 查询不到用户组,直接返回空 - if (conditionReq.isNeedToQueryIamGroups() && iamGroupIdsByCondition.isEmpty()) { - return SQLPage(0, emptyList()) - } - - val conditionDTO = ProjectMembersQueryConditionDTO.build(conditionReq, iamGroupIdsByCondition) - - if (iamGroupIdsByCondition.isNotEmpty()) { - logger.debug("iamGroupIdsByCondition :{}", iamGroupIdsByCondition) - // 根据用户组Id查询出对应用户组中的人员模板成员 - val iamTemplateIds = authResourceGroupMemberDao.listProjectMembersByComplexConditions( - dslContext = dslContext, - conditionDTO = ProjectMembersQueryConditionDTO( - projectCode = conditionDTO.projectCode, - queryTemplate = true, - iamGroupIds = conditionDTO.iamGroupIds - ) - ) - if (iamTemplateIds.isNotEmpty()) { - // 根据查询出的人员模板ID,查询出对应的组ID - val iamGroupIdsFromTemplate = authResourceGroupDao.listIamGroupIdsByConditions( - dslContext = dslContext, - projectCode = conditionDTO.projectCode, - iamTemplateIds = iamTemplateIds.map { it.id.toInt() } - ) - iamGroupIdsByCondition.addAll(iamGroupIdsFromTemplate) - logger.debug("iamGroupIdsByCondition and template :{}", iamGroupIdsByCondition) - } - } - - val records = authResourceGroupMemberDao.listProjectMembersByComplexConditions( - dslContext = dslContext, - conditionDTO = conditionDTO - ) - logger.debug("listProjectMembersByComplexConditions :{}", records) - - val count = authResourceGroupMemberDao.countProjectMembersByComplexConditions( - dslContext = dslContext, - conditionDTO = conditionDTO - ) - logger.debug("listProjectMembersByComplexConditions :$count") - // 添加离职标志 - return if (conditionDTO.departedFlag == false) { - SQLPage(count, records) - } else { - SQLPage(count, permissionResourceMemberService.addDepartedFlagToMembers(records)) - } - } - - companion object { - private val logger = LoggerFactory.getLogger(RbacPermissionResourceMemberService::class.java) - - // 永久过期时间 - private const val PERMANENT_EXPIRED_TIME = 4102444800000L - } -} diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionResourceGroupPermissionService.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionResourceGroupPermissionService.kt index eb461b5bbb52..2d584b29ad48 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionResourceGroupPermissionService.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionResourceGroupPermissionService.kt @@ -71,7 +71,7 @@ import java.util.concurrent.Executors @Suppress("LongParameterList") class RbacPermissionResourceGroupPermissionService( private val v2ManagerService: V2ManagerService, - private val rbacCacheService: RbacCacheService, + private val rbacCommonService: RbacCommonService, private val monitorSpaceService: AuthMonitorSpaceService, private val authResourceGroupDao: AuthResourceGroupDao, private val dslContext: DSLContext, @@ -251,7 +251,7 @@ class RbacPermissionResourceGroupPermissionService( action: String? ): List { val resourceType = if (action != null) { - rbacCacheService.getActionInfo(action).relatedResourceType + rbacCommonService.getActionInfo(action).relatedResourceType } else { relatedResourceType } @@ -278,7 +278,9 @@ class RbacPermissionResourceGroupPermissionService( relatedResourceCode: String, action: String ): Boolean { - val resourceType = rbacCacheService.getActionInfo(action).relatedResourceType + if (filterIamGroupIds.isEmpty()) + return false + val resourceType = rbacCommonService.getActionInfo(action).relatedResourceType val pipelineGroupIds = listPipelineGroupIds( projectCode = projectCode, resourceType = resourceType, @@ -295,13 +297,32 @@ class RbacPermissionResourceGroupPermissionService( ) } + override fun isGroupsHasProjectLevelPermission( + projectCode: String, + filterIamGroupIds: List, + action: String + ): Boolean { + if (filterIamGroupIds.isEmpty()) + return false + val actionRelatedResourceType = rbacCommonService.getActionInfo(action).relatedResourceType + return resourceGroupPermissionDao.isGroupsHasProjectLevelPermission( + dslContext = dslContext, + projectCode = projectCode, + filterIamGroupIds = filterIamGroupIds, + actionRelatedResourceType = actionRelatedResourceType, + action = action + ) + } + override fun listGroupResourcesWithPermission( projectCode: String, filterIamGroupIds: List, relatedResourceType: String, action: String ): Map> { - val resourceType = rbacCacheService.getActionInfo(action).relatedResourceType + if (filterIamGroupIds.isEmpty()) + return emptyMap() + val resourceType = rbacCommonService.getActionInfo(action).relatedResourceType return resourceGroupPermissionDao.listGroupResourcesWithPermission( dslContext = dslContext, projectCode = projectCode, @@ -375,7 +396,7 @@ class RbacPermissionResourceGroupPermissionService( val (actionName, actionRelatedResourceType) = if (iamSystemId == monitorSystemId) { Pair(monitorSpaceService.getMonitorActionName(action = actionId), monitorSystemId) } else { - val actionInfo = rbacCacheService.getActionInfo(action = actionId) + val actionInfo = rbacCommonService.getActionInfo(action = actionId) Pair(actionInfo.actionName, actionInfo.relatedResourceType) } GroupPermissionDetailVo( @@ -390,7 +411,7 @@ class RbacPermissionResourceGroupPermissionService( private fun buildRelatedResourceTypesName(iamSystemId: String, instancesDTO: InstancesDTO) { instancesDTO.let { val resourceTypeName = if (iamSystemId == systemId) { - rbacCacheService.getResourceTypeInfo(it.type).name + rbacCommonService.getResourceTypeInfo(it.type).name } else { I18nUtil.getCodeLanMessage(AuthI18nConstants.BK_MONITOR_SPACE) } diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionResourceGroupService.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionResourceGroupService.kt index 292dba02ec5b..87aaf9a7c08f 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionResourceGroupService.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionResourceGroupService.kt @@ -28,7 +28,6 @@ package com.tencent.devops.auth.provider.rbac.service -import com.tencent.bk.sdk.iam.constants.ManagerScopesEnum import com.tencent.bk.sdk.iam.dto.V2PageInfoDTO import com.tencent.bk.sdk.iam.dto.manager.ManagerRoleGroup import com.tencent.bk.sdk.iam.dto.manager.dto.ManagerRoleGroupDTO @@ -50,6 +49,7 @@ import com.tencent.devops.auth.pojo.dto.GroupAddDTO import com.tencent.devops.auth.pojo.dto.ListGroupConditionDTO import com.tencent.devops.auth.pojo.dto.RenameGroupDTO import com.tencent.devops.auth.pojo.enum.GroupMemberStatus +import com.tencent.devops.auth.pojo.enum.MemberType import com.tencent.devops.auth.pojo.request.CustomGroupCreateReq import com.tencent.devops.auth.pojo.vo.IamGroupInfoVo import com.tencent.devops.auth.pojo.vo.IamGroupMemberInfoVo @@ -181,8 +181,8 @@ class RbacPermissionResourceGroupService @Autowired constructor( dslContext = dslContext, projectCode = condition.projectId ) - val userCount = projectMemberCount[ManagerScopesEnum.getType(ManagerScopesEnum.USER)] ?: 0 - val departmentCount = projectMemberCount[ManagerScopesEnum.getType(ManagerScopesEnum.DEPARTMENT)] ?: 0 + val userCount = projectMemberCount[MemberType.USER.type] ?: 0 + val departmentCount = projectMemberCount[MemberType.DEPARTMENT.type] ?: 0 val allProjectMemberGroup = IamGroupInfoVo( managerId = managerId, defaultGroup = true, diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionResourceGroupSyncService.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionResourceGroupSyncService.kt index 401554d03f89..dd986dbebbda 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionResourceGroupSyncService.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionResourceGroupSyncService.kt @@ -40,6 +40,7 @@ import com.tencent.devops.auth.pojo.AuthResourceGroup import com.tencent.devops.auth.pojo.AuthResourceGroupMember import com.tencent.devops.auth.pojo.enum.ApplyToGroupStatus import com.tencent.devops.auth.pojo.enum.AuthMigrateStatus +import com.tencent.devops.auth.pojo.enum.MemberType import com.tencent.devops.auth.service.DeptService import com.tencent.devops.auth.service.iam.PermissionResourceGroupPermissionService import com.tencent.devops.auth.service.iam.PermissionResourceGroupSyncService @@ -74,7 +75,7 @@ class RbacPermissionResourceGroupSyncService @Autowired constructor( private val authResourceGroupDao: AuthResourceGroupDao, private val iamV2ManagerService: V2ManagerService, private val authResourceGroupMemberDao: AuthResourceGroupMemberDao, - private val rbacCacheService: RbacCacheService, + private val rbacCommonService: RbacCommonService, private val redisOperation: RedisOperation, private val authResourceSyncDao: AuthResourceSyncDao, private val authResourceGroupApplyDao: AuthResourceGroupApplyDao, @@ -505,7 +506,7 @@ class RbacPermissionResourceGroupSyncService @Autowired constructor( val startEpoch = System.currentTimeMillis() logger.info("start to sync resource group member:$projectCode") try { - val resourceTypes = rbacCacheService.listResourceTypes().map { it.resourceType } + val resourceTypes = rbacCommonService.listResourceTypes().map { it.resourceType } val traceId = MDC.get(TraceTag.BIZID) val resourceTypeFuture = resourceTypes.map { resourceType -> CompletableFuture.supplyAsync( @@ -752,7 +753,7 @@ class RbacPermissionResourceGroupSyncService @Autowired constructor( iamGroupId = iamGroupId, memberId = iamGroupTemplate.id, memberName = iamGroupTemplate.name, - memberType = ManagerScopesEnum.getType(ManagerScopesEnum.TEMPLATE), + memberType = MemberType.TEMPLATE.type, expiredTime = expiredTime ) ) diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionResourceMemberService.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionResourceMemberService.kt index 4737aff14d2d..b0ad89bd2453 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionResourceMemberService.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionResourceMemberService.kt @@ -8,7 +8,6 @@ import com.tencent.bk.sdk.iam.dto.manager.V2ManagerRoleGroupInfo import com.tencent.bk.sdk.iam.dto.manager.dto.GroupMemberRenewApplicationDTO import com.tencent.bk.sdk.iam.dto.manager.dto.ManagerMemberGroupDTO import com.tencent.bk.sdk.iam.dto.manager.dto.SearchGroupDTO -import com.tencent.bk.sdk.iam.dto.response.MemberGroupDetailsResponse import com.tencent.bk.sdk.iam.service.v2.V2ManagerService import com.tencent.devops.auth.constant.AuthMessageCode import com.tencent.devops.auth.dao.AuthResourceGroupDao @@ -16,37 +15,22 @@ import com.tencent.devops.auth.dao.AuthResourceGroupMemberDao import com.tencent.devops.auth.pojo.AuthResourceGroupMember import com.tencent.devops.auth.pojo.ResourceMemberInfo import com.tencent.devops.auth.pojo.dto.GroupMemberRenewalDTO -import com.tencent.devops.auth.pojo.enum.BatchOperateType import com.tencent.devops.auth.pojo.enum.MemberType -import com.tencent.devops.auth.pojo.request.GroupMemberCommonConditionReq -import com.tencent.devops.auth.pojo.request.GroupMemberHandoverConditionReq -import com.tencent.devops.auth.pojo.request.GroupMemberRenewalConditionReq -import com.tencent.devops.auth.pojo.request.GroupMemberSingleRenewalReq -import com.tencent.devops.auth.pojo.request.RemoveMemberFromProjectReq -import com.tencent.devops.auth.pojo.vo.BatchOperateGroupMemberCheckVo import com.tencent.devops.auth.pojo.vo.ResourceMemberCountVO import com.tencent.devops.auth.service.DeptService -import com.tencent.devops.auth.service.PermissionAuthorizationService -import com.tencent.devops.auth.service.iam.PermissionResourceGroupSyncService import com.tencent.devops.auth.service.iam.PermissionResourceMemberService import com.tencent.devops.common.api.exception.ErrorCodeException import com.tencent.devops.common.api.model.SQLPage import com.tencent.devops.common.api.util.DateTimeUtil import com.tencent.devops.common.api.util.PageUtil -import com.tencent.devops.common.api.util.timestamp -import com.tencent.devops.common.auth.api.AuthPermission import com.tencent.devops.common.auth.api.AuthResourceType import com.tencent.devops.common.auth.api.pojo.BkAuthGroup import com.tencent.devops.common.auth.api.pojo.BkAuthGroupAndUserList -import com.tencent.devops.common.auth.api.pojo.ResetAllResourceAuthorizationReq -import com.tencent.devops.common.auth.api.pojo.ResourceAuthorizationConditionRequest -import com.tencent.devops.common.service.utils.RetryUtils import com.tencent.devops.project.constant.ProjectMessageCode import org.apache.commons.lang3.RandomUtils import org.jooq.DSLContext import org.slf4j.LoggerFactory import java.time.LocalDateTime -import java.util.concurrent.CompletableFuture import java.util.concurrent.Executors import java.util.concurrent.TimeUnit @@ -57,10 +41,7 @@ class RbacPermissionResourceMemberService( private val authResourceGroupDao: AuthResourceGroupDao, private val authResourceGroupMemberDao: AuthResourceGroupMemberDao, private val dslContext: DSLContext, - private val deptService: DeptService, - private val permissionAuthorizationService: PermissionAuthorizationService, - private val syncIamGroupMemberService: PermissionResourceGroupSyncService, - private val rbacCacheService: RbacCacheService + private val deptService: DeptService ) : PermissionResourceMemberService { override fun getResourceGroupMembers( projectCode: String, @@ -147,8 +128,8 @@ class RbacPermissionResourceMemberService( projectCode = projectCode ) return ResourceMemberCountVO( - userCount = projectMemberCount[ManagerScopesEnum.getType(ManagerScopesEnum.USER)] ?: 0, - departmentCount = projectMemberCount[ManagerScopesEnum.getType(ManagerScopesEnum.DEPARTMENT)] ?: 0 + userCount = projectMemberCount[MemberType.USER.type] ?: 0, + departmentCount = projectMemberCount[MemberType.DEPARTMENT.type] ?: 0 ) } @@ -194,7 +175,7 @@ class RbacPermissionResourceMemberService( override fun addDepartedFlagToMembers(records: List): List { val userMembers = records.filter { - it.type == ManagerScopesEnum.getType(ManagerScopesEnum.USER) + it.type == MemberType.USER.type }.map { it.id } val departedMembers = if (userMembers.isNotEmpty()) { deptService.listDepartedMembers( @@ -204,7 +185,7 @@ class RbacPermissionResourceMemberService( return records } return records.map { - if (it.type != ManagerScopesEnum.getType(ManagerScopesEnum.USER)) { + if (it.type != MemberType.USER.type) { it.copy(departed = false) } else { it.copy(departed = departedMembers.contains(it.id)) @@ -220,7 +201,7 @@ class RbacPermissionResourceMemberService( expiredAt: Long, iamGroupId: Int ): Boolean { - if (memberType == ManagerScopesEnum.getType(ManagerScopesEnum.USER) && + if (memberType == MemberType.USER.type && deptService.isUserDeparted(memberId)) { return true } @@ -287,8 +268,8 @@ class RbacPermissionResourceMemberService( iamGroupId = iamGroupId ) // 获取用户组中用户以及部门 - val userType = ManagerScopesEnum.getType(ManagerScopesEnum.USER) - val deptType = ManagerScopesEnum.getType(ManagerScopesEnum.DEPARTMENT) + val userType = MemberType.USER.type + val deptType = MemberType.DEPARTMENT.type val pageInfoDTO = V2PageInfoDTO().apply { pageSize = 1000 page = 1 @@ -364,33 +345,6 @@ class RbacPermissionResourceMemberService( return true } - override fun isProjectMember( - projectCode: String, - userId: String - ): Boolean { - // 获取用户加入的项目级用户组模板ID - val iamTemplateIds = listProjectMemberGroupTemplateIds( - projectCode = projectCode, - memberId = userId - ) - val memberDeptInfos = deptService.getUserInfo( - userId = "admin", - name = userId - )?.deptInfo?.map { it.name!! } - - return authResourceGroupMemberDao.isMemberInProject( - dslContext = dslContext, - projectCode = projectCode, - userId = userId, - iamTemplateIds = iamTemplateIds, - memberDeptInfos = memberDeptInfos - ) || rbacCacheService.validateUserProjectPermission( - userId = userId, - projectCode = projectCode, - permission = AuthPermission.VISIT - ) - } - private fun verifyGroupBelongToProject( projectCode: String, iamGroupId: Int @@ -450,8 +404,8 @@ class RbacPermissionResourceMemberService( projectCode = projectCode, iamGroupId = iamGroupId ) - val userType = ManagerScopesEnum.getType(ManagerScopesEnum.USER) - val deptType = ManagerScopesEnum.getType(ManagerScopesEnum.DEPARTMENT) + val userType = MemberType.USER.type + val deptType = MemberType.DEPARTMENT.type val allMemberIds = mutableListOf() if (!members.isNullOrEmpty()) { deleteIamGroupMembers( @@ -515,7 +469,7 @@ class RbacPermissionResourceMemberService( val nowTimestamp = System.currentTimeMillis() / 1000 val (members, deptInfoList) = groupMemberInfoList .filter { it.expiredAt > nowTimestamp } - .partition { it.type == ManagerScopesEnum.getType(ManagerScopesEnum.USER) } + .partition { it.type == MemberType.USER.type } return BkAuthGroupAndUserList( displayName = groupInfo.name, @@ -645,25 +599,6 @@ class RbacPermissionResourceMemberService( return true } - override fun renewalGroupMember( - userId: String, - projectCode: String, - renewalConditionReq: GroupMemberSingleRenewalReq - ): Boolean { - logger.info("renewal group member $userId|$projectCode|$renewalConditionReq") - val groupId = renewalConditionReq.groupId - batchOperateGroupMembers( - projectCode = projectCode, - conditionReq = GroupMemberRenewalConditionReq( - groupIds = listOf(groupId), - targetMember = renewalConditionReq.targetMember, - renewalDuration = renewalConditionReq.renewalDuration - ), - operateGroupMemberTask = ::renewalTask - ) - return true - } - override fun renewalIamGroupMembers( groupId: Int, members: List, @@ -682,82 +617,12 @@ class RbacPermissionResourceMemberService( return true } - override fun batchRenewalGroupMembers( - userId: String, - projectCode: String, - renewalConditionReq: GroupMemberRenewalConditionReq - ): Boolean { - logger.info("batch renewal group member $userId|$projectCode|$renewalConditionReq") - batchOperateGroupMembers( - projectCode = projectCode, - conditionReq = renewalConditionReq, - operateGroupMemberTask = ::renewalTask - ) - return true - } - - private fun renewalTask( - projectCode: String, - groupId: Int, - renewalConditionReq: GroupMemberRenewalConditionReq, - expiredAt: Long - ) { - logger.info("renewal group member ${renewalConditionReq.targetMember}|$projectCode|$groupId|$expiredAt") - val targetMember = renewalConditionReq.targetMember - if (targetMember.type == ManagerScopesEnum.getType(ManagerScopesEnum.USER) && - deptService.isUserDeparted(targetMember.id)) { - return - } - val secondsOfRenewalDuration = TimeUnit.DAYS.toSeconds(renewalConditionReq.renewalDuration.toLong()) - val secondsOfCurrentTime = System.currentTimeMillis() / 1000 - // 若权限已过期,则为当前时间+续期天数,若未过期,则为有效期+续期天数 - val finalExpiredAt = if (expiredAt < secondsOfCurrentTime) { - secondsOfCurrentTime - } else { - expiredAt - } + secondsOfRenewalDuration - if (!isNeedToRenewal(finalExpiredAt)) { - return - } - renewalIamGroupMembers( - groupId = groupId, - members = listOf(ManagerMember(targetMember.type, targetMember.id)), - expiredAt = finalExpiredAt - ) - authResourceGroupMemberDao.update( - dslContext = dslContext, - projectCode = projectCode, - iamGroupId = groupId, - expiredTime = DateTimeUtil.convertTimestampToLocalDateTime(finalExpiredAt), - memberId = targetMember.id - ) - } - - private fun isNeedToRenewal(expiredAt: Long): Boolean { - return expiredAt < PERMANENT_EXPIRED_TIME - } - - override fun batchDeleteResourceGroupMembers( - userId: String, - projectCode: String, - removeMemberDTO: GroupMemberCommonConditionReq - ): Boolean { - logger.info("batch delete group members $userId|$projectCode|$removeMemberDTO") - removeMemberDTO.excludedUniqueManagerGroup = true - batchOperateGroupMembers( - projectCode = projectCode, - conditionReq = removeMemberDTO, - operateGroupMemberTask = ::deleteTask - ) - return true - } - override fun deleteIamGroupMembers( groupId: Int, type: String, memberIds: List ): Boolean { - val membersOfNeedToDelete = if (type == ManagerScopesEnum.getType(ManagerScopesEnum.USER)) { + val membersOfNeedToDelete = if (type == MemberType.USER.type) { memberIds.filterNot { deptService.isUserDeparted(it) } } else { memberIds @@ -772,595 +637,15 @@ class RbacPermissionResourceMemberService( return true } - private fun deleteTask( - projectCode: String, - groupId: Int, - removeMemberDTO: GroupMemberCommonConditionReq, - expiredAt: Long - ) { - val targetMember = removeMemberDTO.targetMember - logger.info("delete group member $projectCode|$groupId|$targetMember") - deleteIamGroupMembers( - groupId = groupId, - type = targetMember.type, - memberIds = listOf(targetMember.id) - ) - authResourceGroupMemberDao.batchDeleteGroupMembers( - dslContext = dslContext, - projectCode = projectCode, - iamGroupId = groupId, - memberIds = listOf(removeMemberDTO.targetMember.id) - ) - } - - override fun batchHandoverGroupMembers( - userId: String, - projectCode: String, - handoverMemberDTO: GroupMemberHandoverConditionReq - ): Boolean { - logger.info("batch handover group members $userId|$projectCode|$handoverMemberDTO") - handoverMemberDTO.checkHandoverTo() - batchOperateGroupMembers( - projectCode = projectCode, - conditionReq = handoverMemberDTO, - operateGroupMemberTask = ::handoverTask - ) - return true - } - - override fun batchOperateGroupMembersCheck( - userId: String, - projectCode: String, - batchOperateType: BatchOperateType, - conditionReq: GroupMemberCommonConditionReq - ): BatchOperateGroupMemberCheckVo { - logger.info("batch operate group member check|$userId|$projectCode|$batchOperateType|$conditionReq") - // 获取用户加入的用户组 - val (groupIdsOfDirectJoined, groupInfoIdsOfTemplateJoined) = getGroupIdsByCondition( - projectCode = projectCode, - commonCondition = conditionReq - ) - val totalCount = groupIdsOfDirectJoined.size + groupInfoIdsOfTemplateJoined.size - val groupCountOfTemplateJoined = groupInfoIdsOfTemplateJoined.size - - return when (batchOperateType) { - BatchOperateType.REMOVE -> { - val groupCountOfUniqueManager = authResourceGroupMemberDao.listProjectUniqueManagerGroups( - dslContext = dslContext, - projectCode = projectCode, - iamGroupIds = groupIdsOfDirectJoined - ).size - BatchOperateGroupMemberCheckVo( - totalCount = totalCount, - inoperableCount = groupCountOfUniqueManager + groupCountOfTemplateJoined - ) - } - - BatchOperateType.RENEWAL -> { - with(conditionReq) { - val isUserDeparted = targetMember.type == ManagerScopesEnum.getType(ManagerScopesEnum.USER) && - deptService.isUserDeparted(targetMember.id) - // 离职用户不允许续期 - if (isUserDeparted) { - BatchOperateGroupMemberCheckVo( - totalCount = totalCount, - inoperableCount = totalCount - ) - } else { - // 永久期限 不允许再续期 - val groupCountOfPermanentExpiredTime = listMemberGroupsDetails( - projectCode = projectCode, - memberId = targetMember.id, - memberType = targetMember.type, - groupIds = groupIdsOfDirectJoined - ).filter { - // iam用的是秒级时间戳 - it.expiredAt == PERMANENT_EXPIRED_TIME / 1000 - }.size - BatchOperateGroupMemberCheckVo( - totalCount = totalCount, - inoperableCount = groupCountOfPermanentExpiredTime + groupCountOfTemplateJoined - ) - } - } - } - - BatchOperateType.HANDOVER -> { - // 已过期(除唯一管理员组)或通过模板加入的不允许移交 - with(conditionReq) { - val finalGroupIds = groupIdsOfDirectJoined.toMutableList() - val uniqueManagerGroupIds = authResourceGroupMemberDao.listProjectUniqueManagerGroups( - dslContext = dslContext, - projectCode = projectCode, - iamGroupIds = groupIdsOfDirectJoined - ) - // 去除唯一管理员组 - if (uniqueManagerGroupIds.isNotEmpty()) { - finalGroupIds.removeAll(uniqueManagerGroupIds) - } - val groupCountOfExpired = listMemberGroupsDetails( - projectCode = projectCode, - memberId = targetMember.id, - memberType = targetMember.type, - groupIds = finalGroupIds - ).filter { - // iam用的是秒级时间戳 - it.expiredAt < System.currentTimeMillis() / 1000 - }.size - BatchOperateGroupMemberCheckVo( - totalCount = totalCount, - inoperableCount = groupCountOfTemplateJoined + groupCountOfExpired - ) - } - } - - else -> { - BatchOperateGroupMemberCheckVo( - totalCount = totalCount, - inoperableCount = groupCountOfTemplateJoined - ) - } - } - } - - override fun removeMemberFromProject( - userId: String, - projectCode: String, - removeMemberFromProjectReq: RemoveMemberFromProjectReq - ): List { - logger.info("remove member from project $userId|$projectCode|$removeMemberFromProjectReq") - return with(removeMemberFromProjectReq) { - val memberType = targetMember.type - val isNeedToHandover = handoverTo != null - if (memberType == ManagerScopesEnum.getType(ManagerScopesEnum.USER) && isNeedToHandover) { - removeMemberFromProjectReq.checkHandoverTo() - val handoverMemberDTO = GroupMemberHandoverConditionReq( - allSelection = true, - targetMember = targetMember, - handoverTo = handoverTo!! - ) - batchOperateGroupMembers( - projectCode = projectCode, - conditionReq = handoverMemberDTO, - operateGroupMemberTask = ::handoverTask - ) - permissionAuthorizationService.resetAllResourceAuthorization( - operator = userId, - projectCode = projectCode, - condition = ResetAllResourceAuthorizationReq( - projectCode = projectCode, - handoverFrom = removeMemberFromProjectReq.targetMember.id, - handoverTo = removeMemberFromProjectReq.handoverTo!!.id, - preCheck = false, - checkPermission = false - ) - ) - } else { - val removeMemberDTO = GroupMemberCommonConditionReq( - allSelection = true, - targetMember = targetMember - ) - batchOperateGroupMembers( - projectCode = projectCode, - conditionReq = removeMemberDTO, - operateGroupMemberTask = ::deleteTask - ) - } - - if (memberType == ManagerScopesEnum.getType(ManagerScopesEnum.USER)) { - // 查询用户还存在那些组织中 - val userDeptInfos = deptService.getUserInfo( - userId = "admin", - name = targetMember.id - )?.deptInfo?.map { it.name!! } - if (userDeptInfos != null) { - return authResourceGroupMemberDao.isMembersInProject( - dslContext = dslContext, - projectCode = projectCode, - memberNames = userDeptInfos, - memberType = ManagerScopesEnum.getType(ManagerScopesEnum.DEPARTMENT) - ) - } - } - return emptyList() - } - } - - override fun removeMemberFromProjectCheck( - userId: String, - projectCode: String, - removeMemberFromProjectReq: RemoveMemberFromProjectReq - ): Boolean { - val targetMember = removeMemberFromProjectReq.targetMember - val isMemberHasNoPermission = batchOperateGroupMembersCheck( - userId = userId, - projectCode = projectCode, - batchOperateType = BatchOperateType.HANDOVER, - conditionReq = GroupMemberCommonConditionReq( - allSelection = true, - targetMember = removeMemberFromProjectReq.targetMember - ) - ).let { it.totalCount == it.inoperableCount } - - val isMemberHasNoAuthorizations = - if (targetMember.type == ManagerScopesEnum.getType(ManagerScopesEnum.USER)) { - permissionAuthorizationService.listResourceAuthorizations( - condition = ResourceAuthorizationConditionRequest( - projectCode = projectCode, - handoverFrom = targetMember.id - ) - ).count == 0L - } else { - true - } - return isMemberHasNoPermission && isMemberHasNoAuthorizations - } - - private fun handoverTask( - projectCode: String, - groupId: Int, - handoverMemberDTO: GroupMemberHandoverConditionReq, - expiredAt: Long - ) { - logger.info( - "handover group member $projectCode|$groupId|" + - "${handoverMemberDTO.targetMember}|${handoverMemberDTO.handoverTo}" - ) - val currentTimeSeconds = System.currentTimeMillis() / 1000 - var finalExpiredAt = expiredAt - when { - // 若权限已过期,如果是唯一管理员组,允许交接,交接人将获得半年权限;其他的直接删除。 - expiredAt < currentTimeSeconds -> { - val isUniqueManagerGroup = authResourceGroupMemberDao.listProjectUniqueManagerGroups( - dslContext = dslContext, - projectCode = projectCode, - iamGroupIds = listOf(groupId) - ).isNotEmpty() - if (isUniqueManagerGroup) { - finalExpiredAt = currentTimeSeconds + TimeUnit.DAYS.toSeconds(180) - } else { - deleteTask( - projectCode = projectCode, - groupId = groupId, - removeMemberDTO = GroupMemberCommonConditionReq( - targetMember = handoverMemberDTO.targetMember - ), - expiredAt = finalExpiredAt - ) - return - } - } - // 若交接人已经在用户组内,无需交接。 - authResourceGroupMemberDao.isMemberInGroup( - dslContext = dslContext, - projectCode = projectCode, - iamGroupId = groupId, - memberId = handoverMemberDTO.handoverTo.id - ) -> { - deleteTask( - projectCode = projectCode, - groupId = groupId, - removeMemberDTO = GroupMemberCommonConditionReq( - targetMember = handoverMemberDTO.targetMember - ), - expiredAt = finalExpiredAt - ) - return - } - } - - val members = listOf( - ManagerMember( - handoverMemberDTO.handoverTo.type, - handoverMemberDTO.handoverTo.id - ) - ) - if (finalExpiredAt < currentTimeSeconds) { - throw ErrorCodeException( - errorCode = AuthMessageCode.INVALID_EXPIRED_PERM_NOT_ALLOW_TO_HANDOVER - ) - } - - addIamGroupMember( - groupId = groupId, - members = members, - expiredAt = finalExpiredAt - ) - deleteIamGroupMembers( - groupId = groupId, - type = handoverMemberDTO.targetMember.type, - memberIds = listOf(handoverMemberDTO.targetMember.id) - ) - authResourceGroupMemberDao.handoverGroupMembers( - dslContext = dslContext, - projectCode = projectCode, - iamGroupId = groupId, - handoverFrom = handoverMemberDTO.targetMember, - handoverTo = handoverMemberDTO.handoverTo, - expiredTime = DateTimeUtil.convertTimestampToLocalDateTime(finalExpiredAt) - ) - } - - private fun batchOperateGroupMembers( - projectCode: String, - conditionReq: T, - operateGroupMemberTask: ( - projectCode: String, - groupId: Int, - conditionReq: T, - expiredAt: Long - ) -> Unit - ): Boolean { - val groupIds = getGroupIdsByCondition( - projectCode = projectCode, - commonCondition = conditionReq - ).first - val targetMember = conditionReq.targetMember - val memberGroupsDetailsList = listMemberGroupsDetails( - projectCode = projectCode, - memberId = targetMember.id, - memberType = targetMember.type, - groupIds = groupIds - ) - val outOfSyncGroupIds = mutableListOf() - val futures = groupIds.map { groupId -> - CompletableFuture.supplyAsync( - { - val memberGroupsDetails = memberGroupsDetailsList.firstOrNull { it.id == groupId } - if (memberGroupsDetails == null) { - logger.warn( - "The data is out of sync, and the record no longer exists in the iam.$groupId" - ) - outOfSyncGroupIds.add(groupId) - return@supplyAsync - } - val expiredAt = memberGroupsDetails.expiredAt - RetryUtils.retry(3) { - operateGroupMemberTask.invoke( - projectCode, - groupId, - conditionReq, - expiredAt - ) - } - }, executorService - ) - } - handleFutures( - projectCode = projectCode, - outOfSyncGroupIds = outOfSyncGroupIds, - futures = futures - ) - return true - } - - private fun handleFutures( - projectCode: String, - outOfSyncGroupIds: List, - futures: List> - ) { - try { - CompletableFuture.allOf(*futures.toTypedArray()).join() - // 存在iam那边已经把用户组下成员删除,但蓝盾数据库未同步问题 - outOfSyncGroupIds.forEach { - syncIamGroupMemberService.syncIamGroupMember( - projectCode = projectCode, - iamGroupId = it - ) - } - } catch (ignore: Exception) { - logger.warn("batch operate group members failed", ignore) - throw ErrorCodeException( - errorCode = AuthMessageCode.ERROR_BATCH_OPERATE_GROUP_MEMBERS - ) - } - } - - private fun getGroupIdsByCondition( - projectCode: String, - commonCondition: GroupMemberCommonConditionReq - ): Pair, List> /*直接加入,模板加入*/ { - val finalResourceGroupMembers = mutableListOf() - with(commonCondition) { - val resourceGroupMembersByCondition = when { - // 全选 - allSelection -> { - listResourceGroupMembers( - projectCode = projectCode, - memberId = commonCondition.targetMember.id - ).second - } - // 全选某些资源类型用户组 - resourceTypes.isNotEmpty() -> { - resourceTypes.flatMap { resourceType -> - listResourceGroupMembers( - projectCode = projectCode, - memberId = commonCondition.targetMember.id, - resourceType = resourceType - ).second - } - } - - else -> { - emptyList() - } - } - - if (resourceGroupMembersByCondition.isNotEmpty()) { - finalResourceGroupMembers.addAll(resourceGroupMembersByCondition) - } - - // Select specific groups individually - if (groupIds.isNotEmpty()) { - val resourceGroupMembersOfSelect = listResourceGroupMembers( - projectCode = projectCode, - memberId = commonCondition.targetMember.id, - iamGroupIds = groupIds - ).second - finalResourceGroupMembers.addAll(resourceGroupMembersOfSelect) - } - - val (groupIdsOfDirectJoined, groupInfoIdsOfTemplateJoined) = finalResourceGroupMembers.partition { - it.memberType != ManagerScopesEnum.getType(ManagerScopesEnum.TEMPLATE) - }.run { - first.map { it.iamGroupId }.toMutableList() to second.map { it.iamGroupId }.toMutableList() - } - - // When batch removing, if the user is the only manager of the group, ignore and do not transfer - if (excludedUniqueManagerGroup) { - val excludedUniqueManagerGroupIds = authResourceGroupMemberDao.listProjectUniqueManagerGroups( - dslContext = dslContext, - projectCode = projectCode, - iamGroupIds = groupIdsOfDirectJoined - ) - groupIdsOfDirectJoined.removeAll { - excludedUniqueManagerGroupIds.contains(it) - } - } - return Pair(groupIdsOfDirectJoined, groupInfoIdsOfTemplateJoined) - } - } - - private fun listMemberGroupsDetails( - projectCode: String, - memberId: String, - memberType: String, - groupIds: List - ): List { - val memberGroupsDetailsList = mutableListOf() - val groupIdsChunk = groupIds.chunked(100) - val futures = groupIdsChunk.map { - CompletableFuture.supplyAsync( - { - memberGroupsDetailsList.addAll( - // 若离职,则从数据库获取用户加入组的过期时间,调用iam接口会报错。 - // 虽然数据库的过期时间可能不是最新的。 - if (memberType == ManagerScopesEnum.getType(ManagerScopesEnum.USER) && - deptService.isUserDeparted(userId = memberId)) { - val records = authResourceGroupMemberDao.listMemberGroupDetail( - dslContext = dslContext, - projectCode = projectCode, - memberId = memberId, - iamTemplateIds = emptyList(), - iamGroupIds = it - ) - records.map { record -> - MemberGroupDetailsResponse().apply { - id = record.iamGroupId - expiredAt = record.expiredTime.timestamp() - } - } - } else { - iamV2ManagerService.listMemberGroupsDetails( - memberType, - memberId, - it.joinToString(",") - ) - } - ) - }, executorService - ) - } - try { - CompletableFuture.allOf(*futures.toTypedArray()).join() - } catch (ignore: Exception) { - logger.warn("list member groups details failed!$ignore") - throw ignore - } - return memberGroupsDetailsList - } - - // 查询成员所在资源用户组列表,直接加入+通过用户组(模板)加入 - @Suppress("LongParameterList") - override fun listResourceGroupMembers( - projectCode: String, - memberId: String, - resourceType: String?, - iamGroupIds: List?, - minExpiredAt: Long?, - maxExpiredAt: Long?, - start: Int?, - limit: Int? - ): Pair> { - // 获取用户加入的项目级用户组模板ID - val iamTemplateIds = listProjectMemberGroupTemplateIds( - projectCode = projectCode, - memberId = memberId - ) - val minExpiredTime = minExpiredAt?.let { DateTimeUtil.convertTimestampToLocalDateTime(it / 1000) } - val maxExpiredTime = maxExpiredAt?.let { DateTimeUtil.convertTimestampToLocalDateTime(it / 1000) } - val count = authResourceGroupMemberDao.countMemberGroup( - dslContext = dslContext, - projectCode = projectCode, - memberId = memberId, - iamTemplateIds = iamTemplateIds, - resourceType = resourceType, - iamGroupIds = iamGroupIds, - minExpiredAt = minExpiredTime, - maxExpiredAt = maxExpiredTime - )[resourceType] ?: 0L - val resourceGroupMembers = authResourceGroupMemberDao.listMemberGroupDetail( - dslContext = dslContext, - projectCode = projectCode, - memberId = memberId, - iamTemplateIds = iamTemplateIds, - resourceType = resourceType, - iamGroupIds = iamGroupIds, - minExpiredAt = minExpiredTime, - maxExpiredAt = maxExpiredTime, - offset = start, - limit = limit - ) - return Pair(count, resourceGroupMembers) - } - - override fun listMemberGroupIdsInProject( - projectCode: String, - memberId: String - ): List { - // 获取用户加入的项目级用户组模板ID - val iamTemplateIds = listProjectMemberGroupTemplateIds( - projectCode = projectCode, - memberId = memberId - ) - return authResourceGroupMemberDao.listMemberGroupIdsInProject( - dslContext = dslContext, - projectCode = projectCode, - memberId = memberId, - iamTemplateIds = iamTemplateIds - ) - } - - // 获取用户加入的项目级用户组模板ID - private fun listProjectMemberGroupTemplateIds( - projectCode: String, - memberId: String - ): List { - // 查询项目下包含该成员的组列表 - val projectGroupIds = authResourceGroupMemberDao.listResourceGroupMember( - dslContext = dslContext, - projectCode = projectCode, - resourceType = AuthResourceType.PROJECT.value, - memberId = memberId - ).map { it.iamGroupId.toString() } - // 通过项目组ID获取人员模板ID - return authResourceGroupDao.listByRelationId( - dslContext = dslContext, - projectCode = projectCode, - iamGroupIds = projectGroupIds - ).filter { it.iamTemplateId != null } - .map { it.iamTemplateId.toString() } - } - private fun MutableList.removeDepartedMembers(): List { - val userMemberIds = this.filter { it.type == ManagerScopesEnum.getType(ManagerScopesEnum.USER) }.map { it.id } + val userMemberIds = this.filter { it.type == MemberType.USER.type }.map { it.id } if (userMemberIds.isEmpty()) return this // 获取离职的人员 val departedMembers = deptService.listDepartedMembers( memberIds = userMemberIds ) return this.filterNot { - it.type == ManagerScopesEnum.getType(ManagerScopesEnum.USER) && + it.type == MemberType.USER.type && departedMembers.contains(it.id) } } @@ -1378,8 +663,5 @@ class RbacPermissionResourceMemberService( private val AUTO_RENEWAL_EXPIRED_AT = TimeUnit.DAYS.toSeconds(180) private val executorService = Executors.newFixedThreadPool(30) - - // 永久过期时间 - private const val PERMANENT_EXPIRED_TIME = 4102444800000L } } diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionResourceValidateService.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionResourceValidateService.kt index c0baf8abee19..709b1320b4a5 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionResourceValidateService.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionResourceValidateService.kt @@ -29,7 +29,9 @@ package com.tencent.devops.auth.provider.rbac.service import com.tencent.devops.auth.constant.AuthMessageCode +import com.tencent.devops.auth.dao.AuthAuthorizationDao import com.tencent.devops.auth.pojo.dto.PermissionBatchValidateDTO +import com.tencent.devops.auth.pojo.enum.OperateChannel import com.tencent.devops.auth.service.iam.PermissionResourceValidateService import com.tencent.devops.auth.service.iam.PermissionService import com.tencent.devops.common.api.exception.ErrorCodeException @@ -37,6 +39,7 @@ import com.tencent.devops.common.api.exception.PermissionForbiddenException import com.tencent.devops.common.api.util.Watcher import com.tencent.devops.common.auth.api.AuthPermission import com.tencent.devops.common.auth.api.AuthResourceType +import com.tencent.devops.common.auth.api.pojo.ResourceAuthorizationConditionRequest import com.tencent.devops.common.auth.rbac.utils.RbacAuthUtils import com.tencent.devops.common.client.Client import com.tencent.devops.common.service.utils.LogUtils @@ -44,13 +47,16 @@ import com.tencent.devops.common.web.utils.I18nUtil import com.tencent.devops.project.api.service.ServiceProjectResource import com.tencent.devops.project.constant.ProjectMessageCode import com.tencent.devops.project.pojo.enums.ProjectApproveStatus +import org.jooq.DSLContext import org.slf4j.LoggerFactory import javax.ws.rs.NotFoundException class RbacPermissionResourceValidateService( private val permissionService: PermissionService, - private val rbacCacheService: RbacCacheService, - private val client: Client + private val rbacCommonService: RbacCommonService, + private val client: Client, + private val authAuthorizationDao: AuthAuthorizationDao, + private val dslContext: DSLContext ) : PermissionResourceValidateService { companion object { @@ -69,7 +75,7 @@ class RbacPermissionResourceValidateService( val resourceActionList = mutableSetOf() permissionBatchValidateDTO.actionList.forEach { action -> - val actionInfo = rbacCacheService.getActionInfo(action) + val actionInfo = rbacCommonService.getActionInfo(action) val iamRelatedResourceType = actionInfo.relatedResourceType if (iamRelatedResourceType == AuthResourceType.PROJECT.value) { projectActionList.add(action) @@ -117,7 +123,7 @@ class RbacPermissionResourceValidateService( resourceCode: String ): Boolean { checkProjectApprovalStatus(resourceType, resourceCode) - val checkProjectManage = rbacCacheService.checkProjectManager( + val checkProjectManage = rbacCommonService.checkProjectManager( userId = userId, projectCode = projectId ) @@ -152,6 +158,55 @@ class RbacPermissionResourceValidateService( return true } + override fun validateUserProjectPermissionByChannel( + userId: String, + projectCode: String, + operateChannel: OperateChannel, + targetMemberId: String + ) { + if (operateChannel == OperateChannel.PERSONAL) { + // 个人视角校验 + val hasVisitPermission = permissionService.validateUserResourcePermission( + userId = userId, + resourceType = AuthResourceType.PROJECT.value, + action = RbacAuthUtils.buildAction(AuthPermission.VISIT, AuthResourceType.PROJECT), + projectCode = projectCode + ) + if (hasVisitPermission) return + + val isUserHasProjectAuthorizations = authAuthorizationDao.count( + dslContext = dslContext, + condition = ResourceAuthorizationConditionRequest( + projectCode = projectCode, + handoverFrom = userId + ) + ) > 0 + if (!isUserHasProjectAuthorizations) { + throw PermissionForbiddenException( + message = "The user does not have permission to visit the project!" + ) + } + if (userId != targetMemberId) { + throw PermissionForbiddenException( + message = "You do not have permission to operate other user groups!" + ) + } + } else { + // 管理员视角校验 + val hasProjectManagePermission = permissionService.validateUserResourcePermission( + userId = userId, + resourceType = AuthResourceType.PROJECT.value, + action = RbacAuthUtils.buildAction(AuthPermission.MANAGE, AuthResourceType.PROJECT), + projectCode = projectCode + ) + if (!hasProjectManagePermission) { + throw PermissionForbiddenException( + message = I18nUtil.getCodeLanMessage(AuthMessageCode.ERROR_AUTH_NO_MANAGE_PERMISSION) + ) + } + } + } + private fun checkProjectApprovalStatus(resourceType: String, resourceCode: String) { if (resourceType == AuthResourceType.PROJECT.value) { val projectInfo = diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionService.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionService.kt index 08b14499753e..3523cbf55445 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionService.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionService.kt @@ -29,7 +29,6 @@ package com.tencent.devops.auth.provider.rbac.service import com.tencent.bk.sdk.iam.config.IamConfiguration -import com.tencent.bk.sdk.iam.constants.ManagerScopesEnum import com.tencent.bk.sdk.iam.dto.InstanceDTO import com.tencent.bk.sdk.iam.dto.PathInfoDTO import com.tencent.bk.sdk.iam.dto.SubjectDTO @@ -39,6 +38,7 @@ import com.tencent.bk.sdk.iam.dto.resource.ResourceDTO import com.tencent.bk.sdk.iam.dto.resource.V2ResourceNode import com.tencent.bk.sdk.iam.helper.AuthHelper import com.tencent.bk.sdk.iam.service.PolicyService +import com.tencent.devops.auth.pojo.enum.MemberType import com.tencent.devops.auth.service.AuthProjectUserMetricsService import com.tencent.devops.auth.service.SuperManagerService import com.tencent.devops.auth.service.iam.PermissionService @@ -63,7 +63,7 @@ class RbacPermissionService( private val policyService: PolicyService, private val authResourceCodeConverter: AuthResourceCodeConverter, private val superManagerService: SuperManagerService, - private val rbacCacheService: RbacCacheService, + private val rbacCommonService: RbacCommonService, private val client: Client, private val authProjectUserMetricsService: AuthProjectUserMetricsService ) : PermissionService { @@ -94,7 +94,7 @@ class RbacPermissionService( projectCode: String, resourceType: String? ): Boolean { - val actionInfo = rbacCacheService.getActionInfo(action) + val actionInfo = rbacCommonService.getActionInfo(action) // 如果action关联的资源是项目,则直接查询项目的权限 return if (actionInfo.relatedResourceType == AuthResourceType.PROJECT.value) { validateUserResourcePermissionByRelation( @@ -179,7 +179,7 @@ class RbacPermissionService( } ?: return false val subject = SubjectDTO.builder() .id(userId) - .type(ManagerScopesEnum.getType(ManagerScopesEnum.USER)) + .type(MemberType.USER.type) .build() val actionDTO = ActionDTO() @@ -265,7 +265,7 @@ class RbacPermissionService( ) val startEpoch = System.currentTimeMillis() try { - if (rbacCacheService.checkProjectManager(userId = userId, projectCode = projectCode)) { + if (rbacCommonService.checkProjectManager(userId = userId, projectCode = projectCode)) { return actions.associateWith { true } } val actionList = actions.map { action -> @@ -457,7 +457,7 @@ class RbacPermissionService( ) val startEpoch = System.currentTimeMillis() try { - if (rbacCacheService.checkProjectManager(userId = userId, projectCode = projectCode)) { + if (rbacCommonService.checkProjectManager(userId = userId, projectCode = projectCode)) { return actions.associate { val authPermission = it.substringAfterLast("_") AuthPermission.get(authPermission) to resources.map { resource -> resource.resourceCode } @@ -614,7 +614,7 @@ class RbacPermissionService( resourceType: String, action: String ): Boolean { - return rbacCacheService.checkProjectManager( + return rbacCommonService.checkProjectManager( userId = userId, projectCode = projectCode ) || superManagerService.projectManagerCheck( diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/migrate/AbMigratePolicyService.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/migrate/AbMigratePolicyService.kt index d74c7192ead2..7311fd7c3b4c 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/migrate/AbMigratePolicyService.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/migrate/AbMigratePolicyService.kt @@ -29,7 +29,6 @@ package com.tencent.devops.auth.provider.rbac.service.migrate import com.tencent.bk.sdk.iam.config.IamConfiguration -import com.tencent.bk.sdk.iam.constants.ManagerScopesEnum import com.tencent.bk.sdk.iam.dto.manager.AuthorizationScopes import com.tencent.bk.sdk.iam.dto.manager.ManagerPath import com.tencent.bk.sdk.iam.dto.manager.ManagerResources @@ -41,8 +40,9 @@ import com.tencent.devops.auth.constant.AuthMessageCode import com.tencent.devops.auth.dao.AuthMigrationDao import com.tencent.devops.auth.dao.AuthResourceGroupConfigDao import com.tencent.devops.auth.dao.AuthResourceGroupDao +import com.tencent.devops.auth.pojo.enum.MemberType import com.tencent.devops.auth.provider.rbac.pojo.migrate.MigrateTaskDataResult -import com.tencent.devops.auth.provider.rbac.service.RbacCacheService +import com.tencent.devops.auth.provider.rbac.service.RbacCommonService import com.tencent.devops.auth.provider.rbac.service.migrate.MigrateIamApiService.Companion.GROUP_API_POLICY import com.tencent.devops.auth.provider.rbac.service.migrate.MigrateIamApiService.Companion.GROUP_WEB_POLICY import com.tencent.devops.auth.provider.rbac.service.migrate.MigrateIamApiService.Companion.USER_CUSTOM_POLICY @@ -75,7 +75,7 @@ abstract class AbMigratePolicyService( private val migrateIamApiService: MigrateIamApiService, private val authMigrationDao: AuthMigrationDao, private val permissionService: PermissionService, - private val rbacCacheService: RbacCacheService, + private val rbacCommonService: RbacCommonService, private val deptService: DeptService, private val permissionResourceGroupPermissionService: PermissionResourceGroupPermissionService, private val permissionResourceMemberService: PermissionResourceMemberService @@ -382,7 +382,7 @@ abstract class AbMigratePolicyService( permissionResourceMemberService.addGroupMember( projectCode = projectCode, memberId = userId, - memberType = ManagerScopesEnum.getType(ManagerScopesEnum.USER), + memberType = MemberType.USER.type, expiredAt = System.currentTimeMillis() / MILLISECOND + TimeUnit.DAYS.toSeconds(DEFAULT_EXPIRED_DAY), iamGroupId = groupId @@ -497,7 +497,7 @@ abstract class AbMigratePolicyService( resourceCode: String, userId: String ): Pair { - rbacCacheService.getGroupConfigAction(resourceType).forEach groupConfig@{ groupConfig -> + rbacCommonService.getGroupConfigAction(resourceType).forEach groupConfig@{ groupConfig -> if (groupConfig.actions.containsAll(actions)) { val groupId = authResourceGroupDao.get( dslContext = dslContext, diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/migrate/MigratePermissionHandoverService.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/migrate/MigratePermissionHandoverService.kt index e7597f6ccc14..dd14d20be283 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/migrate/MigratePermissionHandoverService.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/migrate/MigratePermissionHandoverService.kt @@ -32,7 +32,7 @@ import com.tencent.devops.auth.dao.AuthResourceGroupDao import com.tencent.devops.auth.pojo.dto.PermissionHandoverDTO import com.tencent.devops.auth.pojo.enum.JoinedType import com.tencent.devops.auth.provider.rbac.service.AuthResourceService -import com.tencent.devops.auth.service.iam.PermissionResourceGroupAndMemberFacadeService +import com.tencent.devops.auth.service.iam.PermissionManageFacadeService import com.tencent.devops.auth.service.iam.PermissionResourceMemberService import com.tencent.devops.common.auth.api.AuthResourceType import com.tencent.devops.common.auth.api.pojo.DefaultGroupType @@ -43,7 +43,7 @@ class MigratePermissionHandoverService( private val permissionResourceMemberService: PermissionResourceMemberService, private val authResourceGroupDao: AuthResourceGroupDao, private val authResourceService: AuthResourceService, - private val resourceGroupAndMemberFacadeService: PermissionResourceGroupAndMemberFacadeService, + private val permissionManageFacadeService: PermissionManageFacadeService, private val dslContext: DSLContext ) { fun handoverPermissions(permissionHandoverDTO: PermissionHandoverDTO) { @@ -114,7 +114,7 @@ class MigratePermissionHandoverService( ) } // 交接用户组权限 - val userJoinedGroups = resourceGroupAndMemberFacadeService.getMemberGroupsDetails( + val userJoinedGroups = permissionManageFacadeService.getMemberGroupsDetails( projectId = projectCode, memberId = handoverFrom ).records.filter { it.joinedType == JoinedType.DIRECT }.map { it.groupId } diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/migrate/MigrateResourceService.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/migrate/MigrateResourceService.kt index 8f931ec5e7ef..a6ddd6c98420 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/migrate/MigrateResourceService.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/migrate/MigrateResourceService.kt @@ -42,7 +42,7 @@ import com.tencent.devops.auth.dao.AuthResourceGroupDao import com.tencent.devops.auth.pojo.dto.ResourceMigrationCountDTO import com.tencent.devops.auth.provider.rbac.service.AuthResourceService import com.tencent.devops.auth.provider.rbac.service.PermissionGradeManagerService -import com.tencent.devops.auth.provider.rbac.service.RbacCacheService +import com.tencent.devops.auth.provider.rbac.service.RbacCommonService import com.tencent.devops.auth.provider.rbac.service.RbacPermissionResourceService import com.tencent.devops.auth.service.ResourceService import com.tencent.devops.auth.service.iam.MigrateCreatorFixService @@ -66,7 +66,7 @@ import java.util.concurrent.Executors @Suppress("LongParameterList", "MagicNumber") class MigrateResourceService @Autowired constructor( private val resourceService: ResourceService, - private val rbacCacheService: RbacCacheService, + private val rbacCommonService: RbacCommonService, private val rbacPermissionResourceService: RbacPermissionResourceService, private val migrateCreatorFixService: MigrateCreatorFixService, private val authResourceService: AuthResourceService, @@ -89,7 +89,7 @@ class MigrateResourceService @Autowired constructor( val startEpoch = System.currentTimeMillis() logger.info("start to migrate resource:$projectCode") try { - val resourceTypes = rbacCacheService.listResourceTypes() + val resourceTypes = rbacCommonService.listResourceTypes() .map { it.resourceType } .filterNot { noNeedToMigrateResourceType.contains(it) } diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/migrate/MigrateResultService.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/migrate/MigrateResultService.kt index 3b2c76b3b23a..b97904e30853 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/migrate/MigrateResultService.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/migrate/MigrateResultService.kt @@ -33,7 +33,7 @@ import com.tencent.devops.auth.constant.AuthMessageCode import com.tencent.devops.auth.provider.rbac.service.AuthResourceService import com.tencent.devops.auth.service.AuthVerifyRecordService import com.tencent.devops.auth.service.DeptService -import com.tencent.devops.auth.provider.rbac.service.RbacCacheService +import com.tencent.devops.auth.provider.rbac.service.RbacCommonService import com.tencent.devops.auth.service.iam.PermissionService import com.tencent.devops.common.api.exception.ErrorCodeException import com.tencent.devops.common.api.util.PageUtil @@ -55,7 +55,7 @@ import java.util.concurrent.Executors @Suppress("ALL") class MigrateResultService constructor( private val permissionService: PermissionService, - private val rbacCacheService: RbacCacheService, + private val rbacCommonService: RbacCommonService, private val migrateResourceCodeConverter: MigrateResourceCodeConverter, private val authVerifyRecordService: AuthVerifyRecordService, private val migrateResourceService: MigrateResourceService, @@ -79,7 +79,7 @@ class MigrateResultService constructor( logger.info("start to compare policy|$projectCode") val startEpoch = System.currentTimeMillis() try { - val resourceTypes = rbacCacheService.listResourceTypes() + val resourceTypes = rbacCommonService.listResourceTypes() .map { it.resourceType } val traceId = MDC.get(TraceTag.BIZID) val compareFuture = resourceTypes.map { resourceType -> diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/migrate/MigrateV0PolicyService.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/migrate/MigrateV0PolicyService.kt index 7e286071d369..b7846c0d8dcd 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/migrate/MigrateV0PolicyService.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/migrate/MigrateV0PolicyService.kt @@ -29,7 +29,6 @@ package com.tencent.devops.auth.provider.rbac.service.migrate import com.tencent.bk.sdk.iam.config.IamConfiguration -import com.tencent.bk.sdk.iam.constants.ManagerScopesEnum import com.tencent.bk.sdk.iam.dto.V2PageInfoDTO import com.tencent.bk.sdk.iam.dto.manager.Action import com.tencent.bk.sdk.iam.dto.manager.AuthorizationScopes @@ -41,9 +40,10 @@ import com.tencent.bk.sdk.iam.service.v2.V2ManagerService import com.tencent.devops.auth.dao.AuthMigrationDao import com.tencent.devops.auth.dao.AuthResourceGroupConfigDao import com.tencent.devops.auth.dao.AuthResourceGroupDao +import com.tencent.devops.auth.pojo.enum.MemberType import com.tencent.devops.auth.provider.rbac.pojo.migrate.MigrateTaskDataResult import com.tencent.devops.auth.provider.rbac.service.AuthResourceCodeConverter -import com.tencent.devops.auth.provider.rbac.service.RbacCacheService +import com.tencent.devops.auth.provider.rbac.service.RbacCommonService import com.tencent.devops.auth.service.DeptService import com.tencent.devops.auth.service.iam.PermissionResourceGroupPermissionService import com.tencent.devops.auth.service.iam.PermissionResourceMemberService @@ -68,7 +68,7 @@ class MigrateV0PolicyService constructor( private val migrateIamApiService: MigrateIamApiService, private val authResourceCodeConverter: AuthResourceCodeConverter, private val permissionService: PermissionService, - private val rbacCacheService: RbacCacheService, + private val rbacCommonService: RbacCommonService, private val authMigrationDao: AuthMigrationDao, private val deptService: DeptService, private val permissionResourceGroupPermissionService: PermissionResourceGroupPermissionService, @@ -82,7 +82,7 @@ class MigrateV0PolicyService constructor( migrateIamApiService = migrateIamApiService, authMigrationDao = authMigrationDao, permissionService = permissionService, - rbacCacheService = rbacCacheService, + rbacCommonService = rbacCommonService, deptService = deptService, permissionResourceGroupPermissionService = permissionResourceGroupPermissionService, permissionResourceMemberService = permissionResourceMemberService @@ -486,7 +486,7 @@ class MigrateV0PolicyService constructor( groupId = it.toInt(), defaultGroup = true, member = RoleGroupMemberInfo().apply { - type = ManagerScopesEnum.getType(ManagerScopesEnum.TEMPLATE) + type = MemberType.TEMPLATE.type id = subjectTemplateId name = subjectTemplateId expiredAt = 0 diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/migrate/MigrateV3PolicyService.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/migrate/MigrateV3PolicyService.kt index fb8abd5d7885..40900d0a9a35 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/migrate/MigrateV3PolicyService.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/migrate/MigrateV3PolicyService.kt @@ -41,7 +41,7 @@ import com.tencent.devops.auth.dao.AuthResourceGroupConfigDao import com.tencent.devops.auth.dao.AuthResourceGroupDao import com.tencent.devops.auth.provider.rbac.pojo.migrate.MigrateTaskDataResult import com.tencent.devops.auth.provider.rbac.service.AuthResourceCodeConverter -import com.tencent.devops.auth.provider.rbac.service.RbacCacheService +import com.tencent.devops.auth.provider.rbac.service.RbacCommonService import com.tencent.devops.auth.service.DeptService import com.tencent.devops.auth.service.iam.PermissionResourceGroupPermissionService import com.tencent.devops.auth.service.iam.PermissionResourceMemberService @@ -72,7 +72,7 @@ class MigrateV3PolicyService constructor( private val migrateIamApiService: MigrateIamApiService, private val authResourceCodeConverter: AuthResourceCodeConverter, private val permissionService: PermissionService, - private val rbacCacheService: RbacCacheService, + private val rbacCommonService: RbacCommonService, private val authMigrationDao: AuthMigrationDao, private val deptService: DeptService, private val permissionResourceGroupPermissionService: PermissionResourceGroupPermissionService, @@ -86,7 +86,7 @@ class MigrateV3PolicyService constructor( migrateIamApiService = migrateIamApiService, authMigrationDao = authMigrationDao, permissionService = permissionService, - rbacCacheService = rbacCacheService, + rbacCommonService = rbacCommonService, deptService = deptService, permissionResourceGroupPermissionService = permissionResourceGroupPermissionService, permissionResourceMemberService = permissionResourceMemberService diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/migrate/RbacPermissionMigrateService.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/migrate/RbacPermissionMigrateService.kt index 6c6f54198f84..8f0a67ec78fc 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/migrate/RbacPermissionMigrateService.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/migrate/RbacPermissionMigrateService.kt @@ -37,7 +37,6 @@ import com.tencent.devops.auth.pojo.dto.PermissionHandoverDTO import com.tencent.devops.auth.pojo.enum.AuthMigrateStatus import com.tencent.devops.auth.provider.rbac.service.AuthResourceService import com.tencent.devops.auth.provider.rbac.service.PermissionGradeManagerService -import com.tencent.devops.auth.provider.rbac.service.RbacCacheService import com.tencent.devops.auth.service.iam.MigrateCreatorFixService import com.tencent.devops.auth.service.iam.PermissionMigrateService import com.tencent.devops.auth.service.iam.PermissionResourceMemberService @@ -68,7 +67,7 @@ import java.util.concurrent.Executors * rbac迁移服务 */ @Suppress("LongParameterList", "ReturnCount") -class RbacPermissionMigrateService constructor( +class RbacPermissionMigrateService ( private val client: Client, private val migrateResourceService: MigrateResourceService, private val migrateV3PolicyService: MigrateV3PolicyService, @@ -82,7 +81,6 @@ class RbacPermissionMigrateService constructor( private val dslContext: DSLContext, private val authMigrationDao: AuthMigrationDao, private val authMonitorSpaceDao: AuthMonitorSpaceDao, - private val cacheService: RbacCacheService, private val permissionResourceMemberService: PermissionResourceMemberService, private val migrateResourceAuthorizationService: MigrateResourceAuthorizationService, private val migrateResourceGroupService: MigrateResourceGroupService diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/config/MockAuthConfiguration.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/config/MockAuthConfiguration.kt index 84587ca37b79..d7bc98262d44 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/config/MockAuthConfiguration.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/config/MockAuthConfiguration.kt @@ -8,9 +8,10 @@ import com.tencent.devops.auth.provider.sample.service.SampleAuthPermissionServi import com.tencent.devops.auth.provider.sample.service.SampleOrganizationService import com.tencent.devops.auth.provider.sample.service.SamplePermissionApplyService import com.tencent.devops.auth.provider.sample.service.SamplePermissionExtService +import com.tencent.devops.auth.provider.sample.service.SamplePermissionHandoverApplicationService import com.tencent.devops.auth.provider.sample.service.SamplePermissionItsmCallbackService import com.tencent.devops.auth.provider.sample.service.SamplePermissionMigrateService -import com.tencent.devops.auth.provider.sample.service.SamplePermissionResourceGroupAndMemberFacadeService +import com.tencent.devops.auth.provider.sample.service.SamplePermissionManageFacadeService import com.tencent.devops.auth.provider.sample.service.SamplePermissionResourceGroupPermissionService import com.tencent.devops.auth.provider.sample.service.SamplePermissionResourceGroupService import com.tencent.devops.auth.provider.sample.service.SamplePermissionResourceGroupSyncService @@ -27,10 +28,11 @@ import com.tencent.devops.auth.service.PermissionAuthorizationService import com.tencent.devops.auth.service.SuperManagerService import com.tencent.devops.auth.service.iam.PermissionApplyService import com.tencent.devops.auth.service.iam.PermissionExtService +import com.tencent.devops.auth.service.iam.PermissionHandoverApplicationService import com.tencent.devops.auth.service.iam.PermissionItsmCallbackService import com.tencent.devops.auth.service.iam.PermissionMigrateService import com.tencent.devops.auth.service.iam.PermissionProjectService -import com.tencent.devops.auth.service.iam.PermissionResourceGroupAndMemberFacadeService +import com.tencent.devops.auth.service.iam.PermissionManageFacadeService import com.tencent.devops.auth.service.iam.PermissionResourceGroupPermissionService import com.tencent.devops.auth.service.iam.PermissionResourceGroupService import com.tencent.devops.auth.service.iam.PermissionResourceGroupSyncService @@ -86,8 +88,8 @@ class MockAuthConfiguration { fun samplePermissionResourceGroupService() = SamplePermissionResourceGroupService() @Bean - @ConditionalOnMissingBean(PermissionResourceGroupAndMemberFacadeService::class) - fun samplePermissionResourceGroupAndMemberFacadeService() = SamplePermissionResourceGroupAndMemberFacadeService() + @ConditionalOnMissingBean(PermissionManageFacadeService::class) + fun samplePermissionManageFacadeService() = SamplePermissionManageFacadeService() @Bean @ConditionalOnMissingBean(PermissionResourceGroupPermissionService::class) @@ -128,4 +130,8 @@ class MockAuthConfiguration { @Bean @ConditionalOnMissingBean(PermissionResourceGroupSyncService::class) fun samplePermissionResourceGroupSyncService() = SamplePermissionResourceGroupSyncService() + + @Bean + @ConditionalOnMissingBean(PermissionHandoverApplicationService::class) + fun samplePermissionHandoverService() = SamplePermissionHandoverApplicationService() } diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/service/SamplePermissionHandoverApplicationService.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/service/SamplePermissionHandoverApplicationService.kt new file mode 100644 index 000000000000..b0be465262be --- /dev/null +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/service/SamplePermissionHandoverApplicationService.kt @@ -0,0 +1,69 @@ +package com.tencent.devops.auth.provider.sample.service + +import com.tencent.devops.auth.pojo.dto.HandoverDetailDTO +import com.tencent.devops.auth.pojo.dto.HandoverOverviewCreateDTO +import com.tencent.devops.auth.pojo.enum.HandoverType +import com.tencent.devops.auth.pojo.request.HandoverDetailsQueryReq +import com.tencent.devops.auth.pojo.request.HandoverOverviewQueryReq +import com.tencent.devops.auth.pojo.request.HandoverOverviewUpdateReq +import com.tencent.devops.auth.pojo.vo.HandoverAuthorizationDetailVo +import com.tencent.devops.auth.pojo.vo.HandoverGroupDetailVo +import com.tencent.devops.auth.pojo.vo.HandoverOverviewVo +import com.tencent.devops.auth.pojo.vo.ResourceType2CountVo +import com.tencent.devops.auth.service.iam.PermissionHandoverApplicationService +import com.tencent.devops.common.api.model.SQLPage + +class SamplePermissionHandoverApplicationService : PermissionHandoverApplicationService { + override fun createHandoverApplication( + overview: HandoverOverviewCreateDTO, + details: List + ): String { + return "" + } + + override fun generateFlowNo(): String = "" + + override fun updateHandoverApplication(overview: HandoverOverviewUpdateReq) { + return + } + + override fun getHandoverOverview(flowNo: String): HandoverOverviewVo { + TODO("Not yet implemented") + } + + override fun listHandoverOverviews(queryRequest: HandoverOverviewQueryReq): SQLPage { + return SQLPage(0, emptyList()) + } + + override fun listAuthorizationsOfHandoverApplication( + queryReq: HandoverDetailsQueryReq + ): SQLPage { + return SQLPage(0, emptyList()) + } + + override fun listGroupsOfHandoverApplication(queryReq: HandoverDetailsQueryReq): SQLPage { + return SQLPage(0, emptyList()) + } + + override fun getResourceType2CountOfHandoverApplication(flowNo: String): List { + return emptyList() + } + + override fun listHandoverDetails( + projectCode: String, + flowNo: String, + resourceType: String?, + handoverType: HandoverType? + ): List { + return emptyList() + } + + override fun listMemberHandoverDetails( + projectCode: String, + memberId: String, + handoverType: HandoverType, + resourceType: String? + ): List { + return emptyList() + } +} diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/service/SamplePermissionManageFacadeService.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/service/SamplePermissionManageFacadeService.kt new file mode 100644 index 000000000000..ddbd60004fdd --- /dev/null +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/service/SamplePermissionManageFacadeService.kt @@ -0,0 +1,173 @@ +package com.tencent.devops.auth.provider.sample.service + +import com.tencent.devops.auth.pojo.AuthResourceGroupMember +import com.tencent.devops.auth.pojo.ResourceMemberInfo +import com.tencent.devops.auth.pojo.dto.IamGroupIdsQueryConditionDTO +import com.tencent.devops.auth.pojo.dto.InvalidAuthorizationsDTO +import com.tencent.devops.auth.pojo.enum.BatchOperateType +import com.tencent.devops.auth.pojo.enum.MemberType +import com.tencent.devops.auth.pojo.enum.OperateChannel +import com.tencent.devops.auth.pojo.request.GroupMemberCommonConditionReq +import com.tencent.devops.auth.pojo.request.GroupMemberHandoverConditionReq +import com.tencent.devops.auth.pojo.request.GroupMemberRemoveConditionReq +import com.tencent.devops.auth.pojo.request.GroupMemberRenewalConditionReq +import com.tencent.devops.auth.pojo.request.GroupMemberSingleRenewalReq +import com.tencent.devops.auth.pojo.request.HandoverDetailsQueryReq +import com.tencent.devops.auth.pojo.request.HandoverOverviewBatchUpdateReq +import com.tencent.devops.auth.pojo.request.HandoverOverviewUpdateReq +import com.tencent.devops.auth.pojo.request.ProjectMembersQueryConditionReq +import com.tencent.devops.auth.pojo.request.RemoveMemberFromProjectReq +import com.tencent.devops.auth.pojo.request.ResourceType2CountOfHandoverQuery +import com.tencent.devops.auth.pojo.vo.BatchOperateGroupMemberCheckVo +import com.tencent.devops.auth.pojo.vo.GroupDetailsInfoVo +import com.tencent.devops.auth.pojo.vo.HandoverAuthorizationDetailVo +import com.tencent.devops.auth.pojo.vo.HandoverGroupDetailVo +import com.tencent.devops.auth.pojo.vo.ResourceType2CountVo +import com.tencent.devops.auth.service.iam.PermissionManageFacadeService +import com.tencent.devops.common.api.model.SQLPage + +class SamplePermissionManageFacadeService : PermissionManageFacadeService { + override fun getMemberGroupsDetails( + projectId: String, + memberId: String, + resourceType: String?, + iamGroupIds: List?, + groupName: String?, + minExpiredAt: Long?, + maxExpiredAt: Long?, + relatedResourceType: String?, + relatedResourceCode: String?, + action: String?, + operateChannel: OperateChannel?, + start: Int?, + limit: Int? + ): SQLPage = SQLPage(0, emptyList()) + + override fun getMemberGroupsCount( + projectCode: String, + memberId: String, + groupName: String?, + minExpiredAt: Long?, + maxExpiredAt: Long?, + relatedResourceType: String?, + relatedResourceCode: String?, + action: String?, + operateChannel: OperateChannel? + ): List = emptyList() + + override fun listIamGroupIdsByConditions( + condition: IamGroupIdsQueryConditionDTO + ): List = emptyList() + + override fun listMemberGroupIdsInProject( + projectCode: String, + memberId: String + ): List = emptyList() + + override fun listResourceGroupMembers( + projectCode: String, + memberId: String, + resourceType: String?, + iamGroupIds: List?, + minExpiredAt: Long?, + maxExpiredAt: Long?, + operateChannel: OperateChannel?, + filterMemberType: MemberType?, + excludeIamGroupIds: List?, + onlyExcludeUserDirectlyJoined: Boolean?, + start: Int?, + limit: Int? + ): Pair> = Pair(0, emptyList()) + + override fun listProjectMembersByComplexConditions( + conditionReq: ProjectMembersQueryConditionReq + ): SQLPage = SQLPage(0, emptyList()) + + override fun listInvalidAuthorizationsAfterOperatedGroups( + projectCode: String, + iamGroupIds: List, + memberId: String + ): InvalidAuthorizationsDTO = InvalidAuthorizationsDTO(emptyList(), emptyList()) + + override fun renewalGroupMember( + userId: String, + projectCode: String, + renewalConditionReq: GroupMemberSingleRenewalReq + ): Boolean = true + + override fun batchRenewalGroupMembersFromManager( + userId: String, + projectCode: String, + renewalConditionReq: GroupMemberRenewalConditionReq + ): Boolean = true + + override fun batchHandoverGroupMembersFromManager( + userId: String, + projectCode: String, + handoverMemberDTO: GroupMemberHandoverConditionReq + ): Boolean = true + + override fun batchHandoverApplicationFromPersonal( + userId: String, + projectCode: String, + handoverMemberDTO: GroupMemberHandoverConditionReq + ): String = "" + + override fun batchDeleteResourceGroupMembersFromManager( + userId: String, + projectCode: String, + removeMemberDTO: GroupMemberRemoveConditionReq + ): Boolean = true + + override fun batchDeleteResourceGroupMembersFromPersonal( + userId: String, + projectCode: String, + removeMemberDTO: GroupMemberRemoveConditionReq + ): String? = null + + override fun deleteResourceGroupMembers( + userId: String, + projectCode: String, + groupId: Int, + targetMember: ResourceMemberInfo + ): Boolean = true + + override fun batchOperateGroupMembersCheck( + userId: String, + projectCode: String, + batchOperateType: BatchOperateType, + conditionReq: GroupMemberCommonConditionReq + ): BatchOperateGroupMemberCheckVo = BatchOperateGroupMemberCheckVo(totalCount = 0) + + override fun removeMemberFromProject( + userId: String, + projectCode: String, + removeMemberFromProjectReq: RemoveMemberFromProjectReq + ): List = emptyList() + + override fun removeMemberFromProjectCheck( + userId: String, + projectCode: String, + removeMemberFromProjectReq: RemoveMemberFromProjectReq + ): Boolean = true + + override fun handleHanoverApplication(request: HandoverOverviewUpdateReq): Boolean = true + + override fun batchHandleHanoverApplications(request: HandoverOverviewBatchUpdateReq): Boolean = true + + override fun getResourceType2CountOfHandover(queryReq: ResourceType2CountOfHandoverQuery): List { + return emptyList() + } + + override fun listAuthorizationsOfHandover(queryReq: HandoverDetailsQueryReq): SQLPage { + return SQLPage(0, emptyList()) + } + + override fun listGroupsOfHandover(queryReq: HandoverDetailsQueryReq): SQLPage { + return SQLPage(0, emptyList()) + } + + override fun isProjectMember(projectCode: String, userId: String): Boolean { + return true + } +} diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/service/SamplePermissionResourceGroupAndMemberFacadeService.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/service/SamplePermissionResourceGroupAndMemberFacadeService.kt deleted file mode 100644 index 289455d58561..000000000000 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/service/SamplePermissionResourceGroupAndMemberFacadeService.kt +++ /dev/null @@ -1,45 +0,0 @@ -package com.tencent.devops.auth.provider.sample.service - -import com.tencent.devops.auth.pojo.ResourceMemberInfo -import com.tencent.devops.auth.pojo.dto.IamGroupIdsQueryConditionDTO -import com.tencent.devops.auth.pojo.request.ProjectMembersQueryConditionReq -import com.tencent.devops.auth.pojo.vo.GroupDetailsInfoVo -import com.tencent.devops.auth.pojo.vo.MemberGroupCountWithPermissionsVo -import com.tencent.devops.auth.service.iam.PermissionResourceGroupAndMemberFacadeService -import com.tencent.devops.common.api.model.SQLPage - -class SamplePermissionResourceGroupAndMemberFacadeService : PermissionResourceGroupAndMemberFacadeService { - override fun getMemberGroupsDetails( - projectId: String, - memberId: String, - resourceType: String?, - iamGroupIds: List?, - groupName: String?, - minExpiredAt: Long?, - maxExpiredAt: Long?, - relatedResourceType: String?, - relatedResourceCode: String?, - action: String?, - start: Int?, - limit: Int? - ): SQLPage = SQLPage(0, emptyList()) - - override fun getMemberGroupsCount( - projectCode: String, - memberId: String, - groupName: String?, - minExpiredAt: Long?, - maxExpiredAt: Long?, - relatedResourceType: String?, - relatedResourceCode: String?, - action: String? - ): List = emptyList() - - override fun listIamGroupIdsByConditions( - condition: IamGroupIdsQueryConditionDTO - ): List = emptyList() - - override fun listProjectMembersByComplexConditions( - conditionReq: ProjectMembersQueryConditionReq - ): SQLPage = SQLPage(0, emptyList()) -} diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/service/SamplePermissionResourceGroupPermissionService.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/service/SamplePermissionResourceGroupPermissionService.kt index 1688b7133dca..997ad7c9ab6b 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/service/SamplePermissionResourceGroupPermissionService.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/service/SamplePermissionResourceGroupPermissionService.kt @@ -86,6 +86,12 @@ class SamplePermissionResourceGroupPermissionService : PermissionResourceGroupPe action: String ): Boolean = true + override fun isGroupsHasProjectLevelPermission( + projectCode: String, + filterIamGroupIds: List, + action: String + ): Boolean = true + override fun listGroupResourcesWithPermission( projectCode: String, filterIamGroupIds: List, diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/service/SamplePermissionResourceMemberService.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/service/SamplePermissionResourceMemberService.kt index 06ef42f1b197..79d4ada40a7a 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/service/SamplePermissionResourceMemberService.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/service/SamplePermissionResourceMemberService.kt @@ -1,16 +1,8 @@ package com.tencent.devops.auth.provider.sample.service import com.tencent.bk.sdk.iam.dto.manager.ManagerMember -import com.tencent.devops.auth.pojo.AuthResourceGroupMember import com.tencent.devops.auth.pojo.ResourceMemberInfo import com.tencent.devops.auth.pojo.dto.GroupMemberRenewalDTO -import com.tencent.devops.auth.pojo.enum.BatchOperateType -import com.tencent.devops.auth.pojo.request.GroupMemberCommonConditionReq -import com.tencent.devops.auth.pojo.request.GroupMemberHandoverConditionReq -import com.tencent.devops.auth.pojo.request.GroupMemberRenewalConditionReq -import com.tencent.devops.auth.pojo.request.GroupMemberSingleRenewalReq -import com.tencent.devops.auth.pojo.request.RemoveMemberFromProjectReq -import com.tencent.devops.auth.pojo.vo.BatchOperateGroupMemberCheckVo import com.tencent.devops.auth.pojo.vo.ResourceMemberCountVO import com.tencent.devops.auth.service.iam.PermissionResourceMemberService import com.tencent.devops.common.api.model.SQLPage @@ -43,13 +35,6 @@ class SamplePermissionResourceMemberService : PermissionResourceMemberService { departments: List? ) = true - override fun isProjectMember( - projectCode: String, - userId: String - ): Boolean { - return true - } - override fun batchDeleteResourceGroupMembers( projectCode: String, iamGroupId: Int, @@ -77,64 +62,18 @@ class SamplePermissionResourceMemberService : PermissionResourceMemberService { memberRenewalDTO: GroupMemberRenewalDTO ): Boolean = true - override fun renewalGroupMember( - userId: String, - projectCode: String, - renewalConditionReq: GroupMemberSingleRenewalReq - ): Boolean = true - override fun renewalIamGroupMembers( groupId: Int, members: List, expiredAt: Long ): Boolean = true - override fun batchRenewalGroupMembers( - userId: String, - projectCode: String, - renewalConditionReq: GroupMemberRenewalConditionReq - ): Boolean = true - - override fun batchDeleteResourceGroupMembers( - userId: String, - projectCode: String, - removeMemberDTO: GroupMemberCommonConditionReq - ): Boolean = true - override fun deleteIamGroupMembers( groupId: Int, type: String, memberIds: List ): Boolean = true - override fun batchHandoverGroupMembers( - userId: String, - projectCode: String, - handoverMemberDTO: GroupMemberHandoverConditionReq - ): Boolean = true - - override fun batchOperateGroupMembersCheck( - userId: String, - projectCode: String, - batchOperateType: BatchOperateType, - conditionReq: GroupMemberCommonConditionReq - ): BatchOperateGroupMemberCheckVo = BatchOperateGroupMemberCheckVo( - totalCount = 0, - inoperableCount = 0 - ) - - override fun removeMemberFromProject( - userId: String, - projectCode: String, - removeMemberFromProjectReq: RemoveMemberFromProjectReq - ): List = emptyList() - - override fun removeMemberFromProjectCheck( - userId: String, - projectCode: String, - removeMemberFromProjectReq: RemoveMemberFromProjectReq - ): Boolean = true - override fun addGroupMember( projectCode: String, memberId: String, @@ -143,9 +82,11 @@ class SamplePermissionResourceMemberService : PermissionResourceMemberService { iamGroupId: Int ): Boolean = true - override fun addIamGroupMember(groupId: Int, members: List, expiredAt: Long): Boolean { - TODO("Not yet implemented") - } + override fun addIamGroupMember( + groupId: Int, + members: List, + expiredAt: Long + ): Boolean = true override fun getProjectMemberCount(projectCode: String): ResourceMemberCountVO = ResourceMemberCountVO( @@ -168,20 +109,4 @@ class SamplePermissionResourceMemberService : PermissionResourceMemberService { override fun addDepartedFlagToMembers( records: List ): List = emptyList() - - override fun listResourceGroupMembers( - projectCode: String, - memberId: String, - resourceType: String?, - iamGroupIds: List?, - minExpiredAt: Long?, - maxExpiredAt: Long?, - start: Int?, - limit: Int? - ): Pair> = Pair(0, emptyList()) - - override fun listMemberGroupIdsInProject( - projectCode: String, - memberId: String - ): List = emptyList() } diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/service/SamplePermissionResourceValidateService.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/service/SamplePermissionResourceValidateService.kt index b2ba564a4cfa..d8b5f1f1e076 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/service/SamplePermissionResourceValidateService.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/service/SamplePermissionResourceValidateService.kt @@ -29,6 +29,7 @@ package com.tencent.devops.auth.provider.sample.service import com.tencent.devops.auth.pojo.dto.PermissionBatchValidateDTO +import com.tencent.devops.auth.pojo.enum.OperateChannel import com.tencent.devops.auth.service.iam.PermissionResourceValidateService class SamplePermissionResourceValidateService : PermissionResourceValidateService { @@ -46,4 +47,13 @@ class SamplePermissionResourceValidateService : PermissionResourceValidateServic resourceType: String, resourceCode: String ): Boolean = true + + override fun validateUserProjectPermissionByChannel( + userId: String, + projectCode: String, + operateChannel: OperateChannel, + targetMemberId: String + ) { + return + } } diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/service/ServiceProjectAuthResourceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/service/ServiceProjectAuthResourceImpl.kt index 27428c38edb4..8aca9e3d1384 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/service/ServiceProjectAuthResourceImpl.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/service/ServiceProjectAuthResourceImpl.kt @@ -28,7 +28,9 @@ package com.tencent.devops.auth.resources.service import com.tencent.devops.auth.api.service.ServiceProjectAuthResource +import com.tencent.devops.auth.pojo.vo.AuthProjectVO import com.tencent.devops.auth.pojo.vo.ProjectPermissionInfoVO +import com.tencent.devops.auth.service.PermissionAuthorizationService import com.tencent.devops.auth.service.iam.PermissionProjectService import com.tencent.devops.common.api.pojo.Result import com.tencent.devops.common.auth.api.pojo.BKAuthProjectRolesResources @@ -41,7 +43,8 @@ import org.springframework.beans.factory.annotation.Autowired @RestResource class ServiceProjectAuthResourceImpl @Autowired constructor( - val permissionProjectService: PermissionProjectService + val permissionProjectService: PermissionProjectService, + val permissionAuthorizationService: PermissionAuthorizationService ) : ServiceProjectAuthResource { @BkApiPermission([BkApiHandleType.API_OPEN_TOKEN_CHECK]) override fun getProjectUsers( @@ -213,4 +216,12 @@ class ServiceProjectAuthResourceImpl @Autowired constructor( ) ) } + + override fun listUserProjectsWithAuthorization(userId: String): Result> { + return Result( + permissionAuthorizationService.listUserProjectsWithAuthorization( + userId = userId + ) + ) + } } diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/service/ServiceResourceGroupResourceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/service/ServiceResourceGroupResourceImpl.kt index cea4a8350a87..52fde466e357 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/service/ServiceResourceGroupResourceImpl.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/service/ServiceResourceGroupResourceImpl.kt @@ -5,7 +5,7 @@ import com.tencent.devops.auth.pojo.dto.GroupAddDTO import com.tencent.devops.auth.pojo.request.CustomGroupCreateReq import com.tencent.devops.auth.pojo.vo.GroupDetailsInfoVo import com.tencent.devops.auth.pojo.vo.GroupPermissionDetailVo -import com.tencent.devops.auth.service.iam.PermissionResourceGroupAndMemberFacadeService +import com.tencent.devops.auth.service.iam.PermissionManageFacadeService import com.tencent.devops.auth.service.iam.PermissionResourceGroupPermissionService import com.tencent.devops.auth.service.iam.PermissionResourceGroupService import com.tencent.devops.common.api.model.SQLPage @@ -17,7 +17,7 @@ import com.tencent.devops.common.web.RestResource class ServiceResourceGroupResourceImpl( val permissionResourceGroupService: PermissionResourceGroupService, val resourceGroupPermissionService: PermissionResourceGroupPermissionService, - val resourceGroupAndMemberFacadeService: PermissionResourceGroupAndMemberFacadeService + val permissionManageFacadeService: PermissionManageFacadeService ) : ServiceResourceGroupResource { override fun getGroupPermissionDetail( projectCode: String, @@ -45,7 +45,7 @@ class ServiceResourceGroupResourceImpl( limit: Int? ): Result> { return Result( - resourceGroupAndMemberFacadeService.getMemberGroupsDetails( + permissionManageFacadeService.getMemberGroupsDetails( projectId = projectCode, resourceType = resourceType, memberId = memberId, diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/service/ServiceResourceMemberResourceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/service/ServiceResourceMemberResourceImpl.kt index 02c0636974ff..aadd867c2b1d 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/service/ServiceResourceMemberResourceImpl.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/service/ServiceResourceMemberResourceImpl.kt @@ -2,6 +2,7 @@ package com.tencent.devops.auth.resources.service import com.tencent.devops.auth.api.service.ServiceResourceMemberResource import com.tencent.devops.auth.pojo.request.GroupMemberSingleRenewalReq +import com.tencent.devops.auth.service.iam.PermissionManageFacadeService import com.tencent.devops.auth.service.iam.PermissionResourceMemberService import com.tencent.devops.common.api.pojo.Result import com.tencent.devops.common.auth.api.pojo.BkAuthGroup @@ -14,8 +15,9 @@ import com.tencent.devops.project.pojo.ProjectDeleteUserInfo import java.util.concurrent.TimeUnit @RestResource -class ServiceResourceMemberResourceImpl constructor( - private val permissionResourceMemberService: PermissionResourceMemberService +class ServiceResourceMemberResourceImpl( + private val permissionResourceMemberService: PermissionResourceMemberService, + private val permissionManageFacadeService: PermissionManageFacadeService ) : ServiceResourceMemberResource { @BkApiPermission([BkApiHandleType.API_OPEN_TOKEN_CHECK]) override fun getResourceGroupMembers( @@ -107,7 +109,7 @@ class ServiceResourceMemberResourceImpl constructor( renewalConditionReq: GroupMemberSingleRenewalReq ): Result { return Result( - permissionResourceMemberService.renewalGroupMember( + permissionManageFacadeService.renewalGroupMember( userId = userId, projectCode = projectCode, renewalConditionReq = renewalConditionReq diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/user/UserAuthAuthorizationResourceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/user/UserAuthAuthorizationResourceImpl.kt index e4c5ed68aa46..68e72114d5b7 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/user/UserAuthAuthorizationResourceImpl.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/user/UserAuthAuthorizationResourceImpl.kt @@ -1,8 +1,11 @@ package com.tencent.devops.auth.resources.user import com.tencent.devops.auth.api.user.UserAuthAuthorizationResource +import com.tencent.devops.auth.pojo.enum.OperateChannel +import com.tencent.devops.auth.pojo.vo.AuthProjectVO import com.tencent.devops.auth.pojo.vo.ResourceTypeInfoVo import com.tencent.devops.auth.service.PermissionAuthorizationService +import com.tencent.devops.auth.service.iam.PermissionResourceValidateService import com.tencent.devops.common.api.model.SQLPage import com.tencent.devops.common.api.pojo.Result import com.tencent.devops.common.auth.api.BkManagerCheck @@ -16,17 +19,25 @@ import com.tencent.devops.common.web.RestResource @RestResource class UserAuthAuthorizationResourceImpl( - val permissionAuthorizationService: PermissionAuthorizationService + val permissionAuthorizationService: PermissionAuthorizationService, + val permissionResourceValidateService: PermissionResourceValidateService ) : UserAuthAuthorizationResource { - @BkManagerCheck override fun listResourceAuthorization( userId: String, projectId: String, + operateChannel: OperateChannel?, condition: ResourceAuthorizationConditionRequest ): Result> { + permissionResourceValidateService.validateUserProjectPermissionByChannel( + userId = userId, + projectCode = projectId, + operateChannel = operateChannel ?: OperateChannel.MANAGER, + targetMemberId = if (operateChannel == OperateChannel.PERSONAL) condition.handoverFrom!! else userId + ) return Result( permissionAuthorizationService.listResourceAuthorizations( - condition = condition + condition = condition, + operateChannel = operateChannel ) ) } @@ -93,4 +104,8 @@ class UserAuthAuthorizationResourceImpl( ) ) } + + override fun listUserProjectsWithAuthorization(userId: String): Result> { + return Result(permissionAuthorizationService.listUserProjectsWithAuthorization(userId)) + } } diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/user/UserAuthHandoverResourceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/user/UserAuthHandoverResourceImpl.kt new file mode 100644 index 000000000000..10c8f060250d --- /dev/null +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/user/UserAuthHandoverResourceImpl.kt @@ -0,0 +1,101 @@ +package com.tencent.devops.auth.resources.user + +import com.tencent.devops.auth.api.user.UserAuthHandoverResource +import com.tencent.devops.auth.pojo.enum.OperateChannel +import com.tencent.devops.auth.pojo.request.HandoverDetailsQueryReq +import com.tencent.devops.auth.pojo.request.HandoverOverviewBatchUpdateReq +import com.tencent.devops.auth.pojo.request.HandoverOverviewQueryReq +import com.tencent.devops.auth.pojo.request.HandoverOverviewUpdateReq +import com.tencent.devops.auth.pojo.request.ResourceType2CountOfHandoverQuery +import com.tencent.devops.auth.pojo.vo.HandoverAuthorizationDetailVo +import com.tencent.devops.auth.pojo.vo.HandoverGroupDetailVo +import com.tencent.devops.auth.pojo.vo.HandoverOverviewVo +import com.tencent.devops.auth.pojo.vo.ResourceType2CountVo +import com.tencent.devops.auth.service.PermissionAuthorizationService +import com.tencent.devops.auth.service.iam.PermissionHandoverApplicationService +import com.tencent.devops.auth.service.iam.PermissionManageFacadeService +import com.tencent.devops.auth.service.iam.PermissionResourceValidateService +import com.tencent.devops.common.api.exception.PermissionForbiddenException +import com.tencent.devops.common.api.model.SQLPage +import com.tencent.devops.common.api.pojo.Result +import com.tencent.devops.common.auth.api.pojo.ResourceAuthorizationHandoverConditionRequest +import com.tencent.devops.common.web.RestResource + +@RestResource +class UserAuthHandoverResourceImpl( + private val permissionAuthorizationService: PermissionAuthorizationService, + private val permissionManageFacadeService: PermissionManageFacadeService, + private val permissionHandoverApplicationService: PermissionHandoverApplicationService, + private val permissionResourceValidateService: PermissionResourceValidateService +) : UserAuthHandoverResource { + override fun handoverAuthorizationsApplication( + userId: String, + projectId: String, + condition: ResourceAuthorizationHandoverConditionRequest + ): Result { + permissionResourceValidateService.validateUserProjectPermissionByChannel( + userId = userId, + projectCode = projectId, + operateChannel = OperateChannel.PERSONAL, + targetMemberId = condition.handoverFrom!! + ) + return Result( + permissionAuthorizationService.handoverAuthorizationsApplication( + operator = userId, + projectCode = projectId, + condition = condition + ) + ) + } + + override fun listHandoverOverviews( + userId: String, + queryRequest: HandoverOverviewQueryReq + ): Result> { + if (userId != queryRequest.memberId) { + throw PermissionForbiddenException( + message = "You have not permission to view other people's handover details!" + ) + } + + return Result(permissionHandoverApplicationService.listHandoverOverviews(queryRequest = queryRequest)) + } + + override fun getResourceType2CountOfHandover( + userId: String, + queryReq: ResourceType2CountOfHandoverQuery + ): Result> { + return Result(permissionManageFacadeService.getResourceType2CountOfHandover(queryReq = queryReq)) + } + + override fun listAuthorizationsOfHandover( + userId: String, + queryReq: HandoverDetailsQueryReq + ): Result> { + return Result(permissionManageFacadeService.listAuthorizationsOfHandover(queryReq = queryReq)) + } + + override fun listGroupsOfHandover( + userId: String, + queryReq: HandoverDetailsQueryReq + ): Result> { + return Result(permissionManageFacadeService.listGroupsOfHandover(queryReq = queryReq)) + } + + override fun handleHanoverApplication(userId: String, request: HandoverOverviewUpdateReq): Result { + permissionResourceValidateService.validateUserProjectPermissionByChannel( + userId = userId, + projectCode = request.projectCode, + operateChannel = OperateChannel.PERSONAL, + targetMemberId = request.operator + ) + return Result(permissionManageFacadeService.handleHanoverApplication(request = request)) + } + + override fun batchHandleHanoverApplications( + userId: String, + request: HandoverOverviewBatchUpdateReq + ): Result { + return Result(permissionManageFacadeService.batchHandleHanoverApplications(request = request)) + } +} diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/user/UserAuthResourceGroupResourceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/user/UserAuthResourceGroupResourceImpl.kt index 4c99458fb86f..4a07c5e9e6ff 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/user/UserAuthResourceGroupResourceImpl.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/user/UserAuthResourceGroupResourceImpl.kt @@ -28,18 +28,22 @@ package com.tencent.devops.auth.resources.user -import com.tencent.bk.sdk.iam.constants.ManagerScopesEnum import com.tencent.devops.auth.api.user.UserAuthResourceGroupResource import com.tencent.devops.auth.pojo.ResourceMemberInfo import com.tencent.devops.auth.pojo.dto.GroupMemberRenewalDTO +import com.tencent.devops.auth.pojo.dto.MemberGroupJoinedDTO import com.tencent.devops.auth.pojo.dto.RenameGroupDTO -import com.tencent.devops.auth.pojo.request.GroupMemberCommonConditionReq +import com.tencent.devops.auth.pojo.enum.JoinedType +import com.tencent.devops.auth.pojo.enum.MemberType +import com.tencent.devops.auth.pojo.enum.OperateChannel +import com.tencent.devops.auth.pojo.request.GroupMemberRemoveConditionReq import com.tencent.devops.auth.pojo.vo.GroupDetailsInfoVo import com.tencent.devops.auth.pojo.vo.IamGroupPoliciesVo -import com.tencent.devops.auth.service.iam.PermissionResourceGroupAndMemberFacadeService +import com.tencent.devops.auth.service.iam.PermissionManageFacadeService import com.tencent.devops.auth.service.iam.PermissionResourceGroupPermissionService import com.tencent.devops.auth.service.iam.PermissionResourceGroupService import com.tencent.devops.auth.service.iam.PermissionResourceMemberService +import com.tencent.devops.auth.service.iam.PermissionResourceValidateService import com.tencent.devops.common.api.model.SQLPage import com.tencent.devops.common.api.pojo.Result import com.tencent.devops.common.auth.api.BkManagerCheck @@ -50,8 +54,9 @@ import org.springframework.beans.factory.annotation.Autowired class UserAuthResourceGroupResourceImpl @Autowired constructor( private val permissionResourceGroupService: PermissionResourceGroupService, private val permissionResourceMemberService: PermissionResourceMemberService, - private val permissionResourceGroupAndMemberFacadeService: PermissionResourceGroupAndMemberFacadeService, - private val permissionResourceGroupPermissionService: PermissionResourceGroupPermissionService + private val permissionManageFacadeService: PermissionManageFacadeService, + private val permissionResourceGroupPermissionService: PermissionResourceGroupPermissionService, + private val permissionResourceValidateService: PermissionResourceValidateService ) : UserAuthResourceGroupResource { override fun getGroupPolicies( userId: String, @@ -69,7 +74,6 @@ class UserAuthResourceGroupResourceImpl @Autowired constructor( ) } - @BkManagerCheck override fun getMemberGroupsDetails( userId: String, projectId: String, @@ -81,11 +85,19 @@ class UserAuthResourceGroupResourceImpl @Autowired constructor( relatedResourceType: String?, relatedResourceCode: String?, action: String?, + operateChannel: OperateChannel?, start: Int, limit: Int ): Result> { + permissionResourceValidateService.validateUserProjectPermissionByChannel( + userId = userId, + projectCode = projectId, + operateChannel = operateChannel ?: OperateChannel.MANAGER, + targetMemberId = memberId + ) + return Result( - permissionResourceGroupAndMemberFacadeService.getMemberGroupsDetails( + permissionManageFacadeService.getMemberGroupsDetails( projectId = projectId, resourceType = resourceType, memberId = memberId, @@ -94,6 +106,7 @@ class UserAuthResourceGroupResourceImpl @Autowired constructor( maxExpiredAt = maxExpiredAt, relatedResourceType = relatedResourceType, relatedResourceCode = relatedResourceCode, + operateChannel = operateChannel, action = action, start = start, limit = limit @@ -101,6 +114,30 @@ class UserAuthResourceGroupResourceImpl @Autowired constructor( ) } + override fun getMemberGroupDetails( + userId: String, + projectId: String, + resourceType: String, + groupId: Int, + memberId: String + ): Result { + permissionResourceValidateService.validateUserProjectPermissionByChannel( + userId = userId, + projectCode = projectId, + operateChannel = OperateChannel.PERSONAL, + targetMemberId = memberId + ) + return Result( + permissionManageFacadeService.getMemberGroupsDetails( + projectId = projectId, + memberId = memberId, + resourceType = resourceType, + iamGroupIds = listOf(groupId), + operateChannel = OperateChannel.PERSONAL + ).records.first { it.groupId == groupId && it.joinedType == JoinedType.DIRECT } + ) + } + override fun renewal( userId: String, projectId: String, @@ -108,6 +145,12 @@ class UserAuthResourceGroupResourceImpl @Autowired constructor( groupId: Int, memberRenewalDTO: GroupMemberRenewalDTO ): Result { + permissionResourceValidateService.validateUserProjectPermissionByChannel( + userId = userId, + projectCode = projectId, + operateChannel = OperateChannel.PERSONAL, + targetMemberId = userId + ) return Result( permissionResourceMemberService.renewalGroupMember( userId = userId, @@ -126,14 +169,19 @@ class UserAuthResourceGroupResourceImpl @Autowired constructor( groupId: Int ): Result { return Result( - permissionResourceMemberService.batchDeleteResourceGroupMembers( + permissionManageFacadeService.batchDeleteResourceGroupMembersFromManager( userId = userId, projectCode = projectId, - removeMemberDTO = GroupMemberCommonConditionReq( - groupIds = listOf(groupId), + removeMemberDTO = GroupMemberRemoveConditionReq( + groupIds = listOf( + MemberGroupJoinedDTO( + id = groupId, + memberType = MemberType.USER + ) + ), targetMember = ResourceMemberInfo( id = userId, - type = ManagerScopesEnum.getType(ManagerScopesEnum.USER) + type = MemberType.USER.type ) ) ) diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/user/UserAuthResourceMemberResourceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/user/UserAuthResourceMemberResourceImpl.kt index bd76fea6919b..46f62da6ae14 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/user/UserAuthResourceMemberResourceImpl.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/user/UserAuthResourceMemberResourceImpl.kt @@ -3,17 +3,20 @@ package com.tencent.devops.auth.resources.user import com.tencent.devops.auth.api.user.UserAuthResourceMemberResource import com.tencent.devops.auth.pojo.ResourceMemberInfo import com.tencent.devops.auth.pojo.enum.BatchOperateType +import com.tencent.devops.auth.pojo.enum.OperateChannel import com.tencent.devops.auth.pojo.request.GroupMemberCommonConditionReq import com.tencent.devops.auth.pojo.request.GroupMemberHandoverConditionReq +import com.tencent.devops.auth.pojo.request.GroupMemberRemoveConditionReq import com.tencent.devops.auth.pojo.request.GroupMemberRenewalConditionReq import com.tencent.devops.auth.pojo.request.GroupMemberSingleRenewalReq import com.tencent.devops.auth.pojo.request.ProjectMembersQueryConditionReq import com.tencent.devops.auth.pojo.request.RemoveMemberFromProjectReq import com.tencent.devops.auth.pojo.vo.BatchOperateGroupMemberCheckVo import com.tencent.devops.auth.pojo.vo.GroupDetailsInfoVo -import com.tencent.devops.auth.pojo.vo.MemberGroupCountWithPermissionsVo -import com.tencent.devops.auth.service.iam.PermissionResourceGroupAndMemberFacadeService +import com.tencent.devops.auth.pojo.vo.ResourceType2CountVo +import com.tencent.devops.auth.service.iam.PermissionManageFacadeService import com.tencent.devops.auth.service.iam.PermissionResourceMemberService +import com.tencent.devops.auth.service.iam.PermissionResourceValidateService import com.tencent.devops.auth.service.iam.PermissionService import com.tencent.devops.common.api.model.SQLPage import com.tencent.devops.common.api.pojo.Result @@ -27,7 +30,8 @@ import com.tencent.devops.common.web.RestResource class UserAuthResourceMemberResourceImpl( private val permissionResourceMemberService: PermissionResourceMemberService, private val permissionService: PermissionService, - private val permissionResourceGroupAndMemberFacadeService: PermissionResourceGroupAndMemberFacadeService + private val permissionManageFacadeService: PermissionManageFacadeService, + private val permissionResourceValidateService: PermissionResourceValidateService ) : UserAuthResourceMemberResource { override fun listProjectMembers( userId: String, @@ -69,7 +73,7 @@ class UserAuthResourceMemberResourceImpl( projectMembersQueryConditionReq: ProjectMembersQueryConditionReq ): Result> { return Result( - permissionResourceGroupAndMemberFacadeService.listProjectMembersByComplexConditions( + permissionManageFacadeService.listProjectMembersByComplexConditions( conditionReq = projectMembersQueryConditionReq ) ) @@ -81,13 +85,13 @@ class UserAuthResourceMemberResourceImpl( projectId: String, renewalConditionReq: GroupMemberSingleRenewalReq ): Result { - permissionResourceMemberService.renewalGroupMember( + permissionManageFacadeService.renewalGroupMember( userId = userId, projectCode = projectId, renewalConditionReq = renewalConditionReq ) return Result( - permissionResourceGroupAndMemberFacadeService.getMemberGroupsDetails( + permissionManageFacadeService.getMemberGroupsDetails( projectId = projectId, memberId = renewalConditionReq.targetMember.id, iamGroupIds = listOf(renewalConditionReq.groupId) @@ -96,13 +100,13 @@ class UserAuthResourceMemberResourceImpl( } @BkManagerCheck - override fun batchRenewalGroupMembers( + override fun batchRenewalGroupMembersFromManager( userId: String, projectId: String, renewalConditionReq: GroupMemberRenewalConditionReq ): Result { return Result( - permissionResourceMemberService.batchRenewalGroupMembers( + permissionManageFacadeService.batchRenewalGroupMembersFromManager( userId = userId, projectCode = projectId, renewalConditionReq = renewalConditionReq @@ -111,13 +115,13 @@ class UserAuthResourceMemberResourceImpl( } @BkManagerCheck - override fun batchRemoveGroupMembers( + override fun batchRemoveGroupMembersFromManager( userId: String, projectId: String, - removeMemberDTO: GroupMemberCommonConditionReq + removeMemberDTO: GroupMemberRemoveConditionReq ): Result { return Result( - permissionResourceMemberService.batchDeleteResourceGroupMembers( + permissionManageFacadeService.batchDeleteResourceGroupMembersFromManager( userId = userId, projectCode = projectId, removeMemberDTO = removeMemberDTO @@ -125,14 +129,77 @@ class UserAuthResourceMemberResourceImpl( ) } + override fun batchRemoveGroupMembersFromPersonal( + userId: String, + projectId: String, + removeMemberDTO: GroupMemberRemoveConditionReq + ): Result { + permissionResourceValidateService.validateUserProjectPermissionByChannel( + userId = userId, + projectCode = projectId, + operateChannel = OperateChannel.PERSONAL, + targetMemberId = removeMemberDTO.targetMember.id + ) + return Result( + permissionManageFacadeService.batchDeleteResourceGroupMembersFromPersonal( + userId = userId, + projectCode = projectId, + removeMemberDTO = removeMemberDTO + ) + ) + } + + override fun deleteResourceGroupMembers( + userId: String, + projectId: String, + groupId: Int, + operateChannel: OperateChannel, + targetMember: ResourceMemberInfo + ): Result { + permissionResourceValidateService.validateUserProjectPermissionByChannel( + userId = userId, + projectCode = projectId, + operateChannel = operateChannel, + targetMemberId = targetMember.id + ) + return Result( + permissionManageFacadeService.deleteResourceGroupMembers( + userId = userId, + projectCode = projectId, + groupId = groupId, + targetMember = targetMember + ) + ) + } + @BkManagerCheck - override fun batchHandoverGroupMembers( + override fun batchHandoverGroupMembersFromManager( userId: String, projectId: String, handoverMemberDTO: GroupMemberHandoverConditionReq ): Result { return Result( - permissionResourceMemberService.batchHandoverGroupMembers( + permissionManageFacadeService.batchHandoverGroupMembersFromManager( + userId = userId, + projectCode = projectId, + handoverMemberDTO = handoverMemberDTO + ) + ) + } + + override fun batchHandoverApplicationFromPersonal( + userId: String, + projectId: String, + handoverMemberDTO: GroupMemberHandoverConditionReq + ): Result { + permissionResourceValidateService.validateUserProjectPermissionByChannel( + userId = userId, + projectCode = projectId, + operateChannel = OperateChannel.PERSONAL, + targetMemberId = handoverMemberDTO.targetMember.id + ) + return Result( + permissionManageFacadeService.batchHandoverApplicationFromPersonal( userId = userId, projectCode = projectId, handoverMemberDTO = handoverMemberDTO @@ -140,15 +207,20 @@ class UserAuthResourceMemberResourceImpl( ) } - @BkManagerCheck override fun batchOperateGroupMembersCheck( userId: String, projectId: String, batchOperateType: BatchOperateType, conditionReq: GroupMemberCommonConditionReq ): Result { + permissionResourceValidateService.validateUserProjectPermissionByChannel( + userId = userId, + projectCode = projectId, + operateChannel = conditionReq.operateChannel, + targetMemberId = conditionReq.targetMember.id + ) return Result( - permissionResourceMemberService.batchOperateGroupMembersCheck( + permissionManageFacadeService.batchOperateGroupMembersCheck( userId = userId, projectCode = projectId, batchOperateType = batchOperateType, @@ -164,7 +236,7 @@ class UserAuthResourceMemberResourceImpl( removeMemberFromProjectReq: RemoveMemberFromProjectReq ): Result> { return Result( - permissionResourceMemberService.removeMemberFromProject( + permissionManageFacadeService.removeMemberFromProject( userId = userId, projectCode = projectId, removeMemberFromProjectReq = removeMemberFromProjectReq @@ -179,7 +251,7 @@ class UserAuthResourceMemberResourceImpl( removeMemberFromProjectReq: RemoveMemberFromProjectReq ): Result { return Result( - permissionResourceMemberService.removeMemberFromProjectCheck( + permissionManageFacadeService.removeMemberFromProjectCheck( userId = userId, projectCode = projectId, removeMemberFromProjectReq = removeMemberFromProjectReq @@ -187,7 +259,6 @@ class UserAuthResourceMemberResourceImpl( ) } - @BkManagerCheck override fun getMemberGroupCount( userId: String, projectId: String, @@ -197,10 +268,17 @@ class UserAuthResourceMemberResourceImpl( maxExpiredAt: Long?, relatedResourceType: String?, relatedResourceCode: String?, - action: String? - ): Result> { + action: String?, + operateChannel: OperateChannel? + ): Result> { + permissionResourceValidateService.validateUserProjectPermissionByChannel( + userId = userId, + projectCode = projectId, + operateChannel = operateChannel ?: OperateChannel.MANAGER, + targetMemberId = memberId + ) return Result( - permissionResourceGroupAndMemberFacadeService.getMemberGroupsCount( + permissionManageFacadeService.getMemberGroupsCount( projectCode = projectId, memberId = memberId, groupName = groupName, @@ -208,7 +286,8 @@ class UserAuthResourceMemberResourceImpl( maxExpiredAt = maxExpiredAt, relatedResourceType = relatedResourceType, relatedResourceCode = relatedResourceCode, - action = action + action = action, + operateChannel = operateChannel ) ) } diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/PermissionAuthorizationService.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/PermissionAuthorizationService.kt index 301dd66bea84..6e1693899808 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/PermissionAuthorizationService.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/PermissionAuthorizationService.kt @@ -27,6 +27,8 @@ package com.tencent.devops.auth.service +import com.tencent.devops.auth.pojo.enum.OperateChannel +import com.tencent.devops.auth.pojo.vo.AuthProjectVO import com.tencent.devops.auth.pojo.vo.ResourceTypeInfoVo import com.tencent.devops.common.api.model.SQLPage import com.tencent.devops.common.auth.api.pojo.ResetAllResourceAuthorizationReq @@ -77,9 +79,17 @@ interface PermissionAuthorizationService { * 获取项目资源授予记录--根据条件 */ fun listResourceAuthorizations( - condition: ResourceAuthorizationConditionRequest + condition: ResourceAuthorizationConditionRequest, + operateChannel: OperateChannel? = OperateChannel.MANAGER ): SQLPage + /** + * 获取用户授权相关项目 + */ + fun listUserProjectsWithAuthorization( + userId: String + ): List + /** * 修改资源授权管理 */ @@ -87,6 +97,14 @@ interface PermissionAuthorizationService { resourceAuthorizationList: List ): Boolean + /** + * 是否用户拥有项目下授权 + */ + fun isUserHasProjectAuthorizations( + projectCode: String, + userId: String + ): Boolean + /** * 删除资源授权管理 */ @@ -121,6 +139,15 @@ interface PermissionAuthorizationService { condition: ResourceAuthorizationHandoverConditionRequest ): Map> + /** + * 交接授权申请 + */ + fun handoverAuthorizationsApplication( + operator: String, + projectCode: String, + condition: ResourceAuthorizationHandoverConditionRequest + ): String + /** * 批量重置授权人--项目下全量 */ @@ -129,4 +156,15 @@ interface PermissionAuthorizationService { projectCode: String, condition: ResetAllResourceAuthorizationReq ): List + + /** + * 检查交接人是否有代码库授权权限 + */ + fun checkRepertoryAuthorizationsHanover( + operator: String, + projectCode: String, + repertoryIds: List, + handoverFrom: String, + handoverTo: String + ) } diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/PermissionAuthorizationServiceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/PermissionAuthorizationServiceImpl.kt index dac016a70d28..341f4b1fddc7 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/PermissionAuthorizationServiceImpl.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/PermissionAuthorizationServiceImpl.kt @@ -3,14 +3,24 @@ package com.tencent.devops.auth.service import com.tencent.bk.sdk.iam.constants.ManagerScopesEnum import com.tencent.devops.auth.constant.AuthI18nConstants import com.tencent.devops.auth.constant.AuthMessageCode +import com.tencent.devops.auth.constant.AuthMessageCode.ERROR_REPERTORY_HANDOVER_AUTHORIZATION import com.tencent.devops.auth.dao.AuthAuthorizationDao +import com.tencent.devops.auth.dao.AuthResourceDao +import com.tencent.devops.auth.pojo.dto.HandoverDetailDTO +import com.tencent.devops.auth.pojo.dto.HandoverOverviewCreateDTO +import com.tencent.devops.auth.pojo.enum.HandoverStatus +import com.tencent.devops.auth.pojo.enum.HandoverType +import com.tencent.devops.auth.pojo.enum.OperateChannel +import com.tencent.devops.auth.pojo.vo.AuthProjectVO import com.tencent.devops.auth.pojo.vo.ResourceTypeInfoVo +import com.tencent.devops.auth.service.iam.PermissionHandoverApplicationService import com.tencent.devops.auth.service.iam.PermissionResourceValidateService import com.tencent.devops.auth.service.iam.PermissionService import com.tencent.devops.common.api.exception.ErrorCodeException import com.tencent.devops.common.api.model.SQLPage import com.tencent.devops.common.auth.api.AuthPermission import com.tencent.devops.common.auth.api.AuthResourceType +import com.tencent.devops.common.auth.api.ResourceTypeId import com.tencent.devops.common.auth.api.pojo.ResetAllResourceAuthorizationReq import com.tencent.devops.common.auth.api.pojo.ResourceAuthorizationConditionRequest import com.tencent.devops.common.auth.api.pojo.ResourceAuthorizationDTO @@ -24,19 +34,22 @@ import com.tencent.devops.common.client.Client import com.tencent.devops.common.web.utils.I18nUtil import com.tencent.devops.environment.api.ServiceEnvNodeAuthorizationResource import com.tencent.devops.process.api.service.ServicePipelineAuthorizationResource +import com.tencent.devops.project.api.service.ServiceProjectResource import com.tencent.devops.repository.api.ServiceRepositoryAuthorizationResource import org.jooq.DSLContext import org.slf4j.LoggerFactory import org.springframework.stereotype.Service @Service -class PermissionAuthorizationServiceImpl constructor( +class PermissionAuthorizationServiceImpl( private val dslContext: DSLContext, private val authAuthorizationDao: AuthAuthorizationDao, private val client: Client, private val permissionResourceValidateService: PermissionResourceValidateService, private val deptService: DeptService, - private val permissionService: PermissionService + private val permissionService: PermissionService, + private val permissionHandoverApplicationService: PermissionHandoverApplicationService, + private val authResourceDao: AuthResourceDao ) : PermissionAuthorizationService { companion object { private val logger = LoggerFactory.getLogger(PermissionAuthorizationServiceImpl::class.java) @@ -120,22 +133,72 @@ class PermissionAuthorizationServiceImpl constructor( return true } + @Suppress("NestedBlockDepth") override fun listResourceAuthorizations( - condition: ResourceAuthorizationConditionRequest + condition: ResourceAuthorizationConditionRequest, + operateChannel: OperateChannel? ): SQLPage { - logger.info("list resource authorizations:$condition") - val record = authAuthorizationDao.list( - dslContext = dslContext, - condition = condition - ) - val count = authAuthorizationDao.count( + logger.info("list resource authorizations:$condition|$operateChannel") + val (records, count) = if (operateChannel != OperateChannel.PERSONAL) { + val records = authAuthorizationDao.list( + dslContext = dslContext, + condition = condition + ) + val count = authAuthorizationDao.count( + dslContext = dslContext, + condition = condition + ) + Pair(records, count) + } else { + val beingHandoverDetails = permissionHandoverApplicationService.listMemberHandoverDetails( + projectCode = condition.projectCode, + memberId = condition.handoverFrom!!, + handoverType = HandoverType.AUTHORIZATION, + resourceType = condition.resourceType!! + ) + val beingHandoverResourceCodes = beingHandoverDetails.map { it.itemId }.distinct() + if (condition.queryHandover == true && beingHandoverResourceCodes.isEmpty()) { + Pair(emptyList(), 0L) + } else { + val finalCondition = condition.apply { + when (this.queryHandover) { + true -> this.filterResourceCodes = beingHandoverResourceCodes + false -> this.excludeResourceCodes = beingHandoverResourceCodes + else -> {} + } + } + val records = authAuthorizationDao.list( + dslContext = dslContext, + condition = finalCondition + ).map { + it.copy( + beingHandover = beingHandoverResourceCodes.contains(it.resourceCode), + approver = beingHandoverDetails.find { details -> details.itemId == it.resourceCode }?.approver + ) + } + val count = authAuthorizationDao.count( + dslContext = dslContext, + condition = finalCondition + ) + Pair(records, count) + } + } + return SQLPage(count = count.toLong(), records = records) + } + + override fun listUserProjectsWithAuthorization(userId: String): List { + val projectCodesWithAuthorization = authAuthorizationDao.listUserProjects(dslContext, userId) + val projectInfos = authResourceDao.listByResourceCodes( dslContext = dslContext, - condition = condition - ) - return SQLPage( - count = count.toLong(), - records = record + resourceType = ResourceTypeId.PROJECT, + resourceCodes = projectCodesWithAuthorization ) + return projectInfos.map { + AuthProjectVO( + projectCode = it.resourceCode, + projectName = it.resourceName + ) + } } override fun modifyResourceAuthorization(resourceAuthorizationList: List): Boolean { @@ -148,6 +211,19 @@ class PermissionAuthorizationServiceImpl constructor( return true } + override fun isUserHasProjectAuthorizations( + projectCode: String, + userId: String + ): Boolean { + return authAuthorizationDao.count( + dslContext = dslContext, + condition = ResourceAuthorizationConditionRequest( + projectCode = projectCode, + handoverFrom = userId + ) + ) > 0 + } + override fun deleteResourceAuthorization( projectCode: String, resourceType: String, @@ -226,6 +302,60 @@ class PermissionAuthorizationServiceImpl constructor( return result } + override fun handoverAuthorizationsApplication( + operator: String, + projectCode: String, + condition: ResourceAuthorizationHandoverConditionRequest + ): String { + val beingHandoverDetails = permissionHandoverApplicationService.listMemberHandoverDetails( + projectCode = condition.projectCode, + memberId = condition.handoverFrom!!, + handoverType = HandoverType.AUTHORIZATION, + resourceType = condition.resourceType + ).map { it.itemId }.distinct() + + val finalCondition = condition.copy( + preCheck = true, + checkPermission = false, + excludeResourceCodes = beingHandoverDetails + ) + + val handoverResult = resetResourceAuthorizationByResourceType( + operator = operator, + projectCode = projectCode, + condition = finalCondition + ) + if (!handoverResult[ResourceAuthorizationHandoverStatus.FAILED].isNullOrEmpty()) { + throw ErrorCodeException(errorCode = ERROR_REPERTORY_HANDOVER_AUTHORIZATION) + } + val resourceAuthorizationList = getResourceAuthorizationList(condition = finalCondition) + val handoverDetails = mutableListOf() + resourceAuthorizationList.forEach { authorization -> + handoverDetails.add( + HandoverDetailDTO( + projectCode = projectCode, + itemId = authorization.resourceCode, + resourceType = authorization.resourceType, + handoverType = HandoverType.AUTHORIZATION + ) + ) + } + // 创建交接单 + val flowNo = permissionHandoverApplicationService.createHandoverApplication( + overview = HandoverOverviewCreateDTO( + projectCode = projectCode, + projectName = client.get(ServiceProjectResource::class).get(projectCode).data!!.projectName, + applicant = condition.handoverFrom!!, + approver = condition.handoverTo!!, + handoverStatus = HandoverStatus.PENDING, + groupCount = 0, + authorizationCount = resourceAuthorizationList.size + ), + details = handoverDetails + ) + return flowNo + } + override fun resetAllResourceAuthorization( operator: String, projectCode: String, @@ -261,6 +391,35 @@ class PermissionAuthorizationServiceImpl constructor( return result } + override fun checkRepertoryAuthorizationsHanover( + operator: String, + projectCode: String, + repertoryIds: List, + handoverFrom: String, + handoverTo: String + ) { + val canHandoverRepertory = resetResourceAuthorizationByResourceType( + operator = operator, + projectCode = projectCode, + condition = ResourceAuthorizationHandoverConditionRequest( + projectCode = projectCode, + resourceType = ResourceTypeId.REPERTORY, + fullSelection = true, + filterResourceCodes = repertoryIds, + handoverChannel = HandoverChannelCode.MANAGER, + handoverFrom = handoverFrom, + handoverTo = handoverTo, + checkPermission = false, + preCheck = true + ) + )[ResourceAuthorizationHandoverStatus.FAILED].isNullOrEmpty() + if (!canHandoverRepertory) { + throw ErrorCodeException( + errorCode = ERROR_REPERTORY_HANDOVER_AUTHORIZATION + ) + } + } + private fun addHandoverFromCnName( resourceAuthorizationList: List ) { @@ -352,6 +511,7 @@ class PermissionAuthorizationServiceImpl constructor( resourceAuthorizationHandoverDTOs = resourceAuthorizationHandoverDTOs ).data } + AuthResourceType.CODE_REPERTORY.value -> { client.get(ServiceRepositoryAuthorizationResource::class).resetRepositoryAuthorization( projectId = projectId, @@ -359,6 +519,7 @@ class PermissionAuthorizationServiceImpl constructor( resourceAuthorizationHandoverDTOs = resourceAuthorizationHandoverDTOs ).data } + AuthResourceType.ENVIRONMENT_ENV_NODE.value -> { client.get(ServiceEnvNodeAuthorizationResource::class).resetEnvNodeAuthorization( projectId = projectId, @@ -366,6 +527,7 @@ class PermissionAuthorizationServiceImpl constructor( resourceAuthorizationHandoverDTOs = resourceAuthorizationHandoverDTOs ).data } + else -> { null } diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/iam/PermissionHandoverApplicationService.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/iam/PermissionHandoverApplicationService.kt new file mode 100644 index 000000000000..ccb6f2e9b2b5 --- /dev/null +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/iam/PermissionHandoverApplicationService.kt @@ -0,0 +1,78 @@ +package com.tencent.devops.auth.service.iam + +import com.tencent.devops.auth.pojo.dto.HandoverDetailDTO +import com.tencent.devops.auth.pojo.dto.HandoverOverviewCreateDTO +import com.tencent.devops.auth.pojo.enum.HandoverType +import com.tencent.devops.auth.pojo.request.HandoverDetailsQueryReq +import com.tencent.devops.auth.pojo.request.HandoverOverviewQueryReq +import com.tencent.devops.auth.pojo.request.HandoverOverviewUpdateReq +import com.tencent.devops.auth.pojo.vo.HandoverAuthorizationDetailVo +import com.tencent.devops.auth.pojo.vo.HandoverGroupDetailVo +import com.tencent.devops.auth.pojo.vo.HandoverOverviewVo +import com.tencent.devops.auth.pojo.vo.ResourceType2CountVo +import com.tencent.devops.common.api.model.SQLPage + +interface PermissionHandoverApplicationService { + /** + * 创建权限交接申请单 + * */ + fun createHandoverApplication( + overview: HandoverOverviewCreateDTO, + details: List + ): String + + /** + * 生成流程单号 + * */ + fun generateFlowNo(): String + + /** + * 更新权限交接申请单 + * */ + fun updateHandoverApplication(overview: HandoverOverviewUpdateReq) + + /** + * 根据流程单号获取权限交接总览 + * */ + fun getHandoverOverview(flowNo: String): HandoverOverviewVo + + /** + * 权限交接总览列表 + * */ + fun listHandoverOverviews(queryRequest: HandoverOverviewQueryReq): SQLPage + + /** + * 获取交接单详情 + * */ + fun listHandoverDetails( + projectCode: String, + flowNo: String, + resourceType: String? = null, + handoverType: HandoverType? = null + ): List + + /** + * 获取用户在项目下正在交接的组/授权 + * */ + fun listMemberHandoverDetails( + projectCode: String, + memberId: String, + handoverType: HandoverType, + resourceType: String? = null + ): List + + /** + * 获取交接单中授权相关 + * */ + fun listAuthorizationsOfHandoverApplication(queryReq: HandoverDetailsQueryReq): SQLPage + + /** + * 获取交接单中用户组相关 + * */ + fun listGroupsOfHandoverApplication(queryReq: HandoverDetailsQueryReq): SQLPage + + /** + * 根据资源类型进行分类 + * */ + fun getResourceType2CountOfHandoverApplication(flowNo: String): List +} diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/iam/PermissionManageFacadeService.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/iam/PermissionManageFacadeService.kt new file mode 100644 index 000000000000..34c26a12e7e2 --- /dev/null +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/iam/PermissionManageFacadeService.kt @@ -0,0 +1,250 @@ +package com.tencent.devops.auth.service.iam + +import com.tencent.devops.auth.pojo.AuthResourceGroupMember +import com.tencent.devops.auth.pojo.ResourceMemberInfo +import com.tencent.devops.auth.pojo.dto.IamGroupIdsQueryConditionDTO +import com.tencent.devops.auth.pojo.dto.InvalidAuthorizationsDTO +import com.tencent.devops.auth.pojo.enum.BatchOperateType +import com.tencent.devops.auth.pojo.enum.MemberType +import com.tencent.devops.auth.pojo.enum.OperateChannel +import com.tencent.devops.auth.pojo.request.GroupMemberCommonConditionReq +import com.tencent.devops.auth.pojo.request.GroupMemberHandoverConditionReq +import com.tencent.devops.auth.pojo.request.GroupMemberRemoveConditionReq +import com.tencent.devops.auth.pojo.request.GroupMemberRenewalConditionReq +import com.tencent.devops.auth.pojo.request.GroupMemberSingleRenewalReq +import com.tencent.devops.auth.pojo.request.HandoverDetailsQueryReq +import com.tencent.devops.auth.pojo.request.HandoverOverviewBatchUpdateReq +import com.tencent.devops.auth.pojo.request.HandoverOverviewUpdateReq +import com.tencent.devops.auth.pojo.request.ProjectMembersQueryConditionReq +import com.tencent.devops.auth.pojo.request.RemoveMemberFromProjectReq +import com.tencent.devops.auth.pojo.request.ResourceType2CountOfHandoverQuery +import com.tencent.devops.auth.pojo.vo.BatchOperateGroupMemberCheckVo +import com.tencent.devops.auth.pojo.vo.GroupDetailsInfoVo +import com.tencent.devops.auth.pojo.vo.HandoverAuthorizationDetailVo +import com.tencent.devops.auth.pojo.vo.HandoverGroupDetailVo +import com.tencent.devops.auth.pojo.vo.ResourceType2CountVo +import com.tencent.devops.common.api.model.SQLPage + +/** + * 权限管理门面类 + */ +interface PermissionManageFacadeService { + /** + * 查询成员所在资源用户组详情 + * 管理员视角返回用户直接加入/模板加入的用户组 + * 个人视角返回用户直接加入/模板加入/组织加入的用户组 + * */ + fun getMemberGroupsDetails( + projectId: String, + memberId: String, + resourceType: String? = null, + iamGroupIds: List? = null, + groupName: String? = null, + minExpiredAt: Long? = null, + maxExpiredAt: Long? = null, + relatedResourceType: String? = null, + relatedResourceCode: String? = null, + action: String? = null, + operateChannel: OperateChannel? = OperateChannel.MANAGER, + start: Int? = null, + limit: Int? = null + ): SQLPage + + /** + * 获取用户有权限的用户组数量 + * 管理员视角返回用户直接加入/模板加入的用户组 + * 个人视角返回用户直接加入/模板加入/组织加入的用户组 + * */ + fun getMemberGroupsCount( + projectCode: String, + memberId: String, + groupName: String?, + minExpiredAt: Long?, + maxExpiredAt: Long?, + relatedResourceType: String?, + relatedResourceCode: String?, + action: String?, + operateChannel: OperateChannel? = OperateChannel.MANAGER + ): List + + /** + * 根据条件查询组ID + * */ + fun listIamGroupIdsByConditions( + condition: IamGroupIdsQueryConditionDTO + ): List + + /** + * 获取用户在该项目加入的组 + * */ + fun listMemberGroupIdsInProject( + projectCode: String, + memberId: String + ): List + + /** + * 查询成员所在资源用户组列表 + * 管理员视角返回用户直接加入/模板加入的用户组 + * 个人视角返回用户直接加入/模板加入/组织加入的用户组 + * */ + fun listResourceGroupMembers( + projectCode: String, + memberId: String, + resourceType: String? = null, + iamGroupIds: List? = null, + minExpiredAt: Long? = null, + maxExpiredAt: Long? = null, + operateChannel: OperateChannel? = OperateChannel.MANAGER, + filterMemberType: MemberType? = null, + excludeIamGroupIds: List? = null, + onlyExcludeUserDirectlyJoined: Boolean? = false, + start: Int? = null, + limit: Int? = null + ): Pair> + + /** + * 根据复杂条件进行搜索,用于用户管理界面 + * */ + fun listProjectMembersByComplexConditions( + conditionReq: ProjectMembersQueryConditionReq + ): SQLPage + + /** + * 为了避免流水线代持人/代码库oauth权限失效,需要对用户退出/交接用户组进行检查。 + * 返回结果: + * 1、引起代持人权限失效的用户组。 + * 2、引起代持人权限失效的流水线。 + * 3、引起代码库oauth失效的代码库(当用户操作完组后,不再拥有项目访问权限时,会代码库oauth引起失效) + * 4、引起失效的环境节点授权 + **/ + fun listInvalidAuthorizationsAfterOperatedGroups( + projectCode: String, + iamGroupIds: List, + memberId: String + ): InvalidAuthorizationsDTO + + /** + * 续期用户权限-无需审批版本 + * */ + fun renewalGroupMember( + userId: String, + projectCode: String, + renewalConditionReq: GroupMemberSingleRenewalReq + ): Boolean + + /** + * 批量续期用户权限-管理员视角 + * */ + fun batchRenewalGroupMembersFromManager( + userId: String, + projectCode: String, + renewalConditionReq: GroupMemberRenewalConditionReq + ): Boolean + + /** + * 批量交接-管理员视角 + * */ + fun batchHandoverGroupMembersFromManager( + userId: String, + projectCode: String, + handoverMemberDTO: GroupMemberHandoverConditionReq + ): Boolean + + /** + * 批量交接申请-个人视角 + * */ + fun batchHandoverApplicationFromPersonal( + userId: String, + projectCode: String, + handoverMemberDTO: GroupMemberHandoverConditionReq + ): String + + /** + * 批量移除-管理员视角 + * */ + fun batchDeleteResourceGroupMembersFromManager( + userId: String, + projectCode: String, + removeMemberDTO: GroupMemberRemoveConditionReq + ): Boolean + + /** + * 批量退出-个人视角 + * */ + fun batchDeleteResourceGroupMembersFromPersonal( + userId: String, + projectCode: String, + removeMemberDTO: GroupMemberRemoveConditionReq + ): String? + + /** + * 退出单个组 + * */ + fun deleteResourceGroupMembers( + userId: String, + projectCode: String, + groupId: Int, + targetMember: ResourceMemberInfo + ): Boolean + + /** + * 批量操作检查 + * */ + fun batchOperateGroupMembersCheck( + userId: String, + projectCode: String, + batchOperateType: BatchOperateType, + conditionReq: GroupMemberCommonConditionReq + ): BatchOperateGroupMemberCheckVo + + /** + * 将用户移出项目-管理员视角 + * */ + fun removeMemberFromProject( + userId: String, + projectCode: String, + removeMemberFromProjectReq: RemoveMemberFromProjectReq + ): List + + /** + * 将用户移出项目检查-管理员视角 + * */ + fun removeMemberFromProjectCheck( + userId: String, + projectCode: String, + removeMemberFromProjectReq: RemoveMemberFromProjectReq + ): Boolean + + /** + * 处理交接审批单 + * */ + fun handleHanoverApplication(request: HandoverOverviewUpdateReq): Boolean + + /** + * 批量处理交接审批单 + * */ + fun batchHandleHanoverApplications(request: HandoverOverviewBatchUpdateReq): Boolean + + /** + * 根据资源类型进行分类-交接 + * */ + fun getResourceType2CountOfHandover(queryReq: ResourceType2CountOfHandoverQuery): List + + /** + * 获取交接中授权相关-分为预览/交接单审批两个场景 + * */ + fun listAuthorizationsOfHandover(queryReq: HandoverDetailsQueryReq): SQLPage + + /** + * 获取交接中用户组相关-分为预览/交接单审批两个场景 + * */ + fun listGroupsOfHandover(queryReq: HandoverDetailsQueryReq): SQLPage + + /** + * 校验是否为项目成员 + * */ + fun isProjectMember( + projectCode: String, + userId: String + ): Boolean +} diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/iam/PermissionResourceGroupAndMemberFacadeService.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/iam/PermissionResourceGroupAndMemberFacadeService.kt deleted file mode 100644 index 4a591487e588..000000000000 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/iam/PermissionResourceGroupAndMemberFacadeService.kt +++ /dev/null @@ -1,56 +0,0 @@ -package com.tencent.devops.auth.service.iam - -import com.tencent.devops.auth.pojo.ResourceMemberInfo -import com.tencent.devops.auth.pojo.dto.IamGroupIdsQueryConditionDTO -import com.tencent.devops.auth.pojo.request.ProjectMembersQueryConditionReq -import com.tencent.devops.auth.pojo.vo.GroupDetailsInfoVo -import com.tencent.devops.auth.pojo.vo.MemberGroupCountWithPermissionsVo -import com.tencent.devops.common.api.model.SQLPage - -interface PermissionResourceGroupAndMemberFacadeService { - /** - * 查询成员所在资源用户组详情,直接加入+通过用户组(模板)加入 - * */ - fun getMemberGroupsDetails( - projectId: String, - memberId: String, - resourceType: String? = null, - iamGroupIds: List? = null, - groupName: String? = null, - minExpiredAt: Long? = null, - maxExpiredAt: Long? = null, - relatedResourceType: String? = null, - relatedResourceCode: String? = null, - action: String? = null, - start: Int? = null, - limit: Int? = null - ): SQLPage - - /** - * 获取用户有权限的用户组数量 - * */ - fun getMemberGroupsCount( - projectCode: String, - memberId: String, - groupName: String?, - minExpiredAt: Long?, - maxExpiredAt: Long?, - relatedResourceType: String?, - relatedResourceCode: String?, - action: String? - ): List - - /** - * 根据条件查询组ID - * */ - fun listIamGroupIdsByConditions( - condition: IamGroupIdsQueryConditionDTO - ): List - - /** - * 根据复杂条件进行搜索,用于用户管理界面 - * */ - fun listProjectMembersByComplexConditions( - conditionReq: ProjectMembersQueryConditionReq - ): SQLPage -} diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/iam/PermissionResourceGroupPermissionService.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/iam/PermissionResourceGroupPermissionService.kt index 0172b4516c2f..9063615cc7e9 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/iam/PermissionResourceGroupPermissionService.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/iam/PermissionResourceGroupPermissionService.kt @@ -91,6 +91,15 @@ interface PermissionResourceGroupPermissionService { action: String ): Boolean + /** + * 是否用户拥有项目级别权限,如整个项目流水线执行权限/项目的管理权限等。 + * */ + fun isGroupsHasProjectLevelPermission( + projectCode: String, + filterIamGroupIds: List, + action: String + ): Boolean + /** * 获取用户组有权限的资源 * */ diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/iam/PermissionResourceMemberService.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/iam/PermissionResourceMemberService.kt index 6da335376147..7b4aa254ddc3 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/iam/PermissionResourceMemberService.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/iam/PermissionResourceMemberService.kt @@ -1,16 +1,8 @@ package com.tencent.devops.auth.service.iam import com.tencent.bk.sdk.iam.dto.manager.ManagerMember -import com.tencent.devops.auth.pojo.AuthResourceGroupMember import com.tencent.devops.auth.pojo.ResourceMemberInfo import com.tencent.devops.auth.pojo.dto.GroupMemberRenewalDTO -import com.tencent.devops.auth.pojo.enum.BatchOperateType -import com.tencent.devops.auth.pojo.request.GroupMemberCommonConditionReq -import com.tencent.devops.auth.pojo.request.GroupMemberHandoverConditionReq -import com.tencent.devops.auth.pojo.request.GroupMemberRenewalConditionReq -import com.tencent.devops.auth.pojo.request.GroupMemberSingleRenewalReq -import com.tencent.devops.auth.pojo.request.RemoveMemberFromProjectReq -import com.tencent.devops.auth.pojo.vo.BatchOperateGroupMemberCheckVo import com.tencent.devops.auth.pojo.vo.ResourceMemberCountVO import com.tencent.devops.common.api.model.SQLPage import com.tencent.devops.common.auth.api.pojo.BkAuthGroup @@ -50,25 +42,6 @@ interface PermissionResourceMemberService { fun addDepartedFlagToMembers(records: List): List - fun listResourceGroupMembers( - projectCode: String, - memberId: String, - resourceType: String? = null, - iamGroupIds: List? = null, - minExpiredAt: Long? = null, - maxExpiredAt: Long? = null, - start: Int? = null, - limit: Int? = null - ): Pair> - - /** - * 获取用户在该项目加入的组 - * */ - fun listMemberGroupIdsInProject( - projectCode: String, - memberId: String - ): List - fun batchDeleteResourceGroupMembers( projectCode: String, iamGroupId: Int, @@ -76,43 +49,12 @@ interface PermissionResourceMemberService { departments: List? = emptyList() ): Boolean - fun batchDeleteResourceGroupMembers( - userId: String, - projectCode: String, - removeMemberDTO: GroupMemberCommonConditionReq - ): Boolean - fun deleteIamGroupMembers( groupId: Int, type: String, memberIds: List ): Boolean - fun batchHandoverGroupMembers( - userId: String, - projectCode: String, - handoverMemberDTO: GroupMemberHandoverConditionReq - ): Boolean - - fun batchOperateGroupMembersCheck( - userId: String, - projectCode: String, - batchOperateType: BatchOperateType, - conditionReq: GroupMemberCommonConditionReq - ): BatchOperateGroupMemberCheckVo - - fun removeMemberFromProject( - userId: String, - projectCode: String, - removeMemberFromProjectReq: RemoveMemberFromProjectReq - ): List - - fun removeMemberFromProjectCheck( - userId: String, - projectCode: String, - removeMemberFromProjectReq: RemoveMemberFromProjectReq - ): Boolean - fun roleCodeToIamGroupId( projectCode: String, roleCode: String @@ -134,25 +76,12 @@ interface PermissionResourceMemberService { memberRenewalDTO: GroupMemberRenewalDTO ): Boolean - // 无需审批版本 - fun renewalGroupMember( - userId: String, - projectCode: String, - renewalConditionReq: GroupMemberSingleRenewalReq - ): Boolean - fun renewalIamGroupMembers( groupId: Int, members: List, expiredAt: Long ): Boolean - fun batchRenewalGroupMembers( - userId: String, - projectCode: String, - renewalConditionReq: GroupMemberRenewalConditionReq - ): Boolean - fun addGroupMember( projectCode: String, memberId: String, @@ -175,9 +104,4 @@ interface PermissionResourceMemberService { members: List? = emptyList(), departments: List? = emptyList() ): Boolean - - fun isProjectMember( - projectCode: String, - userId: String - ): Boolean } diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/iam/PermissionResourceValidateService.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/iam/PermissionResourceValidateService.kt index e81196b7eec4..c9c9e261ce96 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/iam/PermissionResourceValidateService.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/iam/PermissionResourceValidateService.kt @@ -29,6 +29,7 @@ package com.tencent.devops.auth.service.iam import com.tencent.devops.auth.pojo.dto.PermissionBatchValidateDTO +import com.tencent.devops.auth.pojo.enum.OperateChannel interface PermissionResourceValidateService { fun batchValidateUserResourcePermission( @@ -46,4 +47,14 @@ interface PermissionResourceValidateService { resourceType: String, resourceCode: String ): Boolean + + /** + * 根据渠道来校验用户权限,主要用户管理界面/个人视角 + */ + fun validateUserProjectPermissionByChannel( + userId: String, + projectCode: String, + operateChannel: OperateChannel, + targetMemberId: String + ) } diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/lock/HandleHandoverApplicationLock.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/lock/HandleHandoverApplicationLock.kt new file mode 100644 index 000000000000..741873edb556 --- /dev/null +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/lock/HandleHandoverApplicationLock.kt @@ -0,0 +1,45 @@ +/* + * Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-CI 蓝鲸持续集成平台 is licensed under the MIT license. + * + * A copy of the MIT License is included in this file. + * + * + * Terms of the MIT License: + * --------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.devops.auth.service.lock + +import com.tencent.devops.common.redis.RedisLock +import com.tencent.devops.common.redis.RedisOperation + +class HandleHandoverApplicationLock( + redisOperation: RedisOperation, + flowNo: String +) : + RedisLock( + redisOperation = redisOperation, + lockKey = "auth.handover.$flowNo.lock", + expiredTimeInSeconds = 1800 + ) { + override fun decorateKey(key: String): String { + return key + } +} diff --git a/src/backend/ci/core/auth/biz-auth/src/test/kotlin/com/tencent/devops/auth/rbac/service/migrate/AbMigratePolicyServiceTest.kt b/src/backend/ci/core/auth/biz-auth/src/test/kotlin/com/tencent/devops/auth/rbac/service/migrate/AbMigratePolicyServiceTest.kt index c8cc6766af6c..ac077b5ea459 100644 --- a/src/backend/ci/core/auth/biz-auth/src/test/kotlin/com/tencent/devops/auth/rbac/service/migrate/AbMigratePolicyServiceTest.kt +++ b/src/backend/ci/core/auth/biz-auth/src/test/kotlin/com/tencent/devops/auth/rbac/service/migrate/AbMigratePolicyServiceTest.kt @@ -37,7 +37,7 @@ import com.tencent.devops.auth.dao.AuthResourceGroupConfigDao import com.tencent.devops.auth.dao.AuthResourceGroupDao import com.tencent.devops.auth.provider.rbac.pojo.migrate.MigrateTaskDataResult import com.tencent.devops.auth.provider.rbac.service.AuthResourceCodeConverter -import com.tencent.devops.auth.provider.rbac.service.RbacCacheService +import com.tencent.devops.auth.provider.rbac.service.RbacCommonService import com.tencent.devops.auth.provider.rbac.service.migrate.AbMigratePolicyService import com.tencent.devops.auth.provider.rbac.service.migrate.MigrateIamApiService import com.tencent.devops.auth.provider.rbac.service.migrate.MigrateResourceCodeConverter @@ -63,7 +63,7 @@ open class AbMigratePolicyServiceTest : BkCiAbstractTest() { val migrateIamApiService: MigrateIamApiService = mockk() val authMigrationDao: AuthMigrationDao = mockk() val permissionService: PermissionService = mockk() - val rbacCacheService: RbacCacheService = mockk() + val rbacCommonService: RbacCommonService = mockk() val migrateResourceCodeConverter: MigrateResourceCodeConverter = mockk() val authResourceCodeConverter: AuthResourceCodeConverter = mockk() val deptService: DeptService = mockk() diff --git a/src/backend/ci/core/auth/biz-auth/src/test/kotlin/com/tencent/devops/auth/rbac/service/migrate/MigrateV0PolicyServiceTest.kt b/src/backend/ci/core/auth/biz-auth/src/test/kotlin/com/tencent/devops/auth/rbac/service/migrate/MigrateV0PolicyServiceTest.kt index 5564b1b2d8dc..96221ec2e6d4 100644 --- a/src/backend/ci/core/auth/biz-auth/src/test/kotlin/com/tencent/devops/auth/rbac/service/migrate/MigrateV0PolicyServiceTest.kt +++ b/src/backend/ci/core/auth/biz-auth/src/test/kotlin/com/tencent/devops/auth/rbac/service/migrate/MigrateV0PolicyServiceTest.kt @@ -56,7 +56,7 @@ class MigrateV0PolicyServiceTest : AbMigratePolicyServiceTest() { migrateIamApiService = migrateIamApiService, authResourceCodeConverter = authResourceCodeConverter, permissionService = permissionService, - rbacCacheService = rbacCacheService, + rbacCommonService = rbacCommonService, authMigrationDao = authMigrationDao, deptService = deptService, permissionResourceGroupPermissionService = permissionResourceGroupPermissionService, diff --git a/src/backend/ci/core/auth/biz-auth/src/test/kotlin/com/tencent/devops/auth/rbac/service/migrate/MigrateV3PolicyServiceTest.kt b/src/backend/ci/core/auth/biz-auth/src/test/kotlin/com/tencent/devops/auth/rbac/service/migrate/MigrateV3PolicyServiceTest.kt index f18ee9cf8d2f..bf4e94b3d767 100644 --- a/src/backend/ci/core/auth/biz-auth/src/test/kotlin/com/tencent/devops/auth/rbac/service/migrate/MigrateV3PolicyServiceTest.kt +++ b/src/backend/ci/core/auth/biz-auth/src/test/kotlin/com/tencent/devops/auth/rbac/service/migrate/MigrateV3PolicyServiceTest.kt @@ -55,7 +55,7 @@ class MigrateV3PolicyServiceTest : AbMigratePolicyServiceTest() { migrateIamApiService = migrateIamApiService, authResourceCodeConverter = authResourceCodeConverter, permissionService = permissionService, - rbacCacheService = rbacCacheService, + rbacCommonService = rbacCommonService, authMigrationDao = authMigrationDao, deptService = deptService, permissionResourceGroupPermissionService = permissionResourceGroupPermissionService, diff --git a/src/backend/ci/core/common/common-auth/common-auth-api/src/main/kotlin/com/tencent/devops/common/auth/api/ActionId.kt b/src/backend/ci/core/common/common-auth/common-auth-api/src/main/kotlin/com/tencent/devops/common/auth/api/ActionId.kt index 835cf51999ab..b331faa1d4b6 100644 --- a/src/backend/ci/core/common/common-auth/common-auth-api/src/main/kotlin/com/tencent/devops/common/auth/api/ActionId.kt +++ b/src/backend/ci/core/common/common-auth/common-auth-api/src/main/kotlin/com/tencent/devops/common/auth/api/ActionId.kt @@ -2,6 +2,7 @@ package com.tencent.devops.common.auth.api object ActionId { // 项目 + const val PROJECT_VISIT = "project_visit" const val PROJECT_CREATE = "project_create" const val PROJECT_EDIT = "project_edit" const val PROJECT_ENABLE = "project_enable" diff --git a/src/backend/ci/core/common/common-auth/common-auth-api/src/main/kotlin/com/tencent/devops/common/auth/api/pojo/ResourceAuthorizationConditionRequest.kt b/src/backend/ci/core/common/common-auth/common-auth-api/src/main/kotlin/com/tencent/devops/common/auth/api/pojo/ResourceAuthorizationConditionRequest.kt index d5f8241e2204..9ec5d746ead8 100644 --- a/src/backend/ci/core/common/common-auth/common-auth-api/src/main/kotlin/com/tencent/devops/common/auth/api/pojo/ResourceAuthorizationConditionRequest.kt +++ b/src/backend/ci/core/common/common-auth/common-auth-api/src/main/kotlin/com/tencent/devops/common/auth/api/pojo/ResourceAuthorizationConditionRequest.kt @@ -12,12 +12,18 @@ open class ResourceAuthorizationConditionRequest( open val resourceType: String? = null, @get:Schema(title = "资源名称") open val resourceName: String? = null, + @get:Schema(title = "过滤资源ID列表") + open var filterResourceCodes: List? = null, + @get:Schema(title = "排除资源ID列表") + open var excludeResourceCodes: List? = null, @get:Schema(title = "授予人") open val handoverFrom: String? = null, @get:Schema(title = "greaterThanHandoverTime") open val greaterThanHandoverTime: Long? = null, @get:Schema(title = "lessThanHandoverTime") open val lessThanHandoverTime: Long? = null, + @get:Schema(title = "是否查询交接中单据") + open val queryHandover: Boolean? = null, @Parameter(description = "第几页", required = false) open val page: Int? = null, @Parameter(description = "每页条数", required = false) diff --git a/src/backend/ci/core/common/common-auth/common-auth-api/src/main/kotlin/com/tencent/devops/common/auth/api/pojo/ResourceAuthorizationHandoverConditionRequest.kt b/src/backend/ci/core/common/common-auth/common-auth-api/src/main/kotlin/com/tencent/devops/common/auth/api/pojo/ResourceAuthorizationHandoverConditionRequest.kt index 21fd1979736e..633031a4fc86 100644 --- a/src/backend/ci/core/common/common-auth/common-auth-api/src/main/kotlin/com/tencent/devops/common/auth/api/pojo/ResourceAuthorizationHandoverConditionRequest.kt +++ b/src/backend/ci/core/common/common-auth/common-auth-api/src/main/kotlin/com/tencent/devops/common/auth/api/pojo/ResourceAuthorizationHandoverConditionRequest.kt @@ -12,6 +12,10 @@ data class ResourceAuthorizationHandoverConditionRequest( override val resourceType: String, @get:Schema(title = "资源名称") override val resourceName: String? = null, + @get:Schema(title = "过滤资源ID列表") + override var filterResourceCodes: List? = null, + @get:Schema(title = "排除资源ID列表") + override var excludeResourceCodes: List? = null, @get:Schema(title = "授予人") override val handoverFrom: String? = null, @get:Schema(title = "greaterThanHandoverTime") @@ -38,6 +42,8 @@ data class ResourceAuthorizationHandoverConditionRequest( projectCode = projectCode, resourceType = resourceType, resourceName = resourceName, + filterResourceCodes = filterResourceCodes, + excludeResourceCodes = excludeResourceCodes, handoverFrom = handoverFrom, greaterThanHandoverTime = greaterThanHandoverTime, lessThanHandoverTime = lessThanHandoverTime, diff --git a/src/backend/ci/core/common/common-auth/common-auth-api/src/main/kotlin/com/tencent/devops/common/auth/api/pojo/ResourceAuthorizationResponse.kt b/src/backend/ci/core/common/common-auth/common-auth-api/src/main/kotlin/com/tencent/devops/common/auth/api/pojo/ResourceAuthorizationResponse.kt index 0c396b8c1982..179fc004daa9 100644 --- a/src/backend/ci/core/common/common-auth/common-auth-api/src/main/kotlin/com/tencent/devops/common/auth/api/pojo/ResourceAuthorizationResponse.kt +++ b/src/backend/ci/core/common/common-auth/common-auth-api/src/main/kotlin/com/tencent/devops/common/auth/api/pojo/ResourceAuthorizationResponse.kt @@ -5,6 +5,8 @@ import io.swagger.v3.oas.annotations.media.Schema @Schema(title = "资源授权返回体") @Suppress("LongParameterList") data class ResourceAuthorizationResponse( + @get:Schema(title = "ID") + val id: Long? = null, @get:Schema(title = "项目ID") val projectCode: String, @get:Schema(title = "资源类型") @@ -20,5 +22,9 @@ data class ResourceAuthorizationResponse( @get:Schema(title = "授予人中文名称") val handoverFromCnName: String? = null, @get:Schema(title = "是否有执行权限") - val executePermission: Boolean? = null + val executePermission: Boolean? = null, + @get:Schema(title = "是否正在交接,用于我的授权界面") + val beingHandover: Boolean? = null, + @get:Schema(title = "交接审批人") + val approver: String? = null ) diff --git a/src/backend/ci/core/project/biz-project/src/main/kotlin/com/tencent/devops/project/service/impl/AbsProjectServiceImpl.kt b/src/backend/ci/core/project/biz-project/src/main/kotlin/com/tencent/devops/project/service/impl/AbsProjectServiceImpl.kt index 31bed594fb25..dab3594f4b5f 100644 --- a/src/backend/ci/core/project/biz-project/src/main/kotlin/com/tencent/devops/project/service/impl/AbsProjectServiceImpl.kt +++ b/src/backend/ci/core/project/biz-project/src/main/kotlin/com/tencent/devops/project/service/impl/AbsProjectServiceImpl.kt @@ -818,6 +818,7 @@ abstract class AbsProjectServiceImpl @Autowired constructor( userId = userId, accessToken = accessToken ).toSet() + if (projectsWithVisitPermission.isEmpty() && !unApproved) { return emptyList() } diff --git a/src/backend/ci/core/quality/biz-quality/src/main/kotlin/com/tencent/devops/quality/dao/v2/QualityMetadataDao.kt b/src/backend/ci/core/quality/biz-quality/src/main/kotlin/com/tencent/devops/quality/dao/v2/QualityMetadataDao.kt index 878379d72905..a6b5950add2d 100644 --- a/src/backend/ci/core/quality/biz-quality/src/main/kotlin/com/tencent/devops/quality/dao/v2/QualityMetadataDao.kt +++ b/src/backend/ci/core/quality/biz-quality/src/main/kotlin/com/tencent/devops/quality/dao/v2/QualityMetadataDao.kt @@ -28,6 +28,7 @@ package com.tencent.devops.quality.dao.v2 import com.tencent.devops.common.api.util.PageUtil +import com.tencent.devops.common.db.utils.skipCheck import com.tencent.devops.model.quality.tables.TQualityMetadata import com.tencent.devops.model.quality.tables.records.TQualityMetadataRecord import com.tencent.devops.quality.api.v2.pojo.op.QualityMetaData @@ -65,6 +66,7 @@ class QualityMetadataDao { return with(TQualityMetadata.T_QUALITY_METADATA) { dslContext.selectFrom(this) .where(ELEMENT_TYPE.eq(elementType)) + .skipCheck() .fetch() } } diff --git a/src/backend/ci/core/repository/biz-repository/src/main/kotlin/com/tencent/devops/repository/service/RepositoryAuthService.kt b/src/backend/ci/core/repository/biz-repository/src/main/kotlin/com/tencent/devops/repository/service/RepositoryAuthService.kt index 0d5ae9b7177a..d69d780b489e 100644 --- a/src/backend/ci/core/repository/biz-repository/src/main/kotlin/com/tencent/devops/repository/service/RepositoryAuthService.kt +++ b/src/backend/ci/core/repository/biz-repository/src/main/kotlin/com/tencent/devops/repository/service/RepositoryAuthService.kt @@ -153,7 +153,7 @@ class RepositoryAuthService @Autowired constructor( repositoryInfos.map { val entity = ResourceAuthorizationResponse( projectCode = projectId, - resourceType = AuthResourceType.PIPELINE_DEFAULT.value, + resourceType = AuthResourceType.CODE_REPERTORY.value, resourceName = it.aliasName, resourceCode = it.repositoryHashId!!, handoverTime = it.updatedTime, diff --git a/support-files/i18n/auth/message_en_US.properties b/support-files/i18n/auth/message_en_US.properties index 9b78ae654850..ddb8b3352762 100644 --- a/support-files/i18n/auth/message_en_US.properties +++ b/support-files/i18n/auth/message_en_US.properties @@ -83,6 +83,14 @@ 2121088=The target member and the handover person are not allowed to be the same. 2121089=Expired permissions are not allowed to be transferred 2121090=Please wait until the next day for user information to be synchronized before trying again, as the information of new users has not yet been synchronized. +2121091=Handover overview not exist +2121092=The handover request has already been processed, and duplicate actions are not allowed +2121093=You are not the initiator of this handover request, so you are not authorized to revoke it +2121094=Due to the fact that you are not the approver of this handover request, you are unable to perform any actions on it. +2121095=The handover request is currently being processed. Please be patient and wait for further updates. +2121096=The handover operation is illegal and the user does not have authorization permissions +2121098=Since exiting the group directly will cause the authorization to become invalid, the group must be transferred. + bkAdministratorNotExpired=Permission has not expired and no action is required bkAgreeRenew=Agree to renew bkApproverAgreeRenew=Approver agreed to your permission renewal @@ -333,3 +341,7 @@ rule.resourceType.name=Andon Rule bkMemberExpiredAtDisplayExpired=expired bkMemberExpiredAtDisplayNormal={0} days bkMemberExpiredAtDisplayPermanent=permanent +bkApplyToHandover=apply to handover of +bkHandoverGroups= groups +bkHandoverAuthorizations= authorizations +bk_project=bkci project diff --git a/support-files/i18n/auth/message_zh_CN.properties b/support-files/i18n/auth/message_zh_CN.properties index c883c136e042..27e0dd417fd0 100644 --- a/support-files/i18n/auth/message_zh_CN.properties +++ b/support-files/i18n/auth/message_zh_CN.properties @@ -83,6 +83,13 @@ 2121088=目标对象和交接人不允许相同 2121089=已过期的权限不允许交接 2121090=请等待第二天用户信息同步后再尝试操作,因为新入职用户的信息尚未同步完成。 +2121091=权限交接记录不存在 +2121092=该交接申请单已被处理,不允许重复操作 +2121093=由于您不是该交接申请单的发起人,无法进行撤销操作 +2121094=由于您不是该交接申请单的审批人,无法进行任何操作 +2121095=该交接申请单正在被处理中,请耐心等待 +2121096=交接操作不合法,用户没有对应代码库授权的权限,请交接完代码库授权后再进行重试。 +2121098=由于直接退出用户组,会导致授权失效,必须进行用户组移交 bkAdministratorNotExpired=权限还未过期,不需要操作 bkAgreeRenew=同意续期 bkApproverAgreeRenew=审批人同意了您的权限续期 @@ -332,7 +339,10 @@ project.resourceType.name=项目 quality_group.resourceType.name=质量红线通知组 repertory.resourceType.name=代码库 rule.resourceType.name=质量红线规则 - bkMemberExpiredAtDisplayExpired=已过期 bkMemberExpiredAtDisplayNormal={0} 天 bkMemberExpiredAtDisplayPermanent=永久 +bkApplyToHandover=申请移交 +bkHandoverGroups=个权限用户组 +bkHandoverAuthorizations=个授权 +bk_project=蓝盾项目 diff --git a/support-files/sql/1001_ci_auth_ddl_mysql.sql b/support-files/sql/1001_ci_auth_ddl_mysql.sql index 41e31bc6291c..a9f2fc071dc5 100644 --- a/support-files/sql/1001_ci_auth_ddl_mysql.sql +++ b/support-files/sql/1001_ci_auth_ddl_mysql.sql @@ -440,4 +440,39 @@ CREATE TABLE IF NOT EXISTS `T_AUTH_RESOURCE_GROUP_PERMISSION` ( INDEX `IDX_PROJECT_AND_ACTION_RESOURCE_TYPE` (`PROJECT_CODE`, `ACTION_RELATED_RESOURCE_TYPE`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='资源组权限表'; +CREATE TABLE IF NOT EXISTS `T_AUTH_HANDOVER_OVERVIEW` ( + `ID` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID', + `PROJECT_CODE` varchar(64) NOT NULL COMMENT '项目ID', + `PROJECT_NAME` varchar(64) NOT NULL COMMENT '项目名称', + `FLOW_NO` varchar(64) NOT NULL COMMENT '流程单号', + `TITLE` varchar(256) DEFAULT NULL COMMENT '标题', + `APPLICANT` varchar(32) NOT NULL COMMENT '申请人', + `APPROVER` varchar(32) DEFAULT NULL COMMENT '审批人', + `STATUS` int DEFAULT 0 COMMENT '审批结果,0-审批中,1-审批成功,2-审批拒绝,3-撤销', + `GROUP_COUNT` int DEFAULT 0 comment '用户组数', + `AUTHORIZATION_COUNT` int DEFAULT 0 comment '授权个数', + `REMARK` varchar(256) DEFAULT NULL COMMENT '备注', + `LAST_OPERATOR` varchar(32) DEFAULT NULL COMMENT '最后操作人', + `CREATE_TIME` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `UPDATE_TIME` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间', + PRIMARY KEY (`ID`), + UNIQUE KEY `UNIQ_FLOW_NO` (`FLOW_NO`), + INDEX `IDX_PROJECT` (`PROJECT_CODE`), + INDEX `IDX_APPLICANT` (`APPLICANT`), + INDEX `IDX_APPROVER` (`APPROVER`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='权限交接总览表'; + +CREATE TABLE IF NOT EXISTS `T_AUTH_HANDOVER_DETAIL` ( + `ID` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID', + `PROJECT_CODE` varchar(64) NOT NULL COMMENT '项目ID', + `FLOW_NO` varchar(64) NOT NULL COMMENT '流程单号', + `ITEM_ID` varchar(255) NOT NULL COMMENT '组/授权资源ID', + `RESOURCE_TYPE` varchar(32) NOT NULL COMMENT '组/授权资源关联的资源类型', + `HANDOVER_TYPE` varchar(32) NOT NULL COMMENT '交接类型-group/authorization', + `CREATE_TIME` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `UPDATE_TIME` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间', + PRIMARY KEY (`ID`), + INDEX `IDX_PROJECT_FLOW` (`PROJECT_CODE`,`FLOW_NO`,`HANDOVER_TYPE`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='权限交接详细表'; + SET FOREIGN_KEY_CHECKS = 1; diff --git a/support-files/sql/2004_v3.x/2030_ci_auth-update_v3.0_mysql.sql b/support-files/sql/2004_v3.x/2030_ci_auth-update_v3.0_mysql.sql index dd612f62c830..0d6d3065c783 100644 --- a/support-files/sql/2004_v3.x/2030_ci_auth-update_v3.0_mysql.sql +++ b/support-files/sql/2004_v3.x/2030_ci_auth-update_v3.0_mysql.sql @@ -38,6 +38,13 @@ BEGIN ALTER TABLE T_AUTH_OAUTH2_ACCESS_TOKEN ADD COLUMN `PASS_WORD` VARCHAR(64) DEFAULT NULL COMMENT '用于密码模式' AFTER `USER_NAME`; END IF; + IF NOT EXISTS(SELECT 1 + FROM information_schema.statistics + WHERE TABLE_SCHEMA = db + AND TABLE_NAME = 'T_AUTH_RESOURCE_AUTHORIZATION' + AND INDEX_NAME = 'HANDOVER_FROM_PROJECT_CODE_INDEX') THEN + ALTER TABLE T_AUTH_RESOURCE_AUTHORIZATION ADD INDEX `HANDOVER_FROM_PROJECT_CODE_INDEX` (`HANDOVER_FROM`,`PROJECT_CODE`); + END IF; COMMIT; END DELIMITER ; From 9e93d0b6672317f4ade49dce1ecf6664398cbf4d Mon Sep 17 00:00:00 2001 From: greysonfang Date: Thu, 19 Dec 2024 16:56:00 +0800 Subject: [PATCH 02/33] =?UTF-8?q?feat=EF=BC=9A=E7=94=A8=E6=88=B7=E4=B8=AA?= =?UTF-8?q?=E4=BA=BA=E8=A7=86=E8=A7=92=20=E6=9D=83=E9=99=90=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E4=BC=98=E5=8C=96=20#11138?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rbac/service/migrate/RbacPermissionMigrateService.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/migrate/RbacPermissionMigrateService.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/migrate/RbacPermissionMigrateService.kt index 8f0a67ec78fc..669140ae1b5c 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/migrate/RbacPermissionMigrateService.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/migrate/RbacPermissionMigrateService.kt @@ -67,7 +67,7 @@ import java.util.concurrent.Executors * rbac迁移服务 */ @Suppress("LongParameterList", "ReturnCount") -class RbacPermissionMigrateService ( +class RbacPermissionMigrateService( private val client: Client, private val migrateResourceService: MigrateResourceService, private val migrateV3PolicyService: MigrateV3PolicyService, From ee579875afce303f8ae8e2b83b34036ae55edfbf Mon Sep 17 00:00:00 2001 From: greysonfang Date: Thu, 19 Dec 2024 17:19:35 +0800 Subject: [PATCH 03/33] =?UTF-8?q?feat=EF=BC=9A=E7=94=A8=E6=88=B7=E4=B8=AA?= =?UTF-8?q?=E4=BA=BA=E8=A7=86=E8=A7=92=20=E6=9D=83=E9=99=90=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E4=BC=98=E5=8C=96=20#11138?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rbac/service/RbacPermissionManageFacadeServiceImpl.kt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionManageFacadeServiceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionManageFacadeServiceImpl.kt index c94142d45788..a12b896605a0 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionManageFacadeServiceImpl.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionManageFacadeServiceImpl.kt @@ -766,9 +766,13 @@ class RbacPermissionManageFacadeServiceImpl( action = ActionId.PIPELINE_EXECUTE, filterIamGroupIds = iamGroupIdsOfNotExpired ) - + val invalidGroupIds = if (invalidAuthorizations.isNotEmpty()) { + operatedGroupsWithExecutePerm + } else { + emptyList() + } return InvalidAuthorizationsDTO( - invalidGroupIds = operatedGroupsWithExecutePerm, + invalidGroupIds = invalidGroupIds, invalidPipelineIds = invalidAuthorizations[ResourceTypeId.PIPELINE] ?: emptyList(), invalidRepertoryIds = invalidAuthorizations[ResourceTypeId.REPERTORY] ?: emptyList(), invalidEnvNodeIds = invalidAuthorizations[ResourceTypeId.ENV_NODE] ?: emptyList() From 7c8e7200516128bbc8da8b5ac02470af3ebfb8aa Mon Sep 17 00:00:00 2001 From: greysonfang Date: Thu, 19 Dec 2024 17:45:38 +0800 Subject: [PATCH 04/33] =?UTF-8?q?feat=EF=BC=9A=E7=94=A8=E6=88=B7=E4=B8=AA?= =?UTF-8?q?=E4=BA=BA=E8=A7=86=E8=A7=92=20=E6=9D=83=E9=99=90=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E4=BC=98=E5=8C=96=20#11138?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rbac/service/RbacPermissionManageFacadeServiceImpl.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionManageFacadeServiceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionManageFacadeServiceImpl.kt index a12b896605a0..1573d25ca6e3 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionManageFacadeServiceImpl.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionManageFacadeServiceImpl.kt @@ -977,7 +977,7 @@ class RbacPermissionManageFacadeServiceImpl( } private fun isNeedToRenewal(expiredAt: Long): Boolean { - return expiredAt < PERMANENT_EXPIRED_TIME + return expiredAt < PERMANENT_EXPIRED_TIME / 1000 } override fun batchRenewalGroupMembersFromManager( From eb0d21f9cfcc01995d0fb0c6c6beb6a08a12babd Mon Sep 17 00:00:00 2001 From: greysonfang Date: Thu, 19 Dec 2024 20:38:06 +0800 Subject: [PATCH 05/33] =?UTF-8?q?feat=EF=BC=9A=E7=94=A8=E6=88=B7=E4=B8=AA?= =?UTF-8?q?=E4=BA=BA=E8=A7=86=E8=A7=92=20=E6=9D=83=E9=99=90=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E4=BC=98=E5=8C=96=20#11138?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/pojo/dto/InvalidAuthorizationsDTO.kt | 6 +++- .../RbacPermissionManageFacadeServiceImpl.kt | 34 +++++++++++-------- 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/dto/InvalidAuthorizationsDTO.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/dto/InvalidAuthorizationsDTO.kt index c532f60faa2d..7fe5aced9e3d 100644 --- a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/dto/InvalidAuthorizationsDTO.kt +++ b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/dto/InvalidAuthorizationsDTO.kt @@ -12,4 +12,8 @@ data class InvalidAuthorizationsDTO( val invalidRepertoryIds: List = emptyList(), @get:Schema(title = "失效的CMDB环境节点") val invalidEnvNodeIds: List = emptyList() -) +) { + fun isHasInvalidAuthorizations(): Boolean { + return invalidRepertoryIds.isNotEmpty() || invalidPipelineIds.isNotEmpty() || invalidEnvNodeIds.isNotEmpty() + } +} diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionManageFacadeServiceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionManageFacadeServiceImpl.kt index 1573d25ca6e3..47e7d79e9fc8 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionManageFacadeServiceImpl.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionManageFacadeServiceImpl.kt @@ -1264,12 +1264,14 @@ class RbacPermissionManageFacadeServiceImpl( projectCode = projectCode, commonCondition = removeMemberDTO )[MemberType.USER] ?: return true - val (invalidGroups, invalidPipelines, invalidRepertoryIds, invalidEnvNodeIds) = - listInvalidAuthorizationsAfterOperatedGroups( - projectCode = projectCode, - iamGroupIds = groupIdsDirectlyJoined, - memberId = removeMemberDTO.targetMember.id - ) + + val invalidAuthorizationsDTO = listInvalidAuthorizationsAfterOperatedGroups( + projectCode = projectCode, + iamGroupIds = groupIdsDirectlyJoined, + memberId = removeMemberDTO.targetMember.id + ) + val (invalidGroups, invalidPipelines, invalidRepertoryIds, invalidEnvNodeIds) = invalidAuthorizationsDTO + if (invalidRepertoryIds.isNotEmpty()) { permissionAuthorizationService.checkRepertoryAuthorizationsHanover( operator = userId, @@ -1322,15 +1324,17 @@ class RbacPermissionManageFacadeServiceImpl( operateGroupMemberTask = ::handoverTask ) } - handoverAuthorizationsWhenOperatedGroups( - userId = userId, - projectCode = projectCode, - invalidPipelines = invalidPipelines, - invalidRepertoryIds = invalidRepertoryIds, - invalidEnvNodeIds = invalidEnvNodeIds, - handoverFrom = removeMemberDTO.targetMember.id, - handoverTo = removeMemberDTO.handoverTo!!.id - ) + if (invalidAuthorizationsDTO.isHasInvalidAuthorizations()) { + handoverAuthorizationsWhenOperatedGroups( + userId = userId, + projectCode = projectCode, + invalidPipelines = invalidPipelines, + invalidRepertoryIds = invalidRepertoryIds, + invalidEnvNodeIds = invalidEnvNodeIds, + handoverFrom = removeMemberDTO.targetMember.id, + handoverTo = removeMemberDTO.handoverTo!!.id + ) + } return true } From 89708769d31034e998ae72b2fe1bceb55be61588 Mon Sep 17 00:00:00 2001 From: greysonfang Date: Fri, 20 Dec 2024 11:11:34 +0800 Subject: [PATCH 06/33] =?UTF-8?q?feat=EF=BC=9A=E7=94=A8=E6=88=B7=E4=B8=AA?= =?UTF-8?q?=E4=BA=BA=E8=A7=86=E8=A7=92=20=E6=9D=83=E9=99=90=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E4=BC=98=E5=8C=96=20#11138?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../RbacPermissionManageFacadeServiceImpl.kt | 75 ++++++++++--------- .../SamplePermissionManageFacadeService.kt | 2 +- .../iam/PermissionManageFacadeService.kt | 9 ++- 3 files changed, 46 insertions(+), 40 deletions(-) diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionManageFacadeServiceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionManageFacadeServiceImpl.kt index 47e7d79e9fc8..8baecd0635ee 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionManageFacadeServiceImpl.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionManageFacadeServiceImpl.kt @@ -668,7 +668,7 @@ class RbacPermissionManageFacadeServiceImpl( override fun listInvalidAuthorizationsAfterOperatedGroups( projectCode: String, - iamGroupIds: List, + iamGroupIdsOfDirectlyJoined: List, memberId: String ): InvalidAuthorizationsDTO { val startEpoch = System.currentTimeMillis() @@ -677,13 +677,13 @@ class RbacPermissionManageFacadeServiceImpl( val iamGroupIdsOfNotExpired = getNotExpiredIamGroupIds( projectCode = projectCode, memberId = memberId, - iamGroupIds = iamGroupIds + iamGroupIds = iamGroupIdsOfDirectlyJoined ) - // 获取用户退出/交接以上用户组后,还未退出的用户组 + // 获取用户退出/交接以上用户组后,还未退出的用户组(包含组织/直接/模板加入的组) val (count, userGroupsJoinedAfterOperatedGroups) = listResourceGroupMembers( projectCode = projectCode, memberId = memberId, - excludeIamGroupIds = iamGroupIds, + excludeIamGroupIds = iamGroupIdsOfDirectlyJoined, onlyExcludeUserDirectlyJoined = true, operateChannel = OperateChannel.PERSONAL, minExpiredAt = LocalDateTime.now().timestampmilli() @@ -696,26 +696,33 @@ class RbacPermissionManageFacadeServiceImpl( ) logger.debug("whether the user has project visit perm after operated groups: {}", isHasProjectVisitPermAfterOperatedGroups) - if (count == 0L || !isHasProjectVisitPermAfterOperatedGroups) { - return getInvalidAuthorizationsAfterAllGroupsRemoved(projectCode, memberId, iamGroupIdsOfNotExpired) + val invalidAuthorizationsDTO = if (count == 0L || !isHasProjectVisitPermAfterOperatedGroups) { + // 若用户已退出了所有的用户组或失去了项目访问权限,则直接返回项目下所有的授权 + getInvalidAuthorizationsAfterAllGroupsRemoved( + projectCode = projectCode, + memberId = memberId, + iamGroupIdsOfNotExpired = iamGroupIdsOfNotExpired + ) + } else { + val (invalidGroups, invalidPipelines) = getInvalidPipelinesAfterOperatedGroups( + projectCode = projectCode, + iamGroupIds = iamGroupIdsOfDirectlyJoined, + memberId = memberId, + iamGroupIdsOfNotExpired = iamGroupIdsOfNotExpired + ) + InvalidAuthorizationsDTO( + invalidGroupIds = invalidGroups, + invalidPipelineIds = invalidPipelines + ) } - val (invalidGroups, invalidPipelines) = listInvalidPipelinesAfterOperatedGroups( - projectCode = projectCode, - iamGroupIds = iamGroupIds, - memberId = memberId - ) - val invalidAuthorizationsDTO = InvalidAuthorizationsDTO( - invalidGroupIds = invalidGroups, - invalidPipelineIds = invalidPipelines - ) logger.info( - "invalid authorizations after operated groups|$projectCode|$iamGroupIds|$memberId|$invalidAuthorizationsDTO" + "invalid authorizations after operated groups|$projectCode|$iamGroupIdsOfDirectlyJoined|$memberId|$invalidAuthorizationsDTO" ) return invalidAuthorizationsDTO } finally { logger.info( "It take(${System.currentTimeMillis() - startEpoch})ms to check invalid authorizations after operated groups" + - "|$projectCode|$iamGroupIds|$memberId" + "|$projectCode|$iamGroupIdsOfDirectlyJoined|$memberId" ) } } @@ -779,19 +786,14 @@ class RbacPermissionManageFacadeServiceImpl( ) } - private fun listInvalidPipelinesAfterOperatedGroups( + private fun getInvalidPipelinesAfterOperatedGroups( projectCode: String, iamGroupIds: List, - memberId: String + memberId: String, + iamGroupIdsOfNotExpired: List ): InvalidAuthorizationsDTO { logger.info("list invalid authorizations after operated groups:$projectCode|$iamGroupIds|$memberId") val now = LocalDateTime.now() - // 0.筛选出本次操作中的用户未过期用户组ID - val iamGroupIdsOfNotExpired = getNotExpiredIamGroupIds( - projectCode = projectCode, - memberId = memberId, - iamGroupIds = iamGroupIds - ) logger.debug("list iam group ids of not expired:{}", iamGroupIdsOfNotExpired) // 1.筛选出本次退出/交接中包含流水线执行权限的用户组 val operatedGroupsWithExecutePerm = groupPermissionService.listGroupsByPermissionConditions( @@ -830,7 +832,6 @@ class RbacPermissionManageFacadeServiceImpl( logger.debug("list pipeline and project groups joined after operated groups:{}", userGroupsJoinedAfterOperatedGroups) // 3.查询未退出的流水线/项目级别的用户组中是否包含项目级别的流水线执行权限。 - // 查询用户在未退出的用户组中否还有整个项目的流水线执行权限。若有的话,则对流水线的代持人权限未造成影响。 val hasAllPipelineExecutePermAfterOperateGroups = groupPermissionService.isGroupsHasProjectLevelPermission( projectCode = projectCode, filterIamGroupIds = userGroupsJoinedAfterOperatedGroups, @@ -842,7 +843,7 @@ class RbacPermissionManageFacadeServiceImpl( if (hasAllPipelineExecutePermAfterOperateGroups) return InvalidAuthorizationsDTO(emptyList(), emptyList()) - // 3.2.若没有的话,查询本次退出/交接的用户组中是否包含项目级别的流水线执行权限。 + // 3.2.若不包含整个项目的流水线执行权限,需查询本次退出/交接的用户组中是否包含项目级别的流水线执行权限。 val hasAllPipelineExecutePermInOperateGroups = groupPermissionService.isGroupsHasProjectLevelPermission( projectCode = projectCode, filterIamGroupIds = operatedGroupsWithExecutePerm, @@ -1020,7 +1021,7 @@ class RbacPermissionManageFacadeServiceImpl( val (invalidGroups, invalidPipelines, invalidRepertoryIds, invalidEnvNodeIds) = listInvalidAuthorizationsAfterOperatedGroups( projectCode = projectCode, - iamGroupIds = groupIds, + iamGroupIdsOfDirectlyJoined = groupIds, memberId = handoverMemberDTO.targetMember.id ) // 检查授予人是否有代码库oauth权限 @@ -1139,7 +1140,7 @@ class RbacPermissionManageFacadeServiceImpl( // 本次操作导致失效的授权 val invalidAuthorizations = listInvalidAuthorizationsAfterOperatedGroups( projectCode = projectCode, - iamGroupIds = groupIds, + iamGroupIdsOfDirectlyJoined = groupIds, memberId = handoverMemberDTO.targetMember.id ) @@ -1267,7 +1268,7 @@ class RbacPermissionManageFacadeServiceImpl( val invalidAuthorizationsDTO = listInvalidAuthorizationsAfterOperatedGroups( projectCode = projectCode, - iamGroupIds = groupIdsDirectlyJoined, + iamGroupIdsOfDirectlyJoined = groupIdsDirectlyJoined, memberId = removeMemberDTO.targetMember.id ) val (invalidGroups, invalidPipelines, invalidRepertoryIds, invalidEnvNodeIds) = invalidAuthorizationsDTO @@ -1361,7 +1362,7 @@ class RbacPermissionManageFacadeServiceImpl( val (invalidGroups, invalidPipelines, invalidRepertoryIds, invalidEnvNodeIds) = listInvalidAuthorizationsAfterOperatedGroups( projectCode = projectCode, - iamGroupIds = groupIds, + iamGroupIdsOfDirectlyJoined = groupIds, memberId = removeMemberDTO.targetMember.id ) @@ -1442,7 +1443,7 @@ class RbacPermissionManageFacadeServiceImpl( val (invalidGroups, invalidPipelines, invalidRepertoryIds, invalidEnvNodeIds) = listInvalidAuthorizationsAfterOperatedGroups( projectCode = projectCode, - iamGroupIds = listOf(groupId), + iamGroupIdsOfDirectlyJoined = listOf(groupId), memberId = targetMember.id ) if (invalidGroups.isNotEmpty() || invalidPipelines.isNotEmpty() || @@ -1618,7 +1619,7 @@ class RbacPermissionManageFacadeServiceImpl( val (invalidGroups, invalidPipelines, invalidRepositoryIds, invalidEnvNodeIds) = listInvalidAuthorizationsAfterOperatedGroups( projectCode = projectCode, - iamGroupIds = groupsOfDirectlyJoined, + iamGroupIdsOfDirectlyJoined = groupsOfDirectlyJoined, memberId = conditionReq.targetMember.id ) // 当批量移出时, @@ -1701,7 +1702,7 @@ class RbacPermissionManageFacadeServiceImpl( val (invalidGroups, invalidPipelines, invalidRepositoryIds, invalidEnvNodeIds) = listInvalidAuthorizationsAfterOperatedGroups( projectCode = projectCode, - iamGroupIds = groupsOfDirectlyJoined, + iamGroupIdsOfDirectlyJoined = groupsOfDirectlyJoined, memberId = conditionReq.targetMember.id ) @@ -1937,7 +1938,7 @@ class RbacPermissionManageFacadeServiceImpl( val (invalidGroups, invalidPipelines, invalidRepertoryIds, invalidEnvNodeIds) = listInvalidAuthorizationsAfterOperatedGroups( projectCode = projectCode, - iamGroupIds = groupIdsDirectlyJoined, + iamGroupIdsOfDirectlyJoined = groupIdsDirectlyJoined, memberId = previewConditionReq.targetMember.id ) if (batchOperateType == BatchOperateType.REMOVE) { @@ -2008,7 +2009,7 @@ class RbacPermissionManageFacadeServiceImpl( val (invalidGroups, invalidPipelines, invalidRepertoryIds, invalidEnvNodeIds) = listInvalidAuthorizationsAfterOperatedGroups( projectCode = projectCode, - iamGroupIds = groupIdsDirectlyJoined, + iamGroupIdsOfDirectlyJoined = groupIdsDirectlyJoined, memberId = previewConditionReq.targetMember.id ) @@ -2084,7 +2085,7 @@ class RbacPermissionManageFacadeServiceImpl( )[MemberType.USER] ?: return SQLPage(0, emptyList()) val invalidGroupIds = listInvalidAuthorizationsAfterOperatedGroups( projectCode = projectCode, - iamGroupIds = groupIdsDirectlyJoined, + iamGroupIdsOfDirectlyJoined = groupIdsDirectlyJoined, memberId = previewConditionReq.targetMember.id ).invalidGroupIds // 只有一个成员的管理员组 diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/service/SamplePermissionManageFacadeService.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/service/SamplePermissionManageFacadeService.kt index ddbd60004fdd..c439d1abd943 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/service/SamplePermissionManageFacadeService.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/service/SamplePermissionManageFacadeService.kt @@ -85,7 +85,7 @@ class SamplePermissionManageFacadeService : PermissionManageFacadeService { override fun listInvalidAuthorizationsAfterOperatedGroups( projectCode: String, - iamGroupIds: List, + iamGroupIdsOfDirectlyJoined: List, memberId: String ): InvalidAuthorizationsDTO = InvalidAuthorizationsDTO(emptyList(), emptyList()) diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/iam/PermissionManageFacadeService.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/iam/PermissionManageFacadeService.kt index 34c26a12e7e2..97c836b769c3 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/iam/PermissionManageFacadeService.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/iam/PermissionManageFacadeService.kt @@ -97,6 +97,7 @@ interface PermissionManageFacadeService { operateChannel: OperateChannel? = OperateChannel.MANAGER, filterMemberType: MemberType? = null, excludeIamGroupIds: List? = null, + /*与excludeIamGroupIds参数搭配使用,用于排除用户直接加入的组*/ onlyExcludeUserDirectlyJoined: Boolean? = false, start: Int? = null, limit: Int? = null @@ -110,7 +111,11 @@ interface PermissionManageFacadeService { ): SQLPage /** - * 为了避免流水线代持人/代码库oauth权限失效,需要对用户退出/交接用户组进行检查。 + * 为了避免流授权失效,需要对用户退出/交接用户组进行检查。 + * 入参: + * 1、项目ID + * 2、用户交接/移除的用户组(直接加入) + * 3、用户ID * 返回结果: * 1、引起代持人权限失效的用户组。 * 2、引起代持人权限失效的流水线。 @@ -119,7 +124,7 @@ interface PermissionManageFacadeService { **/ fun listInvalidAuthorizationsAfterOperatedGroups( projectCode: String, - iamGroupIds: List, + iamGroupIdsOfDirectlyJoined: List, memberId: String ): InvalidAuthorizationsDTO From fadc28ec73cc21e49e7e7b17f85462ae5ed9dbb9 Mon Sep 17 00:00:00 2001 From: greysonfang Date: Fri, 20 Dec 2024 14:40:00 +0800 Subject: [PATCH 07/33] =?UTF-8?q?feat=EF=BC=9A=E7=94=A8=E6=88=B7=E4=B8=AA?= =?UTF-8?q?=E4=BA=BA=E8=A7=86=E8=A7=92=20=E6=9D=83=E9=99=90=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E4=BC=98=E5=8C=96=20#11138?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pojo/vo/BatchOperateGroupMemberCheckVo.kt | 4 +++- .../RbacPermissionManageFacadeServiceImpl.kt | 18 ++++++++++-------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/vo/BatchOperateGroupMemberCheckVo.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/vo/BatchOperateGroupMemberCheckVo.kt index e208d27c74fc..7e862bd4ec11 100644 --- a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/vo/BatchOperateGroupMemberCheckVo.kt +++ b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/vo/BatchOperateGroupMemberCheckVo.kt @@ -21,5 +21,7 @@ data class BatchOperateGroupMemberCheckVo( @get:Schema(title = "无效的环境节点授权数量") val invalidEnvNodeAuthorizationCount: Int? = 0, @get:Schema(title = "可交接的组数量") - val canHandoverCount: Int? = 0 + val canHandoverCount: Int? = 0, + @get:Schema(title = "是否需要交接") + val needToHandover: Boolean? = null ) diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionManageFacadeServiceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionManageFacadeServiceImpl.kt index 8baecd0635ee..7c4a4b956386 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionManageFacadeServiceImpl.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionManageFacadeServiceImpl.kt @@ -1616,19 +1616,20 @@ class RbacPermissionManageFacadeServiceImpl( iamGroupIds = groupsOfDirectlyJoined ) // 本次操作导致流水线代持人权限受到影响的用户组及流水线/代码库oauth/环境节点 - val (invalidGroups, invalidPipelines, invalidRepositoryIds, invalidEnvNodeIds) = - listInvalidAuthorizationsAfterOperatedGroups( - projectCode = projectCode, - iamGroupIdsOfDirectlyJoined = groupsOfDirectlyJoined, - memberId = conditionReq.targetMember.id - ) + val invalidAuthorizationsDTO = listInvalidAuthorizationsAfterOperatedGroups( + projectCode = projectCode, + iamGroupIdsOfDirectlyJoined = groupsOfDirectlyJoined, + memberId = conditionReq.targetMember.id + ) + val (invalidGroups, invalidPipelines, invalidRepositoryIds, invalidEnvNodeIds) = invalidAuthorizationsDTO + // 当批量移出时, // 直接加入的组中,唯一管理员组/影响流水线代持权限不允许被移出 // 间接加入的组中,通过组织、模板加入的组不允许被移出 val groupsOfInOperableWhenBatchRemove = groupsOfDirectlyJoined.count { groupsOfUniqueManager.contains(it) || invalidGroups.contains(it) } + groupsOfTemplateOrDeptJoined.size - + val canHandoverCount = groupsOfUniqueManager.union(invalidGroups).size BatchOperateGroupMemberCheckVo( totalCount = totalCount, operableCount = totalCount - groupsOfInOperableWhenBatchRemove, @@ -1638,7 +1639,8 @@ class RbacPermissionManageFacadeServiceImpl( invalidPipelineAuthorizationCount = invalidPipelines.size, invalidRepositoryAuthorizationCount = invalidRepositoryIds.size, invalidEnvNodeAuthorizationCount = invalidEnvNodeIds.size, - canHandoverCount = groupsOfUniqueManager.union(invalidGroups).size + canHandoverCount = canHandoverCount, + needToHandover = invalidAuthorizationsDTO.isHasInvalidAuthorizations() || canHandoverCount > 0 ) } } From 86d215037553c67ca46e6e07067cbd6b7cfe1b6a Mon Sep 17 00:00:00 2001 From: greysonfang Date: Fri, 20 Dec 2024 16:57:06 +0800 Subject: [PATCH 08/33] =?UTF-8?q?feat=EF=BC=9A=E7=94=A8=E6=88=B7=E4=B8=AA?= =?UTF-8?q?=E4=BA=BA=E8=A7=86=E8=A7=92=20=E6=9D=83=E9=99=90=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E4=BC=98=E5=8C=96=20#11138?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../devops/auth/api/user/UserAuthResourceMemberResource.kt | 2 +- .../rbac/service/RbacPermissionManageFacadeServiceImpl.kt | 6 +++--- .../sample/service/SamplePermissionManageFacadeService.kt | 2 +- .../resources/user/UserAuthResourceMemberResourceImpl.kt | 2 +- .../auth/service/iam/PermissionManageFacadeService.kt | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/user/UserAuthResourceMemberResource.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/user/UserAuthResourceMemberResource.kt index 06664d8bb50d..1fc810115126 100644 --- a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/user/UserAuthResourceMemberResource.kt +++ b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/user/UserAuthResourceMemberResource.kt @@ -136,7 +136,7 @@ interface UserAuthResourceMemberResource { projectId: String, @Parameter(description = "批量移除成员请求实体") removeMemberDTO: GroupMemberRemoveConditionReq - ): Result + ): Result @DELETE @Path("/single/{groupId}/{operateChannel}/remove") diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionManageFacadeServiceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionManageFacadeServiceImpl.kt index 7c4a4b956386..190205c683fb 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionManageFacadeServiceImpl.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionManageFacadeServiceImpl.kt @@ -1343,13 +1343,13 @@ class RbacPermissionManageFacadeServiceImpl( userId: String, projectCode: String, removeMemberDTO: GroupMemberRemoveConditionReq - ): String? { + ): String { logger.info("batch delete group members from personal $userId|$projectCode|$removeMemberDTO") // 根据条件获取成员直接加入的用户组 val groupIds = getGroupIdsByGroupMemberCondition( projectCode = projectCode, commonCondition = removeMemberDTO - )[MemberType.USER]?.toMutableList() ?: return null + )[MemberType.USER]?.toMutableList() ?: return "true" // 过滤掉审核中的用户组 val beingHandoverGroups = permissionHandoverApplicationService.listMemberHandoverDetails( @@ -1402,7 +1402,7 @@ class RbacPermissionManageFacadeServiceImpl( operateGroupMemberTask = ::deleteTask ) if (toHandoverGroups.isEmpty() && invalidPipelines.isEmpty() && invalidRepertoryIds.isEmpty() && invalidEnvNodeIds.isEmpty()) { - return null + return "true" } val handoverDetails = buildHandoverDetails( projectCode = projectCode, diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/service/SamplePermissionManageFacadeService.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/service/SamplePermissionManageFacadeService.kt index c439d1abd943..97a78367e418 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/service/SamplePermissionManageFacadeService.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/service/SamplePermissionManageFacadeService.kt @@ -123,7 +123,7 @@ class SamplePermissionManageFacadeService : PermissionManageFacadeService { userId: String, projectCode: String, removeMemberDTO: GroupMemberRemoveConditionReq - ): String? = null + ): String = "true" override fun deleteResourceGroupMembers( userId: String, diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/user/UserAuthResourceMemberResourceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/user/UserAuthResourceMemberResourceImpl.kt index 46f62da6ae14..617e22a6197a 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/user/UserAuthResourceMemberResourceImpl.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/user/UserAuthResourceMemberResourceImpl.kt @@ -133,7 +133,7 @@ class UserAuthResourceMemberResourceImpl( userId: String, projectId: String, removeMemberDTO: GroupMemberRemoveConditionReq - ): Result { + ): Result { permissionResourceValidateService.validateUserProjectPermissionByChannel( userId = userId, projectCode = projectId, diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/iam/PermissionManageFacadeService.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/iam/PermissionManageFacadeService.kt index 97c836b769c3..ea693fbedf60 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/iam/PermissionManageFacadeService.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/iam/PermissionManageFacadeService.kt @@ -180,7 +180,7 @@ interface PermissionManageFacadeService { userId: String, projectCode: String, removeMemberDTO: GroupMemberRemoveConditionReq - ): String? + ): String /** * 退出单个组 From f6b2cb774aefe13fe62fe12d1b26edc4fe9d2586 Mon Sep 17 00:00:00 2001 From: greysonfang Date: Mon, 23 Dec 2024 16:46:21 +0800 Subject: [PATCH 09/33] =?UTF-8?q?feat=EF=BC=9A=E7=94=A8=E6=88=B7=E4=B8=AA?= =?UTF-8?q?=E4=BA=BA=E8=A7=86=E8=A7=92=20=E6=9D=83=E9=99=90=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E4=BC=98=E5=8C=96=20#11138?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...bacPermissionHandoverApplicationService.kt | 16 +++-- .../RbacPermissionManageFacadeServiceImpl.kt | 65 +++++++++---------- .../PermissionHandoverApplicationService.kt | 4 +- .../i18n/auth/message_en_US.properties | 2 +- support-files/i18n/notify/template_en_US.yaml | 46 +++++++++++++ support-files/i18n/notify/template_zh_CN.yaml | 46 +++++++++++++ support-files/sql/1001_ci_auth_ddl_mysql.sql | 3 +- .../2030_ci_auth-update_v3.0_mysql.sql | 2 +- 8 files changed, 140 insertions(+), 44 deletions(-) diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionHandoverApplicationService.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionHandoverApplicationService.kt index ac33d5892ab1..828e63ac0124 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionHandoverApplicationService.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionHandoverApplicationService.kt @@ -39,6 +39,7 @@ import org.jooq.impl.DSL import org.slf4j.LoggerFactory import java.time.LocalDateTime +@Suppress("ALL") class RbacPermissionHandoverApplicationService( private val dslContext: DSLContext, private val handoverOverviewDao: AuthHandoverOverviewDao, @@ -124,8 +125,8 @@ class RbacPermissionHandoverApplicationService( var handoverOverviewContentOfRtx = "" when { groupCount > 0 && authorizationCount > 0 -> { - titleOfApplication = titleOfApplication.plus(groupCount).plus( - bkHandoverGroups.plus(",").plus(authorizationCount).plus(bkHandoverAuthorizations) + titleOfApplication = titleOfApplication.plus(" $groupCount ").plus( + bkHandoverGroups.plus(",").plus(" $authorizationCount ").plus(bkHandoverAuthorizations) ) handoverOverviewContentOfEmail = """$groupCount$bkHandoverGroups,$authorizationCount$bkHandoverAuthorizations""".trimMargin() handoverOverviewContentOfRtx = handoverOverviewContentOfRtx.plus(groupCount).plus( @@ -134,13 +135,13 @@ class RbacPermissionHandoverApplicationService( } groupCount > 0 -> { - titleOfApplication = titleOfApplication.plus(groupCount).plus(bkHandoverGroups) + titleOfApplication = titleOfApplication.plus(" $groupCount ").plus(bkHandoverGroups) handoverOverviewContentOfEmail = """$groupCount$bkHandoverGroups""".trimMargin() handoverOverviewContentOfRtx = handoverOverviewContentOfRtx.plus(groupCount).plus(bkHandoverGroups) } else -> { - titleOfApplication = titleOfApplication.plus(authorizationCount).plus(bkHandoverAuthorizations) + titleOfApplication = titleOfApplication.plus(" $authorizationCount ").plus(bkHandoverAuthorizations) handoverOverviewContentOfEmail = """$authorizationCount$bkHandoverAuthorizations""".trimMargin() handoverOverviewContentOfRtx = handoverOverviewContentOfRtx.plus(authorizationCount).plus(bkHandoverAuthorizations) } @@ -156,7 +157,9 @@ class RbacPermissionHandoverApplicationService( * */ override fun generateFlowNo(): String { val currentTime = DateTimeUtil.toDateTime(LocalDateTime.now(), DateTimeUtil.YYYYMMDD) - val incrementedValue = redisOperation.increment(String.format(FLOW_NO_KEY, currentTime), 1) + val key = String.format(FLOW_NO_KEY, currentTime) + val incrementedValue = redisOperation.increment(key, 1) + redisOperation.expire(key, 3600 * 24) val formattedIncrementedValue = String.format("%05d", incrementedValue) return FLOW_NO_PREFIX + currentTime + formattedIncrementedValue } @@ -353,7 +356,8 @@ class RbacPermissionHandoverApplicationService( ).map { it.copy(approver = flowNo2Approver[it.flowNo]) } } - private val handoverApplicationUrl = "${config.devopsHostGateway}/console/permission/my-handover?type=handoverToMe&flowNo=%s" + private val handoverApplicationUrl = "${config.devopsHostGateway}/console/permission/my-handover?" + + "type=handoverToMe&flowNo=%s" companion object { private val logger = LoggerFactory.getLogger(RbacPermissionHandoverApplicationService::class.java) diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionManageFacadeServiceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionManageFacadeServiceImpl.kt index 190205c683fb..5217956eae7b 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionManageFacadeServiceImpl.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionManageFacadeServiceImpl.kt @@ -1480,35 +1480,16 @@ class RbacPermissionManageFacadeServiceImpl( ) val currentTimeSeconds = System.currentTimeMillis() / 1000 var finalExpiredAt = expiredAt - when { - // 若权限已过期,如果是唯一管理员组,允许交接,交接人将获得半年权限;其他的直接删除。 - expiredAt < currentTimeSeconds -> { - val isUniqueManagerGroup = authResourceGroupMemberDao.listProjectUniqueManagerGroups( - dslContext = dslContext, - projectCode = projectCode, - iamGroupIds = listOf(groupId) - ).isNotEmpty() - if (isUniqueManagerGroup) { - finalExpiredAt = currentTimeSeconds + TimeUnit.DAYS.toSeconds(180) - } else { - deleteTask( - projectCode = projectCode, - groupId = groupId, - removeMemberDTO = GroupMemberRemoveConditionReq( - targetMember = handoverMemberDTO.targetMember - ), - expiredAt = finalExpiredAt - ) - return - } - } - // 若交接人已经在用户组内,无需交接。 - authResourceGroupMemberDao.isMemberInGroup( + // 若交接人的权限已过期,如果是唯一管理员组,允许交接,接收人将获得半年权限;其他的直接删除。 + if (expiredAt < currentTimeSeconds) { + val isUniqueManagerGroup = authResourceGroupMemberDao.listProjectUniqueManagerGroups( dslContext = dslContext, projectCode = projectCode, - iamGroupId = groupId, - memberId = handoverMemberDTO.handoverTo.id - ) -> { + iamGroupIds = listOf(groupId) + ).isNotEmpty() + if (isUniqueManagerGroup) { + finalExpiredAt = currentTimeSeconds + TimeUnit.DAYS.toSeconds(180) + } else { deleteTask( projectCode = projectCode, groupId = groupId, @@ -1521,18 +1502,33 @@ class RbacPermissionManageFacadeServiceImpl( } } - val members = listOf( - ManagerMember( - handoverMemberDTO.handoverTo.type, - handoverMemberDTO.handoverTo.id - ) + val isHandoverToInGroup = authResourceGroupMemberDao.isMemberInGroup( + dslContext = dslContext, + projectCode = projectCode, + iamGroupId = groupId, + memberId = handoverMemberDTO.handoverTo.id ) + if (isHandoverToInGroup) { + deleteTask( + projectCode = projectCode, + groupId = groupId, + removeMemberDTO = GroupMemberRemoveConditionReq( + targetMember = handoverMemberDTO.handoverTo + ), + expiredAt = finalExpiredAt + ) + } if (finalExpiredAt < currentTimeSeconds) { throw ErrorCodeException( errorCode = AuthMessageCode.INVALID_EXPIRED_PERM_NOT_ALLOW_TO_HANDOVER ) } - + val members = listOf( + ManagerMember( + handoverMemberDTO.handoverTo.type, + handoverMemberDTO.handoverTo.id + ) + ) permissionResourceMemberService.addIamGroupMember( groupId = groupId, members = members, @@ -2329,7 +2325,8 @@ class RbacPermissionManageFacadeServiceImpl( } } - private val handoverApplicationUrl = "${config.devopsHostGateway}/console/permission/my-handover?type=handoverFromMe&flowNo=%s" + private val handoverApplicationUrl = "${config.devopsHostGateway}/console/permission/my-handover?" + + "type=handoverFromMe&flowNo=%s" companion object { private val logger = LoggerFactory.getLogger(RbacPermissionResourceMemberService::class.java) diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/iam/PermissionHandoverApplicationService.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/iam/PermissionHandoverApplicationService.kt index ccb6f2e9b2b5..9565b9c44a1b 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/iam/PermissionHandoverApplicationService.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/iam/PermissionHandoverApplicationService.kt @@ -64,7 +64,9 @@ interface PermissionHandoverApplicationService { /** * 获取交接单中授权相关 * */ - fun listAuthorizationsOfHandoverApplication(queryReq: HandoverDetailsQueryReq): SQLPage + fun listAuthorizationsOfHandoverApplication( + queryReq: HandoverDetailsQueryReq + ): SQLPage /** * 获取交接单中用户组相关 diff --git a/support-files/i18n/auth/message_en_US.properties b/support-files/i18n/auth/message_en_US.properties index ddb8b3352762..aea842ed4fd3 100644 --- a/support-files/i18n/auth/message_en_US.properties +++ b/support-files/i18n/auth/message_en_US.properties @@ -344,4 +344,4 @@ bkMemberExpiredAtDisplayPermanent=permanent bkApplyToHandover=apply to handover of bkHandoverGroups= groups bkHandoverAuthorizations= authorizations -bk_project=bkci project +bk_project=BK-CI project diff --git a/support-files/i18n/notify/template_en_US.yaml b/support-files/i18n/notify/template_en_US.yaml index 3ee745dac18e..3e07617d7e3f 100644 --- a/support-files/i18n/notify/template_en_US.yaml +++ b/support-files/i18n/notify/template_en_US.yaml @@ -1009,3 +1009,49 @@ body: "The store component [${name}]V${version} has been reviewed and approved and successfully released" creator: "system" modifior: "system" +- index: 48 + id: "0f7204084e7c46da8702e3a5c377cd31" + templateCode: "BK_PERMISSIONS_HANDOVER_APPLICATION" + templateName: "BK-CI Permission handover application" + notifyTypeScope: + - "EMAIL" + - "WEWORK" + priority: 0 + source: 0 + emailTemplate: + id: "d57d7358f5034b21acafde62f27d75fd" + title: "BK-CI Permission handover application" + body: "BK-CI

BK-CI Permission handover application

Hi,${handoverTo}:

${handoverFrom}has applied to handover the permissions under the BK-CI project[${projectName}]to you.Please confirm whether to accept it./p>

Handle it immediately
" + sender: "DevOps" + bodyFormat: 1 + emailType: 1 + weworkTemplate: + id: "84f28ea9f6c44afaa3b7777cc286c18c" + sender: "DevOps" + title: "BK-CI Permission handover application" + body: "Hi,${handoverTo}: \n${handoverFrom} has applied to handover the permissions under the BK-CI project [${projectName}] to you. Please confirm whether to accept it. Handle it immediately :${url}" + creator: "system" + modifior: "system" +- index: 49 + id: "1519a9a0b92b44fe8e8e813f2b52e012" + templateCode: "BK_PERMISSIONS_HANDOVER_APPLICATION_RESULT" + templateName: "BK-CI Permission handover application result" + notifyTypeScope: + - "EMAIL" + - "WEWORK" + priority: 0 + source: 0 + emailTemplate: + id: "902b30b05d184f478ea8eaaea53533f1" + title: "BK-CI Permission handover application result" + body: "BK-CI

BK-CI Permission handover application result

The permission handover process for your BK-CI project[${projectName}]has ended

See more details
" + sender: "DevOps" + bodyFormat: 1 + emailType: 1 + weworkTemplate: + id: "4ead2e88f2a94fcbb7b1ffc5c523f742" + sender: "DevOps" + title: "BK-CI Permission handover application result" + body: "The permission handover process for your BK-CI project [${projectName}] has ended.See more details:${url}" + creator: "system" + modifior: "system" diff --git a/support-files/i18n/notify/template_zh_CN.yaml b/support-files/i18n/notify/template_zh_CN.yaml index bbb77cdb617d..7e97747a78ba 100644 --- a/support-files/i18n/notify/template_zh_CN.yaml +++ b/support-files/i18n/notify/template_zh_CN.yaml @@ -1009,3 +1009,49 @@ body: "研发商店组件【${name}】V${version} 已经审核通过,成功发布" creator: "system" modifior: "system" +- index: 48 + id: "0f7204084e7c46da8702e3a5c377cd31" + templateCode: "BK_PERMISSIONS_HANDOVER_APPLICATION" + templateName: "蓝盾权限交接申请" + notifyTypeScope: + - "EMAIL" + - "WEWORK" + priority: 0 + source: 0 + emailTemplate: + id: "d57d7358f5034b21acafde62f27d75fd" + title: "蓝盾权限交接申请" + body: "蓝盾DevOps平台

蓝盾权限交接申请

您好,${handoverTo}:

${handoverFrom}已申请将蓝盾项目【${projectName}】下的${handoverOverviews}移交给您,请确认是否接收。

${table}
交接类型资源类型数量
马上处理
" + sender: "DevOps" + bodyFormat: 1 + emailType: 1 + weworkTemplate: + id: "84f28ea9f6c44afaa3b7777cc286c18c" + sender: "DevOps" + title: "【蓝盾权限】移交通知" + body: "${handoverFrom}已申请将蓝盾项目【${projectName}】下的 ${handoverOverviewContentOfRtx}移交给您,请确认是否接收 \n去处理:${url}" + creator: "system" + modifior: "system" +- index: 49 + id: "1519a9a0b92b44fe8e8e813f2b52e012" + templateCode: "BK_PERMISSIONS_HANDOVER_APPLICATION_RESULT" + templateName: "蓝盾权限交接审批结果" + notifyTypeScope: + - "EMAIL" + - "WEWORK" + priority: 0 + source: 0 + emailTemplate: + id: "902b30b05d184f478ea8eaaea53533f1" + title: "蓝盾权限交接审批结果" + body: "蓝盾DevOps平台

蓝盾权限交接审批结果

您在蓝盾项目【${projectName}】下的权限移交${result}

查看更多详情
" + sender: "DevOps" + bodyFormat: 1 + emailType: 1 + weworkTemplate: + id: "4ead2e88f2a94fcbb7b1ffc5c523f742" + sender: "DevOps" + title: "【蓝盾权限】移交审批结果通知" + body: "您在蓝盾项目【${projectName}】下的权限移交${result},更多详情查看:${url}" + creator: "system" + modifior: "system" diff --git a/support-files/sql/1001_ci_auth_ddl_mysql.sql b/support-files/sql/1001_ci_auth_ddl_mysql.sql index a9f2fc071dc5..8d8dc3ddc7e9 100644 --- a/support-files/sql/1001_ci_auth_ddl_mysql.sql +++ b/support-files/sql/1001_ci_auth_ddl_mysql.sql @@ -392,7 +392,8 @@ CREATE TABLE IF NOT EXISTS T_AUTH_RESOURCE_AUTHORIZATION ( `CREATE_TIME` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `UPDATE_TIME` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间', PRIMARY KEY (`ID`), - UNIQUE KEY `UNIQ_PROJECT_RESOURCE` (`PROJECT_CODE`, `RESOURCE_TYPE`, `RESOURCE_CODE`) + UNIQUE KEY `UNIQ_PROJECT_RESOURCE` (`PROJECT_CODE`, `RESOURCE_TYPE`, `RESOURCE_CODE`), + INDEX `IDX_HANDOVER_FROM_PROJECT_CODE` (`HANDOVER_FROM`,`PROJECT_CODE`) ) ENGINE = InnoDB CHARSET = utf8mb4 COMMENT '资源授权管理表'; CREATE TABLE IF NOT EXISTS `T_AUTH_RESOURCE_SYNC` diff --git a/support-files/sql/2004_v3.x/2030_ci_auth-update_v3.0_mysql.sql b/support-files/sql/2004_v3.x/2030_ci_auth-update_v3.0_mysql.sql index 0d6d3065c783..ffb70cc94afa 100644 --- a/support-files/sql/2004_v3.x/2030_ci_auth-update_v3.0_mysql.sql +++ b/support-files/sql/2004_v3.x/2030_ci_auth-update_v3.0_mysql.sql @@ -43,7 +43,7 @@ BEGIN WHERE TABLE_SCHEMA = db AND TABLE_NAME = 'T_AUTH_RESOURCE_AUTHORIZATION' AND INDEX_NAME = 'HANDOVER_FROM_PROJECT_CODE_INDEX') THEN - ALTER TABLE T_AUTH_RESOURCE_AUTHORIZATION ADD INDEX `HANDOVER_FROM_PROJECT_CODE_INDEX` (`HANDOVER_FROM`,`PROJECT_CODE`); + ALTER TABLE T_AUTH_RESOURCE_AUTHORIZATION ADD INDEX `IDX_HANDOVER_FROM_PROJECT_CODE` (`HANDOVER_FROM`,`PROJECT_CODE`); END IF; COMMIT; END From 2368bdd18c38466cd27c5ea42b6c6cdbfe3acf8a Mon Sep 17 00:00:00 2001 From: greysonfang Date: Mon, 23 Dec 2024 16:53:48 +0800 Subject: [PATCH 10/33] =?UTF-8?q?feat=EF=BC=9A=E7=94=A8=E6=88=B7=E4=B8=AA?= =?UTF-8?q?=E4=BA=BA=E8=A7=86=E8=A7=92=20=E6=9D=83=E9=99=90=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E4=BC=98=E5=8C=96=20#11138?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- support-files/sql/1001_ci_auth_ddl_mysql.sql | 2 +- support-files/sql/2004_v3.x/2030_ci_auth-update_v3.0_mysql.sql | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/support-files/sql/1001_ci_auth_ddl_mysql.sql b/support-files/sql/1001_ci_auth_ddl_mysql.sql index 8d8dc3ddc7e9..fcd492157670 100644 --- a/support-files/sql/1001_ci_auth_ddl_mysql.sql +++ b/support-files/sql/1001_ci_auth_ddl_mysql.sql @@ -393,7 +393,7 @@ CREATE TABLE IF NOT EXISTS T_AUTH_RESOURCE_AUTHORIZATION ( `UPDATE_TIME` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间', PRIMARY KEY (`ID`), UNIQUE KEY `UNIQ_PROJECT_RESOURCE` (`PROJECT_CODE`, `RESOURCE_TYPE`, `RESOURCE_CODE`), - INDEX `IDX_HANDOVER_FROM_PROJECT_CODE` (`HANDOVER_FROM`,`PROJECT_CODE`) + INDEX `HANDOVER_FROM_PROJECT_CODE_INDEX` (`HANDOVER_FROM`,`PROJECT_CODE`) ) ENGINE = InnoDB CHARSET = utf8mb4 COMMENT '资源授权管理表'; CREATE TABLE IF NOT EXISTS `T_AUTH_RESOURCE_SYNC` diff --git a/support-files/sql/2004_v3.x/2030_ci_auth-update_v3.0_mysql.sql b/support-files/sql/2004_v3.x/2030_ci_auth-update_v3.0_mysql.sql index ffb70cc94afa..0d6d3065c783 100644 --- a/support-files/sql/2004_v3.x/2030_ci_auth-update_v3.0_mysql.sql +++ b/support-files/sql/2004_v3.x/2030_ci_auth-update_v3.0_mysql.sql @@ -43,7 +43,7 @@ BEGIN WHERE TABLE_SCHEMA = db AND TABLE_NAME = 'T_AUTH_RESOURCE_AUTHORIZATION' AND INDEX_NAME = 'HANDOVER_FROM_PROJECT_CODE_INDEX') THEN - ALTER TABLE T_AUTH_RESOURCE_AUTHORIZATION ADD INDEX `IDX_HANDOVER_FROM_PROJECT_CODE` (`HANDOVER_FROM`,`PROJECT_CODE`); + ALTER TABLE T_AUTH_RESOURCE_AUTHORIZATION ADD INDEX `HANDOVER_FROM_PROJECT_CODE_INDEX` (`HANDOVER_FROM`,`PROJECT_CODE`); END IF; COMMIT; END From f57240496fc0cba2d1be3549ac996627ebdb64de Mon Sep 17 00:00:00 2001 From: greysonfang Date: Mon, 23 Dec 2024 17:39:23 +0800 Subject: [PATCH 11/33] =?UTF-8?q?feat=EF=BC=9A=E7=94=A8=E6=88=B7=E4=B8=AA?= =?UTF-8?q?=E4=BA=BA=E8=A7=86=E8=A7=92=20=E6=9D=83=E9=99=90=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E4=BC=98=E5=8C=96=20#11138?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tencent/devops/project/service/impl/AbsProjectServiceImpl.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/src/backend/ci/core/project/biz-project/src/main/kotlin/com/tencent/devops/project/service/impl/AbsProjectServiceImpl.kt b/src/backend/ci/core/project/biz-project/src/main/kotlin/com/tencent/devops/project/service/impl/AbsProjectServiceImpl.kt index dab3594f4b5f..31bed594fb25 100644 --- a/src/backend/ci/core/project/biz-project/src/main/kotlin/com/tencent/devops/project/service/impl/AbsProjectServiceImpl.kt +++ b/src/backend/ci/core/project/biz-project/src/main/kotlin/com/tencent/devops/project/service/impl/AbsProjectServiceImpl.kt @@ -818,7 +818,6 @@ abstract class AbsProjectServiceImpl @Autowired constructor( userId = userId, accessToken = accessToken ).toSet() - if (projectsWithVisitPermission.isEmpty() && !unApproved) { return emptyList() } From 7d7418962cd498fe5c8471b154e4820dba430718 Mon Sep 17 00:00:00 2001 From: greysonfang Date: Mon, 23 Dec 2024 20:10:27 +0800 Subject: [PATCH 12/33] =?UTF-8?q?feat=EF=BC=9A=E7=94=A8=E6=88=B7=E4=B8=AA?= =?UTF-8?q?=E4=BA=BA=E8=A7=86=E8=A7=92=20=E6=9D=83=E9=99=90=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E4=BC=98=E5=8C=96=20#11138?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/archive/client/BkRepoClient.kt | 60 ++++++++++--------- 1 file changed, 31 insertions(+), 29 deletions(-) diff --git a/src/backend/ci/core/common/common-archive/src/main/kotlin/com/tencent/devops/common/archive/client/BkRepoClient.kt b/src/backend/ci/core/common/common-archive/src/main/kotlin/com/tencent/devops/common/archive/client/BkRepoClient.kt index dee6e2c49048..f1f4e6cb4dfc 100644 --- a/src/backend/ci/core/common/common-archive/src/main/kotlin/com/tencent/devops/common/archive/client/BkRepoClient.kt +++ b/src/backend/ci/core/common/common-archive/src/main/kotlin/com/tencent/devops/common/archive/client/BkRepoClient.kt @@ -164,7 +164,9 @@ class BkRepoClient constructor( fun enableProject(userId: String, projectId: String, enabled: Boolean): Boolean { logger.info("enableProject, userId: $userId, projectId: $projectId, enabled: $enabled") - val requestData = BKRepoProjectUpdateRequest(metadata = listOf(ProjectMetadata(key = "enabled", value = enabled))) + val requestData = BKRepoProjectUpdateRequest( + metadata = listOf(ProjectMetadata(key = "enabled", value = enabled)) + ) val request = Request.Builder() .url("${getGatewayUrl()}/bkrepo/api/service/repository/api/project/$projectId") .headers(getCommonHeaders(SYSTEM_USER, projectId).toHeaders()) @@ -214,7 +216,7 @@ class BkRepoClient constructor( fun setMetadata(userId: String, projectId: String, repoName: String, path: String, metadata: Map) { logger.info( "setMetadata, userId: $userId, projectId: $projectId, repoName: $repoName, path: $path," + - " metadata: $metadata" + " metadata: $metadata" ) val url = "${getGatewayUrl()}/bkrepo/api/service/repository/api/metadata/$projectId/$repoName/$path" val requestData = UserMetadataSaveRequest( @@ -249,10 +251,10 @@ class BkRepoClient constructor( ): List { logger.info( "listFile, userId: $userId, projectId: $projectId, repoName: $repoName, path: $path," + - " includeFolders: $includeFolders, deep: $deep" + " includeFolders: $includeFolders, deep: $deep" ) val url = "${getGatewayUrl()}/bkrepo/api/service/generic/list/$projectId/$repoName/$path" + - "?deep=$deep&includeFolder=$includeFolders" + "?deep=$deep&includeFolder=$includeFolders" val request = Request.Builder() .url(url) .headers(getCommonHeaders(userId, projectId).toHeaders()) @@ -274,12 +276,12 @@ class BkRepoClient constructor( ): Page { logger.info( "listFilePage, userId: $userId, projectId: $projectId, repoName: $repoName, path: $path," + - " includeFolders: $includeFolders, deep: $deep, page: $page, pageSize: $pageSize" + " includeFolders: $includeFolders, deep: $deep, page: $page, pageSize: $pageSize" ) val direction = if (modifiedTimeDesc) Direction.DESC.name else Direction.ASC.name val url = "${getGatewayUrl()}/bkrepo/api/service/repository/api/node/page/$projectId/$repoName/$path" + - "?deep=$deep&includeFolder=$includeFolders&includeMetadata=true&pageNumber=$page&pageSize=$pageSize" + - "&sortProperty=lastModifiedDate&direction=$direction" + "?deep=$deep&includeFolder=$includeFolders&includeMetadata=true&pageNumber=$page&pageSize=$pageSize" + + "&sortProperty=lastModifiedDate&direction=$direction" val request = Request.Builder() .url(url) .headers(getCommonHeaders(userId, projectId).toHeaders()) @@ -300,7 +302,7 @@ class BkRepoClient constructor( ) { logger.info( "uploadFile, userId: $userId, projectId: $projectId, repoName: $repoName, path: $path," + - " fileSizeLimitInMB: $fileSizeLimitInMB" + " fileSizeLimitInMB: $fileSizeLimitInMB" ) if (PathUtil.isFolder(path)) { throw ErrorCodeException(errorCode = INVALID_CUSTOM_ARTIFACTORY_PATH) @@ -353,7 +355,7 @@ class BkRepoClient constructor( fun uploadLocalFile(userId: String, projectId: String, repoName: String, path: String, file: File) { logger.info( "uploadLocalFile, userId: $userId, projectId: $projectId, repoName: $repoName, path: $path," + - " localFile: ${file.canonicalPath}" + " localFile: ${file.canonicalPath}" ) uploadLocalFile( userId = userId, @@ -380,7 +382,7 @@ class BkRepoClient constructor( ) { logger.info( "uploadLocalFile, projectId: $projectId, repoName: $repoName, path: $path," + - " localFile: ${file.canonicalPath}" + " localFile: ${file.canonicalPath}" ) val gateway = gatewayUrl ?: getGatewayUrl() val repoUrlPrefix = if (gatewayFlag) "$gateway/bkrepo/api/service/generic" else bkrepoApiUrl @@ -436,7 +438,7 @@ class BkRepoClient constructor( fun move(userId: String, projectId: String, repoName: String, fromPath: String, toPath: String) { logger.info( "move, userId: $userId, projectId: $projectId, repoName: $repoName, fromPath: $fromPath," + - " toPath: $toPath" + " toPath: $toPath" ) val url = "${getGatewayUrl()}/bkrepo/api/service/repository/api/node/move" val requestData = UserNodeMoveCopyRequest( @@ -468,7 +470,7 @@ class BkRepoClient constructor( ) { logger.info( "copy, userId: $userId, fromProject: $fromProject, fromRepo: $fromRepo, fromPath: $fromPath," + - " toProject: $toProject, toRepo: $toRepo, toPath: $toPath" + " toProject: $toProject, toRepo: $toRepo, toPath: $toPath" ) val url = "${getGatewayUrl()}/bkrepo/api/service/repository/api/node/copy" val requestData = UserNodeMoveCopyRequest( @@ -493,7 +495,7 @@ class BkRepoClient constructor( fun rename(userId: String, projectId: String, repoName: String, fromPath: String, toPath: String) { logger.info( "rename, userId: $userId, projectId: $projectId, repoName: $repoName, fromPath: $fromPath," + - " toPath: $toPath" + " toPath: $toPath" ) val url = "${getGatewayUrl()}/bkrepo/api/service/repository/api/node/rename" val requestData = UserNodeRenameRequest(projectId, repoName, fromPath, toPath) @@ -542,7 +544,7 @@ class BkRepoClient constructor( ): List { logger.info( "matchBkRepoFile, userId: $userId, srcPath: $srcPath, projectId: $projectId," + - " pipelineId: $pipelineId, buildId: $buildId, isCustom: $isCustom" + " pipelineId: $pipelineId, buildId: $buildId, isCustom: $isCustom" ) val repoName: String val filePath: String @@ -639,7 +641,7 @@ class BkRepoClient constructor( ): List { logger.info( "listFileByPattern, userId: $userId, projectId: $projectId, pipelineId: $pipelineId," + - " buildId: $buildId, repoName: $repoName, pathPattern: $pathPattern" + " buildId: $buildId, repoName: $repoName, pathPattern: $pathPattern" ) return if (pathPattern.endsWith("/")) { val path = if (repoName == "pipeline") { @@ -682,7 +684,7 @@ class BkRepoClient constructor( ): List { logger.info( "downloadFileByPattern, userId: $userId, projectId: $projectId, pipelineId: $pipelineId," + - " buildId: $buildId, repoName: $repoName, pathPattern: $pathPattern, destPath: $destPath" + " buildId: $buildId, repoName: $repoName, pathPattern: $pathPattern, destPath: $destPath" ) val fileList = listFileByPattern( userId, @@ -718,8 +720,8 @@ class BkRepoClient constructor( ): String { logger.info( "createShareUri, creatorId: $creatorId, projectId: $projectId, repoName: $repoName, " + - "fullPath: $fullPath, downloadUsers: $downloadUsers, downloadIps: $downloadIps, " + - "timeoutInSeconds: $timeoutInSeconds" + "fullPath: $fullPath, downloadUsers: $downloadUsers, downloadIps: $downloadIps, " + + "timeoutInSeconds: $timeoutInSeconds" ) val repoUrlPrefix = if (bkrepoPrefixUrl.isNullOrBlank()) { "${getGatewayUrl()}/bkrepo/api/service" @@ -778,7 +780,7 @@ class BkRepoClient constructor( ): ApkDefenderTasks { logger.info( "apkDefender , projectId: $projectId , repoName: $repoName , fullPath: $fullPath , " + - "userIds: $userIds, batchSize: $batchSize" + "userIds: $userIds, batchSize: $batchSize" ) val url = "${getGatewayUrl()}/bkrepo/api/external/analyst/api/ext/apk/defender" val apkDefenderRequest = ApkDefenderRequest( @@ -844,8 +846,8 @@ class BkRepoClient constructor( ): List { logger.info( "createTemporaryAccessUrl, userId: $userId, projectId: $projectId, repoName: $repoName, " + - "fullPathSet: $fullPathSet, downloadUsersSet: $downloadUsersSet, downloadIps: $downloadIpsSet," + - " timeoutInSeconds: $timeoutInSeconds" + "fullPathSet: $fullPathSet, downloadUsersSet: $downloadUsersSet, downloadIps: $downloadIpsSet," + + " timeoutInSeconds: $timeoutInSeconds" ) val url = "${getGatewayUrl()}/bkrepo/api/service/generic/temporary/url/create" val requestData = TemporaryTokenCreateRequest( @@ -878,7 +880,7 @@ class BkRepoClient constructor( ): QueryData { logger.info( "queryByRepoAndMetadata, userId: $userId, projectId: $projectId, repoNames: $repoNames," + - " fileNames: $fileNames, metadata: $metadata, page: $page, pageSize: $pageSize" + " fileNames: $fileNames, metadata: $metadata, page: $page, pageSize: $pageSize" ) val projectRule = Rule.QueryRule("projectId", projectId, OperationType.EQ) val ruleList = mutableListOf(projectRule, Rule.QueryRule("folder", false, OperationType.EQ)) @@ -915,8 +917,8 @@ class BkRepoClient constructor( ): QueryData { logger.info( "queryByPathEqOrNameMatchOrMetadataEqAnd, userId: $userId, projectId: $projectId," + - " repoNames: $repoNames, filePaths: $filePaths, fileNames: $fileNames, metadata: $metadata," + - " page: $page, pageSize: $pageSize" + " repoNames: $repoNames, filePaths: $filePaths, fileNames: $fileNames, metadata: $metadata," + + " page: $page, pageSize: $pageSize" ) val projectRule = Rule.QueryRule("projectId", projectId, OperationType.EQ) val repoRule = Rule.QueryRule("repoName", repoNames, OperationType.IN) @@ -963,8 +965,8 @@ class BkRepoClient constructor( ): QueryData { logger.info( "queryByPathNamePairOrMetadataEqAnd, userId: $userId, projectId: $projectId," + - " repoNames: $repoNames, pathNamePairs: $pathNamePairs, metadata: $metadata," + - " page: $page, pageSize: $pageSize" + " repoNames: $repoNames, pathNamePairs: $pathNamePairs, metadata: $metadata," + + " page: $page, pageSize: $pageSize" ) val projectRule = Rule.QueryRule("projectId", projectId, OperationType.EQ) val repoRule = Rule.QueryRule("repoName", repoNames, OperationType.IN) @@ -1000,7 +1002,7 @@ class BkRepoClient constructor( ): QueryData { logger.info( "queryByPattern, userId: $userId, projectId: $projectId, repoNames: $repoNames," + - " fullPathPatterns: $fullPathPatterns, metadata: $metadata" + " fullPathPatterns: $fullPathPatterns, metadata: $metadata" ) val projectRule = Rule.QueryRule("projectId", projectId, OperationType.EQ) val repoRule = Rule.QueryRule("repoName", repoNames, OperationType.IN) @@ -1033,7 +1035,7 @@ class BkRepoClient constructor( ): QueryData { logger.info( "listFileByQuery, userId: $userId, projectId: $projectId, repoName: $repoName," + - " path: $path, includeFolders: $includeFolders" + " path: $path, includeFolders: $includeFolders" ) val projectRule = Rule.QueryRule("projectId", projectId, OperationType.EQ) val repoRule = Rule.QueryRule("repoName", repoName, OperationType.EQ) @@ -1082,7 +1084,7 @@ class BkRepoClient constructor( metadata: Map? = null ): PackageVersionInfo { val url = "${getGatewayUrl()}/bkrepo/api/service/docker/ext/version/detail/$projectId/$repoName" + - "?packageKey=$packageKey&version=$version" + "?packageKey=$packageKey&version=$version" val request = Request.Builder().url(url).headers(getCommonHeaders(userId, projectId).toHeaders()).get().build() return doRequest(request).resolveResponse>()!!.data!! } From 0d4bbb84506ef953bac1fb3c02429ea774982ba8 Mon Sep 17 00:00:00 2001 From: greysonfang Date: Tue, 24 Dec 2024 10:24:15 +0800 Subject: [PATCH 13/33] =?UTF-8?q?feat=EF=BC=9A=E7=94=A8=E6=88=B7=E4=B8=AA?= =?UTF-8?q?=E4=BA=BA=E8=A7=86=E8=A7=92=20=E6=9D=83=E9=99=90=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E4=BC=98=E5=8C=96=20#11138?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/dao/AuthHandoverOverviewDao.kt | 28 +++++++-- .../RbacPermissionManageFacadeServiceImpl.kt | 57 ++++++++++++++----- .../SamplePermissionManageFacadeService.kt | 8 ++- 3 files changed, 70 insertions(+), 23 deletions(-) diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/AuthHandoverOverviewDao.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/AuthHandoverOverviewDao.kt index e4e837b84572..0a33192d780d 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/AuthHandoverOverviewDao.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/AuthHandoverOverviewDao.kt @@ -87,19 +87,23 @@ class AuthHandoverOverviewDao { .where(buildQueryConditions(queryRequest)) .let { when { - queryRequest.sortType == SortType.FLOW_NO && queryRequest.collationType == CollationType.ASC -> { + queryRequest.sortType == SortType.FLOW_NO && + queryRequest.collationType == CollationType.ASC -> { it.orderBy(FLOW_NO.asc()) } - queryRequest.sortType == SortType.FLOW_NO && queryRequest.collationType == CollationType.DESC -> { + queryRequest.sortType == SortType.FLOW_NO && + queryRequest.collationType == CollationType.DESC -> { it.orderBy(FLOW_NO.desc()) } - queryRequest.sortType == SortType.CREATE_TIME && queryRequest.collationType == CollationType.ASC -> { + queryRequest.sortType == SortType.CREATE_TIME && + queryRequest.collationType == CollationType.ASC -> { it.orderBy(CREATE_TIME.asc()) } - queryRequest.sortType == SortType.CREATE_TIME && queryRequest.collationType == CollationType.DESC -> { + queryRequest.sortType == SortType.CREATE_TIME && + queryRequest.collationType == CollationType.DESC -> { it.orderBy(CREATE_TIME.desc()) } @@ -147,8 +151,20 @@ class AuthHandoverOverviewDao { queryRequest.applicant?.let { conditions.add(APPLICANT.like("%${queryRequest.applicant}%")) } queryRequest.approver?.let { conditions.add(APPROVER.like("%${queryRequest.approver}%")) } queryRequest.handoverStatus?.let { conditions.add(STATUS.eq(queryRequest.handoverStatus!!.value)) } - queryRequest.minCreatedTime?.let { conditions.add(CREATE_TIME.ge(DateTimeUtil.convertTimestampToLocalDateTime(it / 1000))) } - queryRequest.maxCreatedTime?.let { conditions.add(CREATE_TIME.le(DateTimeUtil.convertTimestampToLocalDateTime(it / 1000))) } + queryRequest.minCreatedTime?.let { + conditions.add( + CREATE_TIME.ge( + DateTimeUtil.convertTimestampToLocalDateTime(it / 1000) + ) + ) + } + queryRequest.maxCreatedTime?.let { + conditions.add( + CREATE_TIME.le( + DateTimeUtil.convertTimestampToLocalDateTime(it / 1000) + ) + ) + } return conditions } } diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionManageFacadeServiceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionManageFacadeServiceImpl.kt index 5217956eae7b..215447678050 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionManageFacadeServiceImpl.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionManageFacadeServiceImpl.kt @@ -688,13 +688,19 @@ class RbacPermissionManageFacadeServiceImpl( operateChannel = OperateChannel.PERSONAL, minExpiredAt = LocalDateTime.now().timestampmilli() ) - logger.debug("list all user groups joined after operated groups: {}, {}", count, userGroupsJoinedAfterOperatedGroups) + logger.debug( + "list all user groups joined after operated groups: {}, {}", + count, userGroupsJoinedAfterOperatedGroups + ) val isHasProjectVisitPermAfterOperatedGroups = checkProjectVisitPermission( projectCode = projectCode, iamGroupIds = userGroupsJoinedAfterOperatedGroups.map { it.iamGroupId } ) - logger.debug("whether the user has project visit perm after operated groups: {}", isHasProjectVisitPermAfterOperatedGroups) + logger.debug( + "whether the user has project visit perm after operated groups: {}", + isHasProjectVisitPermAfterOperatedGroups + ) val invalidAuthorizationsDTO = if (count == 0L || !isHasProjectVisitPermAfterOperatedGroups) { // 若用户已退出了所有的用户组或失去了项目访问权限,则直接返回项目下所有的授权 @@ -716,13 +722,14 @@ class RbacPermissionManageFacadeServiceImpl( ) } logger.info( - "invalid authorizations after operated groups|$projectCode|$iamGroupIdsOfDirectlyJoined|$memberId|$invalidAuthorizationsDTO" + "invalid authorizations after operated groups|$projectCode|$iamGroupIdsOfDirectlyJoined|$memberId|" + + "$invalidAuthorizationsDTO" ) return invalidAuthorizationsDTO } finally { logger.info( - "It take(${System.currentTimeMillis() - startEpoch})ms to check invalid authorizations after operated groups" + - "|$projectCode|$iamGroupIdsOfDirectlyJoined|$memberId" + "It take(${System.currentTimeMillis() - startEpoch})ms to check invalid authorizations " + + "after operated groups |$projectCode|$iamGroupIdsOfDirectlyJoined|$memberId" ) } } @@ -829,7 +836,10 @@ class RbacPermissionManageFacadeServiceImpl( ).second ) }.map { it.iamGroupId } - logger.debug("list pipeline and project groups joined after operated groups:{}", userGroupsJoinedAfterOperatedGroups) + logger.debug( + "list pipeline and project groups joined after operated groups:{}", + userGroupsJoinedAfterOperatedGroups + ) // 3.查询未退出的流水线/项目级别的用户组中是否包含项目级别的流水线执行权限。 val hasAllPipelineExecutePermAfterOperateGroups = groupPermissionService.isGroupsHasProjectLevelPermission( @@ -837,7 +847,10 @@ class RbacPermissionManageFacadeServiceImpl( filterIamGroupIds = userGroupsJoinedAfterOperatedGroups, action = ActionId.PIPELINE_EXECUTE ) - logger.debug("has all pipeline execute perm after operate groups:{}", hasAllPipelineExecutePermAfterOperateGroups) + logger.debug( + "has all pipeline execute perm after operate groups:{}", + hasAllPipelineExecutePermAfterOperateGroups + ) // 3.1.若用户在未退出的组中拥有整个项目的流水线执行权限,则本次不会对任何的流水线代持人权限造成影响。 if (hasAllPipelineExecutePermAfterOperateGroups) @@ -881,7 +894,10 @@ class RbacPermissionManageFacadeServiceImpl( relatedResourceType = ResourceTypeId.PIPELINE, action = ActionId.PIPELINE_EXECUTE )[ResourceTypeId.PIPELINE] ?: emptyList() - logger.debug("pipelines with execute perm after operate groups:{}", pipelinesWithExecutePermAfterOperatedGroups) + logger.debug( + "pipelines with execute perm after operate groups:{}", + pipelinesWithExecutePermAfterOperatedGroups + ) val pipelinesWithExecutePermInOperateGroups = groupPermissionService.listGroupResourcesWithPermission( projectCode = projectCode, @@ -1401,7 +1417,8 @@ class RbacPermissionManageFacadeServiceImpl( ), operateGroupMemberTask = ::deleteTask ) - if (toHandoverGroups.isEmpty() && invalidPipelines.isEmpty() && invalidRepertoryIds.isEmpty() && invalidEnvNodeIds.isEmpty()) { + if (toHandoverGroups.isEmpty() && invalidPipelines.isEmpty() && invalidRepertoryIds.isEmpty() && + invalidEnvNodeIds.isEmpty()) { return "true" } val handoverDetails = buildHandoverDetails( @@ -1617,7 +1634,8 @@ class RbacPermissionManageFacadeServiceImpl( iamGroupIdsOfDirectlyJoined = groupsOfDirectlyJoined, memberId = conditionReq.targetMember.id ) - val (invalidGroups, invalidPipelines, invalidRepositoryIds, invalidEnvNodeIds) = invalidAuthorizationsDTO + val (invalidGroups, invalidPipelines, invalidRepositoryIds, invalidEnvNodeIds) = + invalidAuthorizationsDTO // 当批量移出时, // 直接加入的组中,唯一管理员组/影响流水线代持权限不允许被移出 @@ -1663,7 +1681,8 @@ class RbacPermissionManageFacadeServiceImpl( // iam用的是秒级时间戳 it.expiredAt == PERMANENT_EXPIRED_TIME / 1000 }.size - val groupsOfInOperableWhenBatchRenewal = groupCountOfPermanentExpiredTime + groupsOfTemplateOrDeptJoined.size + val groupsOfInOperableWhenBatchRenewal = groupCountOfPermanentExpiredTime + + groupsOfTemplateOrDeptJoined.size BatchOperateGroupMemberCheckVo( totalCount = totalCount, operableCount = totalCount - groupsOfInOperableWhenBatchRenewal, @@ -1913,7 +1932,9 @@ class RbacPermissionManageFacadeServiceImpl( return true } - override fun getResourceType2CountOfHandover(queryReq: ResourceType2CountOfHandoverQuery): List { + override fun getResourceType2CountOfHandover( + queryReq: ResourceType2CountOfHandoverQuery + ): List { queryReq.check() return if (queryReq.queryChannel == HandoverQueryChannel.HANDOVER_APPLICATION) { permissionHandoverApplicationService.getResourceType2CountOfHandoverApplication(queryReq.flowNo!!) @@ -1923,7 +1944,9 @@ class RbacPermissionManageFacadeServiceImpl( } // 交接预览 - private fun getResourceType2CountOfHandoverPreview(queryReq: ResourceType2CountOfHandoverQuery): List { + private fun getResourceType2CountOfHandoverPreview( + queryReq: ResourceType2CountOfHandoverQuery + ): List { val projectCode = queryReq.projectCode val previewConditionReq = queryReq.previewConditionReq!! val batchOperateType = queryReq.batchOperateType!! @@ -1988,7 +2011,9 @@ class RbacPermissionManageFacadeServiceImpl( return result } - override fun listAuthorizationsOfHandover(queryReq: HandoverDetailsQueryReq): SQLPage { + override fun listAuthorizationsOfHandover( + queryReq: HandoverDetailsQueryReq + ): SQLPage { queryReq.check() return if (queryReq.queryChannel == HandoverQueryChannel.HANDOVER_APPLICATION) { permissionHandoverApplicationService.listAuthorizationsOfHandoverApplication(queryReq) @@ -1997,7 +2022,9 @@ class RbacPermissionManageFacadeServiceImpl( } } - private fun listAuthorizationsOfHandoverPreview(queryReq: HandoverDetailsQueryReq): SQLPage { + private fun listAuthorizationsOfHandoverPreview( + queryReq: HandoverDetailsQueryReq + ): SQLPage { val projectCode = queryReq.projectCode val previewConditionReq = queryReq.previewConditionReq!! val groupIdsDirectlyJoined = getGroupIdsByGroupMemberCondition( diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/service/SamplePermissionManageFacadeService.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/service/SamplePermissionManageFacadeService.kt index 97a78367e418..f0a56157cf85 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/service/SamplePermissionManageFacadeService.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/service/SamplePermissionManageFacadeService.kt @@ -155,11 +155,15 @@ class SamplePermissionManageFacadeService : PermissionManageFacadeService { override fun batchHandleHanoverApplications(request: HandoverOverviewBatchUpdateReq): Boolean = true - override fun getResourceType2CountOfHandover(queryReq: ResourceType2CountOfHandoverQuery): List { + override fun getResourceType2CountOfHandover( + queryReq: ResourceType2CountOfHandoverQuery + ): List { return emptyList() } - override fun listAuthorizationsOfHandover(queryReq: HandoverDetailsQueryReq): SQLPage { + override fun listAuthorizationsOfHandover( + queryReq: HandoverDetailsQueryReq + ): SQLPage { return SQLPage(0, emptyList()) } From 1cded793486440a7f96bf9d26ba64bd3af2a7587 Mon Sep 17 00:00:00 2001 From: greysonfang Date: Tue, 24 Dec 2024 17:27:37 +0800 Subject: [PATCH 14/33] =?UTF-8?q?feat=EF=BC=9A=E7=94=A8=E6=88=B7=E4=B8=AA?= =?UTF-8?q?=E4=BA=BA=E8=A7=86=E8=A7=92=20=E6=9D=83=E9=99=90=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E4=BC=98=E5=8C=96=20#11138?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notify/service/NotifyMessageTemplateServiceImpl.kt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/backend/ci/core/notify/biz-notify/src/main/kotlin/com/tencent/devops/notify/service/NotifyMessageTemplateServiceImpl.kt b/src/backend/ci/core/notify/biz-notify/src/main/kotlin/com/tencent/devops/notify/service/NotifyMessageTemplateServiceImpl.kt index f3c5e460f907..625015390874 100644 --- a/src/backend/ci/core/notify/biz-notify/src/main/kotlin/com/tencent/devops/notify/service/NotifyMessageTemplateServiceImpl.kt +++ b/src/backend/ci/core/notify/biz-notify/src/main/kotlin/com/tencent/devops/notify/service/NotifyMessageTemplateServiceImpl.kt @@ -39,6 +39,7 @@ import com.tencent.devops.common.notify.enums.NotifyType import com.tencent.devops.common.redis.RedisLock import com.tencent.devops.common.redis.RedisOperation import com.tencent.devops.common.service.config.CommonConfig +import com.tencent.devops.common.service.trace.TraceTag import com.tencent.devops.common.service.utils.SpringContextUtil import com.tencent.devops.common.web.utils.I18nUtil import com.tencent.devops.model.notify.tables.records.TCommonNotifyMessageTemplateRecord @@ -63,6 +64,7 @@ import com.tencent.devops.notify.service.notifier.NotifierUtils import org.jooq.DSLContext import org.jooq.impl.DSL import org.slf4j.LoggerFactory +import org.slf4j.MDC import org.springframework.beans.factory.annotation.Autowired import org.springframework.core.io.ClassPathResource import org.springframework.stereotype.Service @@ -94,7 +96,9 @@ class NotifyMessageTemplateServiceImpl @Autowired constructor( expiredTimeInSeconds = 60 ) + val traceId = MDC.get(TraceTag.BIZID) Executors.newFixedThreadPool(1).submit { + MDC.put(TraceTag.BIZID, traceId) if (redisLock.tryLock()) { try { logger.info("start init MessageTemplate") @@ -117,6 +121,7 @@ class NotifyMessageTemplateServiceImpl @Autowired constructor( val yamlStr = inputStream.bufferedReader(Charsets.UTF_8).use { it.readText() } val templates = YamlUtil.to(yamlStr, object : TypeReference>() {}) templates.forEach { template -> + logger.info("update message template:$template") val tCommonNotifyMessageTemplateRecord = TCommonNotifyMessageTemplateRecord() tCommonNotifyMessageTemplateRecord.id = template.id tCommonNotifyMessageTemplateRecord.templateCode = template.templateCode From b285b6263456411175cbd81fd732b35fb22b4cff Mon Sep 17 00:00:00 2001 From: greysonfang Date: Tue, 24 Dec 2024 19:29:55 +0800 Subject: [PATCH 15/33] =?UTF-8?q?feat=EF=BC=9A=E7=94=A8=E6=88=B7=E4=B8=AA?= =?UTF-8?q?=E4=BA=BA=E8=A7=86=E8=A7=92=20=E6=9D=83=E9=99=90=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E4=BC=98=E5=8C=96=20#11138?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rbac/service/RbacPermissionHandoverApplicationService.kt | 2 +- .../rbac/service/RbacPermissionManageFacadeServiceImpl.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionHandoverApplicationService.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionHandoverApplicationService.kt index 828e63ac0124..6edf1019c4d0 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionHandoverApplicationService.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionHandoverApplicationService.kt @@ -103,7 +103,7 @@ class RbacPermissionHandoverApplicationService( templateCode = HANDOVER_APPLICATION_TEMPLATE_CODE, bodyParams = bodyParams, titleParams = bodyParams, - notifyType = mutableSetOf(NotifyType.RTX.name, NotifyType.EMAIL.name), + notifyType = mutableSetOf(NotifyType.WEWORK.name, NotifyType.EMAIL.name), receivers = mutableSetOf(overview.approver) ) kotlin.runCatching { diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionManageFacadeServiceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionManageFacadeServiceImpl.kt index 215447678050..a4ea28c4e9e5 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionManageFacadeServiceImpl.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionManageFacadeServiceImpl.kt @@ -1874,7 +1874,7 @@ class RbacPermissionManageFacadeServiceImpl( templateCode = HANDOVER_APPLICATION_RESULT_TEMPLATE_CODE, bodyParams = bodyParams, titleParams = bodyParams, - notifyType = mutableSetOf(NotifyType.RTX.name, NotifyType.EMAIL.name), + notifyType = mutableSetOf(NotifyType.WEWORK.name, NotifyType.EMAIL.name), receivers = mutableSetOf(overview.applicant) ) logger.info("send handover application result email:{}|{} ", request, emailRequest) From 3886a9e8e6fac5b4a8097218508667f85fc89c5b Mon Sep 17 00:00:00 2001 From: greysonfang Date: Wed, 25 Dec 2024 10:04:14 +0800 Subject: [PATCH 16/33] =?UTF-8?q?feat=EF=BC=9A=E7=94=A8=E6=88=B7=E4=B8=AA?= =?UTF-8?q?=E4=BA=BA=E8=A7=86=E8=A7=92=20=E6=9D=83=E9=99=90=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E4=BC=98=E5=8C=96=20#11138?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- support-files/i18n/notify/template_zh_CN.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/support-files/i18n/notify/template_zh_CN.yaml b/support-files/i18n/notify/template_zh_CN.yaml index 7e97747a78ba..dead84f87543 100644 --- a/support-files/i18n/notify/template_zh_CN.yaml +++ b/support-files/i18n/notify/template_zh_CN.yaml @@ -1021,7 +1021,7 @@ emailTemplate: id: "d57d7358f5034b21acafde62f27d75fd" title: "蓝盾权限交接申请" - body: "蓝盾DevOps平台

蓝盾权限交接申请

您好,${handoverTo}:

${handoverFrom}已申请将蓝盾项目【${projectName}】下的${handoverOverviews}移交给您,请确认是否接收。

${table}
交接类型资源类型数量
马上处理
" + body: "\n\n\n\n \n \n 蓝盾DevOps平台\n \n\n\n\n \n \n \n \n \n \n
\n \n \n \n \n \n \n \n \n \n
\n \n \n \n \n \n \n \n
\n \n \n \n
\n
\n \n \n \n \n
\n
\n

蓝盾权限交接申请

\n
\n

你好,${handoverTo}:

\n

${handoverFrom} 已申请将蓝盾项目【${projectName}】下的 ${handoverOverviews} 移交给您,请确认是否接收。

\n
\n \n \n \n \n \n \n \n \n ${table}\n
交接类型资源类型数量
\n 马上处理\n
\n
\n
\n
\n\n\n\n" sender: "DevOps" bodyFormat: 1 emailType: 1 From db6b305dd78fd0a646fd2f0069eb13001d96e969 Mon Sep 17 00:00:00 2001 From: greysonfang Date: Wed, 25 Dec 2024 10:43:20 +0800 Subject: [PATCH 17/33] =?UTF-8?q?feat=EF=BC=9A=E7=94=A8=E6=88=B7=E4=B8=AA?= =?UTF-8?q?=E4=BA=BA=E8=A7=86=E8=A7=92=20=E6=9D=83=E9=99=90=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E4=BC=98=E5=8C=96=20#11138?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/RbacPermissionHandoverApplicationService.kt | 2 +- .../service/RbacPermissionManageFacadeServiceImpl.kt | 9 +++++++++ support-files/i18n/notify/template_en_US.yaml | 4 ++-- support-files/i18n/notify/template_zh_CN.yaml | 4 ++-- 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionHandoverApplicationService.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionHandoverApplicationService.kt index 6edf1019c4d0..c330fc8db2ce 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionHandoverApplicationService.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionHandoverApplicationService.kt @@ -363,7 +363,7 @@ class RbacPermissionHandoverApplicationService( private val logger = LoggerFactory.getLogger(RbacPermissionHandoverApplicationService::class.java) private const val FLOW_NO_PREFIX = "REQ" private const val FLOW_NO_KEY = "AUTH:HANDOVER:FLOW:NO:%s" - private const val HANDOVER_APPLICATION_TABLE_OF_EMAIL = "%s%s%s" + private const val HANDOVER_APPLICATION_TABLE_OF_EMAIL = "%s%s%s" private const val HANDOVER_APPLICATION_TEMPLATE_CODE = "BK_PERMISSIONS_HANDOVER_APPLICATION" } } diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionManageFacadeServiceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionManageFacadeServiceImpl.kt index a4ea28c4e9e5..7ef6ecb95d1d 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionManageFacadeServiceImpl.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionManageFacadeServiceImpl.kt @@ -673,6 +673,15 @@ class RbacPermissionManageFacadeServiceImpl( ): InvalidAuthorizationsDTO { val startEpoch = System.currentTimeMillis() try { + if (iamGroupIdsOfDirectlyJoined.isEmpty()) { + return InvalidAuthorizationsDTO( + invalidGroupIds = emptyList(), + invalidPipelineIds = emptyList(), + invalidRepertoryIds = emptyList(), + invalidEnvNodeIds = emptyList() + ) + } + // 筛选出本次操作中未过期的用户组 val iamGroupIdsOfNotExpired = getNotExpiredIamGroupIds( projectCode = projectCode, diff --git a/support-files/i18n/notify/template_en_US.yaml b/support-files/i18n/notify/template_en_US.yaml index 3e07617d7e3f..81a21bb62dd7 100644 --- a/support-files/i18n/notify/template_en_US.yaml +++ b/support-files/i18n/notify/template_en_US.yaml @@ -1021,7 +1021,7 @@ emailTemplate: id: "d57d7358f5034b21acafde62f27d75fd" title: "BK-CI Permission handover application" - body: "BK-CI

BK-CI Permission handover application

Hi,${handoverTo}:

${handoverFrom}has applied to handover the permissions under the BK-CI project[${projectName}]to you.Please confirm whether to accept it./p>

Handle it immediately
" + body: "\n\n\n\n \n \n BK-CI\n \n\n\n\n \n \n \n \n \n \n
\n \n \n \n \n \n \n
\n \n \n \n \n
\n
\n

BK-CI Permission handover application

\n
\n

Hi,${handoverTo}:

\n

\n ${handoverFrom} has applied to handover the permissions under the BK-CI project [${projectName}] to you.Please confirm whether to accept it.

\n Handle it immediately
\n
\n
\n
\n\n\n\n" sender: "DevOps" bodyFormat: 1 emailType: 1 @@ -1044,7 +1044,7 @@ emailTemplate: id: "902b30b05d184f478ea8eaaea53533f1" title: "BK-CI Permission handover application result" - body: "BK-CI

BK-CI Permission handover application result

The permission handover process for your BK-CI project[${projectName}]has ended

See more details
" + body: "\n\n\n \n \n BK-CI\n \n\n\n\n \n \n \n \n \n \n
\n \n \n \n \n \n \n
\n \n \n \n \n
\n
\n

BK-CI Permission handover application result

\n
\n

The permission handover process for your BK-CI project [${projectName}] has ended.

\n See more details
\n
\n
\n
\n\n\n\n\n " sender: "DevOps" bodyFormat: 1 emailType: 1 diff --git a/support-files/i18n/notify/template_zh_CN.yaml b/support-files/i18n/notify/template_zh_CN.yaml index dead84f87543..5d615e636932 100644 --- a/support-files/i18n/notify/template_zh_CN.yaml +++ b/support-files/i18n/notify/template_zh_CN.yaml @@ -1021,7 +1021,7 @@ emailTemplate: id: "d57d7358f5034b21acafde62f27d75fd" title: "蓝盾权限交接申请" - body: "\n\n\n\n \n \n 蓝盾DevOps平台\n \n\n\n\n \n \n \n \n \n \n
\n \n \n \n \n \n \n \n \n \n
\n \n \n \n \n \n \n \n
\n \n \n \n
\n
\n \n \n \n \n
\n
\n

蓝盾权限交接申请

\n
\n

你好,${handoverTo}:

\n

${handoverFrom} 已申请将蓝盾项目【${projectName}】下的 ${handoverOverviews} 移交给您,请确认是否接收。

\n
\n \n \n \n \n \n \n \n \n ${table}\n
交接类型资源类型数量
\n 马上处理\n
\n
\n
\n
\n\n\n\n" + body: "\n\n\n\n \n \n 蓝盾DevOps平台\n \n\n\n\n \n \n \n \n \n \n
\n \n \n \n \n \n \n
\n \n \n \n \n
\n
\n

蓝盾权限交接申请

\n
\n

你好,${handoverTo}:

\n

${handoverFrom} 已申请将蓝盾项目【${projectName}】下的 ${handoverOverviews} 移交给您,请确认是否接收。

\n
\n \n \n \n \n \n \n \n \n ${table}\n
交接类型资源类型数量
\n 马上处理\n
\n
\n
\n
\n\n\n\n\n" sender: "DevOps" bodyFormat: 1 emailType: 1 @@ -1044,7 +1044,7 @@ emailTemplate: id: "902b30b05d184f478ea8eaaea53533f1" title: "蓝盾权限交接审批结果" - body: "蓝盾DevOps平台

蓝盾权限交接审批结果

您在蓝盾项目【${projectName}】下的权限移交${result}

查看更多详情
" + body: "\n\n\n\n \n \n 蓝盾DevOps平台\n \n\n\n\n \n \n \n \n \n \n
\n \n \n \n \n \n \n
\n \n \n \n \n
\n
\n

蓝盾权限交接审批结果

\n
\n

您在蓝盾项目[${projectName}]下的权限移交${result}

\n
\n 查看更多详情\n
\n
\n
\n
\n\n\n\n\n" sender: "DevOps" bodyFormat: 1 emailType: 1 From 84f75191d4096c13615e80676e6b2aefe7542ec3 Mon Sep 17 00:00:00 2001 From: greysonfang Date: Wed, 25 Dec 2024 12:06:08 +0800 Subject: [PATCH 18/33] =?UTF-8?q?feat=EF=BC=9A=E7=94=A8=E6=88=B7=E4=B8=AA?= =?UTF-8?q?=E4=BA=BA=E8=A7=86=E8=A7=92=20=E6=9D=83=E9=99=90=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E4=BC=98=E5=8C=96=20#11138?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rbac/service/RbacPermissionHandoverApplicationService.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionHandoverApplicationService.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionHandoverApplicationService.kt index c330fc8db2ce..fe5d77de329b 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionHandoverApplicationService.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionHandoverApplicationService.kt @@ -363,7 +363,7 @@ class RbacPermissionHandoverApplicationService( private val logger = LoggerFactory.getLogger(RbacPermissionHandoverApplicationService::class.java) private const val FLOW_NO_PREFIX = "REQ" private const val FLOW_NO_KEY = "AUTH:HANDOVER:FLOW:NO:%s" - private const val HANDOVER_APPLICATION_TABLE_OF_EMAIL = "%s%s%s" + private const val HANDOVER_APPLICATION_TABLE_OF_EMAIL = "%s%s%s" private const val HANDOVER_APPLICATION_TEMPLATE_CODE = "BK_PERMISSIONS_HANDOVER_APPLICATION" } } From af374ec0e6c684c3b64b2b8b125f50a30e718f9e Mon Sep 17 00:00:00 2001 From: greysonfang Date: Wed, 25 Dec 2024 12:07:39 +0800 Subject: [PATCH 19/33] =?UTF-8?q?feat=EF=BC=9A=E7=94=A8=E6=88=B7=E4=B8=AA?= =?UTF-8?q?=E4=BA=BA=E8=A7=86=E8=A7=92=20=E6=9D=83=E9=99=90=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E4=BC=98=E5=8C=96=20#11138?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- support-files/i18n/notify/template_zh_CN.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/support-files/i18n/notify/template_zh_CN.yaml b/support-files/i18n/notify/template_zh_CN.yaml index 5d615e636932..0f33fbdd0329 100644 --- a/support-files/i18n/notify/template_zh_CN.yaml +++ b/support-files/i18n/notify/template_zh_CN.yaml @@ -1021,7 +1021,7 @@ emailTemplate: id: "d57d7358f5034b21acafde62f27d75fd" title: "蓝盾权限交接申请" - body: "\n\n\n\n \n \n 蓝盾DevOps平台\n \n\n\n\n \n \n \n \n \n \n
\n \n \n \n \n \n \n
\n \n \n \n \n
\n
\n

蓝盾权限交接申请

\n
\n

你好,${handoverTo}:

\n

${handoverFrom} 已申请将蓝盾项目【${projectName}】下的 ${handoverOverviews} 移交给您,请确认是否接收。

\n
\n \n \n \n \n \n \n \n \n ${table}\n
交接类型资源类型数量
\n 马上处理\n
\n
\n
\n
\n\n\n\n\n" + body: "!DOCTYPE html>\n\n\n\n \n \n 蓝盾DevOps平台\n \n\n\n\n \n \n \n \n \n \n
\n \n \n \n \n \n \n
\n \n \n \n \n
\n
\n

蓝盾权限交接申请

\n
\n

您好,${handoverTo}:

\n

${handoverFrom} 已申请将蓝盾项目[${projectName}]下的 ${handoverOverviews} 移交给您,请确认是否接收。

\n
\n \n \n \n \n \n \n \n \n ${table}\n
交接类型资源类型数量
\n 马上处理\n
\n
\n
\n
\n\n\n\n" sender: "DevOps" bodyFormat: 1 emailType: 1 From 8f4ab3385cf60c7d58d77dd424d38457f667cb4e Mon Sep 17 00:00:00 2001 From: greysonfang Date: Wed, 25 Dec 2024 14:56:05 +0800 Subject: [PATCH 20/33] =?UTF-8?q?feat=EF=BC=9A=E7=94=A8=E6=88=B7=E4=B8=AA?= =?UTF-8?q?=E4=BA=BA=E8=A7=86=E8=A7=92=20=E6=9D=83=E9=99=90=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E4=BC=98=E5=8C=96=20#11138?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tencent/devops/auth/pojo/enum/HandoverAction.kt | 12 ++++++++---- .../auth/pojo/request/HandoverOverviewUpdateReq.kt | 2 +- .../RbacPermissionManageFacadeServiceImpl.kt | 13 +++++++++++++ support-files/i18n/notify/template_zh_CN.yaml | 8 ++++---- 4 files changed, 26 insertions(+), 9 deletions(-) diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/HandoverAction.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/HandoverAction.kt index 7360046c9efd..5ba2c23a654d 100644 --- a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/HandoverAction.kt +++ b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/HandoverAction.kt @@ -1,14 +1,18 @@ package com.tencent.devops.auth.pojo.enum -enum class HandoverAction(val value: Int, val alias: String) { +enum class HandoverAction( + val value: Int, + val alias: String, + val content: String +) { // 审批成功 - AGREE(1, "已通过"), + AGREE(1, "已通过", "您提交的权限交接单 %s 已被 %s 通过。恭喜您完成交接。"), // 审批驳回 - REJECT(2, "已被拒绝"), + REJECT(2, "已拒绝", "您提交的权限交接单 %s 已被 %s 拒绝。请重新交接。"), // 撤销 - REVOKE(3, "撤销"); + REVOKE(3, "撤销","已撤销"); companion object { fun get(value: Int): HandoverAction { diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/request/HandoverOverviewUpdateReq.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/request/HandoverOverviewUpdateReq.kt index cd0c27b2b21e..baefcac6c0e4 100644 --- a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/request/HandoverOverviewUpdateReq.kt +++ b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/request/HandoverOverviewUpdateReq.kt @@ -14,5 +14,5 @@ data class HandoverOverviewUpdateReq( @get:Schema(title = "审批操作") val handoverAction: HandoverAction, @get:Schema(title = "备注") - val remark: String? = null + val remark: String? = "" ) diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionManageFacadeServiceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionManageFacadeServiceImpl.kt index 7ef6ecb95d1d..27a41018ff55 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionManageFacadeServiceImpl.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionManageFacadeServiceImpl.kt @@ -1873,9 +1873,22 @@ class RbacPermissionManageFacadeServiceImpl( resourceType = ResourceTypeId.PROJECT, resourceCode = request.projectCode ).resourceName + val handoverFromCnName = deptService.getMemberInfo( + overview.applicant, ManagerScopesEnum.USER + ).displayName + val handoverToCnName = deptService.getMemberInfo( + overview.approver, ManagerScopesEnum.USER + ).displayName val bodyParams = mapOf( "projectName" to projectName, "result" to request.handoverAction.alias, + "handoverFrom" to overview.applicant.plus("($handoverFromCnName)"), + "remark" to request.remark!!, + "content" to String.format( + request.handoverAction.content, + request.flowNo, + overview.approver.plus("($handoverToCnName)"), + ), "url" to String.format(handoverApplicationUrl, request.flowNo) ) // 发邮件 diff --git a/support-files/i18n/notify/template_zh_CN.yaml b/support-files/i18n/notify/template_zh_CN.yaml index 0f33fbdd0329..cfd92cc6f5af 100644 --- a/support-files/i18n/notify/template_zh_CN.yaml +++ b/support-files/i18n/notify/template_zh_CN.yaml @@ -1043,15 +1043,15 @@ source: 0 emailTemplate: id: "902b30b05d184f478ea8eaaea53533f1" - title: "蓝盾权限交接审批结果" - body: "\n\n\n\n \n \n 蓝盾DevOps平台\n \n\n\n\n \n \n \n \n \n \n
\n \n \n \n \n \n \n
\n \n \n \n \n
\n
\n

蓝盾权限交接审批结果

\n
\n

您在蓝盾项目[${projectName}]下的权限移交${result}

\n
\n 查看更多详情\n
\n
\n
\n
\n\n\n\n\n" + title: "蓝盾权限交接申请" + body: "\n\n\n\n \n \n 蓝盾DevOps平台\n \n\n\n\n \n \n \n \n \n \n
\n \n \n \n \n \n \n
\n \n \n \n \n
\n
\n

蓝盾权限交接申请 - ${result}

\n
\n

您好,${handoverFrom}

\n

${content}

\n

审核意见:${remark}

\n
\n 查看更多\n
\n
\n
\n
\n\n\n\n\n " sender: "DevOps" bodyFormat: 1 emailType: 1 weworkTemplate: id: "4ead2e88f2a94fcbb7b1ffc5c523f742" sender: "DevOps" - title: "【蓝盾权限】移交审批结果通知" - body: "您在蓝盾项目【${projectName}】下的权限移交${result},更多详情查看:${url}" + title: "蓝盾权限交接申请 - ${result}" + body: "您好,${handoverFrom} \n ${content} \n审核意见:${remark} \n 更多详情查看:${url}" creator: "system" modifior: "system" From a5f334fc0e8880fdfb7e3011734013df50957de4 Mon Sep 17 00:00:00 2001 From: greysonfang Date: Wed, 25 Dec 2024 15:03:56 +0800 Subject: [PATCH 21/33] =?UTF-8?q?feat=EF=BC=9A=E7=94=A8=E6=88=B7=E4=B8=AA?= =?UTF-8?q?=E4=BA=BA=E8=A7=86=E8=A7=92=20=E6=9D=83=E9=99=90=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E4=BC=98=E5=8C=96=20#11138?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/com/tencent/devops/auth/dao/AuthAuthorizationDao.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/AuthAuthorizationDao.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/AuthAuthorizationDao.kt index 6d2be47ba955..afaa2feb7cc4 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/AuthAuthorizationDao.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/AuthAuthorizationDao.kt @@ -157,6 +157,7 @@ class AuthAuthorizationDao { return with(TAuthResourceAuthorization.T_AUTH_RESOURCE_AUTHORIZATION) { dslContext.selectFrom(this) .where(buildQueryCondition(condition)) + .orderBy(CREATE_TIME.desc()) .let { if (condition.page != null && condition.pageSize != null) { it.limit((condition.page!! - 1) * condition.pageSize!!, condition.pageSize) From 06e5a79c5a4736438062c08b91a53bb3a3f50e4c Mon Sep 17 00:00:00 2001 From: greysonfang Date: Wed, 25 Dec 2024 15:34:08 +0800 Subject: [PATCH 22/33] =?UTF-8?q?feat=EF=BC=9A=E7=94=A8=E6=88=B7=E4=B8=AA?= =?UTF-8?q?=E4=BA=BA=E8=A7=86=E8=A7=92=20=E6=9D=83=E9=99=90=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E4=BC=98=E5=8C=96=20#11138?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- support-files/i18n/notify/template_zh_CN.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/support-files/i18n/notify/template_zh_CN.yaml b/support-files/i18n/notify/template_zh_CN.yaml index cfd92cc6f5af..0b6ad09d4981 100644 --- a/support-files/i18n/notify/template_zh_CN.yaml +++ b/support-files/i18n/notify/template_zh_CN.yaml @@ -1052,6 +1052,6 @@ id: "4ead2e88f2a94fcbb7b1ffc5c523f742" sender: "DevOps" title: "蓝盾权限交接申请 - ${result}" - body: "您好,${handoverFrom} \n ${content} \n审核意见:${remark} \n 更多详情查看:${url}" + body: "您好,${handoverFrom} \n${content} \n审核意见:${remark} \n更多详情查看:${url}" creator: "system" modifior: "system" From 2e6ba2346ce19f8e4579503a8ae7c34c7265e334 Mon Sep 17 00:00:00 2001 From: greysonfang Date: Wed, 25 Dec 2024 16:09:56 +0800 Subject: [PATCH 23/33] =?UTF-8?q?feat=EF=BC=9A=E7=94=A8=E6=88=B7=E4=B8=AA?= =?UTF-8?q?=E4=BA=BA=E8=A7=86=E8=A7=92=20=E6=9D=83=E9=99=90=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E4=BC=98=E5=8C=96=20#11138?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/com/tencent/devops/auth/pojo/enum/HandoverAction.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/HandoverAction.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/HandoverAction.kt index 5ba2c23a654d..f88de5e99b99 100644 --- a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/HandoverAction.kt +++ b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/HandoverAction.kt @@ -12,7 +12,7 @@ enum class HandoverAction( REJECT(2, "已拒绝", "您提交的权限交接单 %s 已被 %s 拒绝。请重新交接。"), // 撤销 - REVOKE(3, "撤销","已撤销"); + REVOKE(3, "撤销", "已撤销"); companion object { fun get(value: Int): HandoverAction { From 7dc63da2f5fc38c1f1eaf7e8df2f79e8e4d0b1ad Mon Sep 17 00:00:00 2001 From: greysonfang Date: Wed, 25 Dec 2024 16:40:27 +0800 Subject: [PATCH 24/33] =?UTF-8?q?feat=EF=BC=9A=E7=94=A8=E6=88=B7=E4=B8=AA?= =?UTF-8?q?=E4=BA=BA=E8=A7=86=E8=A7=92=20=E6=9D=83=E9=99=90=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E4=BC=98=E5=8C=96=20#11138?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../RbacPermissionManageFacadeServiceImpl.kt | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionManageFacadeServiceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionManageFacadeServiceImpl.kt index 27a41018ff55..2a3d3260403f 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionManageFacadeServiceImpl.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionManageFacadeServiceImpl.kt @@ -531,7 +531,8 @@ class RbacPermissionManageFacadeServiceImpl( private fun getGroupIdsByGroupMemberCondition( projectCode: String, - commonCondition: GroupMemberCommonConditionReq + commonCondition: GroupMemberCommonConditionReq, + minExpiredAt: Long? = null ): Map> { val finalMemberGroups = mutableListOf() @@ -540,7 +541,8 @@ class RbacPermissionManageFacadeServiceImpl( listResourceGroupMembers( projectCode = projectCode, memberId = commonCondition.targetMember.id, - operateChannel = commonCondition.operateChannel + operateChannel = commonCondition.operateChannel, + minExpiredAt = minExpiredAt ).second } @@ -550,7 +552,8 @@ class RbacPermissionManageFacadeServiceImpl( projectCode = projectCode, memberId = commonCondition.targetMember.id, resourceType = resourceType, - operateChannel = commonCondition.operateChannel + operateChannel = commonCondition.operateChannel, + minExpiredAt = minExpiredAt ).second } } @@ -567,7 +570,8 @@ class RbacPermissionManageFacadeServiceImpl( memberId = commonCondition.targetMember.id, iamGroupIds = groupIds.map { it.id }, operateChannel = commonCondition.operateChannel, - filterMemberType = memberType + filterMemberType = memberType, + minExpiredAt = minExpiredAt ).second finalMemberGroups.addAll(groupsOfSelect) } @@ -1147,7 +1151,8 @@ class RbacPermissionManageFacadeServiceImpl( // 成员直接加入的组 val groupIds = getGroupIdsByGroupMemberCondition( projectCode = projectCode, - commonCondition = handoverMemberDTO + commonCondition = handoverMemberDTO, + minExpiredAt = LocalDateTime.now().timestampmilli() )[MemberType.get(MemberType.USER.type)]?.toMutableList() if (groupIds.isNullOrEmpty()) { throw ErrorCodeException( From 669a0878369f34d4948ee48fc20f751eaacba5b2 Mon Sep 17 00:00:00 2001 From: greysonfang Date: Wed, 25 Dec 2024 16:51:21 +0800 Subject: [PATCH 25/33] =?UTF-8?q?feat=EF=BC=9A=E7=94=A8=E6=88=B7=E4=B8=AA?= =?UTF-8?q?=E4=BA=BA=E8=A7=86=E8=A7=92=20=E6=9D=83=E9=99=90=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E4=BC=98=E5=8C=96=20#11138?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rbac/service/RbacPermissionHandoverApplicationService.kt | 4 ++-- .../rbac/service/RbacPermissionManageFacadeServiceImpl.kt | 4 ++-- support-files/i18n/notify/template_zh_CN.yaml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionHandoverApplicationService.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionHandoverApplicationService.kt index fe5d77de329b..b37c10d979e3 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionHandoverApplicationService.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionHandoverApplicationService.kt @@ -356,8 +356,8 @@ class RbacPermissionHandoverApplicationService( ).map { it.copy(approver = flowNo2Approver[it.flowNo]) } } - private val handoverApplicationUrl = "${config.devopsHostGateway}/console/permission/my-handover?" + - "type=handoverToMe&flowNo=%s" + private val handoverApplicationUrl = + "${config.devopsHostGateway}/console/permission/my-handover?type=handoverToMe&flowNo=%s" companion object { private val logger = LoggerFactory.getLogger(RbacPermissionHandoverApplicationService::class.java) diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionManageFacadeServiceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionManageFacadeServiceImpl.kt index 2a3d3260403f..20350c407cef 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionManageFacadeServiceImpl.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionManageFacadeServiceImpl.kt @@ -2379,8 +2379,8 @@ class RbacPermissionManageFacadeServiceImpl( } } - private val handoverApplicationUrl = "${config.devopsHostGateway}/console/permission/my-handover?" + - "type=handoverFromMe&flowNo=%s" + private val handoverApplicationUrl = + "${config.devopsHostGateway}/console/permission/my-handover?type=handoverFromMe&flowNo=%s" companion object { private val logger = LoggerFactory.getLogger(RbacPermissionResourceMemberService::class.java) diff --git a/support-files/i18n/notify/template_zh_CN.yaml b/support-files/i18n/notify/template_zh_CN.yaml index 0b6ad09d4981..5f811083f1dc 100644 --- a/support-files/i18n/notify/template_zh_CN.yaml +++ b/support-files/i18n/notify/template_zh_CN.yaml @@ -1021,7 +1021,7 @@ emailTemplate: id: "d57d7358f5034b21acafde62f27d75fd" title: "蓝盾权限交接申请" - body: "!DOCTYPE html>\n\n\n\n \n \n 蓝盾DevOps平台\n \n\n\n\n \n \n \n \n \n \n
\n \n \n \n \n \n \n
\n \n \n \n \n
\n
\n

蓝盾权限交接申请

\n
\n

您好,${handoverTo}:

\n

${handoverFrom} 已申请将蓝盾项目[${projectName}]下的 ${handoverOverviews} 移交给您,请确认是否接收。

\n
\n \n \n \n \n \n \n \n \n ${table}\n
交接类型资源类型数量
\n 马上处理\n
\n
\n
\n
\n\n\n\n" + body: "\n\n\n\n \n \n 蓝盾DevOps平台\n \n\n\n\n \n \n \n \n \n \n
\n \n \n \n \n \n \n
\n \n \n \n \n
\n
\n

蓝盾权限交接申请

\n
\n

您好,${handoverTo}:

\n

${handoverFrom} 已申请将蓝盾项目[${projectName}]下的 ${handoverOverviews} 移交给您,请确认是否接收。

\n
\n \n \n \n \n \n \n \n \n ${table}\n
交接类型资源类型数量
\n 马上处理\n
\n
\n
\n
\n\n\n\n" sender: "DevOps" bodyFormat: 1 emailType: 1 From c82564c25467604dae299c4750a884b75a71317b Mon Sep 17 00:00:00 2001 From: greysonfang Date: Wed, 25 Dec 2024 17:00:10 +0800 Subject: [PATCH 26/33] =?UTF-8?q?feat=EF=BC=9A=E7=94=A8=E6=88=B7=E4=B8=AA?= =?UTF-8?q?=E4=BA=BA=E8=A7=86=E8=A7=92=20=E6=9D=83=E9=99=90=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E4=BC=98=E5=8C=96=20#11138?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/com/tencent/devops/auth/dao/AuthAuthorizationDao.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/AuthAuthorizationDao.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/AuthAuthorizationDao.kt index afaa2feb7cc4..4b30e0176127 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/AuthAuthorizationDao.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/AuthAuthorizationDao.kt @@ -157,7 +157,7 @@ class AuthAuthorizationDao { return with(TAuthResourceAuthorization.T_AUTH_RESOURCE_AUTHORIZATION) { dslContext.selectFrom(this) .where(buildQueryCondition(condition)) - .orderBy(CREATE_TIME.desc()) + .orderBy(HANDOVER_TIME.desc()) .let { if (condition.page != null && condition.pageSize != null) { it.limit((condition.page!! - 1) * condition.pageSize!!, condition.pageSize) From 3c07a4a96259a363de5862c6986df8709fe4a101 Mon Sep 17 00:00:00 2001 From: greysonfang Date: Wed, 25 Dec 2024 17:37:00 +0800 Subject: [PATCH 27/33] =?UTF-8?q?feat=EF=BC=9A=E7=94=A8=E6=88=B7=E4=B8=AA?= =?UTF-8?q?=E4=BA=BA=E8=A7=86=E8=A7=92=20=E6=9D=83=E9=99=90=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E4=BC=98=E5=8C=96=20#11138?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/tencent/devops/auth/pojo/enum/HandoverAction.kt | 4 ++-- .../service/RbacPermissionHandoverApplicationService.kt | 9 ++++----- support-files/i18n/notify/template_zh_CN.yaml | 8 ++++---- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/HandoverAction.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/HandoverAction.kt index f88de5e99b99..a0adb00ce702 100644 --- a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/HandoverAction.kt +++ b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/HandoverAction.kt @@ -6,10 +6,10 @@ enum class HandoverAction( val content: String ) { // 审批成功 - AGREE(1, "已通过", "您提交的权限交接单 %s 已被 %s 通过。恭喜您完成交接。"), + AGREE(1, "已通过", "您提交的权限交接单 %s 已被 %s 通过。恭喜您完成交接。"), // 审批驳回 - REJECT(2, "已拒绝", "您提交的权限交接单 %s 已被 %s 拒绝。请重新交接。"), + REJECT(2, "已拒绝", "您提交的权限交接单 %s 已被 %s 拒绝。请重新交接。"), // 撤销 REVOKE(3, "撤销", "已撤销"); diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionHandoverApplicationService.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionHandoverApplicationService.kt index b37c10d979e3..56cdcd6eefdf 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionHandoverApplicationService.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionHandoverApplicationService.kt @@ -128,7 +128,7 @@ class RbacPermissionHandoverApplicationService( titleOfApplication = titleOfApplication.plus(" $groupCount ").plus( bkHandoverGroups.plus(",").plus(" $authorizationCount ").plus(bkHandoverAuthorizations) ) - handoverOverviewContentOfEmail = """$groupCount$bkHandoverGroups,$authorizationCount$bkHandoverAuthorizations""".trimMargin() + handoverOverviewContentOfEmail = """ $groupCount $bkHandoverGroups, $authorizationCount $bkHandoverAuthorizations""".trimMargin() handoverOverviewContentOfRtx = handoverOverviewContentOfRtx.plus(groupCount).plus( bkHandoverGroups.plus(",").plus(authorizationCount).plus(bkHandoverAuthorizations) ) @@ -136,13 +136,13 @@ class RbacPermissionHandoverApplicationService( groupCount > 0 -> { titleOfApplication = titleOfApplication.plus(" $groupCount ").plus(bkHandoverGroups) - handoverOverviewContentOfEmail = """$groupCount$bkHandoverGroups""".trimMargin() + handoverOverviewContentOfEmail = """ $groupCount $bkHandoverGroups""".trimMargin() handoverOverviewContentOfRtx = handoverOverviewContentOfRtx.plus(groupCount).plus(bkHandoverGroups) } else -> { titleOfApplication = titleOfApplication.plus(" $authorizationCount ").plus(bkHandoverAuthorizations) - handoverOverviewContentOfEmail = """$authorizationCount$bkHandoverAuthorizations""".trimMargin() + handoverOverviewContentOfEmail = """ $authorizationCount $bkHandoverAuthorizations""".trimMargin() handoverOverviewContentOfRtx = handoverOverviewContentOfRtx.plus(authorizationCount).plus(bkHandoverAuthorizations) } } @@ -356,8 +356,7 @@ class RbacPermissionHandoverApplicationService( ).map { it.copy(approver = flowNo2Approver[it.flowNo]) } } - private val handoverApplicationUrl = - "${config.devopsHostGateway}/console/permission/my-handover?type=handoverToMe&flowNo=%s" + private val handoverApplicationUrl = "${config.devopsHostGateway}/console/permission/my-handover?type=handoverToMe&flowNo=%s" companion object { private val logger = LoggerFactory.getLogger(RbacPermissionHandoverApplicationService::class.java) diff --git a/support-files/i18n/notify/template_zh_CN.yaml b/support-files/i18n/notify/template_zh_CN.yaml index 5f811083f1dc..15040ad23027 100644 --- a/support-files/i18n/notify/template_zh_CN.yaml +++ b/support-files/i18n/notify/template_zh_CN.yaml @@ -1021,7 +1021,7 @@ emailTemplate: id: "d57d7358f5034b21acafde62f27d75fd" title: "蓝盾权限交接申请" - body: "\n\n\n\n \n \n 蓝盾DevOps平台\n \n\n\n\n \n \n \n \n \n \n
\n \n \n \n \n \n \n
\n \n \n \n \n
\n
\n

蓝盾权限交接申请

\n
\n

您好,${handoverTo}:

\n

${handoverFrom} 已申请将蓝盾项目[${projectName}]下的 ${handoverOverviews} 移交给您,请确认是否接收。

\n
\n \n \n \n \n \n \n \n \n ${table}\n
交接类型资源类型数量
\n 马上处理\n
\n
\n
\n
\n\n\n\n" + body: "\n\n\n\n \n \n 蓝盾DevOps平台\n \n\n\n\n \n \n \n \n \n \n
\n \n \n \n \n \n \n
\n \n \n \n \n
\n
\n

蓝盾权限交接申请

\n
\n

你好,${handoverTo}:

\n

${handoverFrom} 已申请将蓝盾项目[${projectName}]下的 ${handoverOverviews}移交给你,请确认是否接收。

\n
\n \n \n \n \n \n \n \n \n ${table}\n
交接类型资源类型数量
\n 马上处理\n
\n
\n
\n
\n\n\n\n" sender: "DevOps" bodyFormat: 1 emailType: 1 @@ -1029,7 +1029,7 @@ id: "84f28ea9f6c44afaa3b7777cc286c18c" sender: "DevOps" title: "【蓝盾权限】移交通知" - body: "${handoverFrom}已申请将蓝盾项目【${projectName}】下的 ${handoverOverviewContentOfRtx}移交给您,请确认是否接收 \n去处理:${url}" + body: "${handoverFrom}申请将蓝盾项目【${projectName}】下的 ${handoverOverviewContentOfRtx}移交给你,请确认是否接收 \n去处理:${url}" creator: "system" modifior: "system" - index: 49 @@ -1044,7 +1044,7 @@ emailTemplate: id: "902b30b05d184f478ea8eaaea53533f1" title: "蓝盾权限交接申请" - body: "\n\n\n\n \n \n 蓝盾DevOps平台\n \n\n\n\n \n \n \n \n \n \n
\n \n \n \n \n \n \n
\n \n \n \n \n
\n
\n

蓝盾权限交接申请 - ${result}

\n
\n

您好,${handoverFrom}

\n

${content}

\n

审核意见:${remark}

\n
\n 查看更多\n
\n
\n
\n
\n\n\n\n\n " + body: "\n\n\n\n \n \n 蓝盾DevOps平台\n \n\n\n\n \n \n \n \n \n \n
\n \n \n \n \n \n \n
\n \n \n \n \n
\n
\n

蓝盾权限交接申请 - ${result}

\n
\n

你好,${handoverFrom}

\n

${content}

\n

审核意见:${remark}

\n
\n 查看详情\n
\n
\n
\n
\n\n\n\n\n " sender: "DevOps" bodyFormat: 1 emailType: 1 @@ -1052,6 +1052,6 @@ id: "4ead2e88f2a94fcbb7b1ffc5c523f742" sender: "DevOps" title: "蓝盾权限交接申请 - ${result}" - body: "您好,${handoverFrom} \n${content} \n审核意见:${remark} \n更多详情查看:${url}" + body: "你好,${handoverFrom} \n${content} \n审核意见:${remark} \n更多详情查看:${url}" creator: "system" modifior: "system" From 95663b1ba9f1319e14535e77e865473d47ee57af Mon Sep 17 00:00:00 2001 From: greysonfang Date: Thu, 26 Dec 2024 10:40:57 +0800 Subject: [PATCH 28/33] =?UTF-8?q?feat=EF=BC=9A=E7=94=A8=E6=88=B7=E4=B8=AA?= =?UTF-8?q?=E4=BA=BA=E8=A7=86=E8=A7=92=20=E6=9D=83=E9=99=90=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E4=BC=98=E5=8C=96=20#11138?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../devops/auth/pojo/enum/HandoverAction.kt | 24 +++++++++++++++---- .../RbacPermissionManageFacadeServiceImpl.kt | 7 +++++- support-files/i18n/notify/template_zh_CN.yaml | 4 ++-- 3 files changed, 28 insertions(+), 7 deletions(-) diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/HandoverAction.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/HandoverAction.kt index a0adb00ce702..45c019f41315 100644 --- a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/HandoverAction.kt +++ b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/HandoverAction.kt @@ -3,16 +3,32 @@ package com.tencent.devops.auth.pojo.enum enum class HandoverAction( val value: Int, val alias: String, - val content: String + val emailContent: String, + val weworkContent: String ) { // 审批成功 - AGREE(1, "已通过", "您提交的权限交接单 %s 已被 %s 通过。恭喜您完成交接。"), + AGREE( + 1, + "已通过", + "你提交的权限交接单 %s 已被 %s 通过。恭喜您完成交接。", + "你提交的权限交接单 %s 已被 %s 通过。恭喜您完成交接。", + ), // 审批驳回 - REJECT(2, "已拒绝", "您提交的权限交接单 %s 已被 %s 拒绝。请重新交接。"), + REJECT( + 2, + "已拒绝", + "您提交的权限交接单 %s 已被 %s 拒绝。请重新交接。", + "您提交的权限交接单 %s 已被 %s 拒绝。请重新交接。" + ), // 撤销 - REVOKE(3, "撤销", "已撤销"); + REVOKE( + 3, + "撤销", + "已撤销", + "已撤销" + ); companion object { fun get(value: Int): HandoverAction { diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionManageFacadeServiceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionManageFacadeServiceImpl.kt index 20350c407cef..f75ff5c10ce0 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionManageFacadeServiceImpl.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionManageFacadeServiceImpl.kt @@ -1890,7 +1890,12 @@ class RbacPermissionManageFacadeServiceImpl( "handoverFrom" to overview.applicant.plus("($handoverFromCnName)"), "remark" to request.remark!!, "content" to String.format( - request.handoverAction.content, + request.handoverAction.emailContent, + request.flowNo, + overview.approver.plus("($handoverToCnName)"), + ), + "weworkContent" to String.format( + request.handoverAction.weworkContent, request.flowNo, overview.approver.plus("($handoverToCnName)"), ), diff --git a/support-files/i18n/notify/template_zh_CN.yaml b/support-files/i18n/notify/template_zh_CN.yaml index 15040ad23027..d71da2273107 100644 --- a/support-files/i18n/notify/template_zh_CN.yaml +++ b/support-files/i18n/notify/template_zh_CN.yaml @@ -1021,7 +1021,7 @@ emailTemplate: id: "d57d7358f5034b21acafde62f27d75fd" title: "蓝盾权限交接申请" - body: "\n\n\n\n \n \n 蓝盾DevOps平台\n \n\n\n\n \n \n \n \n \n \n
\n \n \n \n \n \n \n
\n \n \n \n \n
\n
\n

蓝盾权限交接申请

\n
\n

你好,${handoverTo}:

\n

${handoverFrom} 已申请将蓝盾项目[${projectName}]下的 ${handoverOverviews}移交给你,请确认是否接收。

\n
\n \n \n \n \n \n \n \n \n ${table}\n
交接类型资源类型数量
\n 马上处理\n
\n
\n
\n
\n\n\n\n" + body: "\n\n\n\n \n \n 蓝盾DevOps平台\n \n\n\n\n \n \n \n \n \n \n
\n \n \n \n \n \n \n
\n \n \n \n \n
\n
\n

蓝盾权限交接申请

\n
\n

你好,${handoverTo}:

\n

${handoverFrom} 申请将蓝盾项目[${projectName}]下的 ${handoverOverviews}移交给你,请确认是否接收。

\n
\n \n \n \n \n \n \n \n \n ${table}\n
交接类型资源类型数量
\n 马上处理\n
\n
\n
\n
\n\n\n\n" sender: "DevOps" bodyFormat: 1 emailType: 1 @@ -1052,6 +1052,6 @@ id: "4ead2e88f2a94fcbb7b1ffc5c523f742" sender: "DevOps" title: "蓝盾权限交接申请 - ${result}" - body: "你好,${handoverFrom} \n${content} \n审核意见:${remark} \n更多详情查看:${url}" + body: "你好,${handoverFrom} \n${weworkContent} \n审核意见:${remark} \n更多详情查看:${url}" creator: "system" modifior: "system" From 8af260d4d679207c11b0da9985b0e39d507ac262 Mon Sep 17 00:00:00 2001 From: greysonfang Date: Thu, 26 Dec 2024 10:47:25 +0800 Subject: [PATCH 29/33] =?UTF-8?q?feat=EF=BC=9A=E7=94=A8=E6=88=B7=E4=B8=AA?= =?UTF-8?q?=E4=BA=BA=E8=A7=86=E8=A7=92=20=E6=9D=83=E9=99=90=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E4=BC=98=E5=8C=96=20#11138?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rbac/service/RbacPermissionManageFacadeServiceImpl.kt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionManageFacadeServiceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionManageFacadeServiceImpl.kt index f75ff5c10ce0..c8d6608d9f5f 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionManageFacadeServiceImpl.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionManageFacadeServiceImpl.kt @@ -1899,7 +1899,7 @@ class RbacPermissionManageFacadeServiceImpl( request.flowNo, overview.approver.plus("($handoverToCnName)"), ), - "url" to String.format(handoverApplicationUrl, request.flowNo) + "url" to String.format(url, request.flowNo) ) // 发邮件 val emailRequest = SendNotifyMessageTemplateRequest( @@ -2384,8 +2384,7 @@ class RbacPermissionManageFacadeServiceImpl( } } - private val handoverApplicationUrl = - "${config.devopsHostGateway}/console/permission/my-handover?type=handoverFromMe&flowNo=%s" + private val url = "${config.devopsHostGateway}/console/permission/my-handover?type=handoverFromMe&flowNo=%s" companion object { private val logger = LoggerFactory.getLogger(RbacPermissionResourceMemberService::class.java) From 6236d3d267907c6821caaade27aaa916e2a6f84b Mon Sep 17 00:00:00 2001 From: greysonfang Date: Thu, 26 Dec 2024 11:05:20 +0800 Subject: [PATCH 30/33] =?UTF-8?q?feat=EF=BC=9A=E7=94=A8=E6=88=B7=E4=B8=AA?= =?UTF-8?q?=E4=BA=BA=E8=A7=86=E8=A7=92=20=E6=9D=83=E9=99=90=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E4=BC=98=E5=8C=96=20#11138?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/tencent/devops/auth/pojo/enum/HandoverAction.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/HandoverAction.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/HandoverAction.kt index 45c019f41315..abb01f35bdf8 100644 --- a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/HandoverAction.kt +++ b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/HandoverAction.kt @@ -10,8 +10,8 @@ enum class HandoverAction( AGREE( 1, "已通过", - "你提交的权限交接单 %s 已被 %s 通过。恭喜您完成交接。", - "你提交的权限交接单 %s 已被 %s 通过。恭喜您完成交接。", + "你提交的权限交接单 %s 已被 %s 通过。恭喜你完成交接。", + "你提交的权限交接单 %s 已被 %s 通过。恭喜你完成交接。", ), // 审批驳回 @@ -19,7 +19,7 @@ enum class HandoverAction( 2, "已拒绝", "您提交的权限交接单 %s 已被 %s 拒绝。请重新交接。", - "您提交的权限交接单 %s 已被 %s 拒绝。请重新交接。" + "你提交的权限交接单 %s 已被 %s 拒绝。请重新交接。" ), // 撤销 From 9aeae1a484c56f12044081529fb939bba17c8522 Mon Sep 17 00:00:00 2001 From: greysonfang Date: Thu, 26 Dec 2024 11:30:49 +0800 Subject: [PATCH 31/33] =?UTF-8?q?feat=EF=BC=9A=E7=94=A8=E6=88=B7=E4=B8=AA?= =?UTF-8?q?=E4=BA=BA=E8=A7=86=E8=A7=92=20=E6=9D=83=E9=99=90=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E4=BC=98=E5=8C=96=20#11138?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/com/tencent/devops/auth/pojo/enum/HandoverAction.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/HandoverAction.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/HandoverAction.kt index abb01f35bdf8..0ac4e69749c8 100644 --- a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/HandoverAction.kt +++ b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/HandoverAction.kt @@ -18,7 +18,7 @@ enum class HandoverAction( REJECT( 2, "已拒绝", - "您提交的权限交接单 %s 已被 %s 拒绝。请重新交接。", + "你提交的权限交接单 %s 已被 %s 拒绝。请重新交接。", "你提交的权限交接单 %s 已被 %s 拒绝。请重新交接。" ), From 25a9c8a6e7b81908d1aebef6491faf83a31c7dec Mon Sep 17 00:00:00 2001 From: greysonfang Date: Thu, 26 Dec 2024 11:58:24 +0800 Subject: [PATCH 32/33] =?UTF-8?q?feat=EF=BC=9A=E7=94=A8=E6=88=B7=E4=B8=AA?= =?UTF-8?q?=E4=BA=BA=E8=A7=86=E8=A7=92=20=E6=9D=83=E9=99=90=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E4=BC=98=E5=8C=96=20#11138?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rbac/service/RbacPermissionHandoverApplicationService.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionHandoverApplicationService.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionHandoverApplicationService.kt index 56cdcd6eefdf..fee1d2e7bd89 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionHandoverApplicationService.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionHandoverApplicationService.kt @@ -90,8 +90,8 @@ class RbacPermissionHandoverApplicationService( } val handoverOverviewTable = handoverOverviewTableBuilder.toString() val bodyParams = mapOf( - "handoverFrom" to overview.applicant.plus("($handoverFromCnName)"), - "handoverTo" to overview.approver.plus("($handoverToCnName)"), + "handoverFrom" to overview.applicant.plus("($handoverFromCnName)"), + "handoverTo" to overview.approver.plus("($handoverToCnName)"), "projectName" to overview.projectName, "handoverOverviews" to handoverOverviewContentOfEmail, "handoverOverviewContentOfRtx" to handoverOverviewContentOfRtx, From 02fa3a06616caad2639a83bb5e4430ae71c9daea Mon Sep 17 00:00:00 2001 From: greysonfang Date: Thu, 26 Dec 2024 14:43:18 +0800 Subject: [PATCH 33/33] =?UTF-8?q?feat=EF=BC=9A=E7=94=A8=E6=88=B7=E4=B8=AA?= =?UTF-8?q?=E4=BA=BA=E8=A7=86=E8=A7=92=20=E6=9D=83=E9=99=90=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E4=BC=98=E5=8C=96=20#11138?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/com/tencent/devops/auth/pojo/enum/HandoverAction.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/HandoverAction.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/HandoverAction.kt index 0ac4e69749c8..d4535c868c6a 100644 --- a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/HandoverAction.kt +++ b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/enum/HandoverAction.kt @@ -10,7 +10,7 @@ enum class HandoverAction( AGREE( 1, "已通过", - "你提交的权限交接单 %s 已被 %s 通过。恭喜你完成交接。", + "你提交的权限交接单 %s 已被 %s 通过。恭喜你完成交接。", "你提交的权限交接单 %s 已被 %s 通过。恭喜你完成交接。", ),