首页
/ Rocket.Chat 审计 REST API 实践指南:/v1/audit.* 三个端点如何替代 DDP 审计方法

Rocket.Chat 审计 REST API 实践指南:/v1/audit.* 三个端点如何替代 DDP 审计方法

2026-09-05 23:17:00作者:廉皓灿Ida

本篇基于 Rocket.Chat 仓库中的变更说明 rest-audit-endpoints.md 展开,讲解三个新增的 /v1/audit.* REST 端点的请求参数、权限模型、限流规则与底层实现。读完后你将能够直接调用这些端点完成消息审计、全渠道(Omnichannel)审计与审计日志查询,并理解它们与旧版 DDP 方法的兼容性边界(预计 9.0.0 移除)。

背景:审计功能从 DDP 方法迁移到 REST API

变更说明(changeset)的核心内容如下:在 @rocket.chat/meteor 的 minor 版本中,新增了三个位于 /v1/audit.* 下的 REST 端点,全部为 EE(Enterprise Edition)功能,要求实例持有 auditing 许可证,覆盖此前仅以 DDP 方法形式存在的审计流程:

REST 端点 替换的 DDP 方法 所需权限
GET /v1/audit.auditions?startDate=&endDate={ auditions: IAuditLog[] } auditGetAuditions can-audit-log
POST /v1/audit.messages(body:{ rid?, startDate, endDate, users, msg, type, visitor?, agent? })→ { messages: IMessage[] } auditGetMessages can-audit
POST /v1/audit.omnichannelMessages(body:{ startDate, endDate, users, msg, type, visitor?, agent? })→ { messages: IMessage[] } auditGetOmnichannelMessages can-audit

变更说明同时明确了两条兼容性约定:

  1. 每个端点均限流为 10 次请求 / 60 秒,与 DDP 侧的 DDPRateLimiter 规则保持一致;
  2. 每个端点会写入与 DDP 方法完全相同的 AuditLog 条目;日期在报文线上序列化为 ISO 字符串;旧 DDP 方法在 9.0.0 之前继续注册,并输出指向新路由的弃用(deprecation)日志。

这符合仓库内 API 端点迁移指南 中描述的演进方向:从旧式 API.v1.addRoute() 迁移到 API.v1.get()/.post() 新写法,配套 AJV 编译的请求/响应校验、统一错误格式与自动生成的 OpenAPI 文档。

三个端点的完整实现集中在 audit.ts(注意其位于 ee/ 目录下,即企业版代码),下文逐端点解析。

端点一:GET /v1/audit.auditions 查询审计日志

该端点返回时间窗口内所有审计操作记录,对应旧 DDP 方法 auditGetAuditions

请求与响应

  • 认证:必须登录(authRequired: true);
  • 权限:can-audit-log(注意与消息审计的 can-audit 是两个独立权限);
  • 查询参数(AJV 校验,additionalProperties: false,即不允许携带额外字段):
参数 类型 必填 说明
startDate string 起始时间,ISO 日期字符串(minLength: 1
endDate string 结束时间,ISO 日期字符串
  • 响应:{ success: true, auditions: IAuditLog[] }IAuditLog 的结构定义在 IAuditLog.ts
export interface IAuditLog extends IRocketChatRecord {
	ts: Date;
	results: number;
	u: Pick<IUser, '_id' | 'username' | 'name' | 'avatarETag'>;
	fields: {
		type: string;
		msg: IMessage['msg'];
		startDate?: Date;
		endDate?: Date;
		rids?: IRoom['_id'][];
		room: IRoom['name'];
		users?: IUser['username'][];
		visitor?: ILivechatVisitor['_id'];
		agent?: ILivechatAgent['_id'];
		filters?: string;
	};
}

请求示例:

curl -X GET "https://<your-instance>/api/v1/audit.auditions?startDate=2026-01-01T00:00:00.000Z&endDate=2026-09-01T00:00:00.000Z" \
  -H "Authorization: Bearer <token>"

实现要点

端点注册见 audit.ts

API.v1.get(
	'audit.auditions',
	{
		authRequired: true,
		permissionsRequired: ['can-audit-log'],
		query: isAuditAuditionsProps,
		license: ['auditing'],
		rateLimiterOptions: { numRequestsAllowed: 10, intervalTimeInMS: 60000 },
		response: {
			200: auditAuditionsResponseSchema,
			400: auditErrorResponseSchema,
			401: validateUnauthorizedErrorResponse,
			403: validateForbiddenErrorResponse,
		},
	},
	async function action() {
		const startDate = parseDateOrFail(this.queryParams.startDate, 'startDate');
		const endDate = parseDateOrFail(this.queryParams.endDate, 'endDate');
		const auditions = await auditGetAuditionsMethod(this.userId, startDate, endDate);
		return API.v1.success({ auditions });
	},
);

关键行为(源自 functions.ts 中的 auditGetAuditionsMethod):

  • 二次权限校验:REST 层用 can-audit-log 拦截后,共享函数内还会执行 hasPermissionAsync(userId, 'can-audit-log'),不通过则抛 Not allowed
  • 时间窗口为开区间:查询条件为 ts: { $gt: startDate, $lt: endDate }
  • 敏感字段裁剪:返回前通过 Mongo projection 剔除 u.servicesu.rolesu.lastLoginu.statusConnectionu.emails,避免把用户凭据/服务信息暴露给审计日志消费方;
  • 日期解析parseDateOrFailaudit.ts)使用 Date.parse,非法日期抛错并被转换为 HTTP 400,错误体为 { success: false, error, errorType }

