Skip to content

Commit

Permalink
feat:流水线列表展示权限控制 TencentBlueKing#10895
Browse files Browse the repository at this point in the history
  • Loading branch information
fcfang123 committed Oct 28, 2024
1 parent 9f28c82 commit 4af202f
Show file tree
Hide file tree
Showing 12 changed files with 260 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -165,4 +165,12 @@ interface OpAuthMigrateResource {
@Parameter(description = "迁移项目", required = true)
projectCodes: List<String>
): Result<Boolean>

@POST
@Path("/enablePipelineListPermissionControl")
@Operation(summary = "开启流水线列表权限控制")
fun enablePipelineListPermissionControl(
@Parameter(description = "项目", required = true)
projectCodes: List<String>
): Result<Boolean>
}
Original file line number Diff line number Diff line change
Expand Up @@ -703,4 +703,15 @@ class RbacPermissionMigrateService constructor(
}
return true
}

override fun enablePipelineListPermissionControl(projectCodes: List<String>): Boolean {
projectCodes.forEach {
val projectInfo = client.get(ServiceProjectResource::class).get(it).data!!
val properties = projectInfo.properties ?: ProjectProperties()
properties.pipelineListPermissionControl = true
logger.info("update project($it) properties|$properties")
client.get(ServiceProjectResource::class).updateProjectProperties(it, properties)
}
return true
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -103,4 +103,6 @@ class SamplePermissionMigrateService(
}

override fun fixResourceGroups(projectCodes: List<String>): Boolean = true

override fun enablePipelineListPermissionControl(projectCodes: List<String>) = true
}
Original file line number Diff line number Diff line change
Expand Up @@ -113,4 +113,8 @@ class OpAuthMigrateResourceImpl @Autowired constructor(
override fun fixResourceGroups(projectCodes: List<String>): Result<Boolean> {
return Result(permissionMigrateService.fixResourceGroups(projectCodes))
}

override fun enablePipelineListPermissionControl(projectCodes: List<String>): Result<Boolean> {
return Result(permissionMigrateService.enablePipelineListPermissionControl(projectCodes))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -117,4 +117,9 @@ interface PermissionMigrateService {
* 修复资源组数据,存在同步iam资源组数据,数据库 iam组id为NULL的情况,需要进行修复
*/
fun fixResourceGroups(projectCodes: List<String>): Boolean

/**
* 开启流水线列表权限控制开关
*/
fun enablePipelineListPermissionControl(projectCodes: List<String>): Boolean
}
Original file line number Diff line number Diff line change
Expand Up @@ -154,4 +154,10 @@ interface PipelinePermissionService {
userId: String,
projectId: String
): Boolean

/**
* 判断该项目是否进行列表权限控制
* @param projectId projectId
*/
fun isControlPipelineListPermission(projectId: String): Boolean
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,13 @@ import com.tencent.devops.common.auth.api.AuthResourceType
import com.tencent.devops.common.auth.api.pojo.AuthResourceInstance
import com.tencent.devops.common.auth.api.pojo.BkAuthGroup
import com.tencent.devops.common.auth.code.PipelineAuthServiceCode
import com.tencent.devops.common.client.Client
import com.tencent.devops.process.engine.dao.PipelineInfoDao
import com.tencent.devops.process.service.view.PipelineViewGroupCommonService
import com.tencent.devops.project.api.service.ServiceProjectResource
import org.jooq.DSLContext
import org.slf4j.LoggerFactory
import javax.ws.rs.NotFoundException

@Suppress("LongParameterList")
class RbacPipelinePermissionService(
Expand All @@ -50,7 +53,8 @@ class RbacPipelinePermissionService(
val dslContext: DSLContext,
val pipelineInfoDao: PipelineInfoDao,
val pipelineViewGroupCommonService: PipelineViewGroupCommonService,
val authResourceApi: AuthResourceApi
val authResourceApi: AuthResourceApi,
val client: Client
) : PipelinePermissionService {

override fun checkPipelinePermission(
Expand Down Expand Up @@ -296,6 +300,12 @@ class RbacPipelinePermissionService(
return authProjectApi.checkProjectManager(userId, pipelineAuthServiceCode, projectId)
}

override fun isControlPipelineListPermission(projectId: String): Boolean {
val projectInfo = client.get(ServiceProjectResource::class).get(englishName = projectId).data
?: throw NotFoundException("Fail to find the project info of project($projectId)")
return projectInfo.properties?.pipelineListPermissionControl == true
}

companion object {
private val resourceType = AuthResourceType.PIPELINE_DEFAULT
private val logger = LoggerFactory.getLogger(RbacPipelinePermissionService::class.java)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,8 @@ class StreamPipelinePermissionServiceImpl @Autowired constructor(
).data ?: false
}

override fun isControlPipelineListPermission(projectId: String) = false

private fun getProjectAllInstance(projectId: String): List<String> {
return pipelineInfoDao.searchByProject(dslContext, projectId)?.map { it.pipelineId } ?: emptyList()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,14 +108,16 @@ class PipelinePermConfiguration {
dslContext: DSLContext,
pipelineInfoDao: PipelineInfoDao,
pipelineViewGroupCommonService: PipelineViewGroupCommonService,
authResourceApi: AuthResourceApi
authResourceApi: AuthResourceApi,
client: Client
): PipelinePermissionService = RbacPipelinePermissionService(
authPermissionApi = authPermissionApi,
authProjectApi = authProjectApi,
pipelineAuthServiceCode = pipelineAuthServiceCode,
dslContext = dslContext,
pipelineInfoDao = pipelineInfoDao,
pipelineViewGroupCommonService = pipelineViewGroupCommonService,
authResourceApi = authResourceApi
authResourceApi = authResourceApi,
client = client
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -560,8 +560,8 @@ class PipelineListFacadeService @Autowired constructor(
includeDelete = includeDelete,
userId = userId
)

val pipelineList = mutableListOf<Pipeline>()
val isPipelineListPermissionControl = pipelinePermissionService.isControlPipelineListPermission(projectId)
if (includeDelete) {
handlePipelineQueryList(
pipelineList = pipelineList,
Expand All @@ -581,8 +581,29 @@ class PipelineListFacadeService @Autowired constructor(
userId = userId,
queryByWeb = queryByWeb
)
} else if (!isPipelineListPermissionControl) {
// 无列表权限控制下的流水线获取方法
listPipelineWithoutPermission(
userId = userId,
projectId = projectId,
sortType = sortType,
channelCode = channelCode,
viewId = viewId,
filterInvalid = filterInvalid,
collation = collation,
queryByWeb = queryByWeb,
totalAvailablePipelineSize = totalAvailablePipelineSize,
pipelineList = pipelineList,
filterPipelineIds = pipelineIdsFilterByView,
favorPipelines = favorPipelines,
authPipelines = authPipelines,
pipelineFilterParamList = pipelineFilterParamList,
includeDelete = includeDelete,
page = page,
pageSize = pageSize
)
} else {
// 不分页查询
// 列表权限控制下的流水线获取方法
handlePipelineQueryList(
pipelineList = pipelineList,
projectId = projectId,
Expand Down Expand Up @@ -661,6 +682,179 @@ class PipelineListFacadeService @Autowired constructor(
return pipelineIds
}

/**
* 该方法之所以复杂,是为了达到将有权限列表的流水线展示在前面,
* 无列表权限的流水线挪在最后的目的。
* 这种分页方法,需要对分页的各种情况的临界值进行处理。
* */
private fun listPipelineWithoutPermission(
userId: String,
projectId: String,
sortType: PipelineSortType,
channelCode: ChannelCode,
viewId: String,
filterInvalid: Boolean = false,
collation: PipelineCollation = PipelineCollation.DEFAULT,
queryByWeb: Boolean = false,
totalAvailablePipelineSize: Long,
pipelineList: MutableList<Pipeline>,
filterPipelineIds: Collection<String>? = null,
favorPipelines: List<String> = emptyList(),
authPipelines: List<String> = emptyList(),
pipelineFilterParamList: List<PipelineFilterParam>? = null,
includeDelete: Boolean?,
page: Int?,
pageSize: Int?
) {
// 查询无权限查看的流水线总数
val totalInvalidPipelineSize =
if (filterInvalid) 0 else pipelineBuildSummaryDao.listPipelineInfoBuildSummaryCount(
dslContext = dslContext,
projectId = projectId,
channelCode = channelCode,
pipelineIds = filterPipelineIds,
viewId = viewId,
favorPipelines = favorPipelines,
authPipelines = authPipelines,
pipelineFilterParamList = pipelineFilterParamList,
permissionFlag = false,
includeDelete = includeDelete,
userId = userId
)

if ((null != page && null != pageSize) && !(page == 1 && pageSize == -1)) {
// 判断可用的流水线是否已到最后一页
val totalAvailablePipelinePage = PageUtil.calTotalPage(pageSize, totalAvailablePipelineSize)
if (page < totalAvailablePipelinePage) {
// 当前页未到可用流水线最后一页,不需要处理临界点(最后一页)的情况
handlePipelineQueryList(
pipelineList = pipelineList,
projectId = projectId,
channelCode = channelCode,
sortType = sortType,
pipelineIds = filterPipelineIds,
favorPipelines = favorPipelines,
authPipelines = authPipelines,
viewId = viewId,
pipelineFilterParamList = pipelineFilterParamList,
permissionFlag = true,
page = page,
pageSize = pageSize,
includeDelete = includeDelete,
collation = collation,
userId = userId,
queryByWeb = queryByWeb
)
} else if (page == totalAvailablePipelinePage && totalAvailablePipelineSize > 0) {
// 查询可用流水线最后一页不满页的数量
val lastPageRemainNum = pageSize - totalAvailablePipelineSize % pageSize
handlePipelineQueryList(
pipelineList = pipelineList,
projectId = projectId,
channelCode = channelCode,
sortType = sortType,
pipelineIds = filterPipelineIds,
favorPipelines = favorPipelines,
authPipelines = authPipelines,
viewId = viewId,
pipelineFilterParamList = pipelineFilterParamList,
permissionFlag = true,
page = page,
pageSize = pageSize,
includeDelete = includeDelete,
collation = collation,
userId = userId,
queryByWeb = queryByWeb
)
// 可用流水线最后一页不满页的数量需用不可用的流水线填充
if (lastPageRemainNum > 0 && totalInvalidPipelineSize > 0) {
handlePipelineQueryList(
pipelineList = pipelineList,
projectId = projectId,
channelCode = channelCode,
sortType = sortType,
pipelineIds = filterPipelineIds,
favorPipelines = favorPipelines,
authPipelines = authPipelines,
viewId = viewId,
pipelineFilterParamList = pipelineFilterParamList,
permissionFlag = false,
page = 1,
pageSize = lastPageRemainNum.toInt(),
includeDelete = includeDelete,
collation = collation,
userId = userId,
queryByWeb = queryByWeb
)
}
} else if (totalInvalidPipelineSize > 0) {
// 当前页大于可用流水线最后一页,需要排除掉可用流水线最后一页不满页的数量用不可用的流水线填充的情况
val lastPageRemainNum =
if (totalAvailablePipelineSize > 0) pageSize - totalAvailablePipelineSize % pageSize else 0
handlePipelineQueryList(
pipelineList = pipelineList,
projectId = projectId,
channelCode = channelCode,
sortType = sortType,
pipelineIds = filterPipelineIds,
favorPipelines = favorPipelines,
authPipelines = authPipelines,
viewId = viewId,
pipelineFilterParamList = pipelineFilterParamList,
permissionFlag = false,
page = page - totalAvailablePipelinePage,
pageSize = pageSize,
pageOffsetNum = lastPageRemainNum.toInt(),
includeDelete = includeDelete,
collation = collation,
userId = userId,
queryByWeb = queryByWeb
)
}
} else {
// 不分页查询
handlePipelineQueryList(
pipelineList = pipelineList,
projectId = projectId,
channelCode = channelCode,
sortType = sortType,
pipelineIds = filterPipelineIds,
favorPipelines = favorPipelines,
authPipelines = authPipelines,
viewId = viewId,
pipelineFilterParamList = pipelineFilterParamList,
permissionFlag = true,
page = page,
pageSize = pageSize,
includeDelete = includeDelete,
collation = collation,
userId = userId,
queryByWeb = queryByWeb
)

if (filterInvalid) {
handlePipelineQueryList(
pipelineList = pipelineList,
projectId = projectId,
channelCode = channelCode,
sortType = sortType,
pipelineIds = filterPipelineIds,
favorPipelines = favorPipelines,
authPipelines = authPipelines,
viewId = viewId,
pipelineFilterParamList = pipelineFilterParamList,
permissionFlag = false,
page = page,
pageSize = pageSize,
includeDelete = includeDelete,
collation = collation,
userId = userId,
queryByWeb = queryByWeb
)
}
}
}

private fun pipelineFilterParams(
projectId: String,
filterByPipelineName: String?,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -830,9 +830,14 @@ class PipelineViewGroupService @Autowired constructor(

fun listView(userId: String, projectId: String, projected: Boolean?, viewType: Int?): List<PipelineNewViewSummary> {
val views = pipelineViewDao.list(dslContext, userId, projectId, projected, viewType)
val authPipelines = pipelinePermissionService.getResourceByPermission(
userId = userId, projectId = projectId, permission = AuthPermission.LIST
)
val isControlPipelineListPermission = pipelinePermissionService.isControlPipelineListPermission(projectId)
val authPipelines = if (isControlPipelineListPermission) {
pipelinePermissionService.getResourceByPermission(
userId = userId, projectId = projectId, permission = AuthPermission.LIST
)
} else {
null
}
val countByViewId = pipelineViewGroupDao.countByViewId(
dslContext = dslContext,
projectId = projectId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,7 @@ data class ProjectProperties(
@get:Schema(title = "当项目不活跃时,是否禁用")
var disableWhenInactive: Boolean? = null,
@get:Schema(title = "该项目是否开启流水线可观测数据", required = false)
val buildMetrics: Boolean? = null
val buildMetrics: Boolean? = null,
@get:Schema(title = "是否控制流水线列表权限", required = false)
var pipelineListPermissionControl: Boolean? = null
)

0 comments on commit 4af202f

Please sign in to comment.