guardrail-core 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/autopilot.test.d.ts +7 -0
- package/dist/__tests__/autopilot.test.d.ts.map +1 -0
- package/dist/__tests__/autopilot.test.js +156 -0
- package/dist/__tests__/tier-config.test.d.ts +9 -0
- package/dist/__tests__/tier-config.test.d.ts.map +1 -0
- package/dist/__tests__/tier-config.test.js +230 -0
- package/dist/__tests__/utils/hash-inline.test.d.ts +2 -0
- package/dist/__tests__/utils/hash-inline.test.d.ts.map +1 -0
- package/dist/__tests__/utils/hash-inline.test.js +62 -0
- package/dist/__tests__/utils/hash.test.d.ts +3 -0
- package/dist/__tests__/utils/hash.test.d.ts.map +1 -0
- package/dist/__tests__/utils/hash.test.js +95 -0
- package/dist/__tests__/utils/simple.test.d.ts +1 -0
- package/dist/__tests__/utils/simple.test.d.ts.map +1 -0
- package/dist/__tests__/utils/simple.test.js +10 -0
- package/dist/__tests__/utils/utils-simple.test.d.ts +1 -0
- package/dist/__tests__/utils/utils-simple.test.d.ts.map +1 -0
- package/dist/__tests__/utils/utils-simple.test.js +6 -0
- package/dist/__tests__/utils/utils.test.d.ts +15 -0
- package/dist/__tests__/utils/utils.test.d.ts.map +1 -0
- package/dist/__tests__/utils/utils.test.js +172 -0
- package/dist/autopilot/autopilot-runner.d.ts +33 -0
- package/dist/autopilot/autopilot-runner.d.ts.map +1 -0
- package/dist/autopilot/autopilot-runner.js +479 -0
- package/dist/autopilot/index.d.ts +6 -0
- package/dist/autopilot/index.d.ts.map +1 -0
- package/dist/autopilot/index.js +25 -0
- package/dist/autopilot/types.d.ts +102 -0
- package/dist/autopilot/types.d.ts.map +1 -0
- package/dist/autopilot/types.js +18 -0
- package/dist/cache/index.d.ts +7 -0
- package/dist/cache/index.d.ts.map +1 -0
- package/dist/cache/index.js +22 -0
- package/dist/cache/redis-cache.d.ts +145 -0
- package/dist/cache/redis-cache.d.ts.map +1 -0
- package/dist/cache/redis-cache.js +459 -0
- package/dist/ci/github-actions.d.ts +77 -0
- package/dist/ci/github-actions.d.ts.map +1 -0
- package/dist/ci/github-actions.js +277 -0
- package/dist/ci/index.d.ts +12 -0
- package/dist/ci/index.d.ts.map +1 -0
- package/dist/ci/index.js +27 -0
- package/dist/ci/pre-commit.d.ts +65 -0
- package/dist/ci/pre-commit.d.ts.map +1 -0
- package/dist/ci/pre-commit.js +286 -0
- package/dist/entitlements.d.ts +149 -0
- package/dist/entitlements.d.ts.map +1 -0
- package/dist/entitlements.js +464 -0
- package/dist/env.d.ts +113 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +204 -0
- package/dist/fix-packs/__tests__/generate-fix-packs.test.d.ts +7 -0
- package/dist/fix-packs/__tests__/generate-fix-packs.test.d.ts.map +1 -0
- package/dist/fix-packs/__tests__/generate-fix-packs.test.js +250 -0
- package/dist/fix-packs/generate-fix-packs.d.ts +15 -0
- package/dist/fix-packs/generate-fix-packs.d.ts.map +1 -0
- package/dist/fix-packs/generate-fix-packs.js +505 -0
- package/dist/fix-packs/index.d.ts +8 -0
- package/dist/fix-packs/index.d.ts.map +1 -0
- package/dist/fix-packs/index.js +23 -0
- package/dist/fix-packs/types.d.ts +113 -0
- package/dist/fix-packs/types.d.ts.map +1 -0
- package/dist/fix-packs/types.js +71 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +28 -0
- package/dist/metrics/prometheus.d.ts +99 -0
- package/dist/metrics/prometheus.d.ts.map +1 -0
- package/dist/metrics/prometheus.js +306 -0
- package/dist/quota-ledger.d.ts +119 -0
- package/dist/quota-ledger.d.ts.map +1 -0
- package/dist/quota-ledger.js +462 -0
- package/dist/rbac/__tests__/permissions.test.d.ts +8 -0
- package/dist/rbac/__tests__/permissions.test.d.ts.map +1 -0
- package/dist/rbac/__tests__/permissions.test.js +350 -0
- package/dist/rbac/index.d.ts +9 -0
- package/dist/rbac/index.d.ts.map +1 -0
- package/dist/rbac/index.js +32 -0
- package/dist/rbac/permissions.d.ts +71 -0
- package/dist/rbac/permissions.d.ts.map +1 -0
- package/dist/rbac/permissions.js +247 -0
- package/dist/rbac/types.d.ts +69 -0
- package/dist/rbac/types.d.ts.map +1 -0
- package/dist/rbac/types.js +213 -0
- package/dist/tier-config.d.ts +203 -0
- package/dist/tier-config.d.ts.map +1 -0
- package/dist/tier-config.js +675 -0
- package/dist/types.d.ts +365 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/utils.d.ts +36 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +127 -0
- package/dist/verified-autofix/__tests__/format-validator.test.d.ts +11 -0
- package/dist/verified-autofix/__tests__/format-validator.test.d.ts.map +1 -0
- package/dist/verified-autofix/__tests__/format-validator.test.js +285 -0
- package/dist/verified-autofix/__tests__/pipeline.test.d.ts +11 -0
- package/dist/verified-autofix/__tests__/pipeline.test.d.ts.map +1 -0
- package/dist/verified-autofix/__tests__/pipeline.test.js +389 -0
- package/dist/verified-autofix/__tests__/repo-fingerprint.test.d.ts +11 -0
- package/dist/verified-autofix/__tests__/repo-fingerprint.test.d.ts.map +1 -0
- package/dist/verified-autofix/__tests__/repo-fingerprint.test.js +236 -0
- package/dist/verified-autofix/__tests__/workspace.test.d.ts +11 -0
- package/dist/verified-autofix/__tests__/workspace.test.d.ts.map +1 -0
- package/dist/verified-autofix/__tests__/workspace.test.js +314 -0
- package/dist/verified-autofix/format-validator.d.ts +101 -0
- package/dist/verified-autofix/format-validator.d.ts.map +1 -0
- package/dist/verified-autofix/format-validator.js +446 -0
- package/dist/verified-autofix/index.d.ts +14 -0
- package/dist/verified-autofix/index.d.ts.map +1 -0
- package/dist/verified-autofix/index.js +39 -0
- package/dist/verified-autofix/pipeline.d.ts +68 -0
- package/dist/verified-autofix/pipeline.d.ts.map +1 -0
- package/dist/verified-autofix/pipeline.js +330 -0
- package/dist/verified-autofix/repo-fingerprint.d.ts +56 -0
- package/dist/verified-autofix/repo-fingerprint.d.ts.map +1 -0
- package/dist/verified-autofix/repo-fingerprint.js +396 -0
- package/dist/verified-autofix/workspace.d.ts +83 -0
- package/dist/verified-autofix/workspace.d.ts.map +1 -0
- package/dist/verified-autofix/workspace.js +454 -0
- package/dist/verified-autofix.d.ts +182 -0
- package/dist/verified-autofix.d.ts.map +1 -0
- package/dist/verified-autofix.js +1021 -0
- package/dist/visualization/dependency-graph.d.ts +79 -0
- package/dist/visualization/dependency-graph.d.ts.map +1 -0
- package/dist/visualization/dependency-graph.js +399 -0
- package/dist/visualization/index.d.ts +5 -0
- package/dist/visualization/index.d.ts.map +1 -0
- package/dist/visualization/index.js +20 -0
- package/package.json +29 -0
- package/src/__tests__/autopilot.test.ts +196 -0
- package/src/__tests__/tier-config.test.ts +289 -0
- package/src/__tests__/utils/hash-inline.test.ts +76 -0
- package/src/__tests__/utils/hash.test.ts +119 -0
- package/src/__tests__/utils/simple.test.ts +10 -0
- package/src/__tests__/utils/utils-simple.test.ts +5 -0
- package/src/__tests__/utils/utils.test.ts +203 -0
- package/src/autopilot/autopilot-runner.ts +503 -0
- package/src/autopilot/index.ts +6 -0
- package/src/autopilot/types.ts +119 -0
- package/src/cache/index.ts +7 -0
- package/src/cache/redis-cache.d.ts +155 -0
- package/src/cache/redis-cache.d.ts.map +1 -0
- package/src/cache/redis-cache.ts +517 -0
- package/src/ci/github-actions.ts +335 -0
- package/src/ci/index.ts +12 -0
- package/src/ci/pre-commit.ts +338 -0
- package/src/db/usage-schema.prisma +114 -0
- package/src/entitlements.ts +570 -0
- package/src/env.d.ts +68 -0
- package/src/env.d.ts.map +1 -0
- package/src/env.ts +247 -0
- package/src/fix-packs/__tests__/generate-fix-packs.test.ts +317 -0
- package/src/fix-packs/generate-fix-packs.ts +577 -0
- package/src/fix-packs/index.ts +8 -0
- package/src/fix-packs/types.ts +206 -0
- package/src/index.d.ts +7 -0
- package/src/index.d.ts.map +1 -0
- package/src/index.ts +12 -0
- package/src/metrics/prometheus.d.ts +104 -0
- package/src/metrics/prometheus.d.ts.map +1 -0
- package/src/metrics/prometheus.ts +446 -0
- package/src/quota-ledger.ts +548 -0
- package/src/rbac/__tests__/permissions.test.ts +446 -0
- package/src/rbac/index.ts +46 -0
- package/src/rbac/permissions.ts +301 -0
- package/src/rbac/types.ts +298 -0
- package/src/tier-config.json +157 -0
- package/src/tier-config.ts +815 -0
- package/src/types.d.ts +365 -0
- package/src/types.d.ts.map +1 -0
- package/src/types.ts +441 -0
- package/src/utils.d.ts +36 -0
- package/src/utils.d.ts.map +1 -0
- package/src/utils.ts +140 -0
- package/src/verified-autofix/__tests__/format-validator.test.ts +335 -0
- package/src/verified-autofix/__tests__/pipeline.test.ts +419 -0
- package/src/verified-autofix/__tests__/repo-fingerprint.test.ts +241 -0
- package/src/verified-autofix/__tests__/workspace.test.ts +373 -0
- package/src/verified-autofix/format-validator.ts +517 -0
- package/src/verified-autofix/index.ts +63 -0
- package/src/verified-autofix/pipeline.ts +403 -0
- package/src/verified-autofix/repo-fingerprint.ts +459 -0
- package/src/verified-autofix/workspace.ts +531 -0
- package/src/verified-autofix.ts +1187 -0
- package/src/visualization/dependency-graph.d.ts +85 -0
- package/src/visualization/dependency-graph.d.ts.map +1 -0
- package/src/visualization/dependency-graph.ts +495 -0
- package/src/visualization/index.ts +5 -0
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RBAC Permission Tests
|
|
3
|
+
*
|
|
4
|
+
* Tests for role-based access control permission checking logic.
|
|
5
|
+
* Validates acceptance criteria for different roles.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
canAssignRole,
|
|
10
|
+
canRemoveMember,
|
|
11
|
+
compareRoles,
|
|
12
|
+
generatePermissionMatrix,
|
|
13
|
+
getEffectivePermissions,
|
|
14
|
+
getMinimumRoleForPermission,
|
|
15
|
+
hasAllPermissions,
|
|
16
|
+
hasAnyPermission,
|
|
17
|
+
hasPermission,
|
|
18
|
+
isRoleAtLeast,
|
|
19
|
+
isValidPermission,
|
|
20
|
+
isValidRole,
|
|
21
|
+
parseRole,
|
|
22
|
+
roleHasPermission,
|
|
23
|
+
} from '../permissions';
|
|
24
|
+
import {
|
|
25
|
+
Permission,
|
|
26
|
+
RBACContext,
|
|
27
|
+
Role,
|
|
28
|
+
ROLES,
|
|
29
|
+
} from '../types';
|
|
30
|
+
|
|
31
|
+
// =============================================================================
|
|
32
|
+
// TEST HELPERS
|
|
33
|
+
// =============================================================================
|
|
34
|
+
|
|
35
|
+
function createContext(role: Role, tier?: string): RBACContext {
|
|
36
|
+
return {
|
|
37
|
+
userId: 'user_123',
|
|
38
|
+
teamId: 'team_456',
|
|
39
|
+
role,
|
|
40
|
+
permissions: getEffectivePermissions(role),
|
|
41
|
+
tier,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// =============================================================================
|
|
46
|
+
// ROLE VALIDATION TESTS
|
|
47
|
+
// =============================================================================
|
|
48
|
+
|
|
49
|
+
describe('Role Validation', () => {
|
|
50
|
+
test('isValidRole returns true for valid roles', () => {
|
|
51
|
+
expect(isValidRole('owner')).toBe(true);
|
|
52
|
+
expect(isValidRole('admin')).toBe(true);
|
|
53
|
+
expect(isValidRole('dev')).toBe(true);
|
|
54
|
+
expect(isValidRole('viewer')).toBe(true);
|
|
55
|
+
expect(isValidRole('compliance-auditor')).toBe(true);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test('isValidRole returns false for invalid roles', () => {
|
|
59
|
+
expect(isValidRole('superadmin')).toBe(false);
|
|
60
|
+
expect(isValidRole('')).toBe(false);
|
|
61
|
+
expect(isValidRole('ADMIN')).toBe(false);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test('parseRole returns role for valid input', () => {
|
|
65
|
+
expect(parseRole('owner')).toBe('owner');
|
|
66
|
+
expect(parseRole('admin')).toBe('admin');
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test('parseRole returns null for invalid input', () => {
|
|
70
|
+
expect(parseRole('invalid')).toBeNull();
|
|
71
|
+
expect(parseRole('')).toBeNull();
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// =============================================================================
|
|
76
|
+
// PERMISSION VALIDATION TESTS
|
|
77
|
+
// =============================================================================
|
|
78
|
+
|
|
79
|
+
describe('Permission Validation', () => {
|
|
80
|
+
test('isValidPermission returns true for valid permissions', () => {
|
|
81
|
+
expect(isValidPermission('manage_team')).toBe(true);
|
|
82
|
+
expect(isValidPermission('view_audit')).toBe(true);
|
|
83
|
+
expect(isValidPermission('run_autopilot')).toBe(true);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test('isValidPermission returns false for invalid permissions', () => {
|
|
87
|
+
expect(isValidPermission('invalid_permission')).toBe(false);
|
|
88
|
+
expect(isValidPermission('')).toBe(false);
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// =============================================================================
|
|
93
|
+
// ROLE HIERARCHY TESTS
|
|
94
|
+
// =============================================================================
|
|
95
|
+
|
|
96
|
+
describe('Role Hierarchy', () => {
|
|
97
|
+
test('compareRoles returns correct hierarchy order', () => {
|
|
98
|
+
expect(compareRoles('owner', 'admin')).toBeGreaterThan(0);
|
|
99
|
+
expect(compareRoles('admin', 'dev')).toBeGreaterThan(0);
|
|
100
|
+
expect(compareRoles('dev', 'viewer')).toBeGreaterThan(0);
|
|
101
|
+
expect(compareRoles('viewer', 'owner')).toBeLessThan(0);
|
|
102
|
+
expect(compareRoles('admin', 'admin')).toBe(0);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test('isRoleAtLeast correctly compares roles', () => {
|
|
106
|
+
expect(isRoleAtLeast('owner', 'admin')).toBe(true);
|
|
107
|
+
expect(isRoleAtLeast('owner', 'owner')).toBe(true);
|
|
108
|
+
expect(isRoleAtLeast('admin', 'owner')).toBe(false);
|
|
109
|
+
expect(isRoleAtLeast('dev', 'viewer')).toBe(true);
|
|
110
|
+
expect(isRoleAtLeast('viewer', 'dev')).toBe(false);
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
// =============================================================================
|
|
115
|
+
// PERMISSION CHECKING TESTS
|
|
116
|
+
// =============================================================================
|
|
117
|
+
|
|
118
|
+
describe('Permission Checking', () => {
|
|
119
|
+
test('roleHasPermission returns correct values', () => {
|
|
120
|
+
expect(roleHasPermission('owner', 'manage_billing')).toBe(true);
|
|
121
|
+
expect(roleHasPermission('admin', 'manage_billing')).toBe(false);
|
|
122
|
+
expect(roleHasPermission('viewer', 'view_dashboard')).toBe(true);
|
|
123
|
+
expect(roleHasPermission('viewer', 'run_autopilot')).toBe(false);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
test('hasPermission returns allowed for valid permissions', () => {
|
|
127
|
+
const ownerContext = createContext('owner');
|
|
128
|
+
const result = hasPermission(ownerContext, 'manage_billing');
|
|
129
|
+
expect(result.allowed).toBe(true);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
test('hasPermission returns denied with reason for missing permissions', () => {
|
|
133
|
+
const viewerContext = createContext('viewer');
|
|
134
|
+
const result = hasPermission(viewerContext, 'manage_team');
|
|
135
|
+
expect(result.allowed).toBe(false);
|
|
136
|
+
expect(result.reason).toContain('manage_team');
|
|
137
|
+
expect(result.requiredPermissions).toContain('manage_team');
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
test('hasAllPermissions requires all permissions', () => {
|
|
141
|
+
const adminContext = createContext('admin');
|
|
142
|
+
|
|
143
|
+
const allPresent = hasAllPermissions(adminContext, ['manage_team', 'run_autopilot']);
|
|
144
|
+
expect(allPresent.allowed).toBe(true);
|
|
145
|
+
|
|
146
|
+
const someMissing = hasAllPermissions(adminContext, ['manage_team', 'manage_billing']);
|
|
147
|
+
expect(someMissing.allowed).toBe(false);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
test('hasAnyPermission allows with any matching permission', () => {
|
|
151
|
+
const devContext = createContext('dev');
|
|
152
|
+
|
|
153
|
+
const onePresent = hasAnyPermission(devContext, ['run_scan', 'manage_billing']);
|
|
154
|
+
expect(onePresent.allowed).toBe(true);
|
|
155
|
+
|
|
156
|
+
const nonePresent = hasAnyPermission(devContext, ['manage_billing', 'admin_settings']);
|
|
157
|
+
expect(nonePresent.allowed).toBe(false);
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
// =============================================================================
|
|
162
|
+
// ACCEPTANCE CRITERIA TESTS
|
|
163
|
+
// =============================================================================
|
|
164
|
+
|
|
165
|
+
describe('Acceptance Criteria: Compliance Auditor', () => {
|
|
166
|
+
const auditorContext = createContext('compliance-auditor');
|
|
167
|
+
|
|
168
|
+
test('can view audit logs', () => {
|
|
169
|
+
const result = hasPermission(auditorContext, 'view_audit');
|
|
170
|
+
expect(result.allowed).toBe(true);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
test('can export audit logs', () => {
|
|
174
|
+
const result = hasPermission(auditorContext, 'export_audit');
|
|
175
|
+
expect(result.allowed).toBe(true);
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
test('cannot edit policies', () => {
|
|
179
|
+
const result = hasPermission(auditorContext, 'manage_policies');
|
|
180
|
+
expect(result.allowed).toBe(false);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
test('cannot manage team', () => {
|
|
184
|
+
const result = hasPermission(auditorContext, 'manage_team');
|
|
185
|
+
expect(result.allowed).toBe(false);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
test('cannot run autopilot', () => {
|
|
189
|
+
const result = hasPermission(auditorContext, 'run_autopilot');
|
|
190
|
+
expect(result.allowed).toBe(false);
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
describe('Acceptance Criteria: Viewer', () => {
|
|
195
|
+
const viewerContext = createContext('viewer');
|
|
196
|
+
|
|
197
|
+
test('can see dashboard', () => {
|
|
198
|
+
const result = hasPermission(viewerContext, 'view_dashboard');
|
|
199
|
+
expect(result.allowed).toBe(true);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
test('can view reports', () => {
|
|
203
|
+
const result = hasPermission(viewerContext, 'view_reports');
|
|
204
|
+
expect(result.allowed).toBe(true);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
test('cannot run Reality Mode', () => {
|
|
208
|
+
const result = hasPermission(viewerContext, 'run_reality');
|
|
209
|
+
expect(result.allowed).toBe(false);
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
test('cannot run Autopilot', () => {
|
|
213
|
+
const result = hasPermission(viewerContext, 'run_autopilot');
|
|
214
|
+
expect(result.allowed).toBe(false);
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
test('cannot run scans', () => {
|
|
218
|
+
const result = hasPermission(viewerContext, 'run_scan');
|
|
219
|
+
expect(result.allowed).toBe(false);
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
test('cannot manage policies', () => {
|
|
223
|
+
const result = hasPermission(viewerContext, 'manage_policies');
|
|
224
|
+
expect(result.allowed).toBe(false);
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
describe('Acceptance Criteria: Developer', () => {
|
|
229
|
+
const devContext = createContext('dev');
|
|
230
|
+
|
|
231
|
+
test('can run scans', () => {
|
|
232
|
+
const result = hasPermission(devContext, 'run_scan');
|
|
233
|
+
expect(result.allowed).toBe(true);
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
test('can run Reality Mode', () => {
|
|
237
|
+
const result = hasPermission(devContext, 'run_reality');
|
|
238
|
+
expect(result.allowed).toBe(true);
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
test('can run fixes', () => {
|
|
242
|
+
const result = hasPermission(devContext, 'run_fix');
|
|
243
|
+
expect(result.allowed).toBe(true);
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
test('cannot run Autopilot', () => {
|
|
247
|
+
const result = hasPermission(devContext, 'run_autopilot');
|
|
248
|
+
expect(result.allowed).toBe(false);
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
test('cannot manage team', () => {
|
|
252
|
+
const result = hasPermission(devContext, 'manage_team');
|
|
253
|
+
expect(result.allowed).toBe(false);
|
|
254
|
+
});
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
describe('Acceptance Criteria: Admin', () => {
|
|
258
|
+
const adminContext = createContext('admin');
|
|
259
|
+
|
|
260
|
+
test('can manage team', () => {
|
|
261
|
+
const result = hasPermission(adminContext, 'manage_team');
|
|
262
|
+
expect(result.allowed).toBe(true);
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
test('can run Autopilot', () => {
|
|
266
|
+
const result = hasPermission(adminContext, 'run_autopilot');
|
|
267
|
+
expect(result.allowed).toBe(true);
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
test('can manage policies', () => {
|
|
271
|
+
const result = hasPermission(adminContext, 'manage_policies');
|
|
272
|
+
expect(result.allowed).toBe(true);
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
test('cannot manage billing', () => {
|
|
276
|
+
const result = hasPermission(adminContext, 'manage_billing');
|
|
277
|
+
expect(result.allowed).toBe(false);
|
|
278
|
+
});
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
describe('Acceptance Criteria: Owner', () => {
|
|
282
|
+
const ownerContext = createContext('owner');
|
|
283
|
+
|
|
284
|
+
test('has all permissions', () => {
|
|
285
|
+
const permissions: Permission[] = [
|
|
286
|
+
'manage_team',
|
|
287
|
+
'manage_billing',
|
|
288
|
+
'run_autopilot',
|
|
289
|
+
'manage_policies',
|
|
290
|
+
'admin_settings',
|
|
291
|
+
];
|
|
292
|
+
|
|
293
|
+
for (const permission of permissions) {
|
|
294
|
+
const result = hasPermission(ownerContext, permission);
|
|
295
|
+
expect(result.allowed).toBe(true);
|
|
296
|
+
}
|
|
297
|
+
});
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
// =============================================================================
|
|
301
|
+
// ROLE ASSIGNMENT TESTS
|
|
302
|
+
// =============================================================================
|
|
303
|
+
|
|
304
|
+
describe('Role Assignment', () => {
|
|
305
|
+
test('owner can assign any role', () => {
|
|
306
|
+
expect(canAssignRole('owner', 'admin').allowed).toBe(true);
|
|
307
|
+
expect(canAssignRole('owner', 'dev').allowed).toBe(true);
|
|
308
|
+
expect(canAssignRole('owner', 'viewer').allowed).toBe(true);
|
|
309
|
+
expect(canAssignRole('owner', 'compliance-auditor').allowed).toBe(true);
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
test('admin can assign lower roles', () => {
|
|
313
|
+
expect(canAssignRole('admin', 'dev').allowed).toBe(true);
|
|
314
|
+
expect(canAssignRole('admin', 'viewer').allowed).toBe(true);
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
test('admin cannot assign admin or owner', () => {
|
|
318
|
+
expect(canAssignRole('admin', 'admin').allowed).toBe(false);
|
|
319
|
+
expect(canAssignRole('admin', 'owner').allowed).toBe(false);
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
test('dev cannot assign roles', () => {
|
|
323
|
+
expect(canAssignRole('dev', 'viewer').allowed).toBe(false);
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
test('viewer cannot assign roles', () => {
|
|
327
|
+
expect(canAssignRole('viewer', 'viewer').allowed).toBe(false);
|
|
328
|
+
});
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
// =============================================================================
|
|
332
|
+
// MEMBER REMOVAL TESTS
|
|
333
|
+
// =============================================================================
|
|
334
|
+
|
|
335
|
+
describe('Member Removal', () => {
|
|
336
|
+
test('owner can remove any non-owner member', () => {
|
|
337
|
+
expect(canRemoveMember('owner', 'admin').allowed).toBe(true);
|
|
338
|
+
expect(canRemoveMember('owner', 'dev').allowed).toBe(true);
|
|
339
|
+
expect(canRemoveMember('owner', 'viewer').allowed).toBe(true);
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
test('nobody can remove owner', () => {
|
|
343
|
+
expect(canRemoveMember('owner', 'owner').allowed).toBe(false);
|
|
344
|
+
expect(canRemoveMember('admin', 'owner').allowed).toBe(false);
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
test('admin can remove lower roles', () => {
|
|
348
|
+
expect(canRemoveMember('admin', 'dev').allowed).toBe(true);
|
|
349
|
+
expect(canRemoveMember('admin', 'viewer').allowed).toBe(true);
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
test('admin cannot remove admin', () => {
|
|
353
|
+
expect(canRemoveMember('admin', 'admin').allowed).toBe(false);
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
test('dev cannot remove members', () => {
|
|
357
|
+
expect(canRemoveMember('dev', 'viewer').allowed).toBe(false);
|
|
358
|
+
});
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
// =============================================================================
|
|
362
|
+
// PERMISSION MATRIX TESTS
|
|
363
|
+
// =============================================================================
|
|
364
|
+
|
|
365
|
+
describe('Permission Matrix', () => {
|
|
366
|
+
test('generatePermissionMatrix returns valid structure', () => {
|
|
367
|
+
const matrix = generatePermissionMatrix();
|
|
368
|
+
|
|
369
|
+
expect(matrix.roles).toEqual(ROLES);
|
|
370
|
+
expect(matrix.permissions.length).toBeGreaterThan(0);
|
|
371
|
+
expect(typeof matrix.matrix).toBe('object');
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
test('matrix contains all role-permission combinations', () => {
|
|
375
|
+
const matrix = generatePermissionMatrix();
|
|
376
|
+
|
|
377
|
+
for (const role of ROLES) {
|
|
378
|
+
expect(matrix.matrix[role]).toBeDefined();
|
|
379
|
+
for (const permission of matrix.permissions) {
|
|
380
|
+
expect(typeof matrix.matrix[role][permission as Permission]).toBe('boolean');
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
test('matrix values match roleHasPermission', () => {
|
|
386
|
+
const matrix = generatePermissionMatrix();
|
|
387
|
+
|
|
388
|
+
for (const role of ROLES) {
|
|
389
|
+
for (const permission of matrix.permissions) {
|
|
390
|
+
const matrixValue = matrix.matrix[role][permission as Permission];
|
|
391
|
+
const functionValue = roleHasPermission(role, permission as Permission);
|
|
392
|
+
expect(matrixValue).toBe(functionValue);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
// =============================================================================
|
|
399
|
+
// MINIMUM ROLE TESTS
|
|
400
|
+
// =============================================================================
|
|
401
|
+
|
|
402
|
+
describe('Minimum Role for Permission', () => {
|
|
403
|
+
test('getMinimumRoleForPermission returns correct minimum role', () => {
|
|
404
|
+
expect(getMinimumRoleForPermission('view_dashboard')).toBe('viewer');
|
|
405
|
+
expect(getMinimumRoleForPermission('view_audit')).toBe('compliance-auditor');
|
|
406
|
+
expect(getMinimumRoleForPermission('run_scan')).toBe('dev');
|
|
407
|
+
expect(getMinimumRoleForPermission('manage_team')).toBe('admin');
|
|
408
|
+
expect(getMinimumRoleForPermission('manage_billing')).toBe('owner');
|
|
409
|
+
});
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
// =============================================================================
|
|
413
|
+
// EFFECTIVE PERMISSIONS TESTS
|
|
414
|
+
// =============================================================================
|
|
415
|
+
|
|
416
|
+
describe('Effective Permissions', () => {
|
|
417
|
+
test('getEffectivePermissions returns array for each role', () => {
|
|
418
|
+
for (const role of ROLES) {
|
|
419
|
+
const permissions = getEffectivePermissions(role);
|
|
420
|
+
expect(Array.isArray(permissions)).toBe(true);
|
|
421
|
+
expect(permissions.length).toBeGreaterThan(0);
|
|
422
|
+
}
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
test('owner has more permissions than admin', () => {
|
|
426
|
+
const ownerPerms = getEffectivePermissions('owner');
|
|
427
|
+
const adminPerms = getEffectivePermissions('admin');
|
|
428
|
+
expect(ownerPerms.length).toBeGreaterThan(adminPerms.length);
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
test('admin has more permissions than dev', () => {
|
|
432
|
+
const adminPerms = getEffectivePermissions('admin');
|
|
433
|
+
const devPerms = getEffectivePermissions('dev');
|
|
434
|
+
expect(adminPerms.length).toBeGreaterThan(devPerms.length);
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
test('viewer has fewest permissions', () => {
|
|
438
|
+
const viewerPerms = getEffectivePermissions('viewer');
|
|
439
|
+
for (const role of ROLES) {
|
|
440
|
+
if (role !== 'viewer') {
|
|
441
|
+
const otherPerms = getEffectivePermissions(role);
|
|
442
|
+
expect(viewerPerms.length).toBeLessThanOrEqual(otherPerms.length);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
});
|
|
446
|
+
});
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RBAC Module - Role-Based Access Control
|
|
3
|
+
*
|
|
4
|
+
* Exports all RBAC types, permissions, and utilities.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// Types (type-only exports)
|
|
8
|
+
export type {
|
|
9
|
+
Permission,
|
|
10
|
+
PermissionCheck,
|
|
11
|
+
PermissionMatrix,
|
|
12
|
+
RBACContext,
|
|
13
|
+
Role,
|
|
14
|
+
RoleAssignment,
|
|
15
|
+
RoleMetadata,
|
|
16
|
+
TeamInvitation,
|
|
17
|
+
TeamMemberWithRole,
|
|
18
|
+
} from './types';
|
|
19
|
+
|
|
20
|
+
// Values
|
|
21
|
+
export {
|
|
22
|
+
PERMISSIONS,
|
|
23
|
+
ROLE_HIERARCHY,
|
|
24
|
+
ROLE_METADATA,
|
|
25
|
+
ROLE_PERMISSIONS,
|
|
26
|
+
ROLES,
|
|
27
|
+
} from './types';
|
|
28
|
+
|
|
29
|
+
// Permission checking
|
|
30
|
+
export {
|
|
31
|
+
canAssignRole,
|
|
32
|
+
canRemoveMember,
|
|
33
|
+
checkTierAndPermission,
|
|
34
|
+
compareRoles,
|
|
35
|
+
generatePermissionMatrix,
|
|
36
|
+
getEffectivePermissions,
|
|
37
|
+
getMinimumRoleForPermission,
|
|
38
|
+
hasAllPermissions,
|
|
39
|
+
hasAnyPermission,
|
|
40
|
+
hasPermission,
|
|
41
|
+
isRoleAtLeast,
|
|
42
|
+
isValidPermission,
|
|
43
|
+
isValidRole,
|
|
44
|
+
parseRole,
|
|
45
|
+
roleHasPermission,
|
|
46
|
+
} from './permissions';
|