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 50d7ce1996c..e403ce5b338 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 @@ -92,6 +92,15 @@ interface UserAuthResourceGroupResource { @QueryParam("memberId") @Parameter(description = "组织ID/成员ID") memberId: String, + @QueryParam("groupName") + @Parameter(description = "用户组名称") + groupName: String?, + @QueryParam("minExpiredAt") + @Parameter(description = "最小过期时间") + minExpiredAt: Long?, + @QueryParam("maxExpiredAt") + @Parameter(description = "最大过期时间") + maxExpiredAt: Long?, @Parameter(description = "起始位置,从0开始") @QueryParam("start") start: Int, 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 8e0da2fd2f1..868f0d39d37 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 @@ -6,6 +6,7 @@ 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.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 @@ -32,6 +33,7 @@ import javax.ws.rs.core.MediaType @Path("/user/auth/resource/member/{projectId}/") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) +@Suppress("LongParameterList") interface UserAuthResourceMemberResource { @GET @Path("/listProjectMembers") @@ -64,6 +66,20 @@ interface UserAuthResourceMemberResource { pageSize: Int ): Result> + @POST + @Path("/listProjectMembersByCondition") + @Operation(summary = "根据条件获取项目下全体成员") + fun listProjectMembersByCondition( + @Parameter(description = "用户名", required = true) + @HeaderParam(AUTH_HEADER_USER_ID) + userId: String, + @Parameter(description = "项目ID", required = true) + @PathParam("projectId") + projectId: String, + @Parameter(description = "查询条件", required = true) + projectMembersQueryConditionReq: ProjectMembersQueryConditionReq + ): Result> + @PUT @Path("/renewal") @Operation(summary = "续期单个组成员权限--无需进行审批") @@ -177,6 +193,15 @@ interface UserAuthResourceMemberResource { projectId: String, @QueryParam("memberId") @Parameter(description = "组织ID/成员ID") - memberId: String + memberId: String, + @QueryParam("groupName") + @Parameter(description = "用户组名称") + groupName: String?, + @QueryParam("minExpiredAt") + @Parameter(description = "最小过期时间") + minExpiredAt: Long?, + @QueryParam("maxExpiredAt") + @Parameter(description = "最大过期时间") + maxExpiredAt: Long? ): Result> } diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/dto/ProjectMembersQueryConditionDTO.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/dto/ProjectMembersQueryConditionDTO.kt new file mode 100644 index 00000000000..8034e532bab --- /dev/null +++ b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/dto/ProjectMembersQueryConditionDTO.kt @@ -0,0 +1,61 @@ +package com.tencent.devops.auth.pojo.dto + +import com.tencent.devops.auth.pojo.request.ProjectMembersQueryConditionReq +import com.tencent.devops.common.api.util.DateTimeUtil +import com.tencent.devops.common.api.util.PageUtil +import io.swagger.v3.oas.annotations.media.Schema +import java.time.LocalDateTime + +@Schema(title = "项目成员查询业务处理实体") +data class ProjectMembersQueryConditionDTO( + @get:Schema(title = "项目ID") + val projectCode: String, + @get:Schema(title = "成员类型") + val memberType: String? = null, + @get:Schema(title = "用户名称") + val userName: String? = null, + @get:Schema(title = "部门名称") + val deptName: String? = null, + @get:Schema(title = "用户组名称") + val groupName: String? = null, + @get:Schema(title = "用户组Id") + val iamGroupIds: List? = null, + @get:Schema(title = "最小过期时间") + val minExpiredTime: LocalDateTime? = null, + @get:Schema(title = "最大过期时间") + val maxExpiredTime: LocalDateTime? = null, + @get:Schema(title = "离职标识") + val departedFlag: Boolean? = false, + @get:Schema(title = "是否查询模板") + val queryTemplate: Boolean? = false, + @get:Schema(title = "限制") + val limit: Int? = null, + @get:Schema(title = "起始值") + val offset: Int? = null +) { + companion object { + fun build( + projectMembersQueryConditionReq: ProjectMembersQueryConditionReq, + iamGroupIds: List? + ): ProjectMembersQueryConditionDTO { + return with(projectMembersQueryConditionReq) { + val minExpiredTime = minExpiredAt?.let { DateTimeUtil.convertTimestampToLocalDateTime(it / 1000) } + val maxExpiredTime = maxExpiredAt?.let { DateTimeUtil.convertTimestampToLocalDateTime(it / 1000) } + val limit = PageUtil.convertPageSizeToSQLLimit(page, pageSize) + ProjectMembersQueryConditionDTO( + projectCode = projectCode, + memberType = memberType, + userName = userName, + deptName = deptName, + groupName = groupName, + iamGroupIds = iamGroupIds, + minExpiredTime = minExpiredTime, + maxExpiredTime = maxExpiredTime, + departedFlag = departedFlag, + limit = limit.limit, + offset = limit.offset + ) + } + } + } +} diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/request/ProjectMembersQueryConditionReq.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/request/ProjectMembersQueryConditionReq.kt new file mode 100644 index 00000000000..14209b5fdf9 --- /dev/null +++ b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/request/ProjectMembersQueryConditionReq.kt @@ -0,0 +1,36 @@ +package com.tencent.devops.auth.pojo.request + +import io.swagger.v3.oas.annotations.media.Schema + +@Schema(title = "项目成员查询业务处理请求体") +data class ProjectMembersQueryConditionReq( + @get:Schema(title = "项目ID") + val projectCode: String, + @get:Schema(title = "成员类型") + val memberType: String?, + @get:Schema(title = "用户名称") + val userName: String?, + @get:Schema(title = "部门名称") + val deptName: String?, + @get:Schema(title = "用户组名称") + val groupName: String?, + @get:Schema(title = "最小过期时间") + val minExpiredAt: Long?, + @get:Schema(title = "最大过期时间") + val maxExpiredAt: Long?, + @get:Schema(title = "离职标识") + val departedFlag: Boolean? = false, + @get:Schema(title = "第几页") + val page: Int, + @get:Schema(title = "页数") + val pageSize: Int +) { + // 当查询到权限相关信息时,如组名称,过期时间,操作,资源类型时,走复杂查询逻辑 + fun isComplexQuery(): Boolean { + return groupName != null || minExpiredAt != null || maxExpiredAt != null + } + + fun isNeedToQueryIamGroupIds(): Boolean { + return groupName != null + } +} diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/cron/AuthCronSyncGroupAndMember.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/cron/AuthCronSyncGroupAndMember.kt index 774607c5e2b..aca24708f79 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/cron/AuthCronSyncGroupAndMember.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/cron/AuthCronSyncGroupAndMember.kt @@ -45,7 +45,10 @@ class AuthCronSyncGroupAndMember( } } - @Scheduled(cron = "0 0 8,16 * * ?") + /** + * 一小时,同步一次用户申请加入组的单据,若连续两个月未审批单据,将不再进行扫描 + * */ + @Scheduled(initialDelay = 10000, fixedRate = 3600000) fun syncIamGroupMembersOfApplyRegularly() { if (!enable) { return 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 ef94562a6fc..4dc56b5b18d 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 @@ -96,7 +96,7 @@ class AuthAuthorizationDao { .set(HANDOVER_FROM_CN_NAME, resourceAuthorizationDto.handoverToCnName) .set(HANDOVER_TIME, LocalDateTime.now()) } else { - it + it.set(HANDOVER_TIME, HANDOVER_TIME) } } .set(RESOURCE_NAME, resourceAuthorizationDto.resourceName) 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 49efc0066bd..fa567c941ab 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 @@ -29,6 +29,7 @@ package com.tencent.devops.auth.dao import com.tencent.devops.auth.pojo.AuthResourceGroup +import com.tencent.devops.common.auth.api.AuthResourceType import com.tencent.devops.model.auth.tables.TAuthResourceGroup import com.tencent.devops.model.auth.tables.records.TAuthResourceGroupRecord import org.jooq.DSLContext @@ -252,12 +253,31 @@ class AuthResourceGroupDao { fun listIamGroupIdsByConditions( dslContext: DSLContext, projectCode: String, - iamGroupIds: List + iamGroupIds: List? = null, + groupName: String? = null, + iamTemplateIds: List? = null ): List { return with(TAuthResourceGroup.T_AUTH_RESOURCE_GROUP) { dslContext.select(RELATION_ID).from(this) .where(PROJECT_CODE.eq(projectCode)) - .and(RELATION_ID.`in`(iamGroupIds)) + .let { + if (!iamGroupIds.isNullOrEmpty()) + it.and(RELATION_ID.`in`(iamGroupIds)) + else it + } + .let { + if (groupName != null) + it.and(GROUP_NAME.like("%$groupName%")) + else + it + } + .let { + if (!iamTemplateIds.isNullOrEmpty()) { + it.and(RESOURCE_TYPE.eq(AuthResourceType.PROJECT.value)) + it.and(IAM_TEMPLATE_ID.`in`(iamTemplateIds)) + } else + it + } .fetch().map { it.value1().toInt() } } } 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 1d95eb1b2f8..5f70950b7a4 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 @@ -30,6 +30,7 @@ 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 import com.tencent.devops.common.auth.api.pojo.BkAuthGroup import com.tencent.devops.model.auth.tables.TAuthResourceAuthorization import com.tencent.devops.model.auth.tables.TAuthResourceGroupMember @@ -362,6 +363,74 @@ class AuthResourceGroupMemberDao { } } + fun listProjectMembersByComplexConditions( + dslContext: DSLContext, + conditionDTO: ProjectMembersQueryConditionDTO + ): List { + return with(TAuthResourceGroupMember.T_AUTH_RESOURCE_GROUP_MEMBER) { + dslContext.select(MEMBER_ID, MEMBER_NAME, MEMBER_TYPE).from(this) + .where(buildProjectMembersByComplexConditions(conditionDTO)) + .groupBy(MEMBER_ID) + .orderBy(MEMBER_ID) + .let { + if (conditionDTO.limit != null && conditionDTO.offset != null) { + it.offset(conditionDTO.offset).limit(conditionDTO.limit) + } else { + it + } + } + .fetch().map { + ResourceMemberInfo( + id = it.value1(), + name = it.value2(), + type = it.value3() + ) + } + } + } + + fun countProjectMembersByComplexConditions( + dslContext: DSLContext, + conditionDTO: ProjectMembersQueryConditionDTO + ): Long { + return with(TAuthResourceGroupMember.T_AUTH_RESOURCE_GROUP_MEMBER) { + dslContext.select(countDistinct(MEMBER_ID)).from(this) + .where(buildProjectMembersByComplexConditions(conditionDTO)) + .fetchOne(0, Long::class.java) ?: 0L + } + } + + fun buildProjectMembersByComplexConditions( + projectMembersQueryConditionDTO: ProjectMembersQueryConditionDTO + ): MutableList { + val conditions = mutableListOf() + with(TAuthResourceGroupMember.T_AUTH_RESOURCE_GROUP_MEMBER) { + with(projectMembersQueryConditionDTO) { + conditions.add(PROJECT_CODE.eq(projectCode)) + if (queryTemplate == false) { + conditions.add(MEMBER_TYPE.notEqual(ManagerScopesEnum.getType(ManagerScopesEnum.TEMPLATE))) + } else { + conditions.add(MEMBER_TYPE.eq(ManagerScopesEnum.getType(ManagerScopesEnum.TEMPLATE))) + } + memberType?.let { type -> conditions.add(MEMBER_TYPE.eq(type)) } + userName?.let { name -> + conditions.add(MEMBER_TYPE.eq(ManagerScopesEnum.getType(ManagerScopesEnum.USER))) + 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_NAME.like("%$name%")) + } + minExpiredTime?.let { minTime -> conditions.add(EXPIRED_TIME.ge(minTime)) } + maxExpiredTime?.let { maxTime -> conditions.add(EXPIRED_TIME.le(maxTime)) } + if (!iamGroupIds.isNullOrEmpty()) { + conditions.add(IAM_GROUP_ID.`in`(iamGroupIds)) + } + } + } + return conditions + } + fun countProjectMember( dslContext: DSLContext, projectCode: String @@ -416,6 +485,7 @@ class AuthResourceGroupMemberDao { .from(tResourceGroupMember) .where(tResourceGroupMember.PROJECT_CODE.eq(projectCode)) .and(tResourceGroupMember.MEMBER_TYPE.notEqual(ManagerScopesEnum.getType(ManagerScopesEnum.TEMPLATE))) + .groupBy(tResourceGroupMember.MEMBER_ID) .unionAll( dslContext.select( tResourceAuthorization.HANDOVER_FROM.`as`("MEMBER_ID"), @@ -424,6 +494,7 @@ class AuthResourceGroupMemberDao { ) .from(tResourceAuthorization) .where(tResourceAuthorization.PROJECT_CODE.eq(projectCode)) + .groupBy(tResourceAuthorization.HANDOVER_FROM) ) .asTable(TABLE_NAME) } @@ -452,24 +523,6 @@ class AuthResourceGroupMemberDao { return conditions } - /** - * 查询组下所有成员 - */ - fun listGroupMember( - dslContext: DSLContext, - projectCode: String, - iamGroupId: Int - ): List { - return with(TAuthResourceGroupMember.T_AUTH_RESOURCE_GROUP_MEMBER) { - dslContext.selectFrom(this) - .where(PROJECT_CODE.eq(projectCode)) - .and(IAM_GROUP_ID.eq(iamGroupId)) - .fetch().map { - convert(it) - } - } - } - /** * 获取成员按资源类型分组数量 */ @@ -479,14 +532,18 @@ class AuthResourceGroupMemberDao { memberId: String, iamTemplateIds: List, resourceType: String? = null, - iamGroupIds: List? = null + iamGroupIds: List? = null, + minExpiredAt: LocalDateTime? = null, + maxExpiredAt: LocalDateTime? = null ): Map { val conditions = buildMemberGroupCondition( projectCode = projectCode, memberId = memberId, iamTemplateIds = iamTemplateIds, resourceType = resourceType, - iamGroupIds = iamGroupIds + iamGroupIds = iamGroupIds, + minExpiredAt = minExpiredAt, + maxExpiredAt = maxExpiredAt ) return with(TAuthResourceGroupMember.T_AUTH_RESOURCE_GROUP_MEMBER) { val select = dslContext.select(RESOURCE_TYPE, count()) @@ -507,6 +564,8 @@ class AuthResourceGroupMemberDao { iamTemplateIds: List, resourceType: String? = null, iamGroupIds: List? = null, + minExpiredAt: LocalDateTime? = null, + maxExpiredAt: LocalDateTime? = null, offset: Int? = null, limit: Int? = null ): List { @@ -515,7 +574,9 @@ class AuthResourceGroupMemberDao { memberId = memberId, iamTemplateIds = iamTemplateIds, resourceType = resourceType, - iamGroupIds = iamGroupIds + iamGroupIds = iamGroupIds, + minExpiredAt = minExpiredAt, + maxExpiredAt = maxExpiredAt ) return with(TAuthResourceGroupMember.T_AUTH_RESOURCE_GROUP_MEMBER) { dslContext.selectFrom(this) @@ -532,7 +593,9 @@ class AuthResourceGroupMemberDao { memberId: String, iamTemplateIds: List, resourceType: String? = null, - iamGroupIds: List? = null + iamGroupIds: List? = null, + minExpiredAt: LocalDateTime? = null, + maxExpiredAt: LocalDateTime? = null ): MutableList { val conditions = mutableListOf() with(TAuthResourceGroupMember.T_AUTH_RESOURCE_GROUP_MEMBER) { @@ -552,7 +615,11 @@ class AuthResourceGroupMemberDao { ) ) resourceType?.let { conditions.add(RESOURCE_TYPE.eq(resourceType)) } - iamGroupIds?.let { conditions.add(IAM_GROUP_ID.`in`(iamGroupIds)) } + minExpiredAt?.let { conditions.add(EXPIRED_TIME.ge(minExpiredAt)) } + maxExpiredAt?.let { conditions.add(EXPIRED_TIME.le(maxExpiredAt)) } + if (!iamGroupIds.isNullOrEmpty()) { + conditions.add(IAM_GROUP_ID.`in`(iamGroupIds)) + } } return conditions } 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 6a2a651e8ee..2be996b9242 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 @@ -80,7 +80,7 @@ class RbacPermissionResourceGroupSyncService @Autowired constructor( private val syncExecutorService = Executors.newFixedThreadPool(5) private val syncProjectsExecutorService = Executors.newFixedThreadPool(10) private val syncResourceMemberExecutorService = Executors.newFixedThreadPool(50) - private const val MAX_NUMBER_OF_CHECKS = 120 + private const val MAX_NUMBER_OF_CHECKS = 1440 } override fun syncByCondition(projectConditionDTO: ProjectConditionDTO) { @@ -186,6 +186,7 @@ class RbacPermissionResourceGroupSyncService @Autowired constructor( limit = limit, offset = offset ) + // 检查60天内的申请的单据 val recordIdsOfTimeOut = records.filter { it.numberOfChecks >= MAX_NUMBER_OF_CHECKS }.map { it.id } val (recordsOfSuccess, recordsOfPending) = records.filterNot { recordIdsOfTimeOut.contains(it.id) 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 ee46893139b..302d1ff258b 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 @@ -17,6 +17,7 @@ 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.dto.ProjectMembersQueryConditionDTO import com.tencent.devops.auth.pojo.enum.BatchOperateType import com.tencent.devops.auth.pojo.enum.JoinedType import com.tencent.devops.auth.pojo.enum.RemoveMemberButtonControl @@ -24,6 +25,7 @@ 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.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 @@ -180,52 +182,166 @@ class RbacPermissionResourceMemberService constructor( return SQLPage(count = count, records = records) } + return SQLPage(count = count, records = addDepartedFlagToMembers(records)) + } + + private fun addDepartedFlagToMembers(records: List): List { val userMembers = records.filter { it.type == ManagerScopesEnum.getType(ManagerScopesEnum.USER) }.map { it.id } - val departedMembers = if (userMembers.isNotEmpty()) { deptService.listDepartedMembers( memberIds = userMembers ) } else { - return SQLPage(count = count, records = records) + return records } - - val recordsWithDepartedFlag = records.map { + return records.map { if (it.type != ManagerScopesEnum.getType(ManagerScopesEnum.USER)) { it.copy(departed = false) } else { it.copy(departed = departedMembers.contains(it.id)) } } - return SQLPage(count = count, records = recordsWithDepartedFlag) + } + + 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 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.isNeedToQueryIamGroupIds()) { + queryIamGroupIdsByConditions( + projectCode = conditionReq.projectCode, + groupName = conditionReq.groupName + ) + } else { + emptyList() + }.toMutableList() + + if (conditionReq.isNeedToQueryIamGroupIds() && iamGroupIdsByCondition.isEmpty()) { + return SQLPage(0, emptyList()) + } + + val conditionDTO = ProjectMembersQueryConditionDTO.build(conditionReq, iamGroupIdsByCondition) + + if (iamGroupIdsByCondition.isNotEmpty()) { + // 根据用户组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) + } + } + + val records = authResourceGroupMemberDao.listProjectMembersByComplexConditions( + dslContext = dslContext, + conditionDTO = conditionDTO + ) + + val count = authResourceGroupMemberDao.countProjectMembersByComplexConditions( + dslContext = dslContext, + conditionDTO = conditionDTO + ) + + // 添加离职标志 + return if (conditionDTO.departedFlag == false) { + SQLPage(count, records) + } else { + SQLPage(count, addDepartedFlagToMembers(records)) + } + } + + /** + * 该方法后期可进行扩展,根据用户组名称,操作,资源类型, + * 组策略查询出对应的组ID,传递用户组表进行查询。 + * */ + private fun queryIamGroupIdsByConditions( + projectCode: String, + groupName: String? = null, + iamGroupIds: List? = null + ): List { + val finalGroupIds = mutableListOf() + if (groupName != null) { + val iamGroupIdsByConditions = authResourceGroupDao.listIamGroupIdsByConditions( + dslContext = dslContext, + projectCode = projectCode, + groupName = groupName + ) + finalGroupIds.addAll(iamGroupIdsByConditions) + } + if (!iamGroupIds.isNullOrEmpty()) { + finalGroupIds.addAll(iamGroupIds) + } + return finalGroupIds } override fun getMemberGroupsCount( projectCode: String, - memberId: String + memberId: String, + groupName: String?, + minExpiredAt: Long?, + maxExpiredAt: Long? ): List { - // 1. 查询项目下包含该成员的组列表 + // 查询项目下包含该成员的组列表 val projectGroupIds = authResourceGroupMemberDao.listResourceGroupMember( dslContext = dslContext, projectCode = projectCode, resourceType = AuthResourceType.PROJECT.value, memberId = memberId ).map { it.iamGroupId.toString() } - // 2. 通过项目组ID获取人员模板ID + // 通过项目组ID获取人员模板ID val iamTemplateId = authResourceGroupDao.listByRelationId( dslContext = dslContext, projectCode = projectCode, iamGroupIds = projectGroupIds ).filter { it.iamTemplateId != null } .map { it.iamTemplateId.toString() } - // 3. 获取成员直接加入的组和通过模板加入的组 + + // 后续改造,根据操作/资源类型/资源实例进行筛选,只需要扩展该方法即可 + val iamGroupIdsByConditions = queryIamGroupIdsByConditions( + projectCode = projectCode, + groupName = groupName + ) + // 获取成员直接加入的组和通过模板加入的组 val memberGroupCountMap = authResourceGroupMemberDao.countMemberGroup( dslContext = dslContext, projectCode = projectCode, memberId = memberId, - iamTemplateIds = iamTemplateId + iamTemplateIds = iamTemplateId, + iamGroupIds = iamGroupIdsByConditions, + minExpiredAt = minExpiredAt?.let { DateTimeUtil.convertTimestampToLocalDateTime(it / 1000) }, + maxExpiredAt = maxExpiredAt?.let { DateTimeUtil.convertTimestampToLocalDateTime(it / 1000) } ) val memberGroupCountList = mutableListOf() // 项目排在第一位 @@ -1292,15 +1408,27 @@ class RbacPermissionResourceMemberService constructor( memberId: String, resourceType: String?, iamGroupIds: List?, + groupName: String?, + minExpiredAt: Long?, + maxExpiredAt: Long?, start: Int?, limit: Int? ): SQLPage { + // 后续改造,根据操作/资源类型/资源实例进行筛选,只需要扩展该方法即可 + // 根据查询条件查询得到iam组id + val iamGroupIdsByConditions = queryIamGroupIdsByConditions( + projectCode = projectId, + groupName = groupName, + iamGroupIds = iamGroupIds + ) // 查询成员所在资源用户组列表,直接加入+通过用户组(模板)加入 val (count, resourceGroupMembers) = listResourceGroupMembers( projectCode = projectId, memberId = memberId, resourceType = resourceType, - iamGroupIds = iamGroupIds, + iamGroupIds = iamGroupIdsByConditions, + minExpiredAt = minExpiredAt, + maxExpiredAt = maxExpiredAt, start = start, limit = limit ) @@ -1344,6 +1472,8 @@ class RbacPermissionResourceMemberService constructor( memberId: String, resourceType: String? = null, iamGroupIds: List? = null, + minExpiredAt: Long? = null, + maxExpiredAt: Long? = null, start: Int? = null, limit: Int? = null ): Pair> { @@ -1352,13 +1482,17 @@ class RbacPermissionResourceMemberService constructor( 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 + iamGroupIds = iamGroupIds, + minExpiredAt = minExpiredTime, + maxExpiredAt = maxExpiredTime )[resourceType] ?: 0L val resourceGroupMembers = authResourceGroupMemberDao.listMemberGroupDetail( dslContext = dslContext, @@ -1367,6 +1501,8 @@ class RbacPermissionResourceMemberService constructor( iamTemplateIds = iamTemplateIds, resourceType = resourceType, iamGroupIds = iamGroupIds, + minExpiredAt = minExpiredTime, + maxExpiredAt = maxExpiredTime, offset = start, limit = limit ) 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 e6f5eddc799..6c3456ee138 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 @@ -10,6 +10,7 @@ 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.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 @@ -173,9 +174,18 @@ class SamplePermissionResourceMemberService : PermissionResourceMemberService { return SQLPage(count = 0, records = emptyList()) } + override fun listProjectMembersByComplexConditions( + projectMembersQueryConditionReq: ProjectMembersQueryConditionReq + ): SQLPage { + return SQLPage(count = 0, records = emptyList()) + } + override fun getMemberGroupsCount( projectCode: String, - memberId: String + memberId: String, + groupName: String?, + minExpiredAt: Long?, + maxExpiredAt: Long? ): List { return emptyList() } @@ -185,6 +195,9 @@ class SamplePermissionResourceMemberService : PermissionResourceMemberService { memberId: String, resourceType: String?, iamGroupIds: List?, + groupName: String?, + minExpiredAt: Long?, + maxExpiredAt: Long?, start: Int?, limit: Int? ): SQLPage { diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/UserAuthResourceGroupResourceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/UserAuthResourceGroupResourceImpl.kt index 43ed6851abe..6861454c155 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/UserAuthResourceGroupResourceImpl.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/UserAuthResourceGroupResourceImpl.kt @@ -71,6 +71,9 @@ class UserAuthResourceGroupResourceImpl @Autowired constructor( projectId: String, resourceType: String, memberId: String, + groupName: String?, + minExpiredAt: Long?, + maxExpiredAt: Long?, start: Int, limit: Int ): Result> { @@ -79,6 +82,9 @@ class UserAuthResourceGroupResourceImpl @Autowired constructor( projectId = projectId, resourceType = resourceType, memberId = memberId, + groupName = groupName, + minExpiredAt = minExpiredAt, + maxExpiredAt = maxExpiredAt, start = start, limit = limit ) diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/UserAuthResourceMemberResourceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/UserAuthResourceMemberResourceImpl.kt index 47912b3d569..55b12392dfe 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/UserAuthResourceMemberResourceImpl.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/UserAuthResourceMemberResourceImpl.kt @@ -7,6 +7,7 @@ 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.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 @@ -45,6 +46,19 @@ class UserAuthResourceMemberResourceImpl( ) } + @BkManagerCheck + override fun listProjectMembersByCondition( + userId: String, + projectId: String, + projectMembersQueryConditionReq: ProjectMembersQueryConditionReq + ): Result> { + return Result( + permissionResourceMemberService.listProjectMembersByComplexConditions( + conditionReq = projectMembersQueryConditionReq + ) + ) + } + @BkManagerCheck override fun renewalGroupMember( userId: String, @@ -156,12 +170,18 @@ class UserAuthResourceMemberResourceImpl( override fun getMemberGroupCount( userId: String, projectId: String, - memberId: String + memberId: String, + groupName: String?, + minExpiredAt: Long?, + maxExpiredAt: Long? ): Result> { return Result( permissionResourceMemberService.getMemberGroupsCount( projectCode = projectId, - memberId = memberId + memberId = memberId, + groupName = groupName, + minExpiredAt = minExpiredAt, + maxExpiredAt = maxExpiredAt ) ) } 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 e0f01355221..5bd49626bfc 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 @@ -8,6 +8,7 @@ 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.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 @@ -34,6 +35,11 @@ interface PermissionResourceMemberService { fun getProjectMemberCount(projectCode: String): ResourceMemberCountVO + /** + * 之所以将简单查询接口抽成单独方法,是因为该方法只用于查询用户名称/部门名称等, + 一方面,该方法职责比较单一;另一方面,该接口需要连表查询到授权资源表中授权人。 + 复杂查询虽然需要查询各种权限,但是不需要关联授权资源表。 + * */ fun listProjectMembers( projectCode: String, memberType: String?, @@ -44,21 +50,35 @@ interface PermissionResourceMemberService { pageSize: Int ): SQLPage + /** + * 根据复杂条件进行搜索,用于用户管理界面 + * */ + fun listProjectMembersByComplexConditions( + conditionReq: ProjectMembersQueryConditionReq + ): SQLPage + /** * 获取用户有权限的用户组数量 * */ fun getMemberGroupsCount( projectCode: String, - memberId: String + memberId: String, + groupName: String?, + minExpiredAt: Long?, + maxExpiredAt: Long? ): List - // 查询成员所在资源用户组详情,直接加入+通过用户组(模板)加入 - @Suppress("LongParameterList") + /** + * 查询成员所在资源用户组详情,直接加入+通过用户组(模板)加入 + * */ fun getMemberGroupsDetails( projectId: String, memberId: String, resourceType: String?, iamGroupIds: List? = null, + groupName: String? = null, + minExpiredAt: Long? = null, + maxExpiredAt: Long? = null, start: Int?, limit: Int? ): SQLPage @@ -161,7 +181,6 @@ interface PermissionResourceMemberService { expiredAt: Long ): Boolean - @Suppress("LongParameterList") fun batchAddResourceGroupMembers( projectCode: String, iamGroupId: Int, diff --git a/src/backend/ci/core/common/common-api/src/main/kotlin/com/tencent/devops/common/api/util/DateTimeUtil.kt b/src/backend/ci/core/common/common-api/src/main/kotlin/com/tencent/devops/common/api/util/DateTimeUtil.kt index 53876182365..0159909c2cc 100644 --- a/src/backend/ci/core/common/common-api/src/main/kotlin/com/tencent/devops/common/api/util/DateTimeUtil.kt +++ b/src/backend/ci/core/common/common-api/src/main/kotlin/com/tencent/devops/common/api/util/DateTimeUtil.kt @@ -137,6 +137,9 @@ object DateTimeUtil { return localDateTime?.toEpochSecond(ZoneOffset.ofHours(8)) ?: 0L } + /* + * 用于转化秒级时间戳,非毫秒级 + * */ fun convertTimestampToLocalDateTime(timestamp: Long): LocalDateTime { return LocalDateTime.ofInstant(Instant.ofEpochSecond(timestamp), ZoneId.systemDefault()) }