refactor: share matched group policy evaluation

This commit is contained in:
Peter Steinberger
2026-03-07 23:39:04 +00:00
parent f319ec2dac
commit b0d9246768
8 changed files with 293 additions and 28 deletions

View File

@@ -1,6 +1,7 @@
import { describe, expect, it } from "vitest";
import {
evaluateGroupRouteAccessForPolicy,
evaluateMatchedGroupAccessForPolicy,
evaluateSenderGroupAccess,
evaluateSenderGroupAccessForPolicy,
resolveSenderScopedGroupPolicy,
@@ -120,6 +121,64 @@ describe("evaluateGroupRouteAccessForPolicy", () => {
});
});
describe("evaluateMatchedGroupAccessForPolicy", () => {
it("blocks disabled policy", () => {
expect(
evaluateMatchedGroupAccessForPolicy({
groupPolicy: "disabled",
allowlistConfigured: true,
allowlistMatched: true,
}),
).toEqual({
allowed: false,
groupPolicy: "disabled",
reason: "disabled",
});
});
it("blocks allowlist without configured entries", () => {
expect(
evaluateMatchedGroupAccessForPolicy({
groupPolicy: "allowlist",
allowlistConfigured: false,
allowlistMatched: false,
}),
).toEqual({
allowed: false,
groupPolicy: "allowlist",
reason: "empty_allowlist",
});
});
it("blocks unmatched allowlist sender", () => {
expect(
evaluateMatchedGroupAccessForPolicy({
groupPolicy: "allowlist",
allowlistConfigured: true,
allowlistMatched: false,
}),
).toEqual({
allowed: false,
groupPolicy: "allowlist",
reason: "not_allowlisted",
});
});
it("allows open policy", () => {
expect(
evaluateMatchedGroupAccessForPolicy({
groupPolicy: "open",
allowlistConfigured: false,
allowlistMatched: false,
}),
).toEqual({
allowed: true,
groupPolicy: "open",
reason: "allowed",
});
});
});
describe("evaluateSenderGroupAccess", () => {
it("defaults missing provider config to allowlist", () => {
const decision = evaluateSenderGroupAccess({

View File

@@ -27,6 +27,18 @@ export type GroupRouteAccessDecision = {
reason: GroupRouteAccessReason;
};
export type MatchedGroupAccessReason =
| "allowed"
| "disabled"
| "empty_allowlist"
| "not_allowlisted";
export type MatchedGroupAccessDecision = {
allowed: boolean;
groupPolicy: GroupPolicy;
reason: MatchedGroupAccessReason;
};
export function resolveSenderScopedGroupPolicy(params: {
groupPolicy: GroupPolicy;
groupAllowFrom: string[];
@@ -83,6 +95,43 @@ export function evaluateGroupRouteAccessForPolicy(params: {
};
}
export function evaluateMatchedGroupAccessForPolicy(params: {
groupPolicy: GroupPolicy;
allowlistConfigured: boolean;
allowlistMatched: boolean;
}): MatchedGroupAccessDecision {
if (params.groupPolicy === "disabled") {
return {
allowed: false,
groupPolicy: params.groupPolicy,
reason: "disabled",
};
}
if (params.groupPolicy === "allowlist") {
if (!params.allowlistConfigured) {
return {
allowed: false,
groupPolicy: params.groupPolicy,
reason: "empty_allowlist",
};
}
if (!params.allowlistMatched) {
return {
allowed: false,
groupPolicy: params.groupPolicy,
reason: "not_allowlisted",
};
}
}
return {
allowed: true,
groupPolicy: params.groupPolicy,
reason: "allowed",
};
}
export function evaluateSenderGroupAccessForPolicy(params: {
groupPolicy: GroupPolicy;
providerMissingFallbackApplied?: boolean;

View File

@@ -280,11 +280,14 @@ export {
} from "./allow-from.js";
export {
evaluateGroupRouteAccessForPolicy,
evaluateMatchedGroupAccessForPolicy,
evaluateSenderGroupAccess,
evaluateSenderGroupAccessForPolicy,
resolveSenderScopedGroupPolicy,
type GroupRouteAccessDecision,
type GroupRouteAccessReason,
type MatchedGroupAccessDecision,
type MatchedGroupAccessReason,
type SenderGroupAccessDecision,
type SenderGroupAccessReason,
} from "./group-access.js";

View File

@@ -37,6 +37,7 @@ export type { ChannelPlugin } from "../channels/plugins/types.plugin.js";
export { createReplyPrefixOptions } from "../channels/reply-prefix.js";
export type { OpenClawConfig } from "../config/config.js";
export { mapAllowFromEntries } from "./channel-config-helpers.js";
export { evaluateMatchedGroupAccessForPolicy } from "./group-access.js";
export {
GROUP_POLICY_BLOCKED_LABEL,
resolveAllowlistProviderRuntimeGroupPolicy,