feat: add enterprise Phase 1-4 feature flags and new permissions

This commit is contained in:
SnapOtter
2026-06-13 16:20:24 +08:00
parent cafd2d8b65
commit 86d6f50ea6
6 changed files with 70 additions and 3 deletions
+3
View File
@@ -20,6 +20,9 @@ const ROLE_PERMISSIONS: Record<Role, Permission[]> = {
"features:manage", "features:manage",
"system:health", "system:health",
"audit:read", "audit:read",
"compliance:manage",
"webhooks:manage",
"security:manage",
], ],
editor: [ editor: [
"tools:use", "tools:use",
+3
View File
@@ -211,6 +211,9 @@ export async function ensureBuiltinRoles(): Promise<void> {
"features:manage", "features:manage",
"system:health", "system:health",
"audit:read", "audit:read",
"compliance:manage",
"webhooks:manage",
"security:manage",
], ],
isBuiltin: true, isBuiltin: true,
}, },
+2 -1
View File
@@ -2,6 +2,7 @@ import {
ENTERPRISE_FEATURES, ENTERPRISE_FEATURES,
type EnterpriseFeature, type EnterpriseFeature,
type LicensePayload, type LicensePayload,
PLAN_FEATURES,
validateLicense, validateLicense,
} from "./license.js"; } from "./license.js";
@@ -31,4 +32,4 @@ export type S3StorageModule = typeof import("./storage-s3.js");
export async function loadS3Storage(): Promise<S3StorageModule> { export async function loadS3Storage(): Promise<S3StorageModule> {
return import("./storage-s3.js"); return import("./storage-s3.js");
} }
export { ENTERPRISE_FEATURES, type EnterpriseFeature, type LicensePayload }; export { ENTERPRISE_FEATURES, PLAN_FEATURES, type EnterpriseFeature, type LicensePayload };
+20 -1
View File
@@ -13,12 +13,31 @@ export const ENTERPRISE_FEATURES = [
"audit_export", "audit_export",
"mfa", "mfa",
"per_tool_permissions", "per_tool_permissions",
"siem_forwarding",
"tamper_resistant_audit",
"legal_hold",
"gdpr_lifecycle",
"team_retention_overrides",
"sso_enforcement",
"ip_allowlist",
"config_export_import",
"upgrade_management",
"admin_alerts",
] as const; ] as const;
export type EnterpriseFeature = (typeof ENTERPRISE_FEATURES)[number]; export type EnterpriseFeature = (typeof ENTERPRISE_FEATURES)[number];
export const PLAN_FEATURES: Record<string, readonly EnterpriseFeature[]> = { export const PLAN_FEATURES: Record<string, readonly EnterpriseFeature[]> = {
team: ["saml_sso", "s3_storage", "multi_tenancy"], team: [
"saml_sso",
"s3_storage",
"multi_tenancy",
"audit_export",
"siem_forwarding",
"sso_enforcement",
"upgrade_management",
"admin_alerts",
],
enterprise: ENTERPRISE_FEATURES, enterprise: ENTERPRISE_FEATURES,
}; };
+4 -1
View File
@@ -12,6 +12,9 @@ export type Permission =
| "teams:manage" | "teams:manage"
| "features:manage" | "features:manage"
| "system:health" | "system:health"
| "audit:read"; | "audit:read"
| "compliance:manage"
| "webhooks:manage"
| "security:manage";
export type Role = "admin" | "editor" | "user"; export type Role = "admin" | "editor" | "user";
+38
View File
@@ -0,0 +1,38 @@
import { describe, expect, it } from "vitest";
import { ENTERPRISE_FEATURES, PLAN_FEATURES } from "@snapotter/enterprise";
describe("enterprise feature flags", () => {
it("includes all Phase 1 flags", () => {
expect(ENTERPRISE_FEATURES).toContain("siem_forwarding");
expect(ENTERPRISE_FEATURES).toContain("tamper_resistant_audit");
});
it("includes all Phase 2-4 flags", () => {
expect(ENTERPRISE_FEATURES).toContain("legal_hold");
expect(ENTERPRISE_FEATURES).toContain("gdpr_lifecycle");
expect(ENTERPRISE_FEATURES).toContain("admin_alerts");
});
it("team plan includes operational features", () => {
expect(PLAN_FEATURES.team).toContain("siem_forwarding");
expect(PLAN_FEATURES.team).toContain("audit_export");
expect(PLAN_FEATURES.team).toContain("upgrade_management");
expect(PLAN_FEATURES.team).toContain("admin_alerts");
});
it("team plan does NOT include compliance features", () => {
expect(PLAN_FEATURES.team).not.toContain("tamper_resistant_audit");
expect(PLAN_FEATURES.team).not.toContain("legal_hold");
expect(PLAN_FEATURES.team).not.toContain("gdpr_lifecycle");
});
it("enterprise plan includes everything", () => {
for (const feature of ENTERPRISE_FEATURES) {
expect(PLAN_FEATURES.enterprise).toContain(feature);
}
});
it("has exactly 18 features total", () => {
expect(ENTERPRISE_FEATURES).toHaveLength(18);
});
});