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:活跃用户记录操作和次数 #10891 #10899

Merged
merged 19 commits into from
Sep 10, 2024
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
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
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import javax.ws.rs.POST
import javax.ws.rs.Path
import javax.ws.rs.PathParam
import javax.ws.rs.Produces
import javax.ws.rs.QueryParam
import javax.ws.rs.core.MediaType

@Tag(name = "AUTH_MIGRATE", description = "权限-迁移")
Expand Down Expand Up @@ -137,6 +138,9 @@ interface OpAuthMigrateResource {
@Path("/autoRenewal")
@Operation(summary = "自动续期")
fun autoRenewal(
@Parameter(description = "小于该值才会被续期,若传空,则默认用户在用户组中的过期时间小于180天会被自动续期", required = true)
@QueryParam("validExpiredDay")
validExpiredDay: Int?,
@Parameter(description = "按条件迁移项目实体", required = true)
projectConditionDTO: ProjectConditionDTO
): Result<Boolean>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,6 @@ data class AuthResourceInfo(
val createUser: String,
val updateUser: String,
val createTime: LocalDateTime,
val updateTime: LocalDateTime
val updateTime: LocalDateTime,
val iamGradeManagerId: String? = null
)
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,7 @@ class AuthResourceDao {
relationId = relationId,
createUser = createUser,
updateUser = updateUser,
iamGradeManagerId = relationId,
createTime = createTime,
updateTime = updateTime
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,11 +178,11 @@ class RbacCacheService constructor(
val startEpoch = System.currentTimeMillis()
try {
val actionDTO = ActionDTO()
actionDTO.id = RbacAuthUtils.buildAction(
val action = RbacAuthUtils.buildAction(
authPermission = permission,
authResourceType = AuthResourceType.PROJECT
)

actionDTO.id = action
val resourceNode = V2ResourceNode.builder().system(iamConfiguration.systemId)
.type(AuthResourceType.PROJECT.value)
.id(projectCode)
Expand All @@ -200,7 +200,11 @@ class RbacCacheService constructor(

val result = policyService.verifyPermissions(queryPolicyDTO)
if (result) {
authUserDailyService.save(projectId = projectCode, userId = userId)
authUserDailyService.save(
projectId = projectCode,
userId = userId,
operate = action
)
}
return result
} finally {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -579,7 +579,8 @@ class RbacPermissionResourceMemberService constructor(
override fun autoRenewal(
projectCode: String,
resourceType: String,
resourceCode: String
resourceCode: String,
validExpiredDay: Int
) {
// 1、获取分级管理员或者二级管理员ID
val managerId = authResourceService.get(
Expand All @@ -600,7 +601,7 @@ class RbacPermissionResourceMemberService constructor(
)
val currentTime = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis())
// 预期的自动过期天数
val expectAutoExpiredAt = currentTime + AUTO_VALID_EXPIRED_AT
val expectAutoExpiredAt = currentTime + TimeUnit.DAYS.toSeconds(validExpiredDay.toLong())
val autoRenewalMembers = mutableSetOf<String>()
resourceGroupInfoList.forEach group@{ resourceGroup ->
val iamGroupId = resourceGroup.relationId.toInt()
Expand All @@ -610,11 +611,14 @@ class RbacPermissionResourceMemberService constructor(
}
val groupMemberInfoList = iamV2ManagerService.getRoleGroupMemberV2(iamGroupId, pageInfoDTO).results
groupMemberInfoList.forEach member@{ member ->
// 已过期或者要半年后才过期的,不自动过期
// 已过期或者小于自动续期范围内的不做续期
if (member.expiredAt < currentTime ||
member.expiredAt > expectAutoExpiredAt
) return@member

) {
val dataTime = DateTimeUtil.convertTimestampToLocalDateTime(member.expiredAt)
logger.info("Group member does not need to be renewed|$iamGroupId|$member|$dataTime")
return@member
}
// 自动续期时间由半年+随机天数,防止同一时间同时过期
val expiredTime = currentTime + AUTO_RENEWAL_EXPIRED_AT +
TimeUnit.DAYS.toSeconds(RandomUtils.nextLong(0, 180))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,11 @@ class RbacPermissionService constructor(

val result = policyService.verifyPermissions(queryPolicyDTO)
if (result) {
authProjectUserMetricsService.save(projectId = projectCode, userId = userId)
authProjectUserMetricsService.save(
projectId = projectCode,
userId = userId,
operate = useAction
)
}
return result
} finally {
Expand Down Expand Up @@ -300,8 +304,12 @@ class RbacPermissionService constructor(
actionList,
listOf(resourceDTO)
)
if (result.values.any { it }) {
authProjectUserMetricsService.save(projectId = projectCode, userId = userId)
result.filter { it.value }.keys.forEach { action ->
authProjectUserMetricsService.save(
projectId = projectCode,
userId = userId,
operate = action
)
}
return result
} finally {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -616,7 +616,10 @@ class RbacPermissionMigrateService constructor(
)
}

override fun autoRenewal(projectConditionDTO: ProjectConditionDTO): Boolean {
override fun autoRenewal(
validExpiredDay: Int,
projectConditionDTO: ProjectConditionDTO
): Boolean {
val traceId = MDC.get(TraceTag.BIZID)
toRbacExecutorService.submit {
MDC.put(TraceTag.BIZID, traceId)
Expand All @@ -634,7 +637,8 @@ class RbacPermissionMigrateService constructor(
migrateProjectsExecutorService.submit {
MDC.put(TraceTag.BIZID, traceId)
autoRenewal(
projectCode = migrateProject.englishName
projectCode = migrateProject.englishName,
validExpiredDay = validExpiredDay
)
}
}
Expand All @@ -644,7 +648,10 @@ class RbacPermissionMigrateService constructor(
return true
}

private fun autoRenewal(projectCode: String) {
private fun autoRenewal(
projectCode: String,
validExpiredDay: Int
) {
var offset = 0
val limit = 100
val startTime = System.currentTimeMillis()
Expand All @@ -666,7 +673,8 @@ class RbacPermissionMigrateService constructor(
permissionResourceMemberService.autoRenewal(
projectCode = projectCode,
resourceType = resourceType,
resourceCode = resourceCode
resourceCode = resourceCode,
validExpiredDay = validExpiredDay
)
} catch (ignored: Throwable) {
logger.error("Failed to auto renewal|$projectCode|$resourceType|$resourceCode")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,10 @@ class SamplePermissionMigrateService(
return true
}

override fun autoRenewal(projectConditionDTO: ProjectConditionDTO): Boolean {
override fun autoRenewal(
validExpiredDay: Int,
projectConditionDTO: ProjectConditionDTO
): Boolean {
return true
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ class SamplePermissionResourceMemberService : PermissionResourceMemberService {
override fun autoRenewal(
projectCode: String,
resourceType: String,
resourceCode: String
resourceCode: String,
validExpiredDay: Int
) = Unit

override fun renewalGroupMember(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,11 @@ class OpAuthMigrateResourceImpl @Autowired constructor(
return Result(permissionMigrateService.migrateMonitorResource(projectCodes = projectCodes))
}

override fun autoRenewal(projectConditionDTO: ProjectConditionDTO): Result<Boolean> {
permissionMigrateService.autoRenewal(projectConditionDTO)
override fun autoRenewal(validExpiredDay: Int?, projectConditionDTO: ProjectConditionDTO): Result<Boolean> {
permissionMigrateService.autoRenewal(
validExpiredDay = validExpiredDay ?: 180,
projectConditionDTO = projectConditionDTO
)
return Result(true)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,20 @@ import com.tencent.devops.auth.pojo.vo.BatchOperateGroupMemberCheckVo
import com.tencent.devops.auth.pojo.vo.GroupDetailsInfoVo
import com.tencent.devops.auth.pojo.vo.MemberGroupCountWithPermissionsVo
import com.tencent.devops.auth.service.iam.PermissionResourceMemberService
import com.tencent.devops.auth.service.iam.PermissionService
import com.tencent.devops.common.api.model.SQLPage
import com.tencent.devops.common.api.pojo.Result
import com.tencent.devops.common.auth.api.AuthPermission
import com.tencent.devops.common.auth.api.AuthResourceType
import com.tencent.devops.common.auth.api.BkManagerCheck
import com.tencent.devops.common.auth.rbac.utils.RbacAuthUtils
import com.tencent.devops.common.web.RestResource

@RestResource
class UserAuthResourceMemberResourceImpl(
private val permissionResourceMemberService: PermissionResourceMemberService
private val permissionResourceMemberService: PermissionResourceMemberService,
private val permissionService: PermissionService
) : UserAuthResourceMemberResource {
@BkManagerCheck
override fun listProjectMembers(
userId: String,
projectId: String,
Expand All @@ -32,17 +36,27 @@ class UserAuthResourceMemberResourceImpl(
page: Int,
pageSize: Int
): Result<SQLPage<ResourceMemberInfo>> {
return Result(
permissionResourceMemberService.listProjectMembers(
projectCode = projectId,
memberType = memberType,
userName = userName,
deptName = deptName,
departedFlag = departedFlag ?: false,
page = page,
pageSize = pageSize
)
val hasVisitPermission = permissionService.validateUserResourcePermission(
userId = userId,
resourceType = AuthResourceType.PROJECT.value,
action = RbacAuthUtils.buildAction(AuthPermission.VISIT, AuthResourceType.PROJECT),
projectCode = projectId
)
return if (!hasVisitPermission) {
Result(SQLPage(0, emptyList()))
} else {
Result(
permissionResourceMemberService.listProjectMembers(
projectCode = projectId,
memberType = memberType,
userName = userName,
deptName = deptName,
departedFlag = departedFlag ?: false,
page = page,
pageSize = pageSize
)
)
}
}

@BkManagerCheck
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,56 +28,80 @@

package com.tencent.devops.auth.service

import com.fasterxml.jackson.databind.ObjectMapper
import com.google.common.cache.CacheBuilder
import com.google.common.hash.BloomFilter
import com.google.common.hash.Funnels
import com.tencent.devops.common.event.dispatcher.pipeline.mq.MeasureEventDispatcher
import com.tencent.devops.common.event.pojo.measure.ProjectUserDailyEvent
import com.tencent.devops.common.event.pojo.measure.ProjectUserOperateMetricsData
import com.tencent.devops.common.event.pojo.measure.ProjectUserOperateMetricsEvent
import com.tencent.devops.common.event.pojo.measure.UserOperateCounterData
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.scheduling.annotation.Scheduled
import org.springframework.stereotype.Service
import java.nio.charset.Charset
import java.time.LocalDate
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit

@Service
@Suppress("UnstableApiUsage")
class AuthProjectUserMetricsService @Autowired constructor(
private val measureEventDispatcher: MeasureEventDispatcher
private val measureEventDispatcher: MeasureEventDispatcher,
private val objectMapper: ObjectMapper
) {
private val userOperateCounterData = UserOperateCounterData()

companion object {
private val logger = LoggerFactory.getLogger(AuthProjectUserMetricsService::class.java)

// 期待的用户数10w
private const val EXPECTED_USER_COUNT = 100000

// 错误率0.1%
private const val EXPECTED_FPP = 0.001
private val bloomFilterMap = CacheBuilder.newBuilder()
.maximumSize(2)
.expireAfterWrite(1, TimeUnit.DAYS)
.build<LocalDate, BloomFilter<String>>()

private val projectUserOperateMetricsMap =
mingshewhe marked this conversation as resolved.
Show resolved Hide resolved
mutableMapOf<String/*projectId*/, MutableMap<String, Int>/*projectUserOperateMetricsKey,count*/>()

private val executorService = Executors.newFixedThreadPool(5)
}

fun save(
projectId: String,
userId: String
userId: String,
operate: String
) {
val theDate = LocalDate.now()
try {
val bloomKey = "${projectId}_$userId"
val bloomFilter = getBloomFilter(theDate)
if (!bloomFilter.mightContain(bloomKey)) {
measureEventDispatcher.dispatch(
ProjectUserDailyEvent(
projectId = projectId,
userId = userId,
theDate = theDate
executorService.execute {
val theDate = LocalDate.now()
try {
val bloomKey = "${projectId}_$userId"
val bloomFilter = getBloomFilter(theDate)
if (!bloomFilter.mightContain(bloomKey)) {
measureEventDispatcher.dispatch(
ProjectUserDailyEvent(
projectId = projectId,
userId = userId,
theDate = theDate
)
)
bloomFilter.put(bloomKey)
}
saveProjectUserOperateMetrics(
projectId = projectId,
userId = userId,
operate = operate,
theDate = theDate
)
bloomFilter.put(bloomKey)
} catch (ignored: Throwable) {
logger.error("save auth user error", ignored)
}
} catch (ignored: Throwable) {
logger.error("save auth user error", ignored)
}
}

Expand All @@ -93,4 +117,33 @@ class AuthProjectUserMetricsService @Autowired constructor(
}
return bloomFilter!!
}

private fun saveProjectUserOperateMetrics(
projectId: String,
userId: String,
operate: String,
theDate: LocalDate
) {
val projectUserOperateMetricsKey = ProjectUserOperateMetricsData(
projectId = projectId,
userId = userId,
theDate = theDate,
operate = operate
).getProjectUserOperateMetricsKey()
userOperateCounterData.increment(projectUserOperateMetricsKey)
}

@Scheduled(initialDelay = 20000, fixedDelay = 20000)
private fun uploadProjectUserOperateMetrics() {
val projectUserOperateMetricsMapStr = objectMapper.writeValueAsString(projectUserOperateMetricsMap)
if (logger.isDebugEnabled) {
logger.debug("upload project user operate metrics :$projectUserOperateMetricsMapStr")
}
measureEventDispatcher.dispatch(
ProjectUserOperateMetricsEvent(
userOperateCounterData = userOperateCounterData
)
)
userOperateCounterData.reset()
}
}
Loading
Loading