mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-12 07:20:45 +00:00
Matrix: extract monitor access state
This commit is contained in:
45
extensions/matrix/src/matrix/monitor/access-state.test.ts
Normal file
45
extensions/matrix/src/matrix/monitor/access-state.test.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { resolveMatrixMonitorAccessState } from "./access-state.js";
|
||||
|
||||
describe("resolveMatrixMonitorAccessState", () => {
|
||||
it("normalizes effective allowlists once and exposes reusable matches", () => {
|
||||
const state = resolveMatrixMonitorAccessState({
|
||||
allowFrom: ["matrix:@Alice:Example.org"],
|
||||
storeAllowFrom: ["user:@bob:example.org"],
|
||||
groupAllowFrom: ["@Carol:Example.org"],
|
||||
roomUsers: ["user:@Dana:Example.org"],
|
||||
senderId: "@dana:example.org",
|
||||
isRoom: true,
|
||||
});
|
||||
|
||||
expect(state.effectiveAllowFrom).toEqual([
|
||||
"matrix:@alice:example.org",
|
||||
"user:@bob:example.org",
|
||||
]);
|
||||
expect(state.effectiveGroupAllowFrom).toEqual(["@carol:example.org"]);
|
||||
expect(state.effectiveRoomUsers).toEqual(["user:@dana:example.org"]);
|
||||
expect(state.directAllowMatch.allowed).toBe(false);
|
||||
expect(state.roomUserMatch?.allowed).toBe(true);
|
||||
expect(state.groupAllowMatch?.allowed).toBe(false);
|
||||
expect(state.commandAuthorizers).toEqual([
|
||||
{ configured: true, allowed: false },
|
||||
{ configured: true, allowed: true },
|
||||
{ configured: true, allowed: false },
|
||||
]);
|
||||
});
|
||||
|
||||
it("keeps room-user matching disabled for dm traffic", () => {
|
||||
const state = resolveMatrixMonitorAccessState({
|
||||
allowFrom: [],
|
||||
storeAllowFrom: [],
|
||||
groupAllowFrom: ["@carol:example.org"],
|
||||
roomUsers: ["@dana:example.org"],
|
||||
senderId: "@dana:example.org",
|
||||
isRoom: false,
|
||||
});
|
||||
|
||||
expect(state.roomUserMatch).toBeNull();
|
||||
expect(state.commandAuthorizers[1]).toEqual({ configured: true, allowed: false });
|
||||
expect(state.commandAuthorizers[2]).toEqual({ configured: true, allowed: false });
|
||||
});
|
||||
});
|
||||
77
extensions/matrix/src/matrix/monitor/access-state.ts
Normal file
77
extensions/matrix/src/matrix/monitor/access-state.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
import { normalizeMatrixAllowList, resolveMatrixAllowListMatch } from "./allowlist.js";
|
||||
import type { MatrixAllowListMatch } from "./allowlist.js";
|
||||
|
||||
type MatrixCommandAuthorizer = {
|
||||
configured: boolean;
|
||||
allowed: boolean;
|
||||
};
|
||||
|
||||
export type MatrixMonitorAccessState = {
|
||||
effectiveAllowFrom: string[];
|
||||
effectiveGroupAllowFrom: string[];
|
||||
effectiveRoomUsers: string[];
|
||||
groupAllowConfigured: boolean;
|
||||
directAllowMatch: MatrixAllowListMatch;
|
||||
roomUserMatch: MatrixAllowListMatch | null;
|
||||
groupAllowMatch: MatrixAllowListMatch | null;
|
||||
commandAuthorizers: [MatrixCommandAuthorizer, MatrixCommandAuthorizer, MatrixCommandAuthorizer];
|
||||
};
|
||||
|
||||
export function resolveMatrixMonitorAccessState(params: {
|
||||
allowFrom: Array<string | number>;
|
||||
storeAllowFrom: Array<string | number>;
|
||||
groupAllowFrom: Array<string | number>;
|
||||
roomUsers: Array<string | number>;
|
||||
senderId: string;
|
||||
isRoom: boolean;
|
||||
}): MatrixMonitorAccessState {
|
||||
const effectiveAllowFrom = normalizeMatrixAllowList([
|
||||
...params.allowFrom,
|
||||
...params.storeAllowFrom,
|
||||
]);
|
||||
const effectiveGroupAllowFrom = normalizeMatrixAllowList(params.groupAllowFrom);
|
||||
const effectiveRoomUsers = normalizeMatrixAllowList(params.roomUsers);
|
||||
|
||||
const directAllowMatch = resolveMatrixAllowListMatch({
|
||||
allowList: effectiveAllowFrom,
|
||||
userId: params.senderId,
|
||||
});
|
||||
const roomUserMatch =
|
||||
params.isRoom && effectiveRoomUsers.length > 0
|
||||
? resolveMatrixAllowListMatch({
|
||||
allowList: effectiveRoomUsers,
|
||||
userId: params.senderId,
|
||||
})
|
||||
: null;
|
||||
const groupAllowMatch =
|
||||
effectiveGroupAllowFrom.length > 0
|
||||
? resolveMatrixAllowListMatch({
|
||||
allowList: effectiveGroupAllowFrom,
|
||||
userId: params.senderId,
|
||||
})
|
||||
: null;
|
||||
|
||||
return {
|
||||
effectiveAllowFrom,
|
||||
effectiveGroupAllowFrom,
|
||||
effectiveRoomUsers,
|
||||
groupAllowConfigured: effectiveGroupAllowFrom.length > 0,
|
||||
directAllowMatch,
|
||||
roomUserMatch,
|
||||
groupAllowMatch,
|
||||
commandAuthorizers: [
|
||||
{
|
||||
configured: effectiveAllowFrom.length > 0,
|
||||
allowed: directAllowMatch.allowed,
|
||||
},
|
||||
{
|
||||
configured: effectiveRoomUsers.length > 0,
|
||||
allowed: roomUserMatch?.allowed ?? false,
|
||||
},
|
||||
{
|
||||
configured: effectiveGroupAllowFrom.length > 0,
|
||||
allowed: groupAllowMatch?.allowed ?? false,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
@@ -24,12 +24,8 @@ import {
|
||||
sendReadReceiptMatrix,
|
||||
sendTypingMatrix,
|
||||
} from "../send.js";
|
||||
import { resolveMatrixMonitorAccessState } from "./access-state.js";
|
||||
import { resolveMatrixAckReactionConfig } from "./ack-config.js";
|
||||
import {
|
||||
normalizeMatrixAllowList,
|
||||
resolveMatrixAllowListMatch,
|
||||
resolveMatrixAllowListMatches,
|
||||
} from "./allowlist.js";
|
||||
import { resolveMatrixLocation, type MatrixLocationPayload } from "./location.js";
|
||||
import { downloadMatrixMedia } from "./media.js";
|
||||
import { resolveMentions } from "./mentions.js";
|
||||
@@ -55,6 +51,7 @@ export type MatrixMonitorHandlerParams = {
|
||||
logger: RuntimeLogger;
|
||||
logVerboseMessage: (message: string) => void;
|
||||
allowFrom: string[];
|
||||
groupAllowFrom?: string[];
|
||||
roomsConfig?: Record<string, MatrixRoomConfig>;
|
||||
mentionRegexes: ReturnType<PluginRuntime["channel"]["mentions"]["buildMentionRegexes"]>;
|
||||
groupPolicy: "open" | "allowlist" | "disabled";
|
||||
@@ -89,6 +86,7 @@ export function createMatrixRoomMessageHandler(params: MatrixMonitorHandlerParam
|
||||
logger,
|
||||
logVerboseMessage,
|
||||
allowFrom,
|
||||
groupAllowFrom = [],
|
||||
roomsConfig,
|
||||
mentionRegexes,
|
||||
groupPolicy,
|
||||
@@ -290,22 +288,33 @@ export function createMatrixRoomMessageHandler(params: MatrixMonitorHandlerParam
|
||||
|
||||
const senderName = await getMemberDisplayName(roomId, senderId);
|
||||
const storeAllowFrom = await readStoreAllowFrom();
|
||||
const effectiveAllowFrom = normalizeMatrixAllowList([...allowFrom, ...storeAllowFrom]);
|
||||
const groupAllowFrom = cfg.channels?.["matrix"]?.groupAllowFrom ?? [];
|
||||
const effectiveGroupAllowFrom = normalizeMatrixAllowList(groupAllowFrom);
|
||||
const groupAllowConfigured = effectiveGroupAllowFrom.length > 0;
|
||||
const roomUsers = roomConfig?.users ?? [];
|
||||
const accessState = resolveMatrixMonitorAccessState({
|
||||
allowFrom,
|
||||
storeAllowFrom,
|
||||
groupAllowFrom,
|
||||
roomUsers,
|
||||
senderId,
|
||||
isRoom,
|
||||
});
|
||||
const {
|
||||
effectiveAllowFrom,
|
||||
effectiveGroupAllowFrom,
|
||||
effectiveRoomUsers,
|
||||
groupAllowConfigured,
|
||||
directAllowMatch,
|
||||
roomUserMatch,
|
||||
groupAllowMatch,
|
||||
commandAuthorizers,
|
||||
} = accessState;
|
||||
|
||||
if (isDirectMessage) {
|
||||
if (!dmEnabled || dmPolicy === "disabled") {
|
||||
return;
|
||||
}
|
||||
if (dmPolicy !== "open") {
|
||||
const allowMatch = resolveMatrixAllowListMatch({
|
||||
allowList: effectiveAllowFrom,
|
||||
userId: senderId,
|
||||
});
|
||||
const allowMatchMeta = formatAllowlistMatchMeta(allowMatch);
|
||||
if (!allowMatch.allowed) {
|
||||
const allowMatchMeta = formatAllowlistMatchMeta(directAllowMatch);
|
||||
if (!directAllowMatch.allowed) {
|
||||
if (!isReactionEvent && dmPolicy === "pairing") {
|
||||
const { code, created } = await core.channel.pairing.upsertPairingRequest({
|
||||
channel: "matrix",
|
||||
@@ -351,27 +360,21 @@ export function createMatrixRoomMessageHandler(params: MatrixMonitorHandlerParam
|
||||
}
|
||||
}
|
||||
|
||||
const roomUsers = roomConfig?.users ?? [];
|
||||
if (isRoom && roomUsers.length > 0) {
|
||||
const userMatch = resolveMatrixAllowListMatch({
|
||||
allowList: normalizeMatrixAllowList(roomUsers),
|
||||
userId: senderId,
|
||||
});
|
||||
if (!userMatch.allowed) {
|
||||
logVerboseMessage(
|
||||
`matrix: blocked sender ${senderId} (room users allowlist, ${roomMatchMeta}, ${formatAllowlistMatchMeta(
|
||||
userMatch,
|
||||
)})`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (isRoom && roomUserMatch && !roomUserMatch.allowed) {
|
||||
logVerboseMessage(
|
||||
`matrix: blocked sender ${senderId} (room users allowlist, ${roomMatchMeta}, ${formatAllowlistMatchMeta(
|
||||
roomUserMatch,
|
||||
)})`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (isRoom && groupPolicy === "allowlist" && roomUsers.length === 0 && groupAllowConfigured) {
|
||||
const groupAllowMatch = resolveMatrixAllowListMatch({
|
||||
allowList: effectiveGroupAllowFrom,
|
||||
userId: senderId,
|
||||
});
|
||||
if (!groupAllowMatch.allowed) {
|
||||
if (
|
||||
isRoom &&
|
||||
groupPolicy === "allowlist" &&
|
||||
effectiveRoomUsers.length === 0 &&
|
||||
groupAllowConfigured
|
||||
) {
|
||||
if (groupAllowMatch && !groupAllowMatch.allowed) {
|
||||
logVerboseMessage(
|
||||
`matrix: blocked sender ${senderId} (groupAllowFrom, ${roomMatchMeta}, ${formatAllowlistMatchMeta(
|
||||
groupAllowMatch,
|
||||
@@ -456,31 +459,10 @@ export function createMatrixRoomMessageHandler(params: MatrixMonitorHandlerParam
|
||||
surface: "matrix",
|
||||
});
|
||||
const useAccessGroups = cfg.commands?.useAccessGroups !== false;
|
||||
const senderAllowedForCommands = resolveMatrixAllowListMatches({
|
||||
allowList: effectiveAllowFrom,
|
||||
userId: senderId,
|
||||
});
|
||||
const senderAllowedForGroup = groupAllowConfigured
|
||||
? resolveMatrixAllowListMatches({
|
||||
allowList: effectiveGroupAllowFrom,
|
||||
userId: senderId,
|
||||
})
|
||||
: false;
|
||||
const senderAllowedForRoomUsers =
|
||||
isRoom && roomUsers.length > 0
|
||||
? resolveMatrixAllowListMatches({
|
||||
allowList: normalizeMatrixAllowList(roomUsers),
|
||||
userId: senderId,
|
||||
})
|
||||
: false;
|
||||
const hasControlCommandInMessage = core.channel.text.hasControlCommand(bodyText, cfg);
|
||||
const commandGate = resolveControlCommandGate({
|
||||
useAccessGroups,
|
||||
authorizers: [
|
||||
{ configured: effectiveAllowFrom.length > 0, allowed: senderAllowedForCommands },
|
||||
{ configured: roomUsers.length > 0, allowed: senderAllowedForRoomUsers },
|
||||
{ configured: groupAllowConfigured, allowed: senderAllowedForGroup },
|
||||
],
|
||||
authorizers: commandAuthorizers,
|
||||
allowTextCommands,
|
||||
hasControlCommand: hasControlCommandInMessage,
|
||||
});
|
||||
|
||||
@@ -316,6 +316,7 @@ export async function monitorMatrixProvider(opts: MonitorMatrixOpts = {}): Promi
|
||||
logger,
|
||||
logVerboseMessage,
|
||||
allowFrom,
|
||||
groupAllowFrom,
|
||||
roomsConfig,
|
||||
mentionRegexes,
|
||||
groupPolicy,
|
||||
|
||||
Reference in New Issue
Block a user