ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Activepieces 数据隔离规则解析:多租户安全下的 projectId / platformId 过滤与 ArrayContains 实践

Activepieces 数据隔离规则解析:多租户安全下的 projectId / platformId 过滤与 ArrayContains 实践 Activepieces 数据隔离规则解析多租户安全下的 projectId / platformId 过滤与 ArrayContains 实践【免费下载链接】activepiecesAI Agents MCPs AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows AI Agents • MCPs for AI Agents项目地址: https://gitcode.com/GitHub_Trending/ac/activepieces导读本文以仓库 .claude/rules/data-isolation.md 中定义的工程规范为核心结合 Activepieces 服务端packages/server/api与共享类型定义packages/core/shared的真实源码深入讲解两条多租户数据隔离铁律所有数据库查询必须按projectId或platformId过滤以及跨项目共享资源必须使用 TypeORM 的ArrayContains([projectId])操作符查询projectIds数组列。读完本文你将理解这两条规则背后的数据模型设计、在应用连接App Connection、事件目的地Event Destination等模块中的落地形态以及如何在新增查询时避免跨租户数据泄漏。一、规则总览两条必须遵守的多租户安全基线仓库中 .claude/rules/data-isolation.md 全文定义了 Activepieces 后端开发中数据隔离的最基本约束ALL database queries MUST filter byprojectIdorplatformIdfor multi-tenant safety.所有数据库查询都必须按projectId或platformId进行过滤以保证多租户安全。For connections with multi-project access, useArrayContains([projectId])on theprojectIdsarray column.对于支持多项目访问的连接connections在projectIds数组列上使用ArrayContains([projectId])。这两条规则共同回答了一个核心问题Activepieces 是典型的多租户multi-tenantSaaS 架构平台Platform下挂多个项目Project项目内再承载流程、连接、模板、事件目的地等资源。任何一条 SQL 查询若缺少租户维度的 WHERE 条件就可能把项目 A 的流程、密钥或运行记录暴露给项目 B构成严重的数据越权。规则一是所有资源查询的通用底线无论是findOneBy、update、delete还是createQueryBuilder都必须携带projectId或platformId作为过滤条件规则二是规则一在一连接多项目场景下的具体落地方式由于连接App Connection通过projectIds数组列与多个项目关联无法用简单的等值比较匹配因此必须使用 PostgreSQL 数组包含操作符对应的 TypeORMArrayContains判断目标项目 ID 是否落在数组内。二、规则一的源码落地从查询构建器到仓储层规则一在服务端并非停留在规范文档层面而是渗透在几乎所有业务仓储repository与服务的查询代码中。以下两个代表性模块可以直观印证。2.1 事件目的地Event Destination平台维度的强制过滤packages/server/api/src/app/event-destinations/event-destinations.service.ts 中事件目的地的创建、更新、删除、列表查询全部以platformId为强制条件// 创建实体写入时强制携带 platformId const entity: EventDestination { id: apId(), created: new Date().toISOString(), updated: new Date().toISOString(), platformId, scope: EventDestinationScope.PLATFORM, events: request.events, url: request.url, } return eventDestinationRepo().save(entity) // 更新与删除均以 { id, platformId } 复合条件定位杜绝只知道 id 就能改别人的资源 await eventDestinationRepo().update({ id, platformId }, request) await eventDestinationRepo().delete({ id, platformId }) // 列表查询构建器同样以 platformId 收窄范围 const queryBuilder eventDestinationRepo() .createQueryBuilder(event_destination) .where({ platformId })注意更新与删除操作把platformId与id一起作为FindOptionsWhere条件即便调用方传入了一个属于其他平台的 ID复合条件也无法命中任何行这是规则一在写操作上的典型应用。更进一步事件触发的查询还演示了规则一与规则二的组合使用见下文第三部分。2.2 流程版本Flow Version项目维度的等值过滤在 packages/server/api/src/app/app-connection/app-connection-service/app-connection.handler.ts 中统计引用某连接的项目内流程数量时使用createQueryBuilder同时约束项目与发布版本const query flowVersionRepo() .createQueryBuilder(flow_version) .innerJoin(flow, flow, flow.id flow_version.flowId) .where(flow.projectId :projectId, { projectId }) .andWhere(flow_version.id flow.publishedVersionId) .andWhere(flow_version.connectionIds :externalIds, { externalIds: [externalId] })这里flow.projectId :projectId就是规则一最直接的形式——通过项目 ID 的等值过滤把统计范围严格锁定在单个项目内。三、规则二的源码落地projectIds数组列与ArrayContains规则二针对的是 Activepieces 中一类特殊资源应用连接App Connection。在 packages/core/shared/src/lib/automation/app-connection/app-connection.ts 中可以清晰看到这类资源的数据模型设计export enum AppConnectionScope { PROJECT PROJECT, PLATFORM PLATFORM, } // AppConnection 实体核心字段节选 { externalId: string type: Type scope: AppConnectionScope pieceName: string displayName: string projectIds: string[] // ← 关键连接可同时归属于多个项目 platformId: string status: AppConnectionStatus }从类型定义app-connection.ts 中的 zod schema可以看到projectIds: z.array(ApId)即该字段是一个项目 ID 数组。这意味着一个连接可能同时被多个项目共享——典型场景是平台级PLATFORM作用域连接或跨项目复用的 OAuth2 凭据。由于列是数组而非标量WHERE projectIds :projectId这类等值查询永远无法命中必须借助 PostgreSQL 的数组包含语义。这正是ArrayContains的用途。3.1 查找projectIds: ArrayContains([projectId])在 app-connection-service.ts 中upsert判断同作用域同 externalId 的连接是否已存在时const existingConnection await appConnectionsRepo().findOneBy({ externalId, scope, platformId, ...(projectIds ? { projectIds: ArrayContains(projectIds) } : {}), })而在 app-connection.handler.ts 中连接刷新与校验同样严格遵循该模式// lockAndRefreshConnection按项目维度定位连接 const encryptedAppConnection await appConnectionsRepo().findOneBy({ projectIds: ArrayContains([projectId]), externalId, }) // revalidateConnection同时携带 id / platformId / projectIds 三重条件 const encryptedAppConnection await appConnectionsRepo().findOneBy({ id, platformId, projectIds: ArrayContains([projectId]), })可以看到即使已经用id精确定位代码仍然叠加platformId与projectIds: ArrayContains([projectId])把多租户过滤做成了条件三件套——这正是规则一与规则二在同一查询中的协同体现。3.2 数组列在一对多查询中的其他用法规则二使用ArrayContains判断数组包含某值语义在 template.service.ts 中还可以看到同族操作符ArrayOverlap语义判断两个数组是否有交集的用法if (pieces) { commonFilters.pieces ArrayOverlap(pieces) } if (category) { commonFilters.categories ArrayContains([category]) }这说明 Activepieces 在数组列上同时使用了包含ArrayContains与重叠ArrayOverlap两类操作符规则二强调的场景一个项目 ID 是否属于连接的projectIds数组用ArrayContains([projectId])而模板按多个 piece 过滤的场景则用ArrayOverlap。二者都要求查询条件与列数据类型一致且都依赖 PostgreSQL 数组类型的原生支持。四、跨模块联动一条查询中的规则一 规则二组合事件目的地模块的触发逻辑event-destinations.service.ts是两条规则协同工作的完整示例const conditions: FindOptionsWhereEventDestinationSchema[] [{ platformId, // 规则一平台维度 events: ArrayContains([event.action]), // 规则二数组列包含 scope: EventDestinationScope.PLATFORM, }] const broadcastToProject !isNil(projectId) PROJECT_SCOPE_EVENTS.includes(event.action) if (broadcastToProject) { conditions.push({ platformId, // 规则一平台维度 projectId, // 规则一项目维度等值 events: ArrayContains([event.action]), // 规则二数组列包含 scope: EventDestinationScope.PROJECT, }) }这段代码清晰地展示了过滤条件的组织方式平台级目的地只按platformId过滤规则一即可因为作用域为PLATFORM项目级目的地必须同时按platformId与projectId过滤规则一的完整形态保证事件只会派发到归属项目自己的目的地事件类型匹配events同样是数组列因此使用ArrayContains([event.action])判断事件是否在订阅清单中规则二。五、为什么必须是ArrayContains底层语义与常见误区从 TypeORM 与 PostgreSQL 的对应关系看TypeORM 操作符PostgreSQL 操作符语义适用场景Equal等值比较标量列如platformId、projectIdArrayContains(arr)左数组包含右数组全部元素projectIds数组列包含指定项目 IDArrayOverlap(arr)两数组存在交集按多个值过滤数组列因此查询这个连接属于哪个项目时projectIds: ArrayContains([projectId])是唯一正确的写法直接把projectId与projectIds做等值比较属于常见误区会因类型不匹配或语义错误导致查询失效进而可能退化为不过滤或异常直接违背多租户安全底线反向操作符如数组被包含虽然在 PostgreSQL 中存在但 Activepieces 规范明确要求以ArrayContains([projectId])的正向写法统一表达项目属于连接这一语义便于代码审查者一眼识别过滤意图。六、实践清单新增查询时的自检项结合规则文档与上述源码在 Activepieces 服务端新增或修改查询时建议按以下清单自检租户维度是否齐全查询是否携带了platformId或projectId过滤写操作update/delete是否把租户条件与主键一起放入FindOptionsWhere列类型是否匹配目标列是标量platformId、projectId还是数组projectIds、events、connectionIds数组列必须使用ArrayContains包含或ArrayOverlap交集不能用等值比较。共享资源是否走数组语义连接、事件目的地等多项目/多事件资源是否按规范使用ArrayContains([projectId])/ArrayContains([event.action])复合条件是否防越权即使已按id定位是否仍叠加租户条件参考 app-connection.handler.ts 的条件三件套查询构建器与仓储 API 是否一致createQueryBuilder中的.where(flow.projectId :projectId)与repo.findOneBy({ projectIds: ArrayContains([projectId]) })都遵循同一套租户过滤原则不应在重构时丢失。结语.claude/rules/data-isolation.md 用两句话定义了 Activepieces 后端多租户安全的全部底线而其威力来自源码中的系统性落地无论是 app-connection-service.ts 的 upsert 幂等判断、app-connection.handler.ts 的连接刷新与校验还是 event-destinations.service.ts 的事件派发都能看到platformId/projectId过滤与ArrayContains数组包含查询的组合。理解并遵守这两条规则是保障多租户隔离、避免跨项目数据泄漏的前提将它们作为代码审查的强制项则是把安全从规范变成事实的关键。【免费下载链接】activepiecesAI Agents MCPs AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows AI Agents • MCPs for AI Agents项目地址: https://gitcode.com/GitHub_Trending/ac/activepieces创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表