From 7cc37a6c7eea3e5b1d8aac51580bc43185c5c1df Mon Sep 17 00:00:00 2001 From: RJ Date: Mon, 18 Nov 2024 17:10:01 +0800 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20Turbo=E7=BC=96=E8=AF=91=E5=8A=A0?= =?UTF-8?q?=E9=80=9F=E5=AF=B9=E6=8E=A5=E6=9D=83=E9=99=90=E4=B8=AD=E5=BF=83?= =?UTF-8?q?RBAC=20#316?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/turbo/biz-turbo/build.gradle.kts | 3 - .../UserTurboDaySummaryController.kt | 25 +-- .../controller/UserTurboPlanController.kt | 78 +++++---- .../UserTurboPlanInstanceController.kt | 14 +- .../controller/UserTurboRecordController.kt | 16 +- .../devops/turbo/service/TurboPlanService.kt | 23 +++ .../common-turbo-api/build.gradle.kts | 1 + .../common/api/annotation/RequiresAuth.kt | 19 +++ .../common-turbo-auth/build.gradle.kts | 9 ++ .../common/auth/RBACAuthAutoConfiguration.kt | 27 ++++ .../common/auth/api/AuthPermissionApi.kt | 33 ++++ .../devops/common/auth/api/AuthRegisterApi.kt | 22 +++ .../common/auth/api/RBACAuthPermissionApi.kt | 152 ++++++++++++++++++ .../common/auth/api/RBACAuthProperties.kt | 19 +++ .../common/auth/api/RBACAuthRegisterApi.kt | 107 ++++++++++++ .../main/resources/META-INF/spring.factories | 2 + .../devops/common/util/constants/Header.kt | 2 + .../common/util/enums/ResourceActionType.kt | 15 ++ .../devops/common/util/enums/ResourceType.kt | 11 ++ .../common-turbo-web/build.gradle.kts | 1 + .../common/web/config/WebAutoConfiguration.kt | 11 ++ .../common/web/interceptor/AuthInterceptor.kt | 70 ++++++++ src/backend/turbo/settings.gradle.kts | 1 + 23 files changed, 605 insertions(+), 56 deletions(-) create mode 100644 src/backend/turbo/common-turbo/common-turbo-api/src/main/kotlin/com/tencent/devops/common/api/annotation/RequiresAuth.kt create mode 100644 src/backend/turbo/common-turbo/common-turbo-auth/build.gradle.kts create mode 100644 src/backend/turbo/common-turbo/common-turbo-auth/src/main/kotlin/com/tencent/devops/common/auth/RBACAuthAutoConfiguration.kt create mode 100644 src/backend/turbo/common-turbo/common-turbo-auth/src/main/kotlin/com/tencent/devops/common/auth/api/AuthPermissionApi.kt create mode 100644 src/backend/turbo/common-turbo/common-turbo-auth/src/main/kotlin/com/tencent/devops/common/auth/api/AuthRegisterApi.kt create mode 100644 src/backend/turbo/common-turbo/common-turbo-auth/src/main/kotlin/com/tencent/devops/common/auth/api/RBACAuthPermissionApi.kt create mode 100644 src/backend/turbo/common-turbo/common-turbo-auth/src/main/kotlin/com/tencent/devops/common/auth/api/RBACAuthProperties.kt create mode 100644 src/backend/turbo/common-turbo/common-turbo-auth/src/main/kotlin/com/tencent/devops/common/auth/api/RBACAuthRegisterApi.kt create mode 100644 src/backend/turbo/common-turbo/common-turbo-auth/src/main/resources/META-INF/spring.factories create mode 100644 src/backend/turbo/common-turbo/common-turbo-util/src/main/kotlin/com/tencent/devops/common/util/enums/ResourceActionType.kt create mode 100644 src/backend/turbo/common-turbo/common-turbo-util/src/main/kotlin/com/tencent/devops/common/util/enums/ResourceType.kt create mode 100644 src/backend/turbo/common-turbo/common-turbo-web/src/main/kotlin/com/tencent/devops/common/web/interceptor/AuthInterceptor.kt diff --git a/src/backend/turbo/biz-turbo/build.gradle.kts b/src/backend/turbo/biz-turbo/build.gradle.kts index 9629f96c0..0af96659e 100644 --- a/src/backend/turbo/biz-turbo/build.gradle.kts +++ b/src/backend/turbo/biz-turbo/build.gradle.kts @@ -20,7 +20,4 @@ dependencies { api("com.tencent.bk.devops.ci.auth:api-auth:${Versions.ciVersion}"){ isTransitive = false } - api("com.tencent.bk.devops.ci.common:common-auth-api:${Versions.ciVersion}"){ - isTransitive = false - } } diff --git a/src/backend/turbo/biz-turbo/src/main/kotlin/com/tencent/devops/turbo/controller/UserTurboDaySummaryController.kt b/src/backend/turbo/biz-turbo/src/main/kotlin/com/tencent/devops/turbo/controller/UserTurboDaySummaryController.kt index 682b2e983..554de30a6 100644 --- a/src/backend/turbo/biz-turbo/src/main/kotlin/com/tencent/devops/turbo/controller/UserTurboDaySummaryController.kt +++ b/src/backend/turbo/biz-turbo/src/main/kotlin/com/tencent/devops/turbo/controller/UserTurboDaySummaryController.kt @@ -1,11 +1,10 @@ package com.tencent.devops.turbo.controller import com.tencent.devops.api.pojo.Response -import com.tencent.devops.common.api.exception.TurboException -import com.tencent.devops.common.api.exception.code.IS_NOT_ADMIN_MEMBER -import com.tencent.devops.common.util.constants.NO_ADMIN_MEMBER_MESSAGE +import com.tencent.devops.common.api.annotation.RequiresAuth +import com.tencent.devops.common.util.enums.ResourceActionType +import com.tencent.devops.common.util.enums.ResourceType import com.tencent.devops.turbo.api.IUserTurboDaySummaryController -import com.tencent.devops.turbo.service.TurboAuthService import com.tencent.devops.turbo.service.TurboSummaryService import com.tencent.devops.turbo.vo.TurboOverviewStatRowVO import com.tencent.devops.turbo.vo.TurboOverviewTrendVO @@ -14,47 +13,37 @@ import org.springframework.web.bind.annotation.RestController @RestController class UserTurboDaySummaryController @Autowired constructor( - private val turboSummaryService: TurboSummaryService, - private val turboAuthService: TurboAuthService + private val turboSummaryService: TurboSummaryService ) : IUserTurboDaySummaryController { /** * 获取总览页面统计栏数据 */ + @RequiresAuth(resourceType = ResourceType.PROJECT, permission = ResourceActionType.OVERVIEW) override fun getOverviewStatRowData(projectId: String, user: String): Response { - // 判断是否是管理员 - if (!turboAuthService.getAuthResult(projectId, user)) { - throw TurboException(errorCode = IS_NOT_ADMIN_MEMBER, errorMessage = NO_ADMIN_MEMBER_MESSAGE) - } return Response.success(turboSummaryService.getOverviewStatRowData(projectId)) } /** * 获取总览页面耗时分布趋势图数据 */ + @RequiresAuth(resourceType = ResourceType.PROJECT, permission = ResourceActionType.OVERVIEW) override fun getTimeConsumingTrendData( dateType: String, projectId: String, user: String ): Response> { - // 判断是否是管理员 - if (!turboAuthService.getAuthResult(projectId, user)) { - throw TurboException(errorCode = IS_NOT_ADMIN_MEMBER, errorMessage = NO_ADMIN_MEMBER_MESSAGE) - } return Response.success(turboSummaryService.getTimeConsumingTrendData(dateType, projectId)) } /** * 获取总览页面编译次数趋势图数据 */ + @RequiresAuth(resourceType = ResourceType.PROJECT, permission = ResourceActionType.OVERVIEW) override fun getCompileNumberTrendData( dateType: String, projectId: String, user: String ): Response> { - // 判断是否是管理员 - if (!turboAuthService.getAuthResult(projectId, user)) { - throw TurboException(errorCode = IS_NOT_ADMIN_MEMBER, errorMessage = NO_ADMIN_MEMBER_MESSAGE) - } return Response.success(turboSummaryService.getCompileNumberTrendData(dateType, projectId)) } } diff --git a/src/backend/turbo/biz-turbo/src/main/kotlin/com/tencent/devops/turbo/controller/UserTurboPlanController.kt b/src/backend/turbo/biz-turbo/src/main/kotlin/com/tencent/devops/turbo/controller/UserTurboPlanController.kt index ff1f937b9..d68de995e 100644 --- a/src/backend/turbo/biz-turbo/src/main/kotlin/com/tencent/devops/turbo/controller/UserTurboPlanController.kt +++ b/src/backend/turbo/biz-turbo/src/main/kotlin/com/tencent/devops/turbo/controller/UserTurboPlanController.kt @@ -1,10 +1,13 @@ package com.tencent.devops.turbo.controller import com.tencent.devops.api.pojo.Response +import com.tencent.devops.common.api.annotation.RequiresAuth import com.tencent.devops.common.api.exception.TurboException import com.tencent.devops.common.api.exception.code.IS_NOT_ADMIN_MEMBER import com.tencent.devops.common.api.pojo.Page import com.tencent.devops.common.util.constants.NO_ADMIN_MEMBER_MESSAGE +import com.tencent.devops.common.util.enums.ResourceActionType +import com.tencent.devops.common.util.enums.ResourceType import com.tencent.devops.turbo.api.IUserTurboPlanController import com.tencent.devops.turbo.pojo.TurboPlanModel import com.tencent.devops.turbo.service.TurboAuthService @@ -16,63 +19,82 @@ import com.tencent.devops.turbo.vo.TurboPlanStatusBatchUpdateReqVO import org.springframework.beans.factory.annotation.Autowired import org.springframework.web.bind.annotation.RestController -@Suppress("MaxLineLength") @RestController class UserTurboPlanController @Autowired constructor( private val turboPlanService: TurboPlanService, private val turboAuthService: TurboAuthService ) : IUserTurboPlanController { + @RequiresAuth(resourceType = ResourceType.PROJECT, permission = ResourceActionType.CREATE) override fun addNewTurboPlan(turboPlanModel: TurboPlanModel, projectId: String, user: String): Response { - // 判断是否是管理员 - if (!turboAuthService.getAuthResult(projectId, user)) { - throw TurboException(errorCode = IS_NOT_ADMIN_MEMBER, errorMessage = NO_ADMIN_MEMBER_MESSAGE) - } return Response.success(turboPlanService.addNewTurboPlan(turboPlanModel, user)) } - override fun getTurboPlanStatRowData(projectId: String, pageNum: Int?, pageSize: Int?, user: String): Response { - // 判断是否是管理员 - if (!turboAuthService.getAuthResult(projectId, user)) { - throw TurboException(errorCode = IS_NOT_ADMIN_MEMBER, errorMessage = NO_ADMIN_MEMBER_MESSAGE) - } + @RequiresAuth(resourceType = ResourceType.PROJECT, permission = ResourceActionType.LIST) + override fun getTurboPlanStatRowData( + projectId: String, + pageNum: Int?, + pageSize: Int?, + user: String + ): Response { return Response.success(turboPlanService.getTurboPlanStatRowData(projectId, pageNum, pageSize)) } - override fun getTurboPlanDetailByPlanId(planId: String, projectId: String, user: String): Response { - // 判断是否是管理员 - if (!turboAuthService.getAuthResult(projectId, user)) { - throw TurboException(errorCode = IS_NOT_ADMIN_MEMBER, errorMessage = NO_ADMIN_MEMBER_MESSAGE) - } + @RequiresAuth + override fun getTurboPlanDetailByPlanId( + planId: String, + projectId: String, + user: String + ): Response { return Response.success(turboPlanService.getTurboPlanDetailByPlanId(planId)) } - override fun putTurboPlanDetailNameAndOpenStatus(turboPlanModel: TurboPlanModel, planId: String, user: String, projectId: String): Response { - // 判断是否是管理员 - if (!turboAuthService.getAuthResult(projectId, user)) { - throw TurboException(errorCode = IS_NOT_ADMIN_MEMBER, errorMessage = NO_ADMIN_MEMBER_MESSAGE) - } + @RequiresAuth(permission = ResourceActionType.EDIT) + override fun putTurboPlanDetailNameAndOpenStatus( + turboPlanModel: TurboPlanModel, + planId: String, + user: String, + projectId: String + ): Response { return Response.success(turboPlanService.putTurboPlanDetailNameAndOpenStatus(turboPlanModel, planId, user)) } - override fun putTurboPlanConfigParam(turboPlanModel: TurboPlanModel, planId: String, user: String, projectId: String): Response { - // 判断是否是管理员 - if (!turboAuthService.getAuthResult(projectId, user)) { - throw TurboException(errorCode = IS_NOT_ADMIN_MEMBER, errorMessage = NO_ADMIN_MEMBER_MESSAGE) - } + @RequiresAuth(permission = ResourceActionType.EDIT) + override fun putTurboPlanConfigParam( + turboPlanModel: TurboPlanModel, + planId: String, + user: String, + projectId: String + ): Response { return Response.success(turboPlanService.putTurboPlanConfigParam(turboPlanModel, planId, user)) } + @RequiresAuth(permission = ResourceActionType.EDIT) override fun putTurboPlanTopStatus(planId: String, topStatus: String, user: String): Response { return Response.success(turboPlanService.putTurboPlanTopStatus(planId, topStatus, user)) } - override fun getAvailableTurboPlanList(projectId: String, pageNum: Int?, pageSize: Int?): Response> { + @RequiresAuth(resourceType = ResourceType.PROJECT, permission = ResourceActionType.LIST) + override fun getAvailableTurboPlanList( + projectId: String, + pageNum: Int?, + pageSize: Int? + ): Response> { return Response.success(turboPlanService.getAvailableProjectIdList(projectId, pageNum, pageSize)) } - override fun findTurboPlanIdByProjectIdAndPipelineInfo(projectId: String, pipelineId: String, pipelineElementId: String): Response { - return Response.success(turboPlanService.findMigratedTurboPlanByPipelineInfo(projectId, pipelineId, pipelineElementId)) + override fun findTurboPlanIdByProjectIdAndPipelineInfo( + projectId: String, + pipelineId: String, + pipelineElementId: String + ): Response { + return Response.success( + turboPlanService.findMigratedTurboPlanByPipelineInfo( + projectId, + pipelineId, + pipelineElementId + ) + ) } override fun manualRefreshStatus( diff --git a/src/backend/turbo/biz-turbo/src/main/kotlin/com/tencent/devops/turbo/controller/UserTurboPlanInstanceController.kt b/src/backend/turbo/biz-turbo/src/main/kotlin/com/tencent/devops/turbo/controller/UserTurboPlanInstanceController.kt index efc3a640c..e5a40593e 100644 --- a/src/backend/turbo/biz-turbo/src/main/kotlin/com/tencent/devops/turbo/controller/UserTurboPlanInstanceController.kt +++ b/src/backend/turbo/biz-turbo/src/main/kotlin/com/tencent/devops/turbo/controller/UserTurboPlanInstanceController.kt @@ -1,7 +1,10 @@ package com.tencent.devops.turbo.controller import com.tencent.devops.api.pojo.Response +import com.tencent.devops.common.api.annotation.RequiresAuth import com.tencent.devops.common.api.pojo.Page +import com.tencent.devops.common.util.enums.ResourceActionType +import com.tencent.devops.common.util.enums.ResourceType import com.tencent.devops.turbo.api.IUserTurboPlanInstanceController import com.tencent.devops.turbo.service.TurboPlanInstanceService import com.tencent.devops.turbo.vo.TurboPlanInstanceVO @@ -14,6 +17,7 @@ class UserTurboPlanInstanceController @Autowired constructor( private val turboPlanInstanceService: TurboPlanInstanceService ) : IUserTurboPlanInstanceController { + @RequiresAuth(resourceType = ResourceType.PROJECT, permission = ResourceActionType.LIST_TASK) override fun getTurboPlanInstanceList( turboPlanId: String, pageNum: Int?, @@ -21,6 +25,14 @@ class UserTurboPlanInstanceController @Autowired constructor( sortField: String?, sortType: String? ): Response> { - return Response.success(turboPlanInstanceService.getTurboPlanInstanceList(turboPlanId, pageNum, pageSize, sortField, sortType)) + return Response.success( + turboPlanInstanceService.getTurboPlanInstanceList( + turboPlanId, + pageNum, + pageSize, + sortField, + sortType + ) + ) } } diff --git a/src/backend/turbo/biz-turbo/src/main/kotlin/com/tencent/devops/turbo/controller/UserTurboRecordController.kt b/src/backend/turbo/biz-turbo/src/main/kotlin/com/tencent/devops/turbo/controller/UserTurboRecordController.kt index 8aa9e16b8..daf010eee 100644 --- a/src/backend/turbo/biz-turbo/src/main/kotlin/com/tencent/devops/turbo/controller/UserTurboRecordController.kt +++ b/src/backend/turbo/biz-turbo/src/main/kotlin/com/tencent/devops/turbo/controller/UserTurboRecordController.kt @@ -1,11 +1,14 @@ package com.tencent.devops.turbo.controller import com.tencent.devops.api.pojo.Response +import com.tencent.devops.common.api.annotation.RequiresAuth import com.tencent.devops.common.api.exception.TurboException import com.tencent.devops.common.api.exception.code.IS_NOT_ADMIN_MEMBER import com.tencent.devops.common.api.exception.code.TURBO_PARAM_INVALID import com.tencent.devops.common.api.pojo.Page import com.tencent.devops.common.util.constants.NO_ADMIN_MEMBER_MESSAGE +import com.tencent.devops.common.util.enums.ResourceActionType +import com.tencent.devops.common.util.enums.ResourceType import com.tencent.devops.turbo.api.IUserTurboRecordController import com.tencent.devops.turbo.enums.EnumDistccTaskStatus import com.tencent.devops.turbo.pojo.TurboRecordModel @@ -33,6 +36,7 @@ class UserTurboRecordController @Autowired constructor( private val logger = LoggerFactory.getLogger(UserTurboRecordController::class.java) } + @RequiresAuth(resourceType = ResourceType.PROJECT, permission = ResourceActionType.LIST_TASK) override fun getTurboRecordHistoryList( pageNum: Int?, pageSize: Int?, @@ -67,12 +71,12 @@ class UserTurboRecordController @Autowired constructor( ) } - override fun getTurboDisplayInfoById(turboRecordId: String, projectId: String, user: String): Response { - // 判断是否是管理员 - if (!turboAuthService.getAuthResult(projectId, user)) { - throw TurboException(errorCode = IS_NOT_ADMIN_MEMBER, errorMessage = NO_ADMIN_MEMBER_MESSAGE) - } - + @RequiresAuth(resourceType = ResourceType.PROJECT, permission = ResourceActionType.VIEW_TASK) + override fun getTurboDisplayInfoById( + turboRecordId: String, + projectId: String, + user: String + ): Response { val turboRecordEntity = turboRecordService.findByRecordId(turboRecordId) if (null == turboRecordEntity || turboRecordEntity.turboPlanId.isNullOrBlank()) { logger.info("no turbo record found with id: $turboRecordId") diff --git a/src/backend/turbo/biz-turbo/src/main/kotlin/com/tencent/devops/turbo/service/TurboPlanService.kt b/src/backend/turbo/biz-turbo/src/main/kotlin/com/tencent/devops/turbo/service/TurboPlanService.kt index 7cd0bfa33..b5adc3d4a 100644 --- a/src/backend/turbo/biz-turbo/src/main/kotlin/com/tencent/devops/turbo/service/TurboPlanService.kt +++ b/src/backend/turbo/biz-turbo/src/main/kotlin/com/tencent/devops/turbo/service/TurboPlanService.kt @@ -6,6 +6,7 @@ import com.tencent.devops.common.api.exception.code.TURBO_PARAM_INVALID import com.tencent.devops.common.api.exception.code.TURBO_THIRDPARTY_SYSTEM_FAIL import com.tencent.devops.common.api.pojo.Page import com.tencent.devops.common.api.util.OkhttpUtil +import com.tencent.devops.common.auth.api.AuthRegisterApi import com.tencent.devops.common.client.Client import com.tencent.devops.common.db.PageUtils import com.tencent.devops.common.service.prometheus.BkTimed @@ -40,6 +41,7 @@ import java.time.LocalDateTime @Suppress("MaxLineLength", "ComplexMethod", "NestedBlockDepth", "SpringJavaInjectionPointsAutowiringInspection") @Service class TurboPlanService @Autowired constructor( + private val authRegisterApi: AuthRegisterApi, private val turboPlanDao: TurboPlanDao, private val turboPlanRepository: TurboPlanRepository, private val turboPlanInstanceService: TurboPlanInstanceService, @@ -176,6 +178,21 @@ class TurboPlanService @Autowired constructor( createdDate = LocalDateTime.now() ) turboPlanEntity = turboPlanRepository.save(turboPlanEntity!!) + + // 2.1 注册到权限中心 + try { + val registerTurboPlan = authRegisterApi.registerTurboPlan( + user = user, + turboPlanId = turboPlanEntity!!.id!!, + turboPlanName = turboPlanEntity!!.planName, + projectId = turboPlanEntity!!.projectId + ) + if (!registerTurboPlan) { + rollbackTurboPlan(turboPlanEntity!!, "Failed to register plan to permission center", null) + } + } catch (e: Exception) { + rollbackTurboPlan(turboPlanEntity!!, "Failed to register plan to permission center", e) + } // 3. 调用api,同步信息 updateConfigParamByApi( turboEngineConfigEntity = turboEngineConfigEntity, @@ -189,6 +206,12 @@ class TurboPlanService @Autowired constructor( return turboPlanEntity?.id } + fun rollbackTurboPlan(turboPlanEntity: TTurboPlanEntity, errorMsg: String, e: Exception?) { + logger.error("$errorMsg user: ${turboPlanEntity.createdBy} turbo name: {}", turboPlanEntity.planName, e) + turboPlanRepository.delete(turboPlanEntity) + throw TurboException(TURBO_THIRDPARTY_SYSTEM_FAIL, errorMsg) + } + /** * op系统更新编译加速方案信息 */ diff --git a/src/backend/turbo/common-turbo/common-turbo-api/build.gradle.kts b/src/backend/turbo/common-turbo/common-turbo-api/build.gradle.kts index 58c2f311a..e099a29c9 100644 --- a/src/backend/turbo/common-turbo/common-turbo-api/build.gradle.kts +++ b/src/backend/turbo/common-turbo/common-turbo-api/build.gradle.kts @@ -1,4 +1,5 @@ dependencies { + api(project(":common-turbo:common-turbo-util")) api("com.squareup.okhttp3:okhttp") api("com.tencent.devops:devops-boot-starter-api") compileOnly("org.springframework.boot:spring-boot-starter-web") diff --git a/src/backend/turbo/common-turbo/common-turbo-api/src/main/kotlin/com/tencent/devops/common/api/annotation/RequiresAuth.kt b/src/backend/turbo/common-turbo/common-turbo-api/src/main/kotlin/com/tencent/devops/common/api/annotation/RequiresAuth.kt new file mode 100644 index 000000000..43c8a9692 --- /dev/null +++ b/src/backend/turbo/common-turbo/common-turbo-api/src/main/kotlin/com/tencent/devops/common/api/annotation/RequiresAuth.kt @@ -0,0 +1,19 @@ +package com.tencent.devops.common.api.annotation + +import com.tencent.devops.common.util.enums.ResourceActionType +import com.tencent.devops.common.util.enums.ResourceType + +@MustBeDocumented +@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS) +@Retention(AnnotationRetention.RUNTIME) +annotation class RequiresAuth( + /** + * 资源类型 选填,默认加速方案 + */ + val resourceType: ResourceType = ResourceType.TURBO_PLAN, + + /** + * 对资源的操作权限id 选填,默认查看方案权限 + */ + val permission: ResourceActionType = ResourceActionType.VIEW +) diff --git a/src/backend/turbo/common-turbo/common-turbo-auth/build.gradle.kts b/src/backend/turbo/common-turbo/common-turbo-auth/build.gradle.kts new file mode 100644 index 000000000..050ef34df --- /dev/null +++ b/src/backend/turbo/common-turbo/common-turbo-auth/build.gradle.kts @@ -0,0 +1,9 @@ +dependencies { + api(project(":common-turbo:common-turbo-client")) + api("com.tencent.bk.devops.ci.auth:api-auth:${Versions.ciVersion}") { + isTransitive = false + } + api("com.tencent.bk.devops.ci.common:common-auth-api:${Versions.ciVersion}"){ + isTransitive = false + } +} diff --git a/src/backend/turbo/common-turbo/common-turbo-auth/src/main/kotlin/com/tencent/devops/common/auth/RBACAuthAutoConfiguration.kt b/src/backend/turbo/common-turbo/common-turbo-auth/src/main/kotlin/com/tencent/devops/common/auth/RBACAuthAutoConfiguration.kt new file mode 100644 index 000000000..f708c505c --- /dev/null +++ b/src/backend/turbo/common-turbo/common-turbo-auth/src/main/kotlin/com/tencent/devops/common/auth/RBACAuthAutoConfiguration.kt @@ -0,0 +1,27 @@ +package com.tencent.devops.common.auth + +import com.tencent.devops.common.auth.api.RBACAuthProperties +import com.tencent.devops.common.auth.api.external.RBACAuthPermissionApi +import com.tencent.devops.common.auth.api.RBACAuthRegisterApi +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import com.tencent.devops.common.client.Client + +@Configuration +class RBACAuthAutoConfiguration { + + @Bean + fun rbacAuthProperties() = RBACAuthProperties() + + @Bean + fun rbacAuthPermissionApi( + rbacAuthProperties: RBACAuthProperties, + client: Client + ) = RBACAuthPermissionApi(client, rbacAuthProperties) + + @Bean + fun rbacAuthRegisterApi( + rbacAuthProperties: RBACAuthProperties, + client: Client + ) = RBACAuthRegisterApi(client, rbacAuthProperties) +} diff --git a/src/backend/turbo/common-turbo/common-turbo-auth/src/main/kotlin/com/tencent/devops/common/auth/api/AuthPermissionApi.kt b/src/backend/turbo/common-turbo/common-turbo-auth/src/main/kotlin/com/tencent/devops/common/auth/api/AuthPermissionApi.kt new file mode 100644 index 000000000..67e06dbde --- /dev/null +++ b/src/backend/turbo/common-turbo/common-turbo-auth/src/main/kotlin/com/tencent/devops/common/auth/api/AuthPermissionApi.kt @@ -0,0 +1,33 @@ +package com.tencent.devops.common.auth.api + +interface AuthPermissionApi { + + /** + * 校验用户是否有项目维度的资源操作权限 + */ + fun validateUserProjectPermission( + projectId: String, + userId: String, + action: String + ): Boolean + + /** + * 校验用户是否有指定实例资源的操作权限 + */ + fun validateUserResourcePermission( + projectId: String, + resourceCode: String, + action: String, + userId: String + ): Boolean + + /** + * 校验是否项目管理员 + */ + fun authProjectManager(projectId: String, userId: String): Boolean + + /** + * 校验是否项目管理员 + */ + fun authProjectUser(projectId: String, userId: String): Boolean +} diff --git a/src/backend/turbo/common-turbo/common-turbo-auth/src/main/kotlin/com/tencent/devops/common/auth/api/AuthRegisterApi.kt b/src/backend/turbo/common-turbo/common-turbo-auth/src/main/kotlin/com/tencent/devops/common/auth/api/AuthRegisterApi.kt new file mode 100644 index 000000000..366c876b5 --- /dev/null +++ b/src/backend/turbo/common-turbo/common-turbo-auth/src/main/kotlin/com/tencent/devops/common/auth/api/AuthRegisterApi.kt @@ -0,0 +1,22 @@ +package com.tencent.devops.common.auth.api + +interface AuthRegisterApi { + + /** + * 注册加速方案 + */ + fun registerTurboPlan( + user: String, + turboPlanId: String, + turboPlanName: String, + projectId: String + ): Boolean + + /** + * 删除加速方案 + */ + fun deleteTurboPlan( + turboPlanId: String, + projectId: String + ): Boolean +} diff --git a/src/backend/turbo/common-turbo/common-turbo-auth/src/main/kotlin/com/tencent/devops/common/auth/api/RBACAuthPermissionApi.kt b/src/backend/turbo/common-turbo/common-turbo-auth/src/main/kotlin/com/tencent/devops/common/auth/api/RBACAuthPermissionApi.kt new file mode 100644 index 000000000..00e2ca40b --- /dev/null +++ b/src/backend/turbo/common-turbo/common-turbo-auth/src/main/kotlin/com/tencent/devops/common/auth/api/RBACAuthPermissionApi.kt @@ -0,0 +1,152 @@ +package com.tencent.devops.common.auth.api.external + +import com.tencent.devops.auth.api.service.ServicePermissionAuthResource +import com.tencent.devops.auth.api.service.ServiceProjectAuthResource +import com.tencent.devops.common.api.exception.TurboException +import com.tencent.devops.common.api.exception.code.TURBO_THIRDPARTY_SYSTEM_FAIL +import com.tencent.devops.common.auth.api.AuthPermissionApi +import com.tencent.devops.common.auth.api.RBACAuthProperties +import com.tencent.devops.common.client.Client +import com.tencent.devops.common.util.enums.ResourceType +import org.slf4j.LoggerFactory +import org.springframework.beans.factory.annotation.Autowired + +class RBACAuthPermissionApi @Autowired constructor( + private val client: Client, + private val rbacAuthProperties: RBACAuthProperties +) : AuthPermissionApi { + + companion object { + private val logger = LoggerFactory.getLogger(this::class.java) + } + + /** + * 查询非用户态Access Token + */ + private fun getBackendAccessToken(): String { + return rbacAuthProperties.token ?: "" + } + + /** + * 校验是否项目管理员 + */ + override fun authProjectManager(projectId: String, userId: String): Boolean { + return checkProjectManager(projectId, userId) + } + + + /** + * 校验用户是否有项目维度的资源操作权限 + */ + override fun validateUserProjectPermission( + projectId: String, + userId: String, + action: String + ): Boolean { + logger.info("validateUserProject: $projectId, userId: $userId, action: $action") + return validateUserResourcePermission( + userId, + getBackendAccessToken(), + action, + projectId, + ResourceType.PROJECT.name + ) + } + + /** + * 校验用户是否有具体资源实例的操作权限 + * @param resourceCode 资源实例id + */ + override fun validateUserResourcePermission( + projectId: String, + resourceCode: String, + action: String, + userId: String + ): Boolean { + logger.info("validateUserResource: $projectId, userId: $userId, action: $action, resource: $resourceCode") + return validateUserPermission( + projectId = projectId, + resourceCode = resourceCode, + action = action, + userId = userId + ) + } + + override fun authProjectUser(projectId: String, userId: String): Boolean { + val result = client.getDevopsService(ServiceProjectAuthResource::class.java, projectId) + .isProjectUser( + userId = userId, + token = getBackendAccessToken(), + projectCode = projectId + ) + if (result.isNotOk()) { + throw TurboException(TURBO_THIRDPARTY_SYSTEM_FAIL, "getDevopsService failed: ${result.message}") + } + + return result.data ?: false + } + + + /** + * 校验用户是否有具体资源的操作权限 + * @param resourceCode 此处resourceCode实际为resourceType + */ + private fun validateUserResourcePermission( + userId: String, + token: String, + action: String, + projectCode: String, + resourceCode: String + ): Boolean { + val result = client.getDevopsService(ServicePermissionAuthResource::class.java, projectCode) + .validateUserResourcePermission(userId, token, null, action, projectCode, resourceCode) + if (result.isNotOk()) { + throw TurboException(TURBO_THIRDPARTY_SYSTEM_FAIL, "getDevopsService failed: ${result.message}") + } + return result.data ?: false + } + + /** + * 校验用户是否是项目管理员 + */ + private fun checkProjectManager( + projectCode: String, + userId: String + ): Boolean { + val response = client.getDevopsService(ServiceProjectAuthResource::class.java, projectCode).checkProjectManager( + token = getBackendAccessToken(), + userId = userId, + projectCode = projectCode + ) + if (response.isNotOk()) { + throw TurboException(TURBO_THIRDPARTY_SYSTEM_FAIL, "getDevopsService failed!") + } + return response.data ?: false + } + + /** + * 校验用户是否有具体资源实例的操作权限 + */ + private fun validateUserPermission( + projectId: String, + resourceCode: String, + action: String, + userId: String + ): Boolean { + val result = client.getDevopsService(ServicePermissionAuthResource::class.java) + .validateUserResourcePermissionByRelation( + userId = userId, + token = getBackendAccessToken(), + action = action, + projectCode = projectId, + resourceCode = resourceCode, + resourceType = rbacAuthProperties.rbacResourceType!! + ) + + if (result.isNotOk()) { + throw TurboException(TURBO_THIRDPARTY_SYSTEM_FAIL, "getDevopsService failed: ${result.message}") + } + + return result.data ?: false + } +} diff --git a/src/backend/turbo/common-turbo/common-turbo-auth/src/main/kotlin/com/tencent/devops/common/auth/api/RBACAuthProperties.kt b/src/backend/turbo/common-turbo/common-turbo-auth/src/main/kotlin/com/tencent/devops/common/auth/api/RBACAuthProperties.kt new file mode 100644 index 000000000..017c6f4d5 --- /dev/null +++ b/src/backend/turbo/common-turbo/common-turbo-auth/src/main/kotlin/com/tencent/devops/common/auth/api/RBACAuthProperties.kt @@ -0,0 +1,19 @@ +package com.tencent.devops.common.auth.api + +import org.springframework.beans.factory.annotation.Value +import org.springframework.stereotype.Component + +@Component +class RBACAuthProperties { + /** + * RBAC权限系统资源类型 + */ + @Value("\${auth.rbac.resourceType:turbo_plan}") + val rbacResourceType: String? = null + + /** + * 接口access token + */ + @Value("\${auth.rbac.token:#{null}}") + val token: String? = null +} diff --git a/src/backend/turbo/common-turbo/common-turbo-auth/src/main/kotlin/com/tencent/devops/common/auth/api/RBACAuthRegisterApi.kt b/src/backend/turbo/common-turbo/common-turbo-auth/src/main/kotlin/com/tencent/devops/common/auth/api/RBACAuthRegisterApi.kt new file mode 100644 index 000000000..bcff8b34e --- /dev/null +++ b/src/backend/turbo/common-turbo/common-turbo-auth/src/main/kotlin/com/tencent/devops/common/auth/api/RBACAuthRegisterApi.kt @@ -0,0 +1,107 @@ +package com.tencent.devops.common.auth.api + +import com.tencent.devops.auth.api.service.ServicePermissionAuthResource +import com.tencent.devops.common.api.exception.RemoteServiceException +import com.tencent.devops.common.api.exception.TurboException +import com.tencent.devops.common.api.exception.code.PERMISSION_DENIED +import com.tencent.devops.common.api.pojo.Result +import com.tencent.devops.common.client.Client +import org.slf4j.LoggerFactory +import org.springframework.beans.factory.annotation.Autowired + +class RBACAuthRegisterApi @Autowired constructor( + private val client: Client, + private val rbacAuthProperties: RBACAuthProperties +) : AuthRegisterApi { + + companion object { + private val logger = LoggerFactory.getLogger(this::class.java) + } + + /** + * 查询非用户态Access Token + */ + private fun getBackendAccessToken(): String { + return rbacAuthProperties.token ?: "" + } + + /** + * 注册加速方案 + */ + override fun registerTurboPlan( + user: String, + turboPlanId: String, + turboPlanName: String, + projectId: String + ): Boolean { + val result = registerResource( + projectId = projectId, + resourceCode = turboPlanId, + resourceName = turboPlanName, + creator = user + ) + if (result.isNotOk() || result.data == null || result.data == false) { + logger.error( + "register resource failed! taskId: $turboPlanId, return code:${result.status}," + + " err message: ${result.message}" + ) + throw TurboException(PERMISSION_DENIED, user) + } + return true + } + + /** + * 删除加速方案 + */ + override fun deleteTurboPlan( + turboPlanId: String, + projectId: String + ): Boolean { + val result = deleteResource( + projectId = projectId, + resourceCode = turboPlanId + ) + if (result.isNotOk() || result.data == null || result.data == false) { + logger.error( + "delete resource failed! taskId: $turboPlanId, return code:${result.status}," + + " err message: ${result.message}" + ) + throw RemoteServiceException(errorMessage = "delete resource failed!") + } + return true + } + + /** + * 调用api注册资源 + */ + private fun registerResource( + projectId: String, + resourceCode: String, + resourceName: String, + creator: String + ): Result { + return client.getDevopsService(ServicePermissionAuthResource::class.java, projectId).resourceCreateRelation( + userId = creator, + token = getBackendAccessToken(), + projectCode = projectId, + resourceType = rbacAuthProperties.rbacResourceType!!, + resourceCode = resourceCode, + resourceName = resourceName + ) + } + + /** + * 调用api删除资源 + */ + private fun deleteResource( + projectId: String, + resourceCode: String + ): Result { + return client.getDevopsService(ServicePermissionAuthResource::class.java, projectId).resourceDeleteRelation( + token = getBackendAccessToken(), + projectCode = projectId, + resourceType = rbacAuthProperties.rbacResourceType!!, + resourceCode = resourceCode + ) + } +} diff --git a/src/backend/turbo/common-turbo/common-turbo-auth/src/main/resources/META-INF/spring.factories b/src/backend/turbo/common-turbo/common-turbo-auth/src/main/resources/META-INF/spring.factories new file mode 100644 index 000000000..761c82c13 --- /dev/null +++ b/src/backend/turbo/common-turbo/common-turbo-auth/src/main/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ +com.tencent.devops.common.auth.RBACAuthAutoConfiguration diff --git a/src/backend/turbo/common-turbo/common-turbo-util/src/main/kotlin/com/tencent/devops/common/util/constants/Header.kt b/src/backend/turbo/common-turbo/common-turbo-util/src/main/kotlin/com/tencent/devops/common/util/constants/Header.kt index fbb500bd8..ca2b0f43f 100644 --- a/src/backend/turbo/common-turbo/common-turbo-util/src/main/kotlin/com/tencent/devops/common/util/constants/Header.kt +++ b/src/backend/turbo/common-turbo/common-turbo-util/src/main/kotlin/com/tencent/devops/common/util/constants/Header.kt @@ -3,7 +3,9 @@ package com.tencent.devops.common.util.constants const val AUTH_HEADER_DEVOPS_USER_ID = "X-DEVOPS-UID" const val AUTH_HEADER_DEVOPS_PROJECT_ID: String = "X-DEVOPS-PROJECT-ID" const val AUTH_HEADER_DEVOPS_ACCESS_TOKEN: String = "X-DEVOPS-ACCESS-TOKEN" +const val AUTH_HEADER_DEVOPS_BK_TOKEN: String = "X-DEVOPS-BK-TOKEN" const val AUTH_HEADER_DEVOPS_BK_TICKET: String = "X-DEVOPS-BK-TICKET" const val AUTH_HEADER_DEVOPS_TASK_ID: String = "X-DEVOPS-TASK-ID" const val AUTH_HEADER_DEVOPS_APP_CODE: String = "X-DEVOPS-APP-CODE" +const val AUTH_HEADER_DEVOPS_PLAN_ID: String = "X-DEVOPS-PLAN-ID" const val AUTH_HEADER_DEVOPS_USER_ID_DEFAULT_VALUE: String = "admin" diff --git a/src/backend/turbo/common-turbo/common-turbo-util/src/main/kotlin/com/tencent/devops/common/util/enums/ResourceActionType.kt b/src/backend/turbo/common-turbo/common-turbo-util/src/main/kotlin/com/tencent/devops/common/util/enums/ResourceActionType.kt new file mode 100644 index 000000000..854c716dd --- /dev/null +++ b/src/backend/turbo/common-turbo/common-turbo-util/src/main/kotlin/com/tencent/devops/common/util/enums/ResourceActionType.kt @@ -0,0 +1,15 @@ +package com.tencent.devops.common.util.enums + +enum class ResourceActionType( + val actionId: String, + val alias: String +) { + CREATE("turbo_plan_create", "新增加速方案"), + LIST("turbo_plan_list", "加速方案列表"), + VIEW("turbo_plan_view", "查看加速方案"), + EDIT("turbo_plan_edit", "编辑加速方案"), + DELETE("turbo_plan_delete", "删除加速方案"), + OVERVIEW("turbo_plan_overview", "查看编译加速总览"), + LIST_TASK("turbo_plan_list-task", "编译加速执行历史列表"), + VIEW_TASK("turbo_plan_view-task", "查看编译加速执行详情") +} diff --git a/src/backend/turbo/common-turbo/common-turbo-util/src/main/kotlin/com/tencent/devops/common/util/enums/ResourceType.kt b/src/backend/turbo/common-turbo/common-turbo-util/src/main/kotlin/com/tencent/devops/common/util/enums/ResourceType.kt new file mode 100644 index 000000000..ebc9bf1f5 --- /dev/null +++ b/src/backend/turbo/common-turbo/common-turbo-util/src/main/kotlin/com/tencent/devops/common/util/enums/ResourceType.kt @@ -0,0 +1,11 @@ +package com.tencent.devops.common.util.enums + +/** + * 编译加速RBAC权限粒度(资源类型) + */ +enum class ResourceType( + val id: String +) { + TURBO_PLAN("turbo_plan"), + PROJECT("project") +} diff --git a/src/backend/turbo/common-turbo/common-turbo-web/build.gradle.kts b/src/backend/turbo/common-turbo/common-turbo-web/build.gradle.kts index 3fe91a4ac..5e909e763 100644 --- a/src/backend/turbo/common-turbo/common-turbo-web/build.gradle.kts +++ b/src/backend/turbo/common-turbo/common-turbo-web/build.gradle.kts @@ -1,6 +1,7 @@ dependencies { api(project(":common-turbo:common-turbo-api")) api(project(":common-turbo:common-turbo-util")) + api(project(":common-turbo:common-turbo-auth")) api("com.tencent.devops:devops-boot-starter-web") api("com.github.ulisesbocchio:jasypt-spring-boot-starter") api("org.springframework.boot:spring-boot-starter-amqp") diff --git a/src/backend/turbo/common-turbo/common-turbo-web/src/main/kotlin/com/tencent/devops/common/web/config/WebAutoConfiguration.kt b/src/backend/turbo/common-turbo/common-turbo-web/src/main/kotlin/com/tencent/devops/common/web/config/WebAutoConfiguration.kt index 6f39f5c5d..76125e060 100644 --- a/src/backend/turbo/common-turbo/common-turbo-web/src/main/kotlin/com/tencent/devops/common/web/config/WebAutoConfiguration.kt +++ b/src/backend/turbo/common-turbo/common-turbo-web/src/main/kotlin/com/tencent/devops/common/web/config/WebAutoConfiguration.kt @@ -3,13 +3,16 @@ package com.tencent.devops.common.web.config import com.fasterxml.jackson.databind.module.SimpleModule import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer +import com.tencent.devops.common.web.interceptor.AuthInterceptor import com.tencent.devops.common.web.jasypt.DefaultEncryptor +import org.springframework.beans.factory.annotation.Autowired import org.springframework.beans.factory.annotation.Value import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import org.springframework.context.annotation.Primary import org.springframework.http.converter.HttpMessageConverter import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter +import org.springframework.web.servlet.config.annotation.InterceptorRegistry import org.springframework.web.servlet.config.annotation.WebMvcConfigurer import java.time.LocalDateTime import java.time.format.DateTimeFormatter @@ -22,6 +25,9 @@ class WebAutoConfiguration : WebMvcConfigurer { @Primary fun stringEncryptor(@Value("\${enc.key:rAFOey00bcuMNMrt}") key: String) = DefaultEncryptor(key) + @Bean + fun authInterceptor() = AuthInterceptor() + override fun configureMessageConverters(converters: MutableList>) { converters.forEach { if (it is MappingJackson2HttpMessageConverter) { @@ -33,4 +39,9 @@ class WebAutoConfiguration : WebMvcConfigurer { } super.configureMessageConverters(converters) } + + override fun addInterceptors(registry: InterceptorRegistry) { + registry.addInterceptor(authInterceptor()) + .addPathPatterns("/**") + } } diff --git a/src/backend/turbo/common-turbo/common-turbo-web/src/main/kotlin/com/tencent/devops/common/web/interceptor/AuthInterceptor.kt b/src/backend/turbo/common-turbo/common-turbo-web/src/main/kotlin/com/tencent/devops/common/web/interceptor/AuthInterceptor.kt new file mode 100644 index 000000000..1fd4ba0ff --- /dev/null +++ b/src/backend/turbo/common-turbo/common-turbo-web/src/main/kotlin/com/tencent/devops/common/web/interceptor/AuthInterceptor.kt @@ -0,0 +1,70 @@ +package com.tencent.devops.common.web.interceptor + +import com.tencent.devops.common.api.annotation.RequiresAuth +import com.tencent.devops.common.api.exception.TurboException +import com.tencent.devops.common.api.exception.code.TURBO_PARAM_INVALID +import com.tencent.devops.common.auth.api.AuthPermissionApi +import com.tencent.devops.common.util.constants.AUTH_HEADER_DEVOPS_PLAN_ID +import com.tencent.devops.common.util.constants.AUTH_HEADER_DEVOPS_PROJECT_ID +import com.tencent.devops.common.util.constants.AUTH_HEADER_DEVOPS_USER_ID +import com.tencent.devops.common.util.enums.ResourceType +import com.tencent.devops.web.util.SpringContextHolder +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import org.springframework.web.method.HandlerMethod +import org.springframework.web.servlet.HandlerInterceptor +import javax.servlet.http.HttpServletRequest +import javax.servlet.http.HttpServletResponse + +/** + * 鉴权拦截器 + */ +class AuthInterceptor : HandlerInterceptor{ + + companion object { + val logger: Logger = LoggerFactory.getLogger(this::class.java) + } + + override fun preHandle(request: HttpServletRequest, response: HttpServletResponse, handler: Any): Boolean { + logger.info("handle uri: ${request.requestURI}") + val method = (handler as HandlerMethod).method + + // 如果是没加`@RequiresAuth`注解的请求则放行 + val requiresAuth = method.getAnnotation(RequiresAuth::class.java) ?: return true + val authPermissionApi = SpringContextHolder.getBean(AuthPermissionApi::class.java) + + val resourceType: ResourceType = requiresAuth.resourceType + val actionId: String = requiresAuth.permission.actionId + + val userId = request.getHeader(AUTH_HEADER_DEVOPS_USER_ID) + val projectId = request.getHeader(AUTH_HEADER_DEVOPS_PROJECT_ID) + logger.info("auth resource: ${resourceType.id}, user: $userId, action: $actionId") + + val result = when (resourceType) { + ResourceType.PROJECT -> { + authPermissionApi.validateUserProjectPermission(projectId, userId, actionId) + } + ResourceType.TURBO_PLAN -> { + val turboPlanId = + getPlanIdFromRequest(request) ?: return authPermissionApi.authProjectUser(projectId, userId) + authPermissionApi.validateUserResourcePermission(projectId, turboPlanId, actionId, userId) + } + else -> { + throw TurboException(TURBO_PARAM_INVALID, "Unrecognized resource type: ${resourceType.id}") + } + } + + if (!result) { + response.status = HttpServletResponse.SC_FORBIDDEN + } + + return false + } + + /** + * + */ + private fun getPlanIdFromRequest(request: HttpServletRequest): String? { + return request.getAttribute("planId") as? String ?: request.getHeader(AUTH_HEADER_DEVOPS_PLAN_ID) + } +} diff --git a/src/backend/turbo/settings.gradle.kts b/src/backend/turbo/settings.gradle.kts index 13a4a7ec7..05d97618f 100644 --- a/src/backend/turbo/settings.gradle.kts +++ b/src/backend/turbo/settings.gradle.kts @@ -3,6 +3,7 @@ rootProject.name = "turbo" include("api-turbo") include("common-turbo") include("common-turbo:common-turbo-api") +include("common-turbo:common-turbo-auth") include("common-turbo:common-turbo-client") include("common-turbo:common-turbo-client:common-client-base") include("common-turbo:common-turbo-client:common-client-consul") From 3a374ecb3a8ef2935e31add90104a7741daec50b Mon Sep 17 00:00:00 2001 From: RJ Date: Tue, 19 Nov 2024 15:15:42 +0800 Subject: [PATCH 2/3] =?UTF-8?q?feat:=20Turbo=E7=BC=96=E8=AF=91=E5=8A=A0?= =?UTF-8?q?=E9=80=9F=E5=AF=B9=E6=8E=A5=E6=9D=83=E9=99=90=E4=B8=AD=E5=BF=83?= =?UTF-8?q?RBAC=20#316?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/turbo/api-turbo/build.gradle.kts | 2 +- src/backend/turbo/biz-turbo/build.gradle.kts | 8 +------- src/backend/turbo/build.gradle.kts | 5 +++++ src/backend/turbo/buildSrc/src/main/kotlin/Versions.kt | 1 + .../turbo/common-turbo/common-turbo-auth/build.gradle.kts | 7 +++++-- 5 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/backend/turbo/api-turbo/build.gradle.kts b/src/backend/turbo/api-turbo/build.gradle.kts index bf21cc229..48b8f48ee 100644 --- a/src/backend/turbo/api-turbo/build.gradle.kts +++ b/src/backend/turbo/api-turbo/build.gradle.kts @@ -3,7 +3,7 @@ dependencies { api(project(":common-turbo:common-turbo-util")) compileOnly("org.springframework.boot:spring-boot-starter-web") compileOnly("org.springframework.cloud:spring-cloud-openfeign-core") - api("com.tencent.bk.devops.ci.common:common-api:${Versions.ciVersion}") { + api("com.tencent.bk.devops.ci.common:common-api") { isTransitive = false } } diff --git a/src/backend/turbo/biz-turbo/build.gradle.kts b/src/backend/turbo/biz-turbo/build.gradle.kts index 0af96659e..aa26b5628 100644 --- a/src/backend/turbo/biz-turbo/build.gradle.kts +++ b/src/backend/turbo/biz-turbo/build.gradle.kts @@ -11,13 +11,7 @@ dependencies { api("com.google.guava:guava") api("io.jsonwebtoken:jjwt") api("org.jetbrains.kotlinx:kotlinx-coroutines-core") - api("com.tencent.bk.devops.ci.project:api-project:${Versions.ciVersion}"){ - isTransitive = false - } - api("com.tencent.bk.devops.ci.common:common-api:${Versions.ciVersion}"){ - isTransitive = false - } - api("com.tencent.bk.devops.ci.auth:api-auth:${Versions.ciVersion}"){ + api("com.tencent.bk.devops.ci.project:api-project") { isTransitive = false } } diff --git a/src/backend/turbo/build.gradle.kts b/src/backend/turbo/build.gradle.kts index 4df630b86..88c39d448 100644 --- a/src/backend/turbo/build.gradle.kts +++ b/src/backend/turbo/build.gradle.kts @@ -48,6 +48,11 @@ allprojects { dependency("com.google.guava:guava:${Versions.guavaVersion}") dependency("io.jsonwebtoken:jjwt:${Versions.jjwtVersion}") dependency("commons-io:commons-io:${Versions.commonIo}") + dependency("com.tencent.bk.sdk:iam-java-sdk:${Versions.iamSdkVersion}") + dependency("com.tencent.bk.devops.ci.common:common-api:${Versions.ciVersion}") + dependency("com.tencent.bk.devops.ci.auth:api-auth:${Versions.ciVersion}") + dependency("com.tencent.bk.devops.ci.common:common-auth-api:${Versions.ciVersion}") + dependency("com.tencent.bk.devops.ci.project:api-project:${Versions.ciVersion}") dependencySet("io.swagger:${Versions.swaggerVersion}") { entry("swagger-annotations") entry("swagger-jersey2-jaxrs") diff --git a/src/backend/turbo/buildSrc/src/main/kotlin/Versions.kt b/src/backend/turbo/buildSrc/src/main/kotlin/Versions.kt index 1b4964d91..1da93ddde 100644 --- a/src/backend/turbo/buildSrc/src/main/kotlin/Versions.kt +++ b/src/backend/turbo/buildSrc/src/main/kotlin/Versions.kt @@ -11,4 +11,5 @@ object Versions { const val okHttpVersion = "4.9.0" const val Kotlin = "1.6.0" const val coroutinesCore = "1.3.1" + const val iamSdkVersion = "1.0.21-SNAPSHOT" } diff --git a/src/backend/turbo/common-turbo/common-turbo-auth/build.gradle.kts b/src/backend/turbo/common-turbo/common-turbo-auth/build.gradle.kts index 050ef34df..26755f5f6 100644 --- a/src/backend/turbo/common-turbo/common-turbo-auth/build.gradle.kts +++ b/src/backend/turbo/common-turbo/common-turbo-auth/build.gradle.kts @@ -1,9 +1,12 @@ dependencies { api(project(":common-turbo:common-turbo-client")) - api("com.tencent.bk.devops.ci.auth:api-auth:${Versions.ciVersion}") { + api("com.tencent.bk.sdk:iam-java-sdk") { isTransitive = false } - api("com.tencent.bk.devops.ci.common:common-auth-api:${Versions.ciVersion}"){ + api("com.tencent.bk.devops.ci.auth:api-auth") { + isTransitive = false + } + api("com.tencent.bk.devops.ci.common:common-auth-api"){ isTransitive = false } } From 90135e6f06b25e3f9202369344f7c51ffa46e4f5 Mon Sep 17 00:00:00 2001 From: RJ Date: Wed, 20 Nov 2024 16:56:28 +0800 Subject: [PATCH 3/3] =?UTF-8?q?feat:=20Turbo=E7=BC=96=E8=AF=91=E5=8A=A0?= =?UTF-8?q?=E9=80=9F=E5=AF=B9=E6=8E=A5=E6=9D=83=E9=99=90=E4=B8=AD=E5=BF=83?= =?UTF-8?q?RBAC=20#316?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/turbo/api-turbo/build.gradle.kts | 1 + .../turbo/api/IServiceTurboPlanController.kt | 9 ++ .../controller/ServiceTurboPlanController.kt | 20 ++++- .../turbo/dao/mongotemplate/TurboPlanDao.kt | 7 +- .../devops/turbo/service/TurboPlanService.kt | 90 +++++++++++++++++++ .../common-turbo-auth/build.gradle.kts | 3 - .../common/util/constants/TurboConstants.kt | 7 ++ 7 files changed, 130 insertions(+), 7 deletions(-) diff --git a/src/backend/turbo/api-turbo/build.gradle.kts b/src/backend/turbo/api-turbo/build.gradle.kts index 48b8f48ee..ff7d32869 100644 --- a/src/backend/turbo/api-turbo/build.gradle.kts +++ b/src/backend/turbo/api-turbo/build.gradle.kts @@ -6,6 +6,7 @@ dependencies { api("com.tencent.bk.devops.ci.common:common-api") { isTransitive = false } + api("com.tencent.bk.sdk:iam-java-sdk") } plugins { diff --git a/src/backend/turbo/api-turbo/src/main/kotlin/com/tencent/devops/turbo/api/IServiceTurboPlanController.kt b/src/backend/turbo/api-turbo/src/main/kotlin/com/tencent/devops/turbo/api/IServiceTurboPlanController.kt index cea52578b..5a413097d 100644 --- a/src/backend/turbo/api-turbo/src/main/kotlin/com/tencent/devops/turbo/api/IServiceTurboPlanController.kt +++ b/src/backend/turbo/api-turbo/src/main/kotlin/com/tencent/devops/turbo/api/IServiceTurboPlanController.kt @@ -1,5 +1,6 @@ package com.tencent.devops.turbo.api +import com.tencent.bk.sdk.iam.dto.callback.request.CallbackRequestDTO import com.tencent.devops.api.pojo.Response import com.tencent.devops.common.util.constants.AUTH_HEADER_DEVOPS_PROJECT_ID import io.swagger.annotations.Api @@ -9,6 +10,7 @@ import org.springframework.cloud.openfeign.FeignClient import org.springframework.http.MediaType import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.RequestHeader import org.springframework.web.bind.annotation.RequestMapping @@ -33,4 +35,11 @@ interface IServiceTurboPlanController { @PathVariable("pipelineElementId") pipelineElementId: String ): Response + + @ApiOperation("特定资源列表,注册存量加速方案") + @PostMapping("/instances/list") + fun resourceList( + @ApiParam(value = "回调信息", required = true) + callBackInfo: CallbackRequestDTO + ): Response } diff --git a/src/backend/turbo/biz-turbo/src/main/kotlin/com/tencent/devops/turbo/controller/ServiceTurboPlanController.kt b/src/backend/turbo/biz-turbo/src/main/kotlin/com/tencent/devops/turbo/controller/ServiceTurboPlanController.kt index 4c574b196..41e95be00 100644 --- a/src/backend/turbo/biz-turbo/src/main/kotlin/com/tencent/devops/turbo/controller/ServiceTurboPlanController.kt +++ b/src/backend/turbo/biz-turbo/src/main/kotlin/com/tencent/devops/turbo/controller/ServiceTurboPlanController.kt @@ -1,18 +1,32 @@ package com.tencent.devops.turbo.controller +import com.tencent.bk.sdk.iam.dto.callback.request.CallbackRequestDTO import com.tencent.devops.api.pojo.Response import com.tencent.devops.turbo.api.IServiceTurboPlanController import com.tencent.devops.turbo.service.TurboPlanService import org.springframework.beans.factory.annotation.Autowired import org.springframework.web.bind.annotation.RestController -@Suppress("MaxLineLength") @RestController class ServiceTurboPlanController @Autowired constructor( private val turboPlanService: TurboPlanService ) : IServiceTurboPlanController { - override fun findTurboPlanIdByProjectIdAndPipelineInfo(projectId: String, pipelineId: String, pipelineElementId: String): Response { - return Response.success(turboPlanService.findMigratedTurboPlanByPipelineInfo(projectId, pipelineId, pipelineElementId)?.taskId) + override fun findTurboPlanIdByProjectIdAndPipelineInfo( + projectId: String, + pipelineId: String, + pipelineElementId: String + ): Response { + return Response.success( + turboPlanService.findMigratedTurboPlanByPipelineInfo( + projectId, + pipelineId, + pipelineElementId + )?.taskId + ) + } + + override fun resourceList(callBackInfo: CallbackRequestDTO): Response { + return Response.success(turboPlanService.getInstanceByResource(callBackInfo)) } } diff --git a/src/backend/turbo/biz-turbo/src/main/kotlin/com/tencent/devops/turbo/dao/mongotemplate/TurboPlanDao.kt b/src/backend/turbo/biz-turbo/src/main/kotlin/com/tencent/devops/turbo/dao/mongotemplate/TurboPlanDao.kt index 552033035..d287bd7cc 100644 --- a/src/backend/turbo/biz-turbo/src/main/kotlin/com/tencent/devops/turbo/dao/mongotemplate/TurboPlanDao.kt +++ b/src/backend/turbo/biz-turbo/src/main/kotlin/com/tencent/devops/turbo/dao/mongotemplate/TurboPlanDao.kt @@ -259,7 +259,12 @@ class TurboPlanDao @Autowired constructor( /** * 根据项目id和创建时间获取加速方案列表 */ - fun getTurboPlanByProjectIdAndCreatedDate(projectId: String, startTime: LocalDate?, endTime: LocalDate?, pageable: Pageable): Page { + fun getTurboPlanByProjectIdAndCreatedDate( + projectId: String, + startTime: LocalDate?, + endTime: LocalDate?, + pageable: Pageable + ): Page { val query = turboPlanParameter(projectId, startTime, endTime) //先算总数 val totalCount = mongoTemplate.count(query, TTurboPlanEntity::class.java) diff --git a/src/backend/turbo/biz-turbo/src/main/kotlin/com/tencent/devops/turbo/service/TurboPlanService.kt b/src/backend/turbo/biz-turbo/src/main/kotlin/com/tencent/devops/turbo/service/TurboPlanService.kt index b5adc3d4a..94784f8fc 100644 --- a/src/backend/turbo/biz-turbo/src/main/kotlin/com/tencent/devops/turbo/service/TurboPlanService.kt +++ b/src/backend/turbo/biz-turbo/src/main/kotlin/com/tencent/devops/turbo/service/TurboPlanService.kt @@ -1,5 +1,11 @@ package com.tencent.devops.turbo.service +import com.tencent.bk.sdk.iam.constants.CallbackMethodEnum +import com.tencent.bk.sdk.iam.dto.callback.request.CallbackRequestDTO +import com.tencent.bk.sdk.iam.dto.callback.response.CallbackBaseResponseDTO +import com.tencent.bk.sdk.iam.dto.callback.response.FetchInstanceInfoResponseDTO +import com.tencent.bk.sdk.iam.dto.callback.response.InstanceInfoDTO +import com.tencent.bk.sdk.iam.dto.callback.response.ListInstanceResponseDTO import com.tencent.devops.common.api.exception.TurboException import com.tencent.devops.common.api.exception.code.TURBO_NO_DATA_FOUND import com.tencent.devops.common.api.exception.code.TURBO_PARAM_INVALID @@ -7,11 +13,16 @@ import com.tencent.devops.common.api.exception.code.TURBO_THIRDPARTY_SYSTEM_FAIL import com.tencent.devops.common.api.pojo.Page import com.tencent.devops.common.api.util.OkhttpUtil import com.tencent.devops.common.auth.api.AuthRegisterApi +import com.tencent.devops.common.auth.callback.FetchInstanceInfo +import com.tencent.devops.common.auth.callback.ListInstanceInfo import com.tencent.devops.common.client.Client import com.tencent.devops.common.db.PageUtils import com.tencent.devops.common.service.prometheus.BkTimed import com.tencent.devops.common.util.JsonUtil import com.tencent.devops.common.util.MathUtil +import com.tencent.devops.common.util.constants.COMMON_NUM_0L +import com.tencent.devops.common.util.constants.COMMON_NUM_10L +import com.tencent.devops.common.util.constants.COMMON_NUM_1L import com.tencent.devops.common.util.constants.SYSTEM_ADMIN import com.tencent.devops.project.api.service.ServiceProjectResource import com.tencent.devops.turbo.dao.mongotemplate.TurboPlanDao @@ -33,6 +44,7 @@ import com.tencent.devops.turbo.vo.TurboPlanStatusBatchUpdateReqVO import org.slf4j.LoggerFactory import org.springframework.beans.BeanUtils import org.springframework.beans.factory.annotation.Autowired +import org.springframework.data.domain.Sort import org.springframework.data.repository.findByIdOrNull import org.springframework.stereotype.Service import java.time.LocalDate @@ -750,4 +762,82 @@ class TurboPlanService @Autowired constructor( "all project id turbo plan status updated successfully!" } } + + /** + * iam(蓝盾)回调实现方法 + */ + fun getInstanceByResource(callBackInfo: CallbackRequestDTO): String { + logger.info("CallbackRequestDTO: {}", callBackInfo) + val result: CallbackBaseResponseDTO = when (callBackInfo.method) { + CallbackMethodEnum.LIST_INSTANCE -> handleListInstance(callBackInfo) + CallbackMethodEnum.FETCH_INSTANCE_INFO -> handleFetchInstanceInfo(callBackInfo) + else -> CallbackBaseResponseDTO() + } + return JsonUtil.toJson(result) + } + + /** + * 按项目id分页获取方案清单 + */ + private fun handleListInstance(callBackInfo: CallbackRequestDTO): CallbackBaseResponseDTO { + return callBackInfo.filter.parent?.id?.let { parentId -> + listInstance(parentId, callBackInfo.page.offset, callBackInfo.page.limit) + } ?: ListInstanceInfo().buildListInstanceFailResult() + } + + /** + * 按方案id批量获取方案清单 + */ + private fun handleFetchInstanceInfo(callBackInfo: CallbackRequestDTO): CallbackBaseResponseDTO { + val idList = callBackInfo.filter.idList + return if (idList.isNullOrEmpty()) { + FetchInstanceInfo().buildFetchInstanceFailResult() + } else { + val turboPlanIdSet = idList.mapNotNull { it.toString() }.toSet() + fetchInstance(turboPlanIdSet) + } + } + + /** + * 按项目id + */ + private fun listInstance(projectId: String, offset: Long?, limit: Long?): ListInstanceResponseDTO { + val safeOffset = (offset ?: COMMON_NUM_0L).coerceAtLeast(COMMON_NUM_0L) + val safeLimit = (limit ?: COMMON_NUM_10L).coerceAtLeast(COMMON_NUM_1L) + val pageNum = safeOffset / safeLimit + COMMON_NUM_1L + val pageable = + PageUtils.convertPageSizeToPageable(pageNum.toInt(), safeLimit.toInt(), "_id", Sort.Direction.ASC.name) + + val turboPlanEntityList = turboPlanDao.getAllTurboPlanList(null, null, projectId, pageable).records + val instanceInfoList = turboPlanEntityList.map { it.toDTO() } + val result = ListInstanceInfo() + return if (instanceInfoList.isEmpty()) result.buildListInstanceFailResult() else result.buildListInstanceResult( + instanceInfoList, + instanceInfoList.size.toLong() + ) + } + + /** + * 按方案id + */ + private fun fetchInstance(turboPlanIdSet: Set): FetchInstanceInfoResponseDTO { + val turboPlanEntityList = turboPlanRepository.findByIdIn(turboPlanIdSet.toList()) + val instanceInfos = turboPlanEntityList.map { it.toDTO() } + val result = FetchInstanceInfo() + return if (instanceInfos.isEmpty()) result.buildFetchInstanceFailResult() else result.buildFetchInstanceResult( + instanceInfos + ) + } + + /** + * entity转DTO + */ + private fun TTurboPlanEntity.toDTO(): InstanceInfoDTO { + return InstanceInfoDTO().apply { + id = this@toDTO.id + displayName = this@toDTO.planName + iamApprover = + listOf(if (this@toDTO.createdBy.isNullOrBlank()) this@toDTO.createdBy else this@toDTO.updatedBy) + } + } } diff --git a/src/backend/turbo/common-turbo/common-turbo-auth/build.gradle.kts b/src/backend/turbo/common-turbo/common-turbo-auth/build.gradle.kts index 26755f5f6..a8114ede1 100644 --- a/src/backend/turbo/common-turbo/common-turbo-auth/build.gradle.kts +++ b/src/backend/turbo/common-turbo/common-turbo-auth/build.gradle.kts @@ -1,8 +1,5 @@ dependencies { api(project(":common-turbo:common-turbo-client")) - api("com.tencent.bk.sdk:iam-java-sdk") { - isTransitive = false - } api("com.tencent.bk.devops.ci.auth:api-auth") { isTransitive = false } diff --git a/src/backend/turbo/common-turbo/common-turbo-util/src/main/kotlin/com/tencent/devops/common/util/constants/TurboConstants.kt b/src/backend/turbo/common-turbo/common-turbo-util/src/main/kotlin/com/tencent/devops/common/util/constants/TurboConstants.kt index af6726a40..3ec2fdade 100644 --- a/src/backend/turbo/common-turbo/common-turbo-util/src/main/kotlin/com/tencent/devops/common/util/constants/TurboConstants.kt +++ b/src/backend/turbo/common-turbo/common-turbo-util/src/main/kotlin/com/tencent/devops/common/util/constants/TurboConstants.kt @@ -25,3 +25,10 @@ const val BASE_EXCLUDED_PROJECT_ID_LIST = "EXCLUDED_PROJECT_ID_LIST" * 内部测试的方案,服务计费时需要过滤掉 */ const val BASE_EXCLUDED_COMMON_PLAN_ID = "EXCLUDED_COMMON_PLAN_ID_LIST" + +/** + * 常用整数 + */ +const val COMMON_NUM_0L = 0L +const val COMMON_NUM_1L = 1L +const val COMMON_NUM_10L = 10L