端点二:POST /v1/audit.messages 消息审计

该端点按房间、用户、消息内容等条件检索消息,替代 DDP 方法 auditGetMessages,需要 can-audit 权限。

请求体参数

由 AJV schema(auditMessagesSchemaaudit.ts)严格校验:

字段 类型 必填 说明
rid string 房间 ID;typeu 时用于定位目标房间
startDate string ISO 日期字符串
endDate string ISO 日期字符串
users string[] 用户名数组(可为空数组)
msg string | null 消息内容关键字;非空时做转义后的忽略大小写正则匹配
type string 搜索类型,语义见下文 u/d/l
visitor string | null 全渠道访客 ID
agent string | null 客服坐席 ID

请求示例:

curl -X POST "https://<your-instance>/api/v1/audit.messages" \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "d",
    "users": ["alice"],
    "startDate": "2026-01-01T00:00:00.000Z",
    "endDate": "2026-06-01T00:00:00.000Z",
    "msg": "invoice"
  }'

实现要点与 type 语义

端点注册见 audit.ts,实际检索逻辑在共享函数 auditGetMessagesMethod。从源码结构看,type 字段驱动三条不同的检索路径:

  1. type === 'u'(按用户全局检索):把 users 用户名解析为 u._id 集合后查询 u._id $in usersId;同时通过 Rooms.findAllPrivateRoomsWithAbacAttributes 查出带 ABAC 属性的私密房间并以 rid $nin abacRooms 排除,保证 ABAC 管控房间的消息不会从这条路径泄露。
  2. type === 'd'(私信检索):调用 Rooms.findDirectRoomContainingAllUsernames(usernames) 定位包含全部指定用户的 DM 房间。
  3. 其他(如 l,全渠道):走 getRoomInfoByAuditParams,按 visitor/agentLivechatRooms 中查找房间,并执行 livechat.applyRoomRestrictions 回调附加房间可见性限制;找不到房间时抛 Room doesn't exist 错误。

通用约束:

  • 时间条件为 ts: { $gt: startDate, $lt: endDate }(开区间,含端点日期本身的消息按毫秒精度不含边界时刻);
  • msg 过滤先用 escapeRegExp 转义再做 new RegExp(escaped, 'i'),即字面量、忽略大小写匹配,不会把用户输入当正则解释;
  • 每次成功查询都会向 AuditLog 插入一条记录,字段包含 msgusersridsroomstartDateendDatetypevisitoragent,并累加 Message_Auditing_Panel_Load_Count 统计计数器——这正是 changeset 中“写入与 DDP 方法相同的 AuditLog 条目”的落点;
  • 响应 schema 对 messages 数组元素刻意放宽(audit.ts 的注释说明:IMessage 的 attachment 联合体是含重叠分支的 oneOf,严格 $ref 会导致带附件的真实消息在响应校验阶段误判 400,故在 attachment schema 增加判别字段前保持宽松)。

端点三:POST /v1/audit.omnichannelMessages 全渠道消息审计

替代 DDP 方法 auditGetOmnichannelMessages,同样是 can-audit 权限。请求体与 audit.messages 几乎一致,唯一区别是没有 rid 字段(见 audit.tsauditOmnichannelMessagesSchema):

{
  "startDate": "2026-01-01T00:00:00.000Z",
  "endDate": "2026-06-01T00:00:00.000Z",
  "users": [],
  "msg": "",
  "type": "l",
  "visitor": "<visitorId>",
  "agent": "<agentId>"
}

实现位于 auditGetOmnichannelMessagesMethod,与 audit.messages 的两点差异值得注意:

  • 不走 livechat.applyRoomRestrictions 回调(源码注释明确说明:这与原始 DDP auditGetOmnichannelMessages 保持一致,访问控制由 can-audit 权限把关,审计的本意就是覆盖所有全渠道房间,因此不施加单元/可见性限制);
  • visitor/agent 未匹配到任何房间时,rids 保持为空数组而非 undefined,避免 Mongo 抛出 $in needs an array 错误。

成功检索后同样写入 AuditLog 条目(room 字段记录为国际化字符串 "Omnichannel")。

限流:REST 与 DDP 双轨一致的 10 次 / 60 秒

三个 REST 端点都显式声明了相同的限流配置:

