diff --git a/docs/apidoc/README.md b/docs/apidoc/README.md index c0fb43fca5..eb176303ca 100644 --- a/docs/apidoc/README.md +++ b/docs/apidoc/README.md @@ -2,6 +2,7 @@ - **接口公共说明** - [通用接口协议](/apidoc/common/common.md) - [自定义搜索协议](/apidoc/common/search.md) + - [事件类型](/apidoc/common/event.md) - **项目/仓库** - [项目接口](/apidoc/repo/project.md) - [仓库接口](/apidoc/repo/repository.md) @@ -39,3 +40,5 @@ - **WebHook** - [webhook接口](/apidoc/webhook/webhook.md) - [webhook消息](/apidoc/webhook/payload.md) +- **审计日志** + - [审计日志接口](/apidoc/log/log.md) diff --git a/docs/apidoc/_sidebar.md b/docs/apidoc/_sidebar.md index 24747b8cd0..97ab54e1fc 100644 --- a/docs/apidoc/_sidebar.md +++ b/docs/apidoc/_sidebar.md @@ -1,6 +1,7 @@ - **接口公共说明** - [通用接口协议](/apidoc/common/common.md) - [自定义搜索协议](/apidoc/common/search.md) + - [事件类型](/apidoc/common/event.md) - **项目/仓库** - [项目接口](/apidoc/repo/project.md) - [仓库接口](/apidoc/repo/repository.md) @@ -31,3 +32,5 @@ - **WebHook** - [webhook接口](/apidoc/webhook/webhook.md) - [webhook消息](/apidoc/webhook/payload.md) +- **审计日志** + - [审计日志接口](/apidoc/log/log.md) \ No newline at end of file diff --git a/docs/apidoc/common/event.md b/docs/apidoc/common/event.md new file mode 100644 index 0000000000..951406e706 --- /dev/null +++ b/docs/apidoc/common/event.md @@ -0,0 +1,16 @@ +# 公共事件类型 + +| 枚举值 | 说明 | +| ---------------- | ----------- | +| PROJECT_CREATED | 项目创建 | +| REPO_CREATED | 仓库创建 | +| REPO_UPDATED | 仓库更新 | +| NODE_CREATED | 节点创建 | +| NODE_DOWNLOADED | 节点下载 | +| NODE_RENAMED | 节点重命名 | +| NODE_MOVED | 节点移动 | +| NODE_COPIED | 节点复制 | +| NODE_DELETED | 节点删除 | +| METADATA_DELETED | 元数据删除 | +| METADATA_SAVED | 元数据保存 | +| VERSION_CREATED | 版本创建 | \ No newline at end of file diff --git a/docs/apidoc/log/log.md b/docs/apidoc/log/log.md new file mode 100644 index 0000000000..73a1eb01a9 --- /dev/null +++ b/docs/apidoc/log/log.md @@ -0,0 +1,94 @@ +# 审计日志接口 + +[toc] + +## 分页查询日志 + +- API: POST /repository/api/log/list +- API 名称: list_log +- 功能说明: + - 中文:查询日志 + - English:list_log + +- 请求体 +```json + { + "pageNumber": 1, + "pageSize": 20, + "projectId": "test", + "repoName": "generic-local", + "resourceKey": "/release/1.0.0/boot-example.jar", + "eventType": "NODE_DOWNLOADED", + "sha256": "6671fe83b7a07c8932ee89164d1f2793b2318058eb8b98dc5c06ee0a5a3b0ec1", + "pipelinId": "p-123456", + "buildId": "b-123456", + "userId": "admin", + "startTime": "2022-08-01T15:00:00.000", + "endTime": "2022-08-02T15:00:00.000", + "direction": "DESC" + } +``` + +- 请求字段说明 + + |字段|类型|是否必须|默认值| 说明 |Description| + |---|---|---|---------------------------------------------------------------------|---|---| + |pageNumber|int|否|1| 页数 |page number| + |pageSize|int|否|20| 页大小 |page size| + |projectId|string|是|无| 项目ID |project id| + |repoName|string|是|无| 仓库名称 |repo name| + |resourceKey|string|是|无| 事件资源key,支持通配符*,[详细说明](#resourcekey%E8%AF%B4%E6%98%8E) |resource key| + |eventType|string|是|无| 事件类型,参考[公共事件](../common/event.md) |event type| + |sha256|string|否|无| 文件sha256。查询节点类型事件日志时选填 |sha256| + |pipelinId|string|否|无| 文件元数据pipelineId。查询节点类型事件日志时选填 |pipeline id| + |buildId|string|否|无| 文件元数据buildId。查询节点类型事件日志时选填 |buildId| + |userId|string|否|无| 事件触发用户名 |user id| + |startTime|string|否|当前时间的前3个月| 开始时间 |start time| + |endTime|string|否|当前时间| 截至时间 |end time| + |direction|string|否|DESC| 按时间排序方向,默认降序。可选值ASC/DESC |direction| + + +- 响应体 + +``` json +{ + "code": 0, + "message": null, + "data": { + "pageNumber": 1, + "pageSize": 20, + "totalRecords": 1, + "totalPages": 1, + "records": [ + { + "createdDate": "2022-08-02T15:20:47.235", + "type": "NODE_DOWNLOADED", + "projectId": "test", + "repoName": "generic-local", + "resourceKey": "/release/1.0.0/boot-example.jar", + "userId": "admin", + "clientAddress": "127.0.0.1", + "description": { + "md5": "036208b4a1ab4a235d75c181e685e5a3", + "sha256": "6671fe83b7a07c8932ee89164d1f2793b2318058eb8b98dc5c06ee0a5a3b0ec1" + } + } + ], + "count": 1, + "page": 1 + }, + "traceId": "" +} +``` + +## resourceKey说明 + +事件资源key,具有唯一性 +1. 节点类型对应fullPath +2. 仓库类型对应仓库名称 +3. 包类型对应包名称 + +例如: +- 节点下载事件,resourceKey即为下载节点的fullPath。节点的fullPath为/release/1.0.0/boot-example.jar,那么resourceKey为/release/1.0.0/boot-example.jar。 +- 查询节点下载记录时,可以查询某个文件或目录的下载记录。查询/release/1.0.0/目录下文件的下载记录,resourceKey为/release/1.0.0/;查询/release/1.0.0/目录下jar类型文件的下载记录,resouceKey为/release/1.0.0/*.jar + \ No newline at end of file diff --git a/docs/apidoc/node/node.md b/docs/apidoc/node/node.md index dcc8057ed2..8745b3d560 100644 --- a/docs/apidoc/node/node.md +++ b/docs/apidoc/node/node.md @@ -634,7 +634,6 @@ | repoName | string | 节点所属仓库 | node repository name | | packages | Int | 节点数量 | node count | -## ## 清理创建时间早于{date}的文件节点 @@ -668,5 +667,3 @@ "traceId": null } ``` - -## \ No newline at end of file diff --git a/docs/apidoc/webhook/webhook.md b/docs/apidoc/webhook/webhook.md index bca9ee0619..5e01efef77 100644 --- a/docs/apidoc/webhook/webhook.md +++ b/docs/apidoc/webhook/webhook.md @@ -4,20 +4,11 @@ #### 触发器类型 +触发器类型参考 [公共事件类型](../common/event.md) + +除公共事件类型,WebHook服务还支持以下事件 | 枚举值 | 说明 | | ---------------- | ----------- | -| PROJECT_CREATED | 项目创建 | -| REPO_CREATED | 仓库创建 | -| REPO_UPDATED | 仓库更新 | -| NODE_CREATED | 节点创建 | -| NODE_DOWNLOADED | 节点下载 | -| NODE_RENAMED | 节点重命名 | -| NODE_MOVED | 节点移动 | -| NODE_COPIED | 节点复制 | -| NODE_DELETED | 节点删除 | -| METADATA_DELETED | 元数据删除 | -| METADATA_SAVED | 元数据保存 | -| VERSION_CREATED | 版本创建 | | WEBHOOK_TEST | webhook测试 | #### 关联对象类型 diff --git a/src/backend/common/common-operate/operate-api/src/main/kotlin/com.tencent.bkrepo.common.operate.api/pojo/OpLogListOption.kt b/src/backend/common/common-operate/operate-api/src/main/kotlin/com.tencent.bkrepo.common.operate.api/pojo/OpLogListOption.kt index 81de1ef0c9..57281ca97c 100644 --- a/src/backend/common/common-operate/operate-api/src/main/kotlin/com.tencent.bkrepo.common.operate.api/pojo/OpLogListOption.kt +++ b/src/backend/common/common-operate/operate-api/src/main/kotlin/com.tencent.bkrepo.common.operate.api/pojo/OpLogListOption.kt @@ -62,7 +62,5 @@ data class OpLogListOption( @ApiModelProperty("查询截至时间") val endTime: LocalDateTime = LocalDateTime.now(), @ApiModelProperty("时间排序方向") - val direction: Sort.Direction = Sort.Direction.DESC, - @ApiModelProperty("前缀查询") - val prefixSearch: Boolean = false + val direction: Sort.Direction = Sort.Direction.DESC ) diff --git a/src/backend/common/common-operate/operate-service/build.gradle.kts b/src/backend/common/common-operate/operate-service/build.gradle.kts index 3333ac37ac..fa29f28bae 100644 --- a/src/backend/common/common-operate/operate-service/build.gradle.kts +++ b/src/backend/common/common-operate/operate-service/build.gradle.kts @@ -28,4 +28,5 @@ dependencies { api(project(":common:common-operate:operate-api")) api(project(":common:common-mongo")) + api(project(":common:common-security")) } diff --git a/src/backend/common/common-operate/operate-service/src/main/kotlin/com.tencent.bkrepo.common.operate.service/OperateAutoConfiguration.kt b/src/backend/common/common-operate/operate-service/src/main/kotlin/com.tencent.bkrepo.common.operate.service/OperateAutoConfiguration.kt index 9ea8e095e8..642bb86b78 100644 --- a/src/backend/common/common-operate/operate-service/src/main/kotlin/com.tencent.bkrepo.common.operate.service/OperateAutoConfiguration.kt +++ b/src/backend/common/common-operate/operate-service/src/main/kotlin/com.tencent.bkrepo.common.operate.service/OperateAutoConfiguration.kt @@ -31,6 +31,7 @@ import com.tencent.bkrepo.common.operate.api.OperateLogService import com.tencent.bkrepo.common.operate.service.config.OperateProperties import com.tencent.bkrepo.common.operate.service.dao.OperateLogDao import com.tencent.bkrepo.common.operate.service.service.OperateLogServiceImpl +import com.tencent.bkrepo.common.security.manager.PermissionManager import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication import org.springframework.boot.context.properties.EnableConfigurationProperties import org.springframework.context.annotation.Bean @@ -44,7 +45,11 @@ import org.springframework.context.annotation.Import class OperateAutoConfiguration { @Bean - fun operateLogService(operateProperties: OperateProperties, operateLogDao: OperateLogDao): OperateLogService { - return OperateLogServiceImpl(operateProperties, operateLogDao) + fun operateLogService( + operateProperties: OperateProperties, + operateLogDao: OperateLogDao, + permissionManager: PermissionManager + ): OperateLogService { + return OperateLogServiceImpl(operateProperties, operateLogDao, permissionManager) } } diff --git a/src/backend/common/common-operate/operate-service/src/main/kotlin/com.tencent.bkrepo.common.operate.service/service/OperateLogServiceImpl.kt b/src/backend/common/common-operate/operate-service/src/main/kotlin/com.tencent.bkrepo.common.operate.service/service/OperateLogServiceImpl.kt index 219c911366..fc912d592e 100644 --- a/src/backend/common/common-operate/operate-service/src/main/kotlin/com.tencent.bkrepo.common.operate.service/service/OperateLogServiceImpl.kt +++ b/src/backend/common/common-operate/operate-service/src/main/kotlin/com.tencent.bkrepo.common.operate.service/service/OperateLogServiceImpl.kt @@ -27,7 +27,9 @@ package com.tencent.bkrepo.common.operate.service.service +import com.tencent.bkrepo.auth.pojo.enums.PermissionAction import com.tencent.bkrepo.common.api.pojo.Page +import com.tencent.bkrepo.common.api.util.EscapeUtils import com.tencent.bkrepo.common.api.util.readJsonString import com.tencent.bkrepo.common.api.util.toJsonString import com.tencent.bkrepo.common.artifact.event.base.ArtifactEvent @@ -41,6 +43,10 @@ import com.tencent.bkrepo.common.operate.api.pojo.OperateLogResponse import com.tencent.bkrepo.common.operate.service.config.OperateProperties import com.tencent.bkrepo.common.operate.service.dao.OperateLogDao import com.tencent.bkrepo.common.operate.service.model.TOperateLog +import com.tencent.bkrepo.common.security.exception.PermissionException +import com.tencent.bkrepo.common.security.manager.PermissionManager +import com.tencent.bkrepo.common.security.permission.PrincipalType +import com.tencent.bkrepo.common.security.util.SecurityUtils import org.springframework.data.domain.Sort import org.springframework.data.mongodb.core.query.Criteria import org.springframework.data.mongodb.core.query.Query @@ -58,7 +64,8 @@ import java.time.format.DateTimeFormatter open class OperateLogServiceImpl( private val operateProperties: OperateProperties, - private val operateLogDao: OperateLogDao + private val operateLogDao: OperateLogDao, + private val permissionManager: PermissionManager ) : OperateLogService { @Async @@ -103,22 +110,25 @@ open class OperateLogServiceImpl( } override fun listPage(option: OpLogListOption): Page { + try { + permissionManager.checkPrincipal(SecurityUtils.getUserId(), PrincipalType.ADMIN) + } catch (e: PermissionException) { + permissionManager.checkProjectPermission(PermissionAction.MANAGE, option.projectId) + } with(option) { + val escapeValue = EscapeUtils.escapeRegexExceptWildcard(resourceKey) + val regexPattern = escapeValue.replace("*", ".*") val criteria = where(TOperateLog::projectId).isEqualTo(projectId) .and(TOperateLog::repoName).isEqualTo(repoName) .and(TOperateLog::type).isEqualTo(eventType) .and(TOperateLog::createdDate).gte(startTime).lte(endTime) + .and(TOperateLog::resourceKey).regex("^$regexPattern") .apply { userId?.run { and(TOperateLog::userId).isEqualTo(userId) } sha256?.run { and("${TOperateLog::description.name}.sha256").isEqualTo(sha256) } pipelineId?.run { and("${TOperateLog::description.name}.pipelineId").isEqualTo(pipelineId) } buildId?.run { and("${TOperateLog::description.name}.buildId").isEqualTo(buildId) } } - if (prefixSearch) { - criteria.and(TOperateLog::resourceKey).regex("^$resourceKey") - } else { - criteria.and(TOperateLog::resourceKey).isEqualTo(resourceKey) - } val query = Query(criteria) val totalCount = operateLogDao.count(query) val pageRequest = Pages.ofRequest(pageNumber, pageSize) diff --git a/src/backend/repository/biz-repository/src/test/kotlin/com/tencent/bkrepo/repository/service/OperateLogServiceTest.kt b/src/backend/repository/biz-repository/src/test/kotlin/com/tencent/bkrepo/repository/service/OperateLogServiceTest.kt index cbd5462343..35df850a9c 100644 --- a/src/backend/repository/biz-repository/src/test/kotlin/com/tencent/bkrepo/repository/service/OperateLogServiceTest.kt +++ b/src/backend/repository/biz-repository/src/test/kotlin/com/tencent/bkrepo/repository/service/OperateLogServiceTest.kt @@ -115,7 +115,7 @@ class OperateLogServiceTest @Autowired constructor( ) operateLogDao.insert(log) } - Assertions.assertTrue(sequences.size == 4) + Assertions.assertTrue(sequences.size == 5 || sequences.size == 4) } private fun deleteOperateLog() {