Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: 支持多租户项目初始化 #2957 #2992

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions scripts/build-images.sh
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ if [[ $ALL -eq 1 || $INIT -eq 1 ]] ; then
rm -rf $tmp_dir/*
cp -rf $IMAGE_DIR/init/init-mongodb.sh $tmp_dir/
cp -rf $ROOT_DIR/support-files/sql/init-data.js $tmp_dir/
cp -rf $ROOT_DIR/support-files/sql/init-data-tenant.js $tmp_dir/
cp -rf $ROOT_DIR/support-files/sql/init-data-ext.js $tmp_dir/
docker build -f $IMAGE_DIR/init/init.Dockerfile -t $REGISTRY/$NAMESPACE/bkrepo-init:$VERSION $tmp_dir --no-cache --network=host
if [[ $PUSH -eq 1 ]] ; then
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ class UserController @Autowired constructor(
fun userInfo(
@RequestHeader("x-bkrepo-uid") bkUserId: String?,
@RequestHeader("x-bkrepo-display-name") displayName: String?,
@RequestHeader("x-bkrepo-tenant-id") tenantId: String?,
@RequestHeader("x-bk-tenant-id") tenantId: String?,
): Response<Map<String, Any>> {
val name = if (displayName == null) "" else String(Base64.getDecoder().decode(displayName))
val result = mapOf(
Expand Down
1 change: 1 addition & 0 deletions src/backend/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ if [[ $ALL -eq 1 || $INIT -eq 1 ]] ; then
rm -rf $tmp_dir/*
cp -rf init/init-mongodb.sh $tmp_dir/
cp -rf $ROOT_DIR/support-files/sql/init-data.js $tmp_dir/
cp -rf $ROOT_DIR/support-files/sql/init-data-tenant.js $tmp_dir/
cp -rf $ROOT_DIR/support-files/sql/init-data-ext.js $tmp_dir/
docker build -f init/init.Dockerfile -t $REGISTRY/bkrepo-init:$VERSION $tmp_dir --no-cache --network=host
if [[ $PUSH -eq 1 ]] ; then
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ const val AUTH_HEADER_UID = "X-BKREPO-UID"
const val OAUTH_AUTH_PREFIX = "Oauth "
const val TEMPORARY_TOKEN_AUTH_PREFIX = "Temporary "



/**
* micro service header user id key
*/
Expand Down Expand Up @@ -175,3 +177,9 @@ const val AUDIT_REQUEST_KEY = "http_request"
const val AUDIT_SHARE_USER_ID = "audit_share_user_id"
const val HTTP_RESPONSE_CODE = "http_response_code"
const val HTTP_METHOD = "http_method"


/**
* 多租户相关
*/
const val TENANT_ID = "X-Bk-Tenant-Id"
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.tencent.bkrepo.common.artifact.properties

import org.springframework.boot.context.properties.ConfigurationProperties

@ConfigurationProperties("multitenant")
data class EnableMultiTenantProperties(
var enabled: Boolean = false
)
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@

package com.tencent.bkrepo.common.artifact.resolve

import com.tencent.bkrepo.common.artifact.properties.EnableMultiTenantProperties
import com.tencent.bkrepo.common.artifact.resolve.file.ArtifactFileCleanInterceptor
import com.tencent.bkrepo.common.artifact.resolve.file.ArtifactFileFactory
import com.tencent.bkrepo.common.artifact.resolve.file.UploadConfigElement
Expand All @@ -41,6 +42,7 @@ import com.tencent.bkrepo.common.artifact.resolve.response.DefaultArtifactResour
import com.tencent.bkrepo.common.storage.config.StorageProperties
import com.tencent.bkrepo.common.ratelimiter.service.RequestLimitCheckService
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Import
Expand All @@ -50,6 +52,7 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer

@Configuration
@Import(ArtifactFileFactory::class)
@EnableConfigurationProperties(EnableMultiTenantProperties::class)
class ArtifactResolverConfiguration {

@Bean
Expand All @@ -59,7 +62,10 @@ class ArtifactResolverConfiguration {
fun resolverMap(resolverList: List<ArtifactInfoResolver>) = ResolverMap(resolverList)

@Bean
fun artifactInfoMethodArgumentResolver(resolverMap: ResolverMap) = ArtifactInfoMethodArgumentResolver(resolverMap)
fun artifactInfoMethodArgumentResolver(
resolverMap: ResolverMap,
enableMultiTenantProperties: EnableMultiTenantProperties
) = ArtifactInfoMethodArgumentResolver(resolverMap, enableMultiTenantProperties)

@Bean
fun artifactFileMethodArgumentResolver() = ArtifactFileMethodArgumentResolver()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,13 @@

package com.tencent.bkrepo.common.artifact.resolve.path

import com.tencent.bkrepo.common.api.constant.TENANT_ID
import com.tencent.bkrepo.common.artifact.api.ArtifactInfo
import com.tencent.bkrepo.common.artifact.constant.ARTIFACT_INFO_KEY
import com.tencent.bkrepo.common.artifact.constant.PROJECT_ID
import com.tencent.bkrepo.common.artifact.constant.REPO_NAME
import com.tencent.bkrepo.common.artifact.path.PathUtils
import com.tencent.bkrepo.common.artifact.properties.EnableMultiTenantProperties
import org.springframework.core.MethodParameter
import org.springframework.util.AntPathMatcher
import org.springframework.web.bind.support.WebDataBinderFactory
Expand All @@ -51,10 +53,13 @@ import kotlin.reflect.KClass
*/
@Suppress("UNCHECKED_CAST")
class ArtifactInfoMethodArgumentResolver(
private val resolverMap: ResolverMap
private val resolverMap: ResolverMap,
private val enableMultiTenant: EnableMultiTenantProperties
) : HandlerMethodArgumentResolver {

private val antPathMatcher = AntPathMatcher()


override fun supportsParameter(parameter: MethodParameter): Boolean {
return ArtifactInfo::class.java.isAssignableFrom(parameter.parameterType)
}
Expand All @@ -65,9 +70,15 @@ class ArtifactInfoMethodArgumentResolver(
nativeWebRequest: NativeWebRequest,
factory: WebDataBinderFactory?
): Any {

val attributes = nativeWebRequest.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, 0) as Map<*, *>
val projectId = attributes[PROJECT_ID].toString()
var projectId = attributes[PROJECT_ID].toString()
val repoName = attributes[REPO_NAME].toString()
val tenantId = nativeWebRequest.getHeader(TENANT_ID)
if (enableMultiTenant.enabled && !tenantId.isNullOrEmpty()) {
projectId = "$projectId:$tenantId"
}


val request = nativeWebRequest.getNativeRequest(HttpServletRequest::class.java)!!
val artifactUri = AntPathMatcher.DEFAULT_PATH_SEPARATOR + antPathMatcher.extractPathWithinPattern(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import com.tencent.bkrepo.common.api.pojo.ClusterArchitecture
import com.tencent.bkrepo.common.api.pojo.ClusterNodeType
import com.tencent.bkrepo.common.artifact.event.base.ArtifactEvent
import com.tencent.bkrepo.common.artifact.properties.ArtifactEventProperties
import com.tencent.bkrepo.common.artifact.properties.EnableMultiTenantProperties
import com.tencent.bkrepo.common.artifact.properties.RouterControllerProperties
import com.tencent.bkrepo.common.metadata.condition.SyncCondition
import com.tencent.bkrepo.common.metadata.config.RepositoryProperties
Expand Down Expand Up @@ -70,6 +71,7 @@ import java.util.function.Consumer
RouterControllerProperties::class,
ArtifactEventProperties::class,
RepositoryProperties::class,
EnableMultiTenantProperties::class
)
class MetadataAutoConfiguration {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,10 @@ data class TProject(
* 项目新建仓库默认使用的存储凭据,为null时表示未配置,将会使用全局默认存储凭据
*/
var credentialsKey: String? = null,

/**
* 多租户相关配置
*/
var projectCode: String? = null,
var tenantId: String? = null
)
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ package com.tencent.bkrepo.common.metadata.service.project.impl

import com.tencent.bkrepo.auth.api.ServiceBkiamV3ResourceClient
import com.tencent.bkrepo.auth.api.ServicePermissionClient
import com.tencent.bkrepo.common.artifact.properties.EnableMultiTenantProperties
import com.tencent.bkrepo.common.metadata.condition.SyncCondition
import com.tencent.bkrepo.common.metadata.config.RepositoryProperties
import com.tencent.bkrepo.common.service.cluster.condition.CommitEdgeCenterCondition
Expand All @@ -47,11 +48,13 @@ class CenterProjectServiceImpl(
serviceBkiamV3ResourceClient: ServiceBkiamV3ResourceClient,
storageCredentialService: StorageCredentialService,
repositoryProperties: RepositoryProperties,
) : ProjectServiceImpl(
enableMultiTenant: EnableMultiTenantProperties
) : ProjectServiceImpl(
projectDao,
servicePermissionClient,
projectMetricsDao,
serviceBkiamV3ResourceClient,
storageCredentialService,
repositoryProperties
repositoryProperties,
enableMultiTenant
)
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ package com.tencent.bkrepo.common.metadata.service.project.impl
import com.tencent.bkrepo.auth.api.ServiceBkiamV3ResourceClient
import com.tencent.bkrepo.auth.api.ServicePermissionClient
import com.tencent.bkrepo.common.artifact.message.ArtifactMessageCode
import com.tencent.bkrepo.common.artifact.properties.EnableMultiTenantProperties
import com.tencent.bkrepo.common.metadata.condition.SyncCondition
import com.tencent.bkrepo.common.metadata.config.RepositoryProperties
import com.tencent.bkrepo.common.metadata.util.ClusterUtils.reportMetadataToCenter
Expand All @@ -56,13 +57,15 @@ class EdgeProjectServiceImpl(
projectMetricsDao: ProjectMetricsDao,
storageCredentialService: StorageCredentialService,
repositoryProperties: RepositoryProperties,
) : ProjectServiceImpl(
enableMultiTenant: EnableMultiTenantProperties
) : ProjectServiceImpl(
projectDao,
servicePermissionClient,
projectMetricsDao,
serviceBkiamV3ResourceClient,
storageCredentialService,
repositoryProperties
repositoryProperties,
enableMultiTenant
) {

private val centerProjectClient: ClusterProjectClient by lazy {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import com.tencent.bkrepo.common.api.exception.ErrorCodeException
import com.tencent.bkrepo.common.api.message.CommonMessageCode
import com.tencent.bkrepo.common.api.pojo.Page
import com.tencent.bkrepo.common.artifact.message.ArtifactMessageCode
import com.tencent.bkrepo.common.artifact.properties.EnableMultiTenantProperties
import com.tencent.bkrepo.common.metadata.condition.SyncCondition
import com.tencent.bkrepo.common.metadata.config.RepositoryProperties
import com.tencent.bkrepo.common.metadata.dao.project.ProjectDao
Expand Down Expand Up @@ -86,6 +87,7 @@ class ProjectServiceImpl(
private val serviceBkiamV3ResourceClient: ServiceBkiamV3ResourceClient,
private val storageCredentialService: StorageCredentialService,
private val repositoryProperties: RepositoryProperties,
private val enableMultiTenant: EnableMultiTenantProperties
) : ProjectService {

@Autowired
Expand Down Expand Up @@ -115,6 +117,10 @@ class ProjectServiceImpl(
}

override fun listPermissionProject(userId: String, option: ProjectListOption?): List<ProjectInfo> {
// 校验租户信息
if (enableMultiTenant.enabled) {
validateTenantId()
}
var names = servicePermissionClient.listPermissionProject(userId).data.orEmpty()
option?.names?.let { names = names.intersect(option.names!!).toList() }
val query = buildListQuery(names, option)
Expand Down Expand Up @@ -158,10 +164,16 @@ class ProjectServiceImpl(
override fun createProject(request: ProjectCreateRequest): ProjectInfo {
val name = request.name
validateParameter(request)

if (checkExist(name)) {
throw ErrorCodeException(ArtifactMessageCode.PROJECT_EXISTED, name)
}
val project = request.buildProject()
// 校验租户信息
if (enableMultiTenant.enabled) {
logger.info("check tenant")
validateTenantId()
}
val project = request.buildProject(ProjectServiceHelper.getTenantId())
return try {
projectDao.insert(project)
resourcePermissionListener.handle(buildCreatedEvent(request))
Expand Down Expand Up @@ -222,6 +234,12 @@ class ProjectServiceImpl(
}
}

private fun validateTenantId() {
if (ProjectServiceHelper.getTenantId().isNullOrEmpty()) {
throw ErrorCodeException(CommonMessageCode.PARAMETER_INVALID, "tenantId")
}
}

private fun checkCredentialsKey(key: String) {
storageCredentialService.findByKey(key) ?: throw ErrorCodeException(CommonMessageCode.RESOURCE_NOT_FOUND, key)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,8 @@ class RProjectServiceImpl(
if (checkExist(name)) {
throw ErrorCodeException(ArtifactMessageCode.PROJECT_EXISTED, name)
}
val project = buildProject()
// TODO ,多租户暂时不涉及
val project = buildProject(null)
return try {
projectDao.insert(project)
resourcePermissionListener.handle(buildCreatedEvent(request))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,14 @@ package com.tencent.bkrepo.common.metadata.util

import com.tencent.bkrepo.common.api.constant.CLOSED_SOURCE_PREFIX
import com.tencent.bkrepo.common.api.constant.CODE_PROJECT_PREFIX
import com.tencent.bkrepo.common.api.constant.TENANT_ID
import com.tencent.bkrepo.common.api.exception.ErrorCodeException
import com.tencent.bkrepo.common.api.message.CommonMessageCode
import com.tencent.bkrepo.common.api.util.EscapeUtils
import com.tencent.bkrepo.common.api.util.Preconditions
import com.tencent.bkrepo.common.metadata.model.TProject
import com.tencent.bkrepo.common.metadata.model.TProjectMetrics
import com.tencent.bkrepo.common.service.util.HttpContextHolder
import com.tencent.bkrepo.repository.pojo.project.ProjectCreateRequest
import com.tencent.bkrepo.repository.pojo.project.ProjectInfo
import com.tencent.bkrepo.repository.pojo.project.ProjectListOption
Expand Down Expand Up @@ -96,17 +98,35 @@ object ProjectServiceHelper {
}
}

fun ProjectCreateRequest.buildProject() = TProject(
name = name,
displayName = displayName,
description = description.orEmpty(),
createdBy = operator,
createdDate = LocalDateTime.now(),
lastModifiedBy = operator,
lastModifiedDate = LocalDateTime.now(),
metadata = metadata,
credentialsKey = credentialsKey,
)
fun ProjectCreateRequest.buildProject(tenantId: String?): TProject {
if (tenantId != null) {
return TProject(
name = "$tenantId-$name",
displayName = displayName,
description = description.orEmpty(),
createdBy = operator,
createdDate = LocalDateTime.now(),
lastModifiedBy = operator,
lastModifiedDate = LocalDateTime.now(),
metadata = metadata,
credentialsKey = credentialsKey,
projectCode = name,
tenantId = tenantId
)
} else {
return TProject(
name = name,
displayName = displayName,
description = description.orEmpty(),
createdBy = operator,
createdDate = LocalDateTime.now(),
lastModifiedBy = operator,
lastModifiedDate = LocalDateTime.now(),
metadata = metadata,
credentialsKey = credentialsKey,
)
}
}

fun checkPropertyAndDirection(option: ProjectListOption) {
Preconditions.checkArgument(
Expand All @@ -126,6 +146,10 @@ object ProjectServiceHelper {
return enabled
}

fun getTenantId(): String? {
return HttpContextHolder.getRequestOrNull()?.getHeader(TENANT_ID)
}

fun buildListQuery(): Query {
val criteria1 = TProject::name.regex("^$CLOSED_SOURCE_PREFIX")
val criteria2 = TProject::name.regex("^$CODE_PROJECT_PREFIX")
Expand All @@ -147,9 +171,11 @@ object ProjectServiceHelper {
names: List<String>,
option: ProjectListOption?
): Query {
val tenantId = getTenantId()
val query = Query.query(
where(TProject::name).`in`(names)
.apply { option?.displayNames?.let { and(TProject::displayName).`in`(option.displayNames!!) } }
.apply { tenantId?.let { and(TProject::tenantId).`is`(tenantId) } }
)
if (option?.sortProperty?.isNotEmpty() == true) {
checkPropertyAndDirection(option)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
package com.tencent.bkrepo.job.batch

import com.tencent.bkrepo.common.artifact.event.base.ArtifactEvent
import com.tencent.bkrepo.common.artifact.properties.EnableMultiTenantProperties
import com.tencent.bkrepo.common.artifact.properties.RouterControllerProperties
import com.tencent.bkrepo.common.job.JobAutoConfiguration
import com.tencent.bkrepo.common.metadata.properties.ProjectUsageStatisticsProperties
Expand Down Expand Up @@ -64,6 +65,7 @@ import org.springframework.test.context.TestPropertySource
StorageAutoConfiguration::class,
RouterControllerProperties::class,
ProjectUsageStatisticsProperties::class,
EnableMultiTenantProperties::class
)
@TestPropertySource(
locations = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import com.tencent.bkrepo.common.artifact.event.project.ProjectCreatedEvent
import com.tencent.bkrepo.common.artifact.pojo.RepositoryCategory
import com.tencent.bkrepo.common.artifact.pojo.RepositoryType
import com.tencent.bkrepo.common.artifact.pojo.configuration.local.LocalConfiguration
import com.tencent.bkrepo.common.artifact.properties.EnableMultiTenantProperties
import com.tencent.bkrepo.common.artifact.properties.RouterControllerProperties
import com.tencent.bkrepo.common.metadata.config.RepositoryProperties
import com.tencent.bkrepo.common.metadata.dao.node.NodeDao
Expand Down Expand Up @@ -99,7 +100,8 @@ import org.springframework.test.context.TestPropertySource
SpringContextUtils::class,
NodeDao::class,
RouterControllerProperties::class,
RepositoryProperties::class
RepositoryProperties::class,
EnableMultiTenantProperties::class,
)
@ComponentScan(value = ["com.tencent.bkrepo.repository.service", "com.tencent.bkrepo.common.metadata"])
@TestPropertySource(locations = ["classpath:bootstrap-ut.properties", "classpath:center-ut.properties"])
Expand Down
Loading