diff --git a/src/backend/auth/api-auth/src/main/kotlin/com/tencent/bkrepo/auth/api/ServicePermissionClient.kt b/src/backend/auth/api-auth/src/main/kotlin/com/tencent/bkrepo/auth/api/ServicePermissionClient.kt index 2597f6c411..2b1f255bab 100644 --- a/src/backend/auth/api-auth/src/main/kotlin/com/tencent/bkrepo/auth/api/ServicePermissionClient.kt +++ b/src/backend/auth/api-auth/src/main/kotlin/com/tencent/bkrepo/auth/api/ServicePermissionClient.kt @@ -33,6 +33,7 @@ package com.tencent.bkrepo.auth.api import com.tencent.bkrepo.auth.constant.AUTH_SERVICE_PERMISSION_PREFIX import com.tencent.bkrepo.auth.pojo.permission.CheckPermissionRequest +import com.tencent.bkrepo.auth.pojo.permission.ListPathResult import com.tencent.bkrepo.common.api.constant.AUTH_SERVICE_NAME import com.tencent.bkrepo.common.api.pojo.Response import io.swagger.annotations.Api @@ -70,6 +71,17 @@ interface ServicePermissionClient { @RequestParam userId: String ): Response> + @ApiOperation("list有权限路径") + @GetMapping("/path/list") + fun listPermissionPath( + @ApiParam(value = "用户ID") + @RequestParam userId: String, + @ApiParam(value = "项目ID") + @RequestParam projectId: String, + @ApiParam(value = "仓库名称") + @RequestParam repoName: String + ): Response + @ApiOperation("校验权限") @PostMapping("/check") fun checkPermission( diff --git a/src/backend/auth/api-auth/src/main/kotlin/com/tencent/bkrepo/auth/pojo/permission/ListPathResult.kt b/src/backend/auth/api-auth/src/main/kotlin/com/tencent/bkrepo/auth/pojo/permission/ListPathResult.kt new file mode 100644 index 0000000000..168f8d07dd --- /dev/null +++ b/src/backend/auth/api-auth/src/main/kotlin/com/tencent/bkrepo/auth/pojo/permission/ListPathResult.kt @@ -0,0 +1,42 @@ +/* + * Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available. + * + * Copyright (C) 2020 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.permission + +import io.swagger.annotations.ApiModel +import com.tencent.bkrepo.common.query.enums.OperationType + +@ApiModel("校验权限请求") +data class ListPathResult( + // when true, need to compare + val status: Boolean, + val path: Map>, +) diff --git a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/controller/service/ServicePermissionController.kt b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/controller/service/ServicePermissionController.kt index 7ab0db8dfe..71acc9613d 100644 --- a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/controller/service/ServicePermissionController.kt +++ b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/controller/service/ServicePermissionController.kt @@ -34,8 +34,10 @@ package com.tencent.bkrepo.auth.controller.service import com.tencent.bkrepo.auth.api.ServicePermissionClient import com.tencent.bkrepo.auth.controller.OpenResource import com.tencent.bkrepo.auth.pojo.permission.CheckPermissionRequest +import com.tencent.bkrepo.auth.pojo.permission.ListPathResult import com.tencent.bkrepo.auth.service.PermissionService import com.tencent.bkrepo.common.api.pojo.Response +import com.tencent.bkrepo.common.query.enums.OperationType import com.tencent.bkrepo.common.service.util.ResponseBuilder import org.springframework.beans.factory.annotation.Autowired import org.springframework.web.bind.annotation.RestController @@ -45,6 +47,14 @@ class ServicePermissionController @Autowired constructor( private val permissionService: PermissionService ) : ServicePermissionClient, OpenResource(permissionService) { + override fun listPermissionPath(userId: String, projectId: String, repoName: String): Response { + val permissionPath = permissionService.listPermissionPath(userId, projectId, repoName) + val status = permissionPath.isNotEmpty() + val result = ListPathResult(status = status, path = mapOf(OperationType.NIN to permissionPath)) + return ResponseBuilder.success(result) + } + + override fun checkPermission(request: CheckPermissionRequest): Response { checkRequest(request) return ResponseBuilder.success(permissionService.checkPermission(request)) diff --git a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/repository/PermissionRepository.kt b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/repository/PermissionRepository.kt index 67741036c6..bb282ba1c4 100644 --- a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/repository/PermissionRepository.kt +++ b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/repository/PermissionRepository.kt @@ -62,6 +62,8 @@ interface PermissionRepository : MongoRepository { resourceType: ResourceType ): TPermission? + + fun findByUsers(userId: String): List fun findByProjectIdAndUsers(projectId: String, userId: String): List 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 ed64fa293e..527ce1bb98 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 @@ -64,6 +64,11 @@ interface PermissionService { */ fun listPermissionProject(userId: String): List + /** + * 获取有权限路径列表 + */ + fun listPermissionPath(userId: String, projectId: String, repoName: String): List + fun createPermission(request: CreatePermissionRequest): Boolean fun listPermission(projectId: String, repoName: String?): List 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 b714959fee..5311e6f7ae 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 @@ -83,7 +83,7 @@ class DevopsPermissionServiceImpl constructor( } // devops 体系 - if (checkDevopsProjectPermission(userId, projectId, PermissionAction.READ.toString())) { + if (checkDevopsProjectPermission(userId, projectId, PermissionAction.READ.name)) { return getAllRepoByProjectId(projectId) } return super.listPermissionRepo(projectId, userId, appId) @@ -113,6 +113,13 @@ class DevopsPermissionServiceImpl constructor( return allProjectList.distinct() } + override fun listPermissionPath(userId: String, projectId: String, repoName: String): List { + if (checkDevopsProjectPermission(userId, projectId, PermissionAction.MANAGE.name)) { + return emptyList() + } + return super.listPermissionPath(userId, projectId, repoName) + } + private fun parsePipelineId(path: String): String? { val roads = PathUtils.normalizeFullPath(path).split("/") return if (roads.size < 2 || roads[1].isBlank()) { @@ -131,7 +138,7 @@ class DevopsPermissionServiceImpl constructor( return true } // project权限 - if (resourceType == ResourceType.PROJECT.toString()) { + if (resourceType == ResourceType.PROJECT.name) { return checkDevopsProjectPermission(uid, projectId!!, action) || super.checkBkIamV3ProjectPermission(projectId!!, uid, action) } @@ -175,9 +182,9 @@ class DevopsPermissionServiceImpl constructor( } private fun checkDevopsReportPermission(action: String): Boolean { - return action == PermissionAction.READ.toString() || - action == PermissionAction.WRITE.toString() || - action == PermissionAction.VIEW.toString() + return action == PermissionAction.READ.name || + action == PermissionAction.WRITE.name || + action == PermissionAction.VIEW.name } private fun checkDevopsPipelinePermission( @@ -188,8 +195,8 @@ class DevopsPermissionServiceImpl constructor( action: String ): Boolean { return when (resourceType) { - ResourceType.REPO.toString() -> checkDevopsProjectPermission(uid, projectId, action) - ResourceType.NODE.toString() -> { + ResourceType.REPO.name -> checkDevopsProjectPermission(uid, projectId, action) + ResourceType.NODE.name -> { val pipelineId = parsePipelineId(path ?: return false) ?: return false pipelinePermission(uid, projectId, pipelineId, action) } @@ -200,7 +207,7 @@ class DevopsPermissionServiceImpl constructor( private fun checkDevopsProjectPermission(userId: String, projectId: String, action: String): Boolean { logger.debug("checkDevopsProjectPermission: [$userId,$projectId,$action]") return when (action) { - PermissionAction.MANAGE.toString() -> devopsProjectService.isProjectManager(userId, projectId) + PermissionAction.MANAGE.name -> devopsProjectService.isProjectManager(userId, projectId) else -> devopsProjectService.isProjectMember(userId, projectId, action) } } 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 c968ab2605..4b70e89c0f 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 @@ -46,6 +46,10 @@ import com.tencent.bkrepo.auth.pojo.enums.PermissionAction.MANAGE import com.tencent.bkrepo.auth.pojo.enums.PermissionAction.WRITE import com.tencent.bkrepo.auth.pojo.enums.PermissionAction.DELETE import com.tencent.bkrepo.auth.pojo.enums.ResourceType +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.enums.ResourceType.SYSTEM import com.tencent.bkrepo.auth.pojo.enums.RoleType import com.tencent.bkrepo.auth.pojo.permission.CheckPermissionRequest import com.tencent.bkrepo.auth.pojo.permission.CreatePermissionRequest @@ -88,10 +92,10 @@ open class PermissionServiceImpl constructor( override fun listPermission(projectId: String, repoName: String?): List { logger.debug("list permission projectId: [$projectId], repoName: [$repoName]") repoName?.let { - return permissionRepository.findByResourceTypeAndProjectIdAndRepos(ResourceType.REPO, projectId, repoName) + return permissionRepository.findByResourceTypeAndProjectIdAndRepos(REPO, projectId, repoName) .map { PermRequestUtil.convToPermission(it) } } - return permissionRepository.findByResourceTypeAndProjectId(ResourceType.PROJECT, projectId) + return permissionRepository.findByResourceTypeAndProjectId(PROJECT, projectId) .map { PermRequestUtil.convToPermission(it) } } @@ -188,46 +192,30 @@ open class PermissionServiceImpl constructor( // check user admin permission if (user.admin) return true // check role project admin - if (checkProjectAdmin(request, user.roles)) return true + if (checkProjectAdmin(request)) return true // check role project user if (checkProjectUser(request, user.roles)) return true // check role repo admin if (checkRepoAdmin(request, user.roles)) return true // check repo action - return checkAction(request, user.roles) + if (request.resourceType == NODE.name) { + return checkNodeAction(request, user.roles) + } + return false } - private fun checkProjectAdmin(request: CheckPermissionRequest, roles: List): Boolean { - var queryRoles = emptyList() - if (roles.isNotEmpty() && request.projectId != null) { - queryRoles = roles.filter { !it.isNullOrEmpty() }.toList() - } - if (queryRoles.isEmpty()) { + private fun checkProjectAdmin(request: CheckPermissionRequest): Boolean { + if (request.projectId == null) { return false } - val result = roleRepository.findByProjectIdAndTypeAndAdminAndIdIn( - projectId = request.projectId!!, type = RoleType.PROJECT, admin = true, ids = queryRoles - ) - if (result.isNotEmpty()) { - return true - } - return false + return isUserLocalProjectAdmin(request.uid, request.projectId!!) } private fun checkProjectUser(request: CheckPermissionRequest, roles: List): Boolean { - var queryRoles = emptyList() - if (roles.isNotEmpty() && request.projectId != null) { - queryRoles = roles.filter { !it.isNullOrEmpty() }.toList() - } - if (queryRoles.isEmpty()) return false - - if (roleRepository.findByIdIn(queryRoles) - .any { tRole -> tRole.projectId == request.projectId && tRole.roleId == PROJECT_VIEWER_ID } - && request.action == READ.toString() - ) { - return true + if (request.projectId == null || roles.isEmpty()) { + return false } - return false + return isUserLocalProjectUser(roles, request.projectId!!) && request.action == READ.name } private fun checkRepoAdmin(request: CheckPermissionRequest, roles: List): Boolean { @@ -249,41 +237,45 @@ open class PermissionServiceImpl constructor( return false } - private fun checkAction(request: CheckPermissionRequest, roles: List): Boolean { + private fun checkNodeAction(request: CheckPermissionRequest, roles: List): Boolean { with(request) { val query = PermissionQueryHelper.buildPermissionCheck( - projectId, repoName, uid, action, resourceType, roles + projectId, repoName, uid, resourceType, roles ) val result = mongoTemplate.find(query, TPermission::class.java) - if (result.isEmpty()) return false - - // result is not empty and path is null - if (path == null) return true + if (result.isEmpty() || path == null) return false result.forEach { + if (checkIncludePatternAction(it.includePattern, path!!, it.actions, action)) return true - if (checkIncludePattern(it.includePattern, path!!)) return true - - if (!checkExcludePattern(it.excludePattern, path!!)) return false + if (checkExcludePatternAction(it.excludePattern, path!!, it.actions, action)) return false } } return false } - private fun checkIncludePattern(patternList: List, path: String): Boolean { - if (patternList.isEmpty()) return true + private fun checkIncludePatternAction( + patternList: List, + path: String, + actions: List, + checkAction: String + ): Boolean { patternList.forEach { - if (path.contains(it)) return true + if (path.startsWith(it) && (actions.contains(MANAGE.name) || actions.contains(checkAction))) return true } return false } - private fun checkExcludePattern(patternList: List, path: String): Boolean { - if (patternList.isEmpty()) return true + private fun checkExcludePatternAction( + patternList: List, + path: String, + actions: List, + checkAction: String + ): Boolean { patternList.forEach { - if (path.contains(it)) return false + if (path.startsWith(it) && actions.contains(checkAction)) return true } - return true + return false } override fun listPermissionProject(userId: String): List { @@ -381,6 +373,35 @@ open class PermissionServiceImpl constructor( return repoList.distinct() } + override fun listPermissionPath(userId: String, projectId: String, repoName: String): List { + val projectPermission = permissionRepository.findByResourceTypeAndProjectIdAndRepos( + NODE, + projectId, + repoName, + ) + if (isUserLocalAdmin(userId) || isUserLocalProjectAdmin(userId, projectId)) { + return emptyList() + } + val excludePath = mutableListOf() + val includePath = mutableListOf() + projectPermission.forEach { + if (it.users.contains(userId)) { + if (it.excludePattern.isNotEmpty()) { + excludePath.addAll(it.excludePattern) + } + if (it.includePattern.isNotEmpty()) { + includePath.addAll(it.includePattern) + } + } else { + if (it.includePattern.isNotEmpty()) { + excludePath.addAll(it.includePattern) + } + } + } + val filterPath = includePath.distinct() + return excludePath.distinct().filter { !filterPath.contains(it) } + } + private fun isUserLocalProjectUser(roleIds: List, projectId: String): Boolean { return roleRepository.findAllById(roleIds) .any { role -> role.projectId == projectId && role.roleId == PROJECT_VIEWER_ID } @@ -458,14 +479,14 @@ open class PermissionServiceImpl constructor( actions: Set ): TPermission { permissionRepository.findOneByProjectIdAndReposAndPermNameAndResourceType( - projectId, repoName, permName, ResourceType.REPO + projectId, repoName, permName, REPO ) ?: run { val request = TPermission( projectId = projectId, repos = listOf(repoName), permName = permName, - actions = actions.map { it.toString() }, - resourceType = ResourceType.REPO.toString(), + actions = actions.map { it.name }, + resourceType = REPO.name, createAt = LocalDateTime.now(), updateAt = LocalDateTime.now(), createBy = AUTH_ADMIN, @@ -475,7 +496,7 @@ open class PermissionServiceImpl constructor( permissionRepository.insert(request) } return permissionRepository.findOneByProjectIdAndReposAndPermNameAndResourceType( - projectId = projectId, repoName = repoName, permName = permName, resourceType = ResourceType.REPO + projectId = projectId, repoName = repoName, permName = permName, resourceType = REPO )!! } @@ -491,8 +512,8 @@ open class PermissionServiceImpl constructor( val reqResourceType = ResourceType.lookup(resourceType) if (!platform.scope!!.contains(reqResourceType)) return false when (reqResourceType) { - ResourceType.SYSTEM -> return true - ResourceType.PROJECT -> { + SYSTEM -> return true + PROJECT -> { return checkPlatformProject(projectId, platform.scopeDesc) } else -> return false @@ -503,7 +524,7 @@ open class PermissionServiceImpl constructor( override fun listProjectBuiltinPermission(projectId: String): List { val projectManager = Permission( id = PROJECT_MANAGE_ID, - resourceType = ResourceType.PROJECT.toString(), + resourceType = PROJECT.name, projectId = projectId, permName = "project_manage_permission", users = getProjectAdminUser(projectId), @@ -514,7 +535,7 @@ open class PermissionServiceImpl constructor( ) val projectViewer = Permission( id = PROJECT_VIEWER_ID, - resourceType = ResourceType.PROJECT.toString(), + resourceType = PROJECT.name, projectId = projectId, permName = "project_view_permission", users = getProjectCommonUser(projectId), diff --git a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/util/query/PermissionQueryHelper.kt b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/util/query/PermissionQueryHelper.kt index 3f9b6450a7..1f2ee6821e 100644 --- a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/util/query/PermissionQueryHelper.kt +++ b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/util/query/PermissionQueryHelper.kt @@ -10,7 +10,6 @@ object PermissionQueryHelper { projectId: String?, repoName: String?, uid: String, - action: String, resourceType: String, roles: List ): Query { @@ -18,7 +17,7 @@ object PermissionQueryHelper { var celeriac = criteria.orOperator( Criteria.where(TPermission::users.name).`is`(uid), Criteria.where(TPermission::roles.name).`in`(roles) - ).and(TPermission::resourceType.name).`is`(resourceType).and(TPermission::actions.name).`is`(action) + ).and(TPermission::resourceType.name).`is`(resourceType) projectId?.let { celeriac = celeriac.and(TPermission::projectId.name).`is`(projectId) }