diff --git a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/controller/user/PermissionController.kt b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/controller/user/PermissionController.kt index 834573ba1a..11ceb434e8 100644 --- a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/controller/user/PermissionController.kt +++ b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/controller/user/PermissionController.kt @@ -40,6 +40,8 @@ import com.tencent.bkrepo.auth.pojo.permission.UpdatePermissionUserRequest import com.tencent.bkrepo.auth.controller.OpenResource import com.tencent.bkrepo.auth.pojo.enums.AuthPermissionType import com.tencent.bkrepo.auth.pojo.permission.UpdatePermissionDeployInRepoRequest +import com.tencent.bkrepo.auth.pojo.role.ExternalRoleResult +import com.tencent.bkrepo.auth.pojo.role.RoleSource import com.tencent.bkrepo.auth.service.PermissionService import com.tencent.bkrepo.common.api.pojo.Response import com.tencent.bkrepo.common.service.util.ResponseBuilder @@ -182,4 +184,14 @@ class PermissionController @Autowired constructor( preCheckUserInProject(AuthPermissionType.REPO, projectId, repoName) return ResponseBuilder.success(permissionService.getOrCreatePersonalPath(projectId, repoName)) } + + @ApiOperation("查询外部用户组") + @GetMapping("/external/group/{projectId}/{source}") + fun getExternalRole( + @PathVariable projectId: String, + @PathVariable source: RoleSource + ): Response> { + preCheckProjectAdmin(projectId) + return ResponseBuilder.success(permissionService.listExternalRoleByProject(projectId, source)) + } } \ No newline at end of file diff --git a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/dao/repository/RoleRepository.kt b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/dao/repository/RoleRepository.kt index f7890e0858..c9b1831679 100644 --- a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/dao/repository/RoleRepository.kt +++ b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/dao/repository/RoleRepository.kt @@ -33,6 +33,7 @@ package com.tencent.bkrepo.auth.dao.repository import com.tencent.bkrepo.auth.model.TRole import com.tencent.bkrepo.auth.pojo.enums.RoleType +import com.tencent.bkrepo.auth.pojo.role.RoleSource import org.springframework.data.mongodb.repository.MongoRepository import org.springframework.stereotype.Repository @@ -49,10 +50,23 @@ interface RoleRepository : MongoRepository { ): List fun findByTypeAndProjectIdAndAdmin(type: RoleType, projectId: String, admin: Boolean): List - fun findByProjectIdAndRepoNameAndType(projectId: String, repoName: String, type: RoleType): List + fun findByTypeAndProjectIdAndRepoName(type: RoleType, projectId: String, repoName: String): List fun findFirstByRoleIdAndProjectId(roleId: String, projectId: String): TRole? - fun findFirstByProjectIdAndTypeAndName(projectId: String, type: RoleType, name: String): TRole? - fun findFirstByRoleIdAndProjectIdAndRepoName(roleId: String, projectId: String, repoName: String): TRole? + fun findFirstByTypeAndProjectIdAndName(type: RoleType, projectId: String, name: String): TRole? + fun findFirstByTypeAndRoleIdAndProjectIdAndRepoName( + type: RoleType, + roleId: String, + projectId: String, + repoName: String + ): TRole? + + fun findFirstByTypeAndRoleIdAndProjectIdAndSource( + type: RoleType, + roleId: String, + projectId: String, + source: RoleSource + ): TRole? + fun findByProjectIdAndTypeAndAdminAndIdIn( projectId: String, type: RoleType, @@ -60,12 +74,14 @@ interface RoleRepository : MongoRepository { ids: List ): List - fun findByProjectIdAndTypeAndAdminAndRepoNameAndIdIn( - projectId: String, + fun findByTypeAndProjectIdAndAdminAndRepoNameAndIdIn( type: RoleType, + projectId: String, admin: Boolean, repoName: String, ids: List ): List + fun findBySource(source: RoleSource): List + } diff --git a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/helper/PermissionHelper.kt b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/helper/PermissionHelper.kt index f23127b2fd..7870077218 100644 --- a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/helper/PermissionHelper.kt +++ b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/helper/PermissionHelper.kt @@ -89,7 +89,7 @@ class PermissionHelper constructor( } if (queryRoles.isEmpty()) return false - val result = roleRepository.findByProjectIdAndTypeAndAdminAndRepoNameAndIdIn( + val result = roleRepository.findByTypeAndProjectIdAndAdminAndRepoNameAndIdIn( projectId = request.projectId!!, type = RoleType.REPO, repoName = request.repoName!!, diff --git a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/helper/UserHelper.kt b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/helper/UserHelper.kt index d0edd8516a..b7c8470c00 100644 --- a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/helper/UserHelper.kt +++ b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/helper/UserHelper.kt @@ -35,7 +35,6 @@ package com.tencent.bkrepo.auth.helper import com.tencent.bkrepo.auth.dao.UserDao import com.tencent.bkrepo.auth.dao.repository.RoleRepository import com.tencent.bkrepo.auth.message.AuthMessageCode -import com.tencent.bkrepo.auth.model.TRole import com.tencent.bkrepo.auth.model.TUser import com.tencent.bkrepo.auth.pojo.enums.RoleType import com.tencent.bkrepo.auth.pojo.role.CreateRoleRequest @@ -106,18 +105,33 @@ class UserHelper constructor( fun createRoleCommon(request: CreateRoleRequest): String? { logger.info("create role request:[$request] ") - val role: TRole? = if (request.type == RoleType.REPO) { - roleRepository.findFirstByRoleIdAndProjectIdAndRepoName( - request.roleId!!, - request.projectId, - request.repoName!! - ) - } else { - roleRepository.findFirstByProjectIdAndTypeAndName( - projectId = request.projectId, - type = RoleType.PROJECT, - name = request.name - ) + val role = when (request.type) { + RoleType.REPO -> { + require(request.roleId != null) + roleRepository.findFirstByTypeAndRoleIdAndProjectIdAndRepoName( + type = RoleType.REPO, + roleId = request.roleId, + projectId = request.projectId, + repoName = request.repoName!! + ) + } + RoleType.PROJECT -> { + if (request.source == null) { + roleRepository.findFirstByTypeAndProjectIdAndName( + type = RoleType.PROJECT, + projectId = request.projectId, + name = request.name + ) + } else { + require(request.roleId != null) + roleRepository.findFirstByTypeAndRoleIdAndProjectIdAndSource( + type = RoleType.PROJECT, + roleId = request.roleId, + projectId = request.projectId, + source = request.source + ) + } + } } role?.let { @@ -151,13 +165,17 @@ class UserHelper constructor( } private fun findUsableProjectTypeRoleId(roleId: String?, projectId: String): String { - var tempRoleId = roleId ?: "${projectId}_role_${IDUtil.shortUUID()}" + var tempRoleId = roleId ?: buildProjectRoleId(projectId) while (true) { val role = roleRepository.findFirstByRoleIdAndProjectId(tempRoleId, projectId) - if (role == null) return tempRoleId else tempRoleId = "${projectId}_role_${IDUtil.shortUUID()}" + if (role == null) return tempRoleId else tempRoleId = buildProjectRoleId(projectId) } } + private fun buildProjectRoleId(projectId: String): String { + return "${projectId}_role_${IDUtil.shortUUID()}" + } + companion object { private val logger = LoggerFactory.getLogger(UserHelper::class.java) } diff --git a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/job/ExternalGroupSyncJob.kt b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/job/ExternalGroupSyncJob.kt new file mode 100644 index 0000000000..312f0acc81 --- /dev/null +++ b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/job/ExternalGroupSyncJob.kt @@ -0,0 +1,81 @@ +/* + * 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.bkrepo.auth.job + +import com.tencent.bkrepo.auth.pojo.role.RoleSource +import com.tencent.bkrepo.auth.pojo.role.UpdateRoleRequest +import com.tencent.bkrepo.auth.service.PermissionService +import com.tencent.bkrepo.auth.service.RoleService +import net.javacrumbs.shedlock.spring.annotation.SchedulerLock +import org.slf4j.LoggerFactory +import org.springframework.scheduling.annotation.Scheduled +import org.springframework.stereotype.Component + +@Component +class ExternalGroupSyncJob( + private val roleService: RoleService, + private val permissionService: PermissionService +) { + + @Scheduled(cron = "0 */10 * * * ? ") + @SchedulerLock(name = "DevopsUserGroupSync", lockAtMostFor = "PT10M") + fun runDevopsUserGroupSync() { + logger.info("start to update external role") + val roleList = roleService.listRoleBySource(RoleSource.DEVOPS) + val projectIdSet = mutableSetOf() + val indexIdMap = mutableMapOf() + val projectIdMap = mutableMapOf() + roleList.forEach { + projectIdSet.add(it.projectId) + indexIdMap[it.roleId] = it.id!! + projectIdMap[it.roleId] = it.projectId + } + + projectIdSet.forEach { project -> + val roleUserMap = mutableMapOf>() + permissionService.listExternalRoleByProject(project, RoleSource.DEVOPS).forEach { externalRole -> + roleUserMap[externalRole.roleId] = externalRole.userList + } + roleList.forEach { role -> + if (projectIdMap[role.roleId] == project) { + val updateRequest = UpdateRoleRequest( + userIds = roleUserMap[role.roleId]!!.toSet(), + description = null, + name = null + ) + logger.info("to update external role [${role.roleId}] ") + roleService.updateRoleInfo(indexIdMap[role.roleId]!!, updateRequest) + } + } + } + } + + companion object { + private val logger = LoggerFactory.getLogger(ExternalGroupSyncJob::class.java) + } +} diff --git a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/model/TRole.kt b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/model/TRole.kt index ab54c7a297..d3dd3d5c72 100644 --- a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/model/TRole.kt +++ b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/model/TRole.kt @@ -32,6 +32,7 @@ package com.tencent.bkrepo.auth.model import com.tencent.bkrepo.auth.pojo.enums.RoleType +import com.tencent.bkrepo.auth.pojo.role.RoleSource import org.springframework.data.mongodb.core.index.CompoundIndex import org.springframework.data.mongodb.core.index.CompoundIndexes import org.springframework.data.mongodb.core.mapping.Document @@ -44,7 +45,8 @@ import org.springframework.data.mongodb.core.mapping.Document CompoundIndex(name = "roleId_idx", def = "{'roleId': 1}", background = true), CompoundIndex(name = "type_idx", def = "{'type': 1}", background = true), CompoundIndex(name = "projectId_idx", def = "{'projectId': 1}", background = true), - CompoundIndex(name = "repoName_idx", def = "{'repoName': 1}", background = true) + CompoundIndex(name = "repoName_idx", def = "{'repoName': 1}", background = true), + CompoundIndex(name = "source_idx", def = "{'source': 1}", background = true) ) data class TRole( val id: String? = null, @@ -54,5 +56,6 @@ data class TRole( val projectId: String, val repoName: String? = null, val admin: Boolean = false, - var description: String? = null + var description: String? = null, + var source: RoleSource? = null ) diff --git a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/pojo/BkciRoleListResponse.kt b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/pojo/BkciRoleListResponse.kt new file mode 100644 index 0000000000..c97133923b --- /dev/null +++ b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/pojo/BkciRoleListResponse.kt @@ -0,0 +1,37 @@ +/* + * Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available. + * + * Copyright (C) 2021 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.bkrepo.auth.pojo + +data class BkciRoleListResponse( + val status: Int, + val data: List +) diff --git a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/pojo/BkciRoleResult.kt b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/pojo/BkciRoleResult.kt new file mode 100644 index 0000000000..84f482ebf6 --- /dev/null +++ b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/pojo/BkciRoleResult.kt @@ -0,0 +1,47 @@ +/* + * Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available. + * + * Copyright (C) 2021 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.bkrepo.auth.pojo + +import com.fasterxml.jackson.annotation.JsonProperty + +data class BkciRoleResult( + @JsonProperty("display_name") + val displayName: String, + @JsonProperty("role_id") + val roleId: Long, + @JsonProperty("role_name") + val roleName: String, + @JsonProperty("user_id_list") + val userIdList: List, + @JsonProperty("type") + val type: String +) diff --git a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/pojo/role/CreateRoleRequest.kt b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/pojo/role/CreateRoleRequest.kt index caa83cf519..d78e14f467 100644 --- a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/pojo/role/CreateRoleRequest.kt +++ b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/pojo/role/CreateRoleRequest.kt @@ -46,5 +46,7 @@ data class CreateRoleRequest( @ApiModelProperty("管理员") val admin: Boolean = false, @ApiModelProperty("描述信息") - val description: String? = null + val description: String? = null, + @ApiModelProperty("角色来源") + val source: RoleSource? = null ) diff --git a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/pojo/role/ExternalRoleResult.kt b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/pojo/role/ExternalRoleResult.kt new file mode 100644 index 0000000000..79b0175336 --- /dev/null +++ b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/pojo/role/ExternalRoleResult.kt @@ -0,0 +1,7 @@ +package com.tencent.bkrepo.auth.pojo.role + +data class ExternalRoleResult( + val name: String, + val roleId: String, + val userList: List +) diff --git a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/pojo/role/Role.kt b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/pojo/role/Role.kt index 6697bb90b0..647c38f9d4 100644 --- a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/pojo/role/Role.kt +++ b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/pojo/role/Role.kt @@ -49,5 +49,7 @@ data class Role( @ApiModelProperty("绑定的用户") val users: List = listOf(), @ApiModelProperty("描述信息") - val description: String? = null + val description: String? = null, + @ApiModelProperty("角色来源") + val source: RoleSource? = null ) diff --git a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/pojo/role/RoleSource.kt b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/pojo/role/RoleSource.kt new file mode 100644 index 0000000000..56cccd85a5 --- /dev/null +++ b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/pojo/role/RoleSource.kt @@ -0,0 +1,36 @@ +/* + * Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available. + * + * Copyright (C) 2022 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.bkrepo.auth.pojo.role + +import io.swagger.annotations.ApiModel + +@ApiModel("角色来源") +enum class RoleSource { + DEVOPS, + TAI +} diff --git a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/service/PermissionService.kt b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/service/PermissionService.kt index c4cffa4152..03929aeef8 100644 --- a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/service/PermissionService.kt +++ b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/service/PermissionService.kt @@ -37,6 +37,8 @@ import com.tencent.bkrepo.auth.pojo.permission.Permission import com.tencent.bkrepo.auth.pojo.permission.UpdatePermissionDeployInRepoRequest import com.tencent.bkrepo.auth.pojo.permission.UpdatePermissionRepoRequest import com.tencent.bkrepo.auth.pojo.permission.UpdatePermissionUserRequest +import com.tencent.bkrepo.auth.pojo.role.ExternalRoleResult +import com.tencent.bkrepo.auth.pojo.role.RoleSource interface PermissionService { /** @@ -98,4 +100,6 @@ interface PermissionService { fun getPathCheckConfig(): Boolean fun getOrCreatePersonalPath(projectId: String, repoName: String): String + + fun listExternalRoleByProject(projectId: String, source: RoleSource): List } \ No newline at end of file diff --git a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/service/RoleService.kt b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/service/RoleService.kt index 4b65855320..4890e9f346 100644 --- a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/service/RoleService.kt +++ b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/service/RoleService.kt @@ -33,6 +33,7 @@ package com.tencent.bkrepo.auth.service import com.tencent.bkrepo.auth.pojo.role.CreateRoleRequest import com.tencent.bkrepo.auth.pojo.role.Role +import com.tencent.bkrepo.auth.pojo.role.RoleSource import com.tencent.bkrepo.auth.pojo.role.UpdateRoleRequest import com.tencent.bkrepo.auth.pojo.user.UserResult @@ -44,6 +45,8 @@ interface RoleService { fun listRoleByProject(projectId: String, repoName: String? = null): List + fun listRoleBySource(source: RoleSource): List + fun detail(id: String): Role? fun detail(rid: String, projectId: String): Role? diff --git a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/service/bkauth/CIAuthService.kt b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/service/bkauth/CIAuthService.kt index 5f222fed09..c6b89b7324 100644 --- a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/service/bkauth/CIAuthService.kt +++ b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/service/bkauth/CIAuthService.kt @@ -37,6 +37,8 @@ import com.tencent.bkrepo.auth.condition.DevopsAuthCondition import com.tencent.bkrepo.auth.config.DevopsAuthConfig import com.tencent.bkrepo.auth.pojo.BkciAuthCheckResponse import com.tencent.bkrepo.auth.pojo.BkciAuthListResponse +import com.tencent.bkrepo.auth.pojo.BkciRoleListResponse +import com.tencent.bkrepo.auth.pojo.BkciRoleResult import com.tencent.bkrepo.auth.pojo.enums.BkAuthPermission import com.tencent.bkrepo.auth.pojo.enums.BkAuthResourceType import com.tencent.bkrepo.auth.pojo.enums.PermissionAction @@ -230,6 +232,22 @@ class CIAuthService @Autowired constructor(private val devopsAuthConfig: DevopsA } } + fun getRoleAndUserByProject(projectCode: String): List { + return try { + val url = "${devopsAuthConfig.getBkciAuthServer()}/auth/api/open/service/auth/projects/$projectCode/users" + val request = Request.Builder().url(url).header(DEVOPS_BK_TOKEN, devopsAuthConfig.getBkciAuthToken()) + .get().build() + logger.debug("getRoleAndUserByProject, requestUrl: [$url]") + val apiResponse = HttpUtils.doRequest(okHttpClient, request, 2, allowHttpStatusSet) + val responseObject = objectMapper.readValue(apiResponse.content) + logger.debug("getRoleAndUserByProject, requestUrl: [$url], result : [${apiResponse.content}]") + return responseObject.data + } catch (exception: Exception) { + logger.error("getRoleAndUserByProject error : ", exception) + emptyList() + } + } + companion object { private val logger = LoggerFactory.getLogger(CIAuthService::class.java) private val allowHttpStatusSet = diff --git a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/service/bkauth/DevopsPermissionServiceImpl.kt b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/service/bkauth/DevopsPermissionServiceImpl.kt index 3b483627fc..fac665a8da 100644 --- a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/service/bkauth/DevopsPermissionServiceImpl.kt +++ b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/service/bkauth/DevopsPermissionServiceImpl.kt @@ -50,6 +50,8 @@ import com.tencent.bkrepo.auth.pojo.enums.PermissionAction.VIEW import com.tencent.bkrepo.auth.pojo.enums.ResourceType.NODE import com.tencent.bkrepo.auth.pojo.enums.ResourceType.REPO import com.tencent.bkrepo.auth.pojo.enums.ResourceType.PROJECT +import com.tencent.bkrepo.auth.pojo.role.ExternalRoleResult +import com.tencent.bkrepo.auth.pojo.role.RoleSource import com.tencent.bkrepo.auth.service.bkiamv3.BkIamV3PermissionServiceImpl import com.tencent.bkrepo.auth.service.bkiamv3.BkIamV3Service import com.tencent.bkrepo.common.artifact.path.PathUtils @@ -129,6 +131,13 @@ class DevopsPermissionServiceImpl constructor( return devopsAuthConfig.enablePathCheck } + override fun listExternalRoleByProject(projectId: String, source: RoleSource): List { + if (source == RoleSource.DEVOPS) { + return devopsProjectService.listRoleAndUserByProject(projectId) + } + return emptyList() + } + private fun parsePipelineId(path: String): String? { val roads = PathUtils.normalizeFullPath(path).split("/") return if (roads.size < 2 || roads[1].isBlank()) { diff --git a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/service/bkauth/DevopsProjectService.kt b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/service/bkauth/DevopsProjectService.kt index 9a342f6b35..314109b665 100644 --- a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/service/bkauth/DevopsProjectService.kt +++ b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/service/bkauth/DevopsProjectService.kt @@ -34,6 +34,7 @@ package com.tencent.bkrepo.auth.service.bkauth import com.tencent.bkrepo.auth.condition.DevopsAuthCondition import com.tencent.bkrepo.auth.pojo.enums.BkAuthPermission import com.tencent.bkrepo.auth.pojo.enums.BkAuthResourceType +import com.tencent.bkrepo.auth.pojo.role.ExternalRoleResult import org.springframework.beans.factory.annotation.Autowired import org.springframework.context.annotation.Conditional import org.springframework.stereotype.Service @@ -58,4 +59,12 @@ class DevopsProjectService @Autowired constructor(private val ciAuthService: CIA fun listProjectByUser(user: String): List { return ciAuthService.getProjectListByUser(user) } + + fun listRoleAndUserByProject(projectCode: String): List { + val externalRoleList = mutableListOf() + ciAuthService.getRoleAndUserByProject(projectCode).forEach { + externalRoleList.add(ExternalRoleResult(it.roleName, it.roleId.toString(), it.userIdList)) + } + return externalRoleList + } } diff --git a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/service/local/PermissionServiceImpl.kt b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/service/local/PermissionServiceImpl.kt index d11fdb1976..c2452724ac 100644 --- a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/service/local/PermissionServiceImpl.kt +++ b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/service/local/PermissionServiceImpl.kt @@ -62,6 +62,8 @@ import com.tencent.bkrepo.auth.pojo.permission.CheckPermissionRequest import com.tencent.bkrepo.auth.pojo.permission.UpdatePermissionRepoRequest import com.tencent.bkrepo.auth.pojo.permission.UpdatePermissionDeployInRepoRequest import com.tencent.bkrepo.auth.pojo.permission.UpdatePermissionUserRequest +import com.tencent.bkrepo.auth.pojo.role.ExternalRoleResult +import com.tencent.bkrepo.auth.pojo.role.RoleSource import com.tencent.bkrepo.auth.service.PermissionService import com.tencent.bkrepo.auth.util.RequestUtil import com.tencent.bkrepo.auth.util.request.PermRequestUtil @@ -398,6 +400,10 @@ open class PermissionServiceImpl constructor( return true } + override fun listExternalRoleByProject(projectId: String, source: RoleSource): List { + return emptyList() + } + fun isUserLocalProjectAdmin(userId: String, projectId: String?): Boolean { return permHelper.isUserLocalProjectAdmin(userId, projectId) } diff --git a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/service/local/RoleServiceImpl.kt b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/service/local/RoleServiceImpl.kt index bb182fd15c..0a3976f6e7 100644 --- a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/service/local/RoleServiceImpl.kt +++ b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/service/local/RoleServiceImpl.kt @@ -43,6 +43,7 @@ import com.tencent.bkrepo.auth.pojo.role.UpdateRoleRequest import com.tencent.bkrepo.auth.pojo.user.UserResult import com.tencent.bkrepo.auth.dao.repository.RoleRepository import com.tencent.bkrepo.auth.helper.UserHelper +import com.tencent.bkrepo.auth.pojo.role.RoleSource import com.tencent.bkrepo.auth.service.RoleService import com.tencent.bkrepo.auth.service.UserService import com.tencent.bkrepo.common.api.exception.ErrorCodeException @@ -75,7 +76,9 @@ class RoleServiceImpl constructor( override fun detail(rid: String, projectId: String, repoName: String): Role? { logger.debug("get role detail rid : [$rid,$projectId,$repoName]") - val result = roleRepository.findFirstByRoleIdAndProjectIdAndRepoName(rid, projectId, repoName) ?: return null + val result = + roleRepository.findFirstByTypeAndRoleIdAndProjectIdAndRepoName(RoleType.REPO, rid, projectId, repoName) + ?: return null return transfer(result) } @@ -111,7 +114,7 @@ class RoleServiceImpl constructor( override fun listRoleByProject(projectId: String, repoName: String?): List { logger.debug("list role by project ,[$projectId , $repoName]") repoName?.let { - return roleRepository.findByProjectIdAndRepoNameAndType(projectId, repoName, RoleType.REPO) + return roleRepository.findByTypeAndProjectIdAndRepoName(RoleType.REPO, projectId, repoName) .map { transfer(it) } } return roleRepository.findByTypeAndProjectIdAndAdminAndRoleIdNotIn( @@ -122,6 +125,11 @@ class RoleServiceImpl constructor( ).map { transfer(it) } } + override fun listRoleBySource(source: RoleSource): List { + logger.debug("list role by role ,[$source]") + return roleRepository.findBySource(source).map { transfer(it) } + } + override fun deleteRoleById(id: String): Boolean { logger.info("delete role id : [$id]") val role = roleRepository.findFirstById(id) @@ -149,7 +157,8 @@ class RoleServiceImpl constructor( projectId = tRole.projectId, admin = tRole.admin, users = users, - description = tRole.description + description = tRole.description, + source = tRole.source ) } diff --git a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/util/request/RoleRequestUtil.kt b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/util/request/RoleRequestUtil.kt index 8343d816ba..fd4c1b3b3f 100644 --- a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/util/request/RoleRequestUtil.kt +++ b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/util/request/RoleRequestUtil.kt @@ -40,7 +40,8 @@ object RoleRequestUtil { projectId = request.projectId, repoName = request.repoName, admin = request.admin, - description = request.description + description = request.description, + source = request.source ) } } diff --git a/src/backend/repository/api-repository/src/main/kotlin/com/tencent/bkrepo/repository/pojo/node/NodeListOption.kt b/src/backend/repository/api-repository/src/main/kotlin/com/tencent/bkrepo/repository/pojo/node/NodeListOption.kt index b9bbbf8f68..2caa0340c6 100644 --- a/src/backend/repository/api-repository/src/main/kotlin/com/tencent/bkrepo/repository/pojo/node/NodeListOption.kt +++ b/src/backend/repository/api-repository/src/main/kotlin/com/tencent/bkrepo/repository/pojo/node/NodeListOption.kt @@ -57,6 +57,8 @@ data class NodeListOption( val direction: List = emptyList(), @ApiModelProperty("无权限路径") var noPermissionPath: List = emptyList(), + @ApiModelProperty("有权限的路径") + var hasPermissionPath: List? = null, @ApiModelProperty("操作用户") var operator: String = SYSTEM_USER, ) diff --git a/src/backend/repository/biz-repository/src/main/kotlin/com/tencent/bkrepo/repository/search/common/RepoNameRuleInterceptor.kt b/src/backend/repository/biz-repository/src/main/kotlin/com/tencent/bkrepo/repository/search/common/RepoNameRuleInterceptor.kt index 4a5c5d072a..717842554f 100644 --- a/src/backend/repository/biz-repository/src/main/kotlin/com/tencent/bkrepo/repository/search/common/RepoNameRuleInterceptor.kt +++ b/src/backend/repository/biz-repository/src/main/kotlin/com/tencent/bkrepo/repository/search/common/RepoNameRuleInterceptor.kt @@ -45,6 +45,7 @@ import com.tencent.bkrepo.common.security.util.SecurityUtils import com.tencent.bkrepo.repository.pojo.node.NodeInfo import com.tencent.bkrepo.repository.pojo.repo.RepoListOption import com.tencent.bkrepo.repository.service.repo.RepositoryService +import com.tencent.bkrepo.repository.util.NodeQueryHelper.listPermissionPaths import org.slf4j.LoggerFactory import org.springframework.data.mongodb.core.query.Criteria import org.springframework.stereotype.Component @@ -151,30 +152,40 @@ class RepoNameRuleInterceptor( private fun buildRule(projectId: String, repoName: String): Rule { val repoRule = Rule.QueryRule(NodeInfo::repoName.name, repoName, OperationType.EQ).toFixed() - return listNoPermissionPath(projectId, repoName)?.let { - val pathRules = it.flatMapTo(ArrayList(it.size)) { path -> - listOf( - Rule.QueryRule(NodeInfo::fullPath.name, path.ensureSuffix("/"), OperationType.PREFIX) as Rule, - Rule.QueryRule(NodeInfo::fullPath.name, path, OperationType.EQ) as Rule, + + // 获取有权限或无权限的路径 + val (hasPermissionPaths, noPermissionPaths) = servicePermissionClient.listPermissionPaths( + SecurityUtils.getUserId(), projectId, repoName + ) + + val paths: List + val relationType: Rule.NestedRule.RelationType + if (hasPermissionPaths?.isNotEmpty() == true) { + paths = hasPermissionPaths + relationType = Rule.NestedRule.RelationType.OR + } else if (hasPermissionPaths?.isEmpty() == true) { + // hasPermissionPath为empty时所有路径都无权限,构造一个永远不成立的条件使查询结果为空列表 + return Rule.NestedRule( + mutableListOf( + repoRule, Rule.QueryRule(NodeInfo::projectId.name, false, OperationType.NULL) ) - } - val pathRule = Rule.NestedRule(pathRules, Rule.NestedRule.RelationType.NOR) - Rule.NestedRule(mutableListOf(repoRule, pathRule)) - } ?: repoRule - } + ) + } else if (noPermissionPaths.isNotEmpty()) { + paths = noPermissionPaths + relationType = Rule.NestedRule.RelationType.NOR + } else { + return repoRule + } - private fun listNoPermissionPath(projectId: String, repoName: String): List? { - val userId = SecurityUtils.getUserId() - val result = servicePermissionClient.listPermissionPath(userId, projectId, repoName).data!! - if (result.status) { - val paths = result.path.flatMap { - require(it.key == OperationType.NIN) - it.value - } - logger.info("user[$userId] does not have permission to $paths of [$projectId/$repoName], will be filtered") - return paths.ifEmpty { null } + // 构造rule + val pathRules = paths.flatMapTo(ArrayList(paths.size)) { path -> + listOf( + Rule.QueryRule(NodeInfo::fullPath.name, path.ensureSuffix("/"), OperationType.PREFIX) as Rule, + Rule.QueryRule(NodeInfo::fullPath.name, path, OperationType.EQ) as Rule, + ) } - return null + val pathRule = Rule.NestedRule(pathRules, relationType) + return Rule.NestedRule(mutableListOf(repoRule, pathRule)) } private fun hasRepoPermission( diff --git a/src/backend/repository/biz-repository/src/main/kotlin/com/tencent/bkrepo/repository/service/node/impl/NodeBaseService.kt b/src/backend/repository/biz-repository/src/main/kotlin/com/tencent/bkrepo/repository/service/node/impl/NodeBaseService.kt index 60ec9f5cdd..801d7bf930 100644 --- a/src/backend/repository/biz-repository/src/main/kotlin/com/tencent/bkrepo/repository/service/node/impl/NodeBaseService.kt +++ b/src/backend/repository/biz-repository/src/main/kotlin/com/tencent/bkrepo/repository/service/node/impl/NodeBaseService.kt @@ -43,7 +43,6 @@ import com.tencent.bkrepo.common.artifact.path.PathUtils import com.tencent.bkrepo.common.artifact.pojo.RepositoryType import com.tencent.bkrepo.common.artifact.router.RouterControllerProperties import com.tencent.bkrepo.common.mongo.dao.util.Pages -import com.tencent.bkrepo.common.query.enums.OperationType import com.tencent.bkrepo.common.query.model.Sort import com.tencent.bkrepo.common.security.manager.PermissionManager import com.tencent.bkrepo.common.security.util.SecurityUtils @@ -76,6 +75,7 @@ import com.tencent.bkrepo.repository.service.repo.StorageCredentialService import com.tencent.bkrepo.repository.util.MetadataUtils import com.tencent.bkrepo.repository.util.NodeEventFactory.buildCreatedEvent import com.tencent.bkrepo.repository.util.NodeQueryHelper +import com.tencent.bkrepo.repository.util.NodeQueryHelper.listPermissionPaths import com.tencent.bkrepo.router.api.RouterControllerClient import org.slf4j.LoggerFactory import org.springframework.beans.factory.annotation.Autowired @@ -122,7 +122,10 @@ abstract class NodeBaseService( override fun listNode(artifact: ArtifactInfo, option: NodeListOption): List { checkNodeListOption(option) with(artifact) { - getNoPermissionPaths(SecurityUtils.getUserId(), projectId, repoName)?.let { option.noPermissionPath = it } + val userId = SecurityUtils.getUserId() + val (hasPermissionPaths, noPermissionPaths) = getPermissionPaths(userId, projectId, repoName) + option.hasPermissionPath = hasPermissionPaths + option.noPermissionPath = noPermissionPaths val query = NodeQueryHelper.nodeListQuery(projectId, repoName, getArtifactFullPath(), option) if (nodeDao.count(query) > repositoryProperties.listCountLimit) { throw ErrorCodeException(ArtifactMessageCode.NODE_LIST_TOO_LARGE) @@ -134,7 +137,10 @@ abstract class NodeBaseService( override fun listNodePage(artifact: ArtifactInfo, option: NodeListOption): Page { checkNodeListOption(option) with(artifact) { - getNoPermissionPaths(SecurityUtils.getUserId(), projectId, repoName)?.let { option.noPermissionPath = it } + val userId = SecurityUtils.getUserId() + val (hasPermissionPaths, noPermissionPaths) = getPermissionPaths(userId, projectId, repoName) + option.hasPermissionPath = hasPermissionPaths + option.noPermissionPath = noPermissionPaths val pageNumber = option.pageNumber val pageSize = option.pageSize Preconditions.checkArgument(pageNumber >= 0, "pageNumber") @@ -420,22 +426,15 @@ abstract class NodeBaseService( /** * 获取用户无权限路径列表 */ - private fun getNoPermissionPaths(userId: String, projectId: String, repoName: String): List? { + private fun getPermissionPaths( + userId: String, + projectId: String, + repoName: String + ): Pair?, List> { if (userId == SYSTEM_USER) { - return null - } - val result = servicePermissionClient.listPermissionPath(userId, projectId, repoName).data!! - if (result.status) { - val paths = result.path.flatMap { - require(it.key == OperationType.NIN) - it.value - }.ifEmpty { null } - logger.info( - "user[$userId] does not have permission of paths[$paths] in [$projectId/$repoName], will be filterd" - ) - return paths + return Pair(emptyList(), emptyList()) } - return null + return servicePermissionClient.listPermissionPaths(userId, projectId, repoName) } companion object { diff --git a/src/backend/repository/biz-repository/src/main/kotlin/com/tencent/bkrepo/repository/util/NodeQueryHelper.kt b/src/backend/repository/biz-repository/src/main/kotlin/com/tencent/bkrepo/repository/util/NodeQueryHelper.kt index 3cb664f7ae..ec05171aed 100644 --- a/src/backend/repository/biz-repository/src/main/kotlin/com/tencent/bkrepo/repository/util/NodeQueryHelper.kt +++ b/src/backend/repository/biz-repository/src/main/kotlin/com/tencent/bkrepo/repository/util/NodeQueryHelper.kt @@ -27,16 +27,20 @@ package com.tencent.bkrepo.repository.util +import com.tencent.bkrepo.auth.api.ServicePermissionClient import com.tencent.bkrepo.common.artifact.path.PathUtils.escapeRegex import com.tencent.bkrepo.common.artifact.path.PathUtils.toFullPath import com.tencent.bkrepo.common.artifact.path.PathUtils.toPath +import com.tencent.bkrepo.common.query.enums.OperationType import com.tencent.bkrepo.repository.model.TNode import com.tencent.bkrepo.repository.pojo.node.NodeListOption +import org.slf4j.LoggerFactory import org.springframework.data.domain.Sort import org.springframework.data.mongodb.core.query.Criteria import org.springframework.data.mongodb.core.query.Query import org.springframework.data.mongodb.core.query.Update import org.springframework.data.mongodb.core.query.and +import org.springframework.data.mongodb.core.query.exists import org.springframework.data.mongodb.core.query.inValues import org.springframework.data.mongodb.core.query.isEqualTo import org.springframework.data.mongodb.core.query.regex @@ -47,6 +51,7 @@ import java.time.LocalDateTime * 查询条件构造工具 */ object NodeQueryHelper { + private val logger = LoggerFactory.getLogger(NodeQueryHelper::class.java) fun nodeQuery(projectId: String, repoName: String, fullPath: String? = null): Query { val criteria = where(TNode::projectId).isEqualTo(projectId) @@ -86,14 +91,19 @@ object NodeQueryHelper { if (!option.includeFolder) { criteria.and(TNode::folder).isEqualTo(false) } - return if (option.noPermissionPath.isNotEmpty()) { - val noPermissionPathCriteria = option.noPermissionPath.flatMap { - listOf( - TNode::fullPath.isEqualTo(it), - TNode::fullPath.regex("^${escapeRegex(it)}") - ) - } - Criteria().andOperator(criteria, Criteria().norOperator(noPermissionPathCriteria)) + return if (option.hasPermissionPath?.isEmpty() == true) { + // hasPermissionPath为empty时所有路径都无权限,构造一个永远不成立的条件使查询结果为空列表 + TNode::projectId.exists(false) + } else if (option.hasPermissionPath?.isNotEmpty() == true) { + Criteria().andOperator( + criteria, + Criteria().orOperator(buildPermissionPathCriteria(option.hasPermissionPath!!)) + ) + } else if (option.noPermissionPath.isNotEmpty()) { + Criteria().andOperator( + criteria, + Criteria().norOperator(buildPermissionPathCriteria(option.noPermissionPath)) + ) } else { criteria } @@ -211,6 +221,54 @@ object NodeQueryHelper { .set(TNode::deleted.name, deleteTime) } + /** + * 查询有权限与无权限的路径 + * + * @param userId 用户 + * @param projectId 项目 + * @param repoName 仓库 + * + * @return first为有权限的路径,为空时表示所有路径均无权限,为null时表示未配置,second为无权限路径 + */ + fun ServicePermissionClient.listPermissionPaths( + userId: String, + projectId: String, + repoName: String + ): Pair?, List> { + val result = listPermissionPath(userId, projectId, repoName).data!! + if (result.status) { + require(result.path.isNotEmpty()) + require(result.path.all { it.key == OperationType.IN } || result.path.all { it.key == OperationType.NIN }) + val opType = result.path.entries.first().key + return listPermissionPaths(userId, projectId, repoName, opType, result.path.values.flatten()) + } + return Pair(null, emptyList()) + } + + private fun listPermissionPaths( + userId: String, + projectId: String, + repoName: String, + operationType: OperationType, + paths: List, + ): Pair?, List> { + if (operationType == OperationType.NIN) { + logger.info("user[$userId] does not have permission to $paths of [$projectId/$repoName]") + return Pair(null, paths) + } + + if (operationType == OperationType.IN) { + logger.info("user[$userId] have permission to $paths of [$projectId/$repoName]") + return Pair(paths, emptyList()) + } + + throw UnsupportedOperationException("Unsupported operation [$operationType].") + } + + private fun buildPermissionPathCriteria(paths: List) = paths.flatMap { + listOf(TNode::fullPath.isEqualTo(it), TNode::fullPath.regex("^${escapeRegex(it)}")) + } + private fun update(operator: String): Update { return Update() .set(TNode::lastModifiedDate.name, LocalDateTime.now()) diff --git a/src/backend/repository/biz-repository/src/test/kotlin/com/tencent/bkrepo/repository/service/NodeSearchServiceTest.kt b/src/backend/repository/biz-repository/src/test/kotlin/com/tencent/bkrepo/repository/service/NodeSearchServiceTest.kt index aa9840bf9d..f9ef78c7d6 100644 --- a/src/backend/repository/biz-repository/src/test/kotlin/com/tencent/bkrepo/repository/service/NodeSearchServiceTest.kt +++ b/src/backend/repository/biz-repository/src/test/kotlin/com/tencent/bkrepo/repository/service/NodeSearchServiceTest.kt @@ -64,6 +64,7 @@ import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test import org.junit.jupiter.api.TestInstance +import org.junit.jupiter.api.assertThrows import org.mockito.ArgumentMatchers.anyString import org.mockito.kotlin.isNull import org.mockito.kotlin.whenever @@ -310,6 +311,47 @@ class NodeSearchServiceTest @Autowired constructor( Assertions.assertEquals(4, result.totalRecords) } + @Test + fun testHasPermissionPathSearch() { + whenever(servicePermissionClient.listPermissionPath(anyString(), anyString(), anyString())).thenReturn( + ResponseBuilder.success(ListPathResult(status = true, path = mapOf(OperationType.IN to listOf("/a")))) + ) + whenever(servicePermissionClient.listPermissionRepo(anyString(), anyString(), isNull())).thenReturn( + ResponseBuilder.success(listOf(UT_REPO_NAME)) + ) + + // 创建node + val createNodeRequest = createRequest("/a/a1.txt", false) + nodeService.createNode(createNodeRequest) + nodeService.createNode(createNodeRequest.copy(fullPath = "/b/b1.txt")) + nodeService.createNode(createNodeRequest.copy(fullPath = "/c/c1.txt")) + + // 查询 + val queryModel = createQueryBuilder().build() + val result = nodeSearchService.search(queryModel) + Assertions.assertEquals(2, result.totalRecords) + + // 测试所有路径均无权限 + whenever(servicePermissionClient.listPermissionPath(anyString(), anyString(), anyString())).thenReturn( + ResponseBuilder.success(ListPathResult(status = true, path = mapOf(OperationType.IN to emptyList()))) + ) + Assertions.assertEquals(0, nodeSearchService.search(queryModel).totalRecords) + + // 测试同时存在NIN与IN + whenever(servicePermissionClient.listPermissionPath(anyString(), anyString(), anyString())).thenReturn( + ResponseBuilder.success( + ListPathResult( + status = true, + path = mapOf( + OperationType.IN to listOf("/a"), + OperationType.NIN to listOf("/b"), + ) + ) + ) + ) + assertThrows { nodeSearchService.search(queryModel) } + } + private fun testLocalDateTimeOperation( createdDate: String, operationType: OperationType, diff --git a/src/backend/repository/biz-repository/src/test/kotlin/com/tencent/bkrepo/repository/service/NodeServiceTest.kt b/src/backend/repository/biz-repository/src/test/kotlin/com/tencent/bkrepo/repository/service/NodeServiceTest.kt index d59f07722b..cd08142d78 100644 --- a/src/backend/repository/biz-repository/src/test/kotlin/com/tencent/bkrepo/repository/service/NodeServiceTest.kt +++ b/src/backend/repository/biz-repository/src/test/kotlin/com/tencent/bkrepo/repository/service/NodeServiceTest.kt @@ -260,18 +260,10 @@ class NodeServiceTest @Autowired constructor( } @Test - @DisplayName("测试查询无路径权限的目录") - fun testListNoPathPermissionNode() { + @DisplayName("测试查询存在路径权限配置的目录") + fun testListPermissionNode() { val size = 5L repeat(size.toInt()) { i -> nodeService.createNode(createRequest("/a/$i/$i.txt", false)) } - whenever(servicePermissionClient.listPermissionPath(anyString(), anyString(), anyString())).thenReturn( - ResponseBuilder.success( - ListPathResult( - status = true, - path = mapOf(OperationType.NIN to listOf("/a/1", "/a/2")) - ) - ) - ) val option = NodeListOption( pageNumber = 0, @@ -281,17 +273,67 @@ class NodeServiceTest @Autowired constructor( deep = false, sort = false ) - val page = nodeService.listNodePage(node("/a"), option) + + // 测试查询无权限路径 + whenever(servicePermissionClient.listPermissionPath(anyString(), anyString(), anyString())).thenReturn( + ResponseBuilder.success( + ListPathResult( + status = true, + path = mapOf(OperationType.NIN to listOf("/a/1", "/a/2")) + ) + ) + ) + var page = nodeService.listNodePage(node("/a"), option) assertEquals(3, page.totalRecords) assertEquals("/a/0", page.records[0].fullPath) assertEquals("/a/3", page.records[1].fullPath) assertEquals("/a/4", page.records[2].fullPath) - val result = nodeService.listNode(node("/a"), option) + var result = nodeService.listNode(node("/a"), option) assertEquals(3, result.size) assertEquals("/a/0", result[0].fullPath) assertEquals("/a/3", result[1].fullPath) assertEquals("/a/4", result[2].fullPath) + + // 测试查询有权限路径 + whenever(servicePermissionClient.listPermissionPath(anyString(), anyString(), anyString())).thenReturn( + ResponseBuilder.success( + ListPathResult( + status = true, + path = mapOf(OperationType.IN to listOf("/a/1", "/a/2")) + ) + ) + ) + page = nodeService.listNodePage(node("/a"), option) + assertEquals(2, page.totalRecords) + assertEquals("/a/1", page.records[0].fullPath) + assertEquals("/a/2", page.records[1].fullPath) + + result = nodeService.listNode(node("/a"), option) + assertEquals(2, result.size) + assertEquals("/a/1", result[0].fullPath) + assertEquals("/a/2", result[1].fullPath) + + // 测试所有路径均无权限 + whenever(servicePermissionClient.listPermissionPath(anyString(), anyString(), anyString())).thenReturn( + ResponseBuilder.success(ListPathResult(status = true, path = mapOf(OperationType.IN to emptyList()))) + ) + assertEquals(0, nodeService.listNodePage(node("/a"), option).totalRecords) + assertEquals(0, nodeService.listNode(node("/a"), option).size) + + // 测试同时包含有权限与无权限 + whenever(servicePermissionClient.listPermissionPath(anyString(), anyString(), anyString())).thenReturn( + ResponseBuilder.success( + ListPathResult( + status = true, + path = mapOf( + OperationType.IN to listOf("/a/1", "/a/2"), + OperationType.NIN to listOf("/a/3", "/a/4"), + ) + ) + ) + ) + assertThrows { nodeService.listNode(node("/a"), option) } } @Test diff --git a/src/frontend/devops-op/src/utils/copy.js b/src/frontend/devops-op/src/utils/copy.js new file mode 100644 index 0000000000..4568b6e5a2 --- /dev/null +++ b/src/frontend/devops-op/src/utils/copy.js @@ -0,0 +1,16 @@ +export function copyToClipboard(text) { + const textArea = document.createElement('textarea') + textArea.style.position = 'absolute' + textArea.style.width = 0 + textArea.style.height = 0 + textArea.style.left = '-10px' + textArea.style.top = '-10px' + document.body.appendChild(textArea) + + textArea.value = text + textArea.focus() + textArea.select() + const result = document.execCommand('copy') + textArea.remove() + return result ? Promise.resolve() : Promise.reject(new Error()) +} diff --git a/src/frontend/devops-op/src/views/node/FileSystem.vue b/src/frontend/devops-op/src/views/node/FileSystem.vue index 1ab3689589..afb2c4edf2 100644 --- a/src/frontend/devops-op/src/views/node/FileSystem.vue +++ b/src/frontend/devops-op/src/views/node/FileSystem.vue @@ -58,6 +58,13 @@ @click="changeRouteQueryParams(1)" >查询 + + 复制当前所有IP + @@ -108,6 +115,7 @@ import { queryFileSystemClient } from '@/api/fileSystem' import { searchProjects } from '@/api/project' import { listRepositories } from '@/api/repository' import { formatNormalDate } from '@/utils/date' +import { copyToClipboard } from '@/utils/copy' import FileSystemStatusRecordDialog from '@/views/node/components/FileSystemStatusRecordDialog' export default { @@ -237,9 +245,21 @@ export default { return row.online === value }, showRecord(row) { - console.log(row) this.param = row this.showDialog = true + }, + copy() { + const ips = [] + let text = '' + if (this.clients.length > 0) { + for (let i = 0; i < this.clients.length; i++) { + ips.push(this.clients[i].ip) + } + text = ips.join('\n') + } + copyToClipboard(text).then(res => { + this.$message.success('复制成功') + }) } } } diff --git a/src/frontend/devops-repository/src/components/AddUserDialog/addUserDialog.vue b/src/frontend/devops-repository/src/components/AddUserDialog/addUserDialog.vue index c23ebd1877..e64c404cb1 100644 --- a/src/frontend/devops-repository/src/components/AddUserDialog/addUserDialog.vue +++ b/src/frontend/devops-repository/src/components/AddUserDialog/addUserDialog.vue @@ -72,12 +72,6 @@ visible: function (newVal) { if (newVal) { this.showDialog = true - this.editUserConfig = { - users: this.showData.users, - search: '', - newUser: '', - originUsers: this.showData.originUsers - } } else { this.cancel() } diff --git a/src/frontend/devops-repository/src/store/actions/permission.js b/src/frontend/devops-repository/src/store/actions/permission.js index 606bcd5592..a325473871 100644 --- a/src/frontend/devops-repository/src/store/actions/permission.js +++ b/src/frontend/devops-repository/src/store/actions/permission.js @@ -309,6 +309,10 @@ export default { commit('SET_REPO_PERMISSION_LIMIT', res) }) }, + // 获取蓝盾传输的用户组 + getUserGroupByExternal (_, { projectId, sourceId }) { + return Vue.prototype.$ajax.get(`${authPrefix}/permission/external/group/${projectId}/${sourceId}`) + }, // 获取当前repo的根目录权限 getRootPermission (_, { projectId, repoName }) { return Vue.prototype.$ajax.get( diff --git a/src/frontend/devops-repository/src/views/userGroup/importUserDialog.vue b/src/frontend/devops-repository/src/views/userGroup/importUserDialog.vue new file mode 100644 index 0000000000..acc8d25a26 --- /dev/null +++ b/src/frontend/devops-repository/src/views/userGroup/importUserDialog.vue @@ -0,0 +1,186 @@ + + + + + diff --git a/src/frontend/devops-repository/src/views/userGroup/index.vue b/src/frontend/devops-repository/src/views/userGroup/index.vue index 3ff4f8c325..f4febb868c 100644 --- a/src/frontend/devops-repository/src/views/userGroup/index.vue +++ b/src/frontend/devops-repository/src/views/userGroup/index.vue @@ -1,7 +1,8 @@