rateLimiterOptions: { numRequestsAllowed: 10, intervalTimeInMS: 60000 },

路由级限流的注册与执行由 ApiClass.ts 负责(registerRateLimiterForRoute / enforceRateLimitForRoute);从源码结构看,TEST_MODE 环境变量开启时会自动跳过限流注册,方便端到端测试反复调用。

对应的 DDP 侧规则在 methods.ts 中:

DDPRateLimiter.addRule(
	{ type: 'method', name: 'auditGetAuditions', userId() { return true; } },
	10,
	60000,
);

DDP 侧同样以 10 次 / 60000ms 对 auditGetAuditionsauditGetMessages 登记了 DDPRateLimiter 规则,与 REST 侧配额对齐,保证无论走哪条通道,审计查询的访问速率上限一致。

兼容性:DDP 方法保留至 9.0.0 并输出弃用日志

旧的三个 DDP 方法并未删除,而是保留并挂上了弃用标记(methods.ts):

Meteor.methods<ServerMethods>({
	async auditGetOmnichannelMessages(params) {
		methodDeprecationLogger.method('auditGetOmnichannelMessages', '9.0.0', '/v1/audit.omnichannelMessages');
		check(params.startDate, Date);
		check(params.endDate, Date);
		return auditGetOmnichannelMessagesMethod(Meteor.userId(), params);
	},
	async auditGetMessages(params) {
		methodDeprecationLogger.method('auditGetMessages', '9.0.0', '/v1/audit.messages');
		// ...
	},
	async auditGetAuditions({ startDate, endDate }) {
		methodDeprecationLogger.method('auditGetAuditions', '9.0.0', '/v1/audit.auditions');
		// ...
	},
});

每次调用旧方法都会经过 deprecationWarningLogger

  • 通过 Logger('DeprecationWarning') 输出 warn 级别日志,消息形如 The method "auditGetAuditions" is deprecated and will be removed on version 9.0.0 (Use the "/v1/audit.auditions" endpoint instead)
  • 递增 deprecations / deprecationsTotal 指标,便于运维侧统计遗留调用量;
  • 计划移除版本以类型 DeprecationLoggerNextPlannedVersion = '9.0.0'deprecationWarningLogger.ts)收敛在单处;TEST_MODE=true 时该日志会直接抛错,让测试运行尽早暴露对弃用接口的误用。

由于 REST 端点与 DDP 方法最终都调用同一组 auditGet*Method 共享函数,行为(权限校验、检索条件、审计留痕)完全同源。Rocket.Chat 自带前端也已经切到新端点:useAuditMutation.ts 通过 useEndpoint('POST', '/v1/audit.messages')/v1/audit.omnichannelMessages 发起请求,日期在客户端以 toISOString() 序列化为 ISO 字符串后再提交,与 changeset 中“日期在线上为 ISO 字符串”的约定一致。

端到端测试如何验证这些行为

端到端用例 tests/end-to-end/api/audit.ts(仅 EE 构建执行,由 IS_EE 控制)为上述行为提供了可复制的验收清单:

  • 401:未携带凭据访问 audit.auditions / audit.messages / audit.omnichannelMessages
  • 400:缺少 startDate/endDate 或必填 body 字段(usersmsgtype),以及日期非法(如 'not-a-date');
  • 403can-audit-log(针对 auditions)或 can-audit(针对两个 messages 端点)被收回后请求被拒;
  • 200 + 结构断言:成功时响应体包含 success: trueauditions / messages 数组;
  • 审计留痕闭环:先查询 audit.auditions 记录条数,调用一次 audit.messagesaudit.omnichannelMessages 后再次查询,断言条数增加——直接验证了“每个端点写入 AuditLog 条目”的承诺。

测试中还体现了权限细节:auditor 角色默认只有 can-audit 而无 can-audit-log,测试套件通过 updatePermission('can-audit-log', ['admin', 'auditor']) 临时授权后再验证 auditions 端点。

关键结论

  1. 三个端点均为 EE 功能license: ['auditing']),部署在 apps/meteor/ee/ 下的企业版目录中,社区版实例不可用;
  2. 权限分工明确:查审计日志用 can-audit-log,查审计消息用 can-audit,两者在 REST 层与共享函数层各校验一次;
  3. 限流统一为 10 次 / 60 秒,REST(rateLimiterOptions)与 DDP(DDPRateLimiter.addRule)双通道配额一致;
  4. 时间参数在报文中为 ISO 字符串,实际查询为开区间 $gt / $lt;消息关键字检索是转义后的字面量匹配,忽略大小写;
  5. 旧 DDP 方法在 9.0.0 前继续可用,调用时会在服务端日志与指标中留下弃用记录,集成方应尽快切换到 /v1/audit.*

主要参考文件:变更说明REST 端点实现DDP 方法与限流规则共享审计函数IAuditLog 类型弃用日志器路由限流端到端测试端点迁移指南

登录后查看全文
热门项目推荐
相关项目推荐