-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathindex.ts
More file actions
160 lines (150 loc) · 6.99 KB
/
Copy pathindex.ts
File metadata and controls
160 lines (150 loc) · 6.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
/**
* Security capability chain — a role hierarchy, a permission set that layers
* object CRUD + field-level security (FLS) + row-level security (RLS), two
* sharing rules (criteria- and owner-based), and an org security policy.
* Together these exercise RBAC, FLS, RLS, and sharing in one place. These are
* plain objects validated by `defineStack` (which coerces string predicates
* and fills CRUD defaults).
*/
// ── Roles (hierarchy) ──────────────────────────────────────────────────────
export const ContributorRole = {
name: 'contributor',
label: 'Contributor',
description: 'Works tasks on their own projects.',
};
export const ManagerRole = {
name: 'manager',
label: 'Project Manager',
description: 'Manages projects and the contributors on them.',
parent: 'contributor',
};
export const ExecRole = {
name: 'exec',
label: 'Executive',
description: 'Read-all visibility for reporting.',
parent: 'manager',
};
// ── Permission set: CRUD + FLS + RLS ──────────────────────────────────────
export const ContributorPermissionSet = {
name: 'showcase_contributor',
label: 'Showcase Contributor',
description: 'Standard access for contributors, with budget fields hidden and row-level scoping to own records.',
isProfile: false,
objects: {
showcase_project: { allowRead: true, allowCreate: false, allowEdit: true, allowDelete: false },
showcase_task: { allowRead: true, allowCreate: true, allowEdit: true, allowDelete: false },
showcase_account: { allowRead: true, allowCreate: false, allowEdit: false, allowDelete: false },
// Invoice graph: contributors fully manage invoices + their lines. Read/write
// is scoped by the owner RLS below (invoice) and DERIVED for the lines, which
// are `controlled_by_parent` — no line RLS is authored (ADR-0055).
showcase_invoice: { allowRead: true, allowCreate: true, allowEdit: true, allowDelete: false },
showcase_invoice_line: { allowRead: true, allowCreate: true, allowEdit: true, allowDelete: false },
},
// Field-level security — contributors can read but not edit budget figures.
fields: {
budget: { readable: true, editable: false },
spent: { readable: true, editable: false },
budget_remaining: { readable: true, editable: false },
},
// Row-level security — contributors only see tasks assigned to them.
rowLevelSecurity: [
{
name: 'task_own_rows',
label: 'Own Tasks Only',
description: 'Contributors can only select tasks assigned to them.',
object: 'showcase_task',
operation: 'select' as const,
using: "assignee = current_user.email",
roles: ['contributor'],
enabled: true,
priority: 10,
},
// Owner RLS on the MASTER invoice. Because `showcase_invoice_line` is
// `controlled_by_parent`, a contributor seeing only their own invoices also
// sees only those invoices' lines — and can by-id read/write a line only when
// they can read/write its master (ADR-0055). No line rule is authored here.
{
name: 'invoice_own_rows',
label: 'Own Invoices Only',
description: "Contributors only see invoices they own; their lines follow via controlled_by_parent.",
object: 'showcase_invoice',
operation: 'select' as const,
using: "owner = current_user.email",
roles: ['contributor'],
enabled: true,
priority: 10,
},
],
};
// ── App-declared DEFAULT PROFILE (ADR-0056 D7) ──────────────────────────────
/**
* The showcase's default access posture for a freshly signed-up user who holds
* no explicit grants. `isDefault: true` makes the app declare what "a new member
* can do" instead of inheriting the built-in `member_default` wildcard. The CLI
* (`pnpm dev`) reads this off the stack and wires it as the SecurityPlugin
* fallback (ADR-0056 D7) — without that wiring an `isDefault` flag in app
* metadata is silently ignored. Deliberately read-mostly: a brand-new member can
* browse the shared catalog + announcements and file tasks/inquiries, but cannot
* edit or delete anyone's records (owner/OWD enforcement still applies on top).
*/
export const MemberDefaultProfile = {
name: 'showcase_member_default',
label: 'Showcase Member (Default)',
description: 'App-declared default profile for new sign-ups — read-mostly baseline (ADR-0056 D7).',
isProfile: true,
isDefault: true,
objects: {
showcase_account: { allowRead: true },
showcase_product: { allowRead: true },
showcase_project: { allowRead: true },
showcase_task: { allowRead: true, allowCreate: true },
showcase_announcement: { allowRead: true },
showcase_inquiry: { allowRead: true, allowCreate: true },
},
};
// ── Sharing rules ──────────────────────────────────────────────────────────
/** criteria-based: red-health projects are shared up to executives. */
export const RedProjectSharingRule = {
type: 'criteria' as const,
name: 'share_red_projects_with_execs',
label: 'Red Projects → Executives',
description: 'Automatically share at-risk (red health) projects with executives.',
object: 'showcase_project',
condition: "record.health == 'red'",
accessLevel: 'read' as const,
sharedWith: { type: 'role' as const, value: 'exec' },
active: true,
};
/** owner-based: a contributor's tasks are shared read-only with managers. */
export const ContributorTaskSharingRule = {
type: 'owner' as const,
name: 'share_contributor_tasks_with_manager',
label: "Contributor Tasks → Manager",
description: "Share each contributor's tasks with managers for oversight.",
object: 'showcase_task',
ownedBy: { type: 'role' as const, value: 'contributor' },
accessLevel: 'read' as const,
sharedWith: { type: 'role' as const, value: 'manager' },
active: true,
};
// ── Org security policy ─────────────────────────────────────────────────────
export const ShowcasePolicy = {
name: 'showcase_default_policy',
password: {
minLength: 12,
requireUppercase: true,
requireLowercase: true,
requireNumbers: true,
requireSymbols: true,
expirationDays: 90,
historyCount: 5,
},
session: { idleTimeout: 30, absoluteTimeout: 480, forceMfa: false },
audit: { logRetentionDays: 365, sensitiveFields: ['budget', 'spent'], captureRead: false },
isDefault: true,
};
export const allRoles = [ContributorRole, ManagerRole, ExecRole];
export const allPermissionSets = [ContributorPermissionSet, MemberDefaultProfile];
export const allSharingRules = [RedProjectSharingRule, ContributorTaskSharingRule];
export const allPolicies = [ShowcasePolicy];