@elevasis/core 0.28.0 → 0.30.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/auth/index.d.ts +5289 -0
- package/dist/auth/index.js +595 -0
- package/dist/index.d.ts +11 -11
- package/dist/knowledge/index.d.ts +1 -1
- package/dist/organization-model/index.d.ts +11 -11
- package/dist/test-utils/index.d.ts +24 -1
- package/package.json +7 -3
- package/src/__tests__/publish.test.ts +8 -7
- package/src/auth/__tests__/access-key-coverage.test.ts +42 -0
- package/src/auth/__tests__/access-key-scan.ts +117 -0
- package/src/auth/__tests__/access-keys.test.ts +81 -0
- package/src/auth/__tests__/access-model.test.ts +257 -0
- package/src/auth/__tests__/access-test-fixtures.ts +50 -0
- package/src/auth/__tests__/key-catalog-drift.test.ts +33 -0
- package/src/auth/__tests__/platform-admin-bypass-parity.test.ts +67 -0
- package/src/auth/access-keys.ts +173 -0
- package/src/auth/access-model.ts +185 -0
- package/src/auth/index.ts +6 -2
- package/src/auth/multi-tenancy/memberships/membership.ts +2 -4
- package/src/auth/multi-tenancy/permissions.ts +1 -1
- package/src/auth/multi-tenancy/types.ts +3 -12
- package/src/business/acquisition/api-schemas.test.ts +59 -8
- package/src/business/acquisition/api-schemas.ts +10 -5
- package/src/business/acquisition/build-templates.test.ts +187 -240
- package/src/business/acquisition/build-templates.ts +87 -98
- package/src/business/acquisition/types.ts +390 -389
- package/src/execution/engine/index.ts +6 -4
- package/src/execution/engine/tools/lead-service-types.ts +63 -34
- package/src/execution/engine/tools/platform/acquisition/types.ts +7 -8
- package/src/execution/engine/tools/registry.ts +6 -4
- package/src/execution/engine/tools/tool-maps.ts +23 -1
- package/src/organization-model/domains/prospecting.ts +2 -327
- package/src/organization-model/migration-helpers.ts +16 -12
- package/src/reference/_generated/contracts.md +352 -328
- package/src/reference/glossary.md +8 -6
- package/src/supabase/database.types.ts +13 -0
|
@@ -0,0 +1,595 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
// src/auth/multi-tenancy/theme-presets.ts
|
|
4
|
+
var THEME_PRESETS = [
|
|
5
|
+
"default",
|
|
6
|
+
"tactical",
|
|
7
|
+
"regal",
|
|
8
|
+
"cyber-volt",
|
|
9
|
+
"aurora",
|
|
10
|
+
"rose-gold",
|
|
11
|
+
"midnight",
|
|
12
|
+
"titanium",
|
|
13
|
+
"canopy",
|
|
14
|
+
"slate",
|
|
15
|
+
"cyber-strike",
|
|
16
|
+
"cyber-chrome",
|
|
17
|
+
"cyber-void",
|
|
18
|
+
"nirvana",
|
|
19
|
+
"wave",
|
|
20
|
+
"synapse",
|
|
21
|
+
"cortex",
|
|
22
|
+
"helios",
|
|
23
|
+
"graphite",
|
|
24
|
+
"quarry"
|
|
25
|
+
];
|
|
26
|
+
var ThemePresetEnum = z.enum(THEME_PRESETS);
|
|
27
|
+
|
|
28
|
+
// src/auth/multi-tenancy/permissions.ts
|
|
29
|
+
var PERMISSIONS = {
|
|
30
|
+
ORG_READ: "org.read",
|
|
31
|
+
ORG_MANAGE: "org.manage",
|
|
32
|
+
ORG_DELETE: "org.delete",
|
|
33
|
+
MEMBERS_MANAGE: "members.manage",
|
|
34
|
+
ROLES_MANAGE: "roles.manage",
|
|
35
|
+
SECRETS_MANAGE: "secrets.manage",
|
|
36
|
+
OPERATIONS_READ: "operations.read",
|
|
37
|
+
OPERATIONS_MANAGE: "operations.manage",
|
|
38
|
+
ACQUISITION_MANAGE: "acquisition.manage",
|
|
39
|
+
PROJECTS_MANAGE: "projects.manage",
|
|
40
|
+
CLIENTS_MANAGE: "clients.manage"
|
|
41
|
+
};
|
|
42
|
+
var PERMISSION_CATALOG = [
|
|
43
|
+
{
|
|
44
|
+
key: "org.read",
|
|
45
|
+
description: "Read organization profile and listings",
|
|
46
|
+
isOrgGrantable: true
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
key: "org.manage",
|
|
50
|
+
description: "Update organization settings",
|
|
51
|
+
isOrgGrantable: false
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
key: "org.delete",
|
|
55
|
+
description: "Delete the organization (owner-only)",
|
|
56
|
+
isOrgGrantable: false
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
key: "members.manage",
|
|
60
|
+
description: "Invite, remove, and reassign roles for members",
|
|
61
|
+
isOrgGrantable: false
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
key: "roles.manage",
|
|
65
|
+
description: "Grant or revoke privileged system roles (owner, admin) within the organization",
|
|
66
|
+
isOrgGrantable: false
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
key: "secrets.manage",
|
|
70
|
+
description: "Create, update, and delete API keys and credentials",
|
|
71
|
+
isOrgGrantable: false
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
key: "operations.read",
|
|
75
|
+
description: "View executions, sessions, schedules, and command queue",
|
|
76
|
+
isOrgGrantable: true
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
key: "operations.manage",
|
|
80
|
+
description: "Run and modify executions, sessions, schedules, queue",
|
|
81
|
+
isOrgGrantable: true
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
key: "acquisition.manage",
|
|
85
|
+
description: "Create, update, and delete acquisition records (acq_companies, acq_contacts, acq_deals, acq_lists*, acq_content*, acquisition storage files)",
|
|
86
|
+
isOrgGrantable: false
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
key: "projects.manage",
|
|
90
|
+
description: "Create, update, and delete project records (prj_projects, prj_milestones, prj_tasks, prj_notes)",
|
|
91
|
+
isOrgGrantable: false
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
key: "clients.manage",
|
|
95
|
+
description: "Create, update, and delete client hub records (clients, clt_* satellites)",
|
|
96
|
+
isOrgGrantable: false
|
|
97
|
+
}
|
|
98
|
+
];
|
|
99
|
+
var PERMISSION_KEY_SET = new Set(PERMISSION_CATALOG.map((p) => p.key));
|
|
100
|
+
function isPermissionKey(value) {
|
|
101
|
+
return typeof value === "string" && PERMISSION_KEY_SET.has(value);
|
|
102
|
+
}
|
|
103
|
+
var PermissionKeySchema = z.string().min(1).max(100);
|
|
104
|
+
var OrgRoleParamsSchema = z.object({
|
|
105
|
+
orgId: z.string().uuid(),
|
|
106
|
+
roleId: z.string().uuid()
|
|
107
|
+
}).strict();
|
|
108
|
+
var OrgRolesParamsSchema = z.object({
|
|
109
|
+
orgId: z.string().uuid()
|
|
110
|
+
}).strict();
|
|
111
|
+
var MembershipRoleParamsSchema = z.object({
|
|
112
|
+
membershipId: z.string().min(1),
|
|
113
|
+
roleId: z.string().uuid()
|
|
114
|
+
}).strict();
|
|
115
|
+
var MembershipParamsSchema = z.object({
|
|
116
|
+
membershipId: z.string().min(1)
|
|
117
|
+
}).strict();
|
|
118
|
+
var CreateOrgRoleRequestSchema = z.object({
|
|
119
|
+
name: z.string().min(1).max(100).trim(),
|
|
120
|
+
slug: z.string().min(1).max(100).regex(/^[a-z0-9]+(?:[-_][a-z0-9]+)*$/, "Role slug must be lowercase letters, numbers, dashes, or underscores"),
|
|
121
|
+
description: z.string().max(500).trim().optional(),
|
|
122
|
+
permissionKeys: z.array(PermissionKeySchema).default([])
|
|
123
|
+
}).strict();
|
|
124
|
+
var UpdateOrgRoleRequestSchema = z.object({
|
|
125
|
+
name: z.string().min(1).max(100).trim().optional(),
|
|
126
|
+
slug: z.string().min(1).max(100).regex(/^[a-z0-9]+(?:[-_][a-z0-9]+)*$/, "Role slug must be lowercase letters, numbers, dashes, or underscores").optional(),
|
|
127
|
+
description: z.string().max(500).trim().nullable().optional(),
|
|
128
|
+
permissionKeys: z.array(PermissionKeySchema).optional()
|
|
129
|
+
}).strict().refine(
|
|
130
|
+
(data) => data.name !== void 0 || data.slug !== void 0 || data.description !== void 0 || data.permissionKeys !== void 0,
|
|
131
|
+
{ message: "At least one field must be provided" }
|
|
132
|
+
);
|
|
133
|
+
var AssignMembershipRoleRequestSchema = z.object({
|
|
134
|
+
roleId: z.string().uuid()
|
|
135
|
+
}).strict();
|
|
136
|
+
var UuidSchema = z.string().uuid();
|
|
137
|
+
z.string().trim().min(1).max(1e3);
|
|
138
|
+
z.enum(["agent", "workflow"]);
|
|
139
|
+
z.enum(["agent", "workflow", "scheduler", "api"]);
|
|
140
|
+
z.string().trim().toLowerCase().min(1, "Credential name required").max(100, "Credential name too long (max 100 chars)").regex(
|
|
141
|
+
/^[a-z0-9]+(-[a-z0-9]+)+$/,
|
|
142
|
+
"Credential name must be lowercase letters, numbers, and hyphens in format: service-environment (e.g., gmail-prod, attio-dev)"
|
|
143
|
+
);
|
|
144
|
+
z.enum(["google-sheets", "google-calendar", "dropbox"]);
|
|
145
|
+
z.string().min(10, "Authorization code too short").max(1e3, "Authorization code too long");
|
|
146
|
+
z.string().min(10, "State parameter too short").max(2048, "State parameter too long");
|
|
147
|
+
z.string().trim().transform((str) => str.replace(/[<>'"]/g, ""));
|
|
148
|
+
var EmailSchema = z.string().email();
|
|
149
|
+
z.string().url();
|
|
150
|
+
z.object({
|
|
151
|
+
limit: z.coerce.number().int().min(1).max(100).default(20),
|
|
152
|
+
offset: z.coerce.number().int().min(0).default(0)
|
|
153
|
+
});
|
|
154
|
+
z.string().datetime();
|
|
155
|
+
z.object({
|
|
156
|
+
startDate: z.string().datetime(),
|
|
157
|
+
endDate: z.string().datetime()
|
|
158
|
+
});
|
|
159
|
+
function createStringSchema(minLength, maxLength, fieldName) {
|
|
160
|
+
const schema = z.string().trim().min(minLength).max(maxLength);
|
|
161
|
+
return fieldName ? schema.describe(`${fieldName} (${minLength}-${maxLength} characters)`) : schema;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// src/auth/multi-tenancy/organizations/api-schemas.ts
|
|
165
|
+
var OrganizationNameSchema = z.string().min(2, "Organization name must be at least 2 characters").max(100, "Organization name must be at most 100 characters").trim().regex(
|
|
166
|
+
/^[a-zA-Z0-9\s\-_]+$/,
|
|
167
|
+
"Organization name must contain only letters, numbers, spaces, hyphens, and underscores"
|
|
168
|
+
);
|
|
169
|
+
var OrganizationIdSchema = z.union([
|
|
170
|
+
UuidSchema,
|
|
171
|
+
z.string().regex(/^org_[a-zA-Z0-9]+$/, "Invalid WorkOS organization ID")
|
|
172
|
+
]);
|
|
173
|
+
var OrganizationDomainSchema = z.object({
|
|
174
|
+
domain: z.string().min(3).max(255),
|
|
175
|
+
state: z.enum(["verified", "pending", "failed"]).optional()
|
|
176
|
+
});
|
|
177
|
+
var OrganizationIdParamSchema = z.object({
|
|
178
|
+
id: OrganizationIdSchema
|
|
179
|
+
}).strict();
|
|
180
|
+
var CreateOrganizationSchema = z.object({
|
|
181
|
+
name: OrganizationNameSchema,
|
|
182
|
+
domainData: z.array(OrganizationDomainSchema).max(10).optional(),
|
|
183
|
+
metadata: z.record(z.string(), z.unknown()).refine((val) => JSON.stringify(val).length <= 10240, "Metadata must be under 10KB").optional()
|
|
184
|
+
}).strict();
|
|
185
|
+
var UpdateOrganizationSchema = CreateOrganizationSchema.partial().strict().refine((data) => Object.keys(data).length > 0, {
|
|
186
|
+
message: "At least one field (name, domainData, or metadata) must be provided"
|
|
187
|
+
});
|
|
188
|
+
var ListOrganizationsQuerySchema = z.object({
|
|
189
|
+
limit: z.coerce.number().int().min(1).max(100).default(20),
|
|
190
|
+
before: z.string().optional(),
|
|
191
|
+
// WorkOS pagination cursor
|
|
192
|
+
after: z.string().optional()
|
|
193
|
+
// WorkOS pagination cursor
|
|
194
|
+
});
|
|
195
|
+
var UserIdParamSchema = z.object({
|
|
196
|
+
id: z.string().min(1)
|
|
197
|
+
// WorkOS user IDs can be UUID or 'user_' prefixed strings
|
|
198
|
+
}).strict();
|
|
199
|
+
var ExternalIdParamSchema = z.object({
|
|
200
|
+
externalId: z.string().min(1)
|
|
201
|
+
}).strict();
|
|
202
|
+
var UpdateUserSchema = z.object({
|
|
203
|
+
email: EmailSchema.optional(),
|
|
204
|
+
firstName: createStringSchema(1, 100, "First name").optional(),
|
|
205
|
+
lastName: createStringSchema(1, 100, "Last name").optional()
|
|
206
|
+
}).strict();
|
|
207
|
+
var UpdateMyProfileSchema = z.object({
|
|
208
|
+
firstName: createStringSchema(1, 100, "First name").optional(),
|
|
209
|
+
lastName: createStringSchema(1, 100, "Last name").optional(),
|
|
210
|
+
profilePictureUrl: z.string().url().refine((url) => url.startsWith("https://"), { message: "Only HTTPS URLs are allowed" }).optional(),
|
|
211
|
+
lastVisitedOrg: z.string().uuid().optional(),
|
|
212
|
+
config: z.object({
|
|
213
|
+
theme: z.object({
|
|
214
|
+
// `.catch('default')` makes the write path tolerant of stale/unknown preset strings.
|
|
215
|
+
// Any value that fails enum validation silently coerces to 'default' instead of 400ing.
|
|
216
|
+
// This protects against preset renames (e.g. cyber-punk → cyber-chrome) and drift between
|
|
217
|
+
// the enum and legacy DB values. Paired with a read-path sync-back in main.tsx that
|
|
218
|
+
// persists the corrected value back to DB on next profile load.
|
|
219
|
+
// ThemePresetEnum is derived from THEME_PRESETS — the single source of truth in
|
|
220
|
+
// packages/core/src/auth/multi-tenancy/theme-presets.ts.
|
|
221
|
+
preset: ThemePresetEnum.catch("default").optional(),
|
|
222
|
+
colorScheme: z.enum(["light", "dark", "auto"]).catch("auto").optional()
|
|
223
|
+
}).strict().optional(),
|
|
224
|
+
onboarding: z.object({
|
|
225
|
+
completed: z.boolean().optional(),
|
|
226
|
+
completedAt: z.string().datetime().nullable().optional(),
|
|
227
|
+
role: z.string().max(100).nullable().optional(),
|
|
228
|
+
primaryUseCase: z.array(z.string().max(100)).max(10).nullable().optional(),
|
|
229
|
+
experienceLevel: z.string().max(100).nullable().optional(),
|
|
230
|
+
guides: z.object({
|
|
231
|
+
completedIds: z.array(z.string().max(100)).max(20).optional(),
|
|
232
|
+
dismissed: z.boolean().optional(),
|
|
233
|
+
completedAt: z.string().datetime().nullable().optional()
|
|
234
|
+
}).strict().optional()
|
|
235
|
+
}).strict().optional()
|
|
236
|
+
}).strict().optional()
|
|
237
|
+
}).strict();
|
|
238
|
+
var ListUsersQuerySchema = z.object({
|
|
239
|
+
email: EmailSchema.optional(),
|
|
240
|
+
organizationId: z.string().optional(),
|
|
241
|
+
// WorkOS org IDs can be UUID or 'org_' prefixed
|
|
242
|
+
limit: z.coerce.number().int().min(1).max(100).optional(),
|
|
243
|
+
before: z.string().optional(),
|
|
244
|
+
// WorkOS pagination cursor
|
|
245
|
+
after: z.string().optional()
|
|
246
|
+
// WorkOS pagination cursor
|
|
247
|
+
}).strict();
|
|
248
|
+
|
|
249
|
+
// src/auth/multi-tenancy/memberships/membership.ts
|
|
250
|
+
function transformMembershipToTableRow(membership) {
|
|
251
|
+
return {
|
|
252
|
+
id: membership.id,
|
|
253
|
+
userId: membership.userId,
|
|
254
|
+
organizationId: membership.organizationId,
|
|
255
|
+
userEmail: membership.user?.email || "Unknown",
|
|
256
|
+
userFullName: membership.user?.firstName && membership.user?.lastName ? `${membership.user.firstName} ${membership.user.lastName}` : membership.user?.email || "Unknown User",
|
|
257
|
+
organizationName: membership.organization?.name || "Unknown Organization",
|
|
258
|
+
role: membership.role.slug,
|
|
259
|
+
status: membership.status,
|
|
260
|
+
statusBadge: membership.status,
|
|
261
|
+
joinedAt: new Date(membership.createdAt),
|
|
262
|
+
updatedAt: new Date(membership.updatedAt),
|
|
263
|
+
canEdit: membership.status === "active",
|
|
264
|
+
canRemove: true
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
var MEMBERSHIP_STATUS_COLORS = {
|
|
268
|
+
active: "green",
|
|
269
|
+
inactive: "gray"
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
// src/auth/multi-tenancy/memberships/supabase.ts
|
|
273
|
+
function mapSupabaseStatus(status) {
|
|
274
|
+
if (status === "active") return "active";
|
|
275
|
+
return "inactive";
|
|
276
|
+
}
|
|
277
|
+
function transformSupabaseToMembership(supabaseData) {
|
|
278
|
+
return {
|
|
279
|
+
object: "organization_membership",
|
|
280
|
+
id: supabaseData.workos_membership_id ?? supabaseData.id,
|
|
281
|
+
userId: supabaseData.user_id,
|
|
282
|
+
// Always use Supabase user_id
|
|
283
|
+
organizationId: supabaseData.organization_id,
|
|
284
|
+
// Always use Supabase organization_id
|
|
285
|
+
role: { slug: supabaseData.role_slug || "member" },
|
|
286
|
+
status: mapSupabaseStatus(supabaseData.membership_status),
|
|
287
|
+
createdAt: supabaseData.created_at || (/* @__PURE__ */ new Date()).toISOString(),
|
|
288
|
+
updatedAt: supabaseData.updated_at || (/* @__PURE__ */ new Date()).toISOString()
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
var MembershipRoleSchema = z.string().min(1).max(64);
|
|
292
|
+
var MembershipStatusSchema = z.enum(["active", "inactive"]);
|
|
293
|
+
var MembershipIdParamSchema = z.object({
|
|
294
|
+
id: z.string().min(1)
|
|
295
|
+
// WorkOS membership IDs can be various formats
|
|
296
|
+
}).strict();
|
|
297
|
+
var OrgIdParamSchema = z.object({
|
|
298
|
+
orgId: z.string().uuid()
|
|
299
|
+
}).strict();
|
|
300
|
+
var MyOrgPermissionsResponseSchema = z.object({
|
|
301
|
+
permissions: z.array(z.string())
|
|
302
|
+
});
|
|
303
|
+
var CreateMembershipSchema = z.object({
|
|
304
|
+
userId: z.string().min(1),
|
|
305
|
+
organizationId: z.string().min(1),
|
|
306
|
+
roleSlug: MembershipRoleSchema.default("member")
|
|
307
|
+
}).strict();
|
|
308
|
+
var UpdateMembershipSchema = z.object({
|
|
309
|
+
roleSlug: MembershipRoleSchema
|
|
310
|
+
}).strict();
|
|
311
|
+
var ListMembershipsQuerySchema = z.object({
|
|
312
|
+
userId: z.string().optional(),
|
|
313
|
+
organizationId: z.string().optional(),
|
|
314
|
+
limit: z.coerce.number().int().min(1).max(100).optional(),
|
|
315
|
+
before: z.string().optional(),
|
|
316
|
+
// WorkOS pagination cursor
|
|
317
|
+
after: z.string().optional()
|
|
318
|
+
// WorkOS pagination cursor
|
|
319
|
+
}).strict().refine((data) => data.userId || data.organizationId, {
|
|
320
|
+
message: "Either userId or organizationId must be provided"
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
// src/auth/multi-tenancy/invitations/supabase.ts
|
|
324
|
+
function transformSupabaseToInvitation(supabaseInvitation) {
|
|
325
|
+
return {
|
|
326
|
+
id: supabaseInvitation.id,
|
|
327
|
+
workosInvitationId: supabaseInvitation.workos_invitation_id,
|
|
328
|
+
email: supabaseInvitation.email,
|
|
329
|
+
organizationId: supabaseInvitation.organization_id,
|
|
330
|
+
inviterUserId: supabaseInvitation.inviter_user_id,
|
|
331
|
+
invitationState: supabaseInvitation.invitation_state,
|
|
332
|
+
roleSlug: supabaseInvitation.role_slug,
|
|
333
|
+
invitationToken: supabaseInvitation.invitation_token,
|
|
334
|
+
acceptInvitationUrl: supabaseInvitation.accept_invitation_url,
|
|
335
|
+
acceptedAt: supabaseInvitation.accepted_at,
|
|
336
|
+
revokedAt: supabaseInvitation.revoked_at,
|
|
337
|
+
expiresAt: supabaseInvitation.expires_at,
|
|
338
|
+
createdAt: supabaseInvitation.created_at,
|
|
339
|
+
updatedAt: supabaseInvitation.updated_at
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
var InvitationIdParamSchema = z.object({
|
|
343
|
+
id: z.string().min(1)
|
|
344
|
+
// WorkOS invitation IDs
|
|
345
|
+
}).strict();
|
|
346
|
+
var SendInvitationSchema = z.object({
|
|
347
|
+
email: EmailSchema,
|
|
348
|
+
organizationId: z.string().optional(),
|
|
349
|
+
// For WorkOS API - but typically from JWT
|
|
350
|
+
roleSlug: MembershipRoleSchema.default("member"),
|
|
351
|
+
expiresInDays: z.number().int().min(1).max(90).default(7)
|
|
352
|
+
}).strict();
|
|
353
|
+
var AcceptInvitationSchema = z.object({
|
|
354
|
+
invitation_token: z.string().min(1, "Invitation token is required")
|
|
355
|
+
}).strict();
|
|
356
|
+
var ListInvitationsQuerySchema = z.object({
|
|
357
|
+
organizationId: z.string().optional(),
|
|
358
|
+
userId: z.string().optional(),
|
|
359
|
+
email: EmailSchema.optional(),
|
|
360
|
+
limit: z.coerce.number().int().min(1).max(100).optional(),
|
|
361
|
+
before: z.string().optional(),
|
|
362
|
+
// WorkOS pagination cursor
|
|
363
|
+
after: z.string().optional()
|
|
364
|
+
// WorkOS pagination cursor
|
|
365
|
+
}).strict().refine((data) => data.organizationId || data.userId, { message: "Either organizationId or userId must be provided" });
|
|
366
|
+
|
|
367
|
+
// src/organization-model/helpers.ts
|
|
368
|
+
function childSystemsOf(system) {
|
|
369
|
+
return system.systems ?? system.subsystems ?? {};
|
|
370
|
+
}
|
|
371
|
+
function getSystem(model, path) {
|
|
372
|
+
const segments = path.split(".");
|
|
373
|
+
let current = model.systems;
|
|
374
|
+
let node;
|
|
375
|
+
for (const seg of segments) {
|
|
376
|
+
node = current[seg];
|
|
377
|
+
if (node === void 0) return void 0;
|
|
378
|
+
current = childSystemsOf(node);
|
|
379
|
+
}
|
|
380
|
+
return node;
|
|
381
|
+
}
|
|
382
|
+
function listAllSystems(model) {
|
|
383
|
+
const results = [];
|
|
384
|
+
function walk(map, prefix) {
|
|
385
|
+
for (const [localId, system] of Object.entries(map)) {
|
|
386
|
+
const fullPath = prefix ? `${prefix}.${localId}` : localId;
|
|
387
|
+
results.push({ path: fullPath, system });
|
|
388
|
+
const childSystems = childSystemsOf(system);
|
|
389
|
+
if (Object.keys(childSystems).length > 0) {
|
|
390
|
+
walk(childSystems, fullPath);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
walk(model.systems, "");
|
|
395
|
+
return results;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// src/auth/access-keys.ts
|
|
399
|
+
var DEFAULT_ACCESS_ACTION = "view";
|
|
400
|
+
var PLATFORM_ADMIN_ACCESS_KEY = "platform.admin";
|
|
401
|
+
var PLATFORM_ADMIN_ACCESS_KEY_SHORTHAND = "platformAdmin";
|
|
402
|
+
var AccessActionSchema = z.enum(["view", "manage"]);
|
|
403
|
+
var AccessKeyObjectSchema = z.object({
|
|
404
|
+
systemPath: z.string().trim().min(1),
|
|
405
|
+
action: AccessActionSchema.default(DEFAULT_ACCESS_ACTION)
|
|
406
|
+
}).strict();
|
|
407
|
+
var AccessKeyInputSchema = z.union([z.string().trim().min(1), AccessKeyObjectSchema]);
|
|
408
|
+
var NormalizedAccessKeySchema = AccessKeyObjectSchema;
|
|
409
|
+
var AccessKeySchema = AccessKeyInputSchema;
|
|
410
|
+
var DIAGNOSTIC_VIEW_ACCESS_KEYS = [
|
|
411
|
+
"diagnostic.operations.overview",
|
|
412
|
+
"diagnostic.operations.recent-executions",
|
|
413
|
+
"diagnostic.monitoring.execution-logs",
|
|
414
|
+
"diagnostic.monitoring.notifications"
|
|
415
|
+
];
|
|
416
|
+
var AccessKeys = {
|
|
417
|
+
platformAdmin: { systemPath: PLATFORM_ADMIN_ACCESS_KEY, action: DEFAULT_ACCESS_ACTION },
|
|
418
|
+
organizationManage: { systemPath: "permission.org", action: "manage" },
|
|
419
|
+
membersManage: { systemPath: "permission.members", action: "manage" },
|
|
420
|
+
rolesManage: { systemPath: "permission.roles", action: "manage" },
|
|
421
|
+
secretsManage: { systemPath: "permission.secrets", action: "manage" },
|
|
422
|
+
operationsRead: { systemPath: "permission.operations", action: DEFAULT_ACCESS_ACTION },
|
|
423
|
+
operationsManage: { systemPath: "permission.operations", action: "manage" },
|
|
424
|
+
acquisitionManage: { systemPath: "permission.acquisition", action: "manage" },
|
|
425
|
+
projectsManage: { systemPath: "permission.projects", action: "manage" },
|
|
426
|
+
clientsManage: { systemPath: "permission.clients", action: "manage" },
|
|
427
|
+
operationsOverview: { systemPath: "diagnostic.operations.overview", action: DEFAULT_ACCESS_ACTION },
|
|
428
|
+
operationsRecentExecutions: { systemPath: "diagnostic.operations.recent-executions", action: DEFAULT_ACCESS_ACTION },
|
|
429
|
+
monitoringExecutionLogs: { systemPath: "diagnostic.monitoring.execution-logs", action: DEFAULT_ACCESS_ACTION },
|
|
430
|
+
monitoringNotifications: { systemPath: "diagnostic.monitoring.notifications", action: DEFAULT_ACCESS_ACTION }
|
|
431
|
+
};
|
|
432
|
+
var PERMISSION_ACCESS_KEY_DEFINITIONS = [
|
|
433
|
+
{ key: AccessKeys.organizationManage, rolePermission: PERMISSIONS.ORG_MANAGE },
|
|
434
|
+
{ key: AccessKeys.membersManage, rolePermission: PERMISSIONS.MEMBERS_MANAGE },
|
|
435
|
+
{ key: AccessKeys.rolesManage, rolePermission: PERMISSIONS.ROLES_MANAGE },
|
|
436
|
+
{ key: AccessKeys.secretsManage, rolePermission: PERMISSIONS.SECRETS_MANAGE },
|
|
437
|
+
{ key: AccessKeys.operationsRead, rolePermission: PERMISSIONS.OPERATIONS_READ },
|
|
438
|
+
{ key: AccessKeys.operationsManage, rolePermission: PERMISSIONS.OPERATIONS_MANAGE },
|
|
439
|
+
{ key: AccessKeys.acquisitionManage, rolePermission: PERMISSIONS.ACQUISITION_MANAGE },
|
|
440
|
+
{ key: AccessKeys.projectsManage, rolePermission: PERMISSIONS.PROJECTS_MANAGE },
|
|
441
|
+
{ key: AccessKeys.clientsManage, rolePermission: PERMISSIONS.CLIENTS_MANAGE }
|
|
442
|
+
];
|
|
443
|
+
function normalizeAccessKey(input) {
|
|
444
|
+
const parsed = AccessKeyInputSchema.parse(input);
|
|
445
|
+
const normalized = typeof parsed === "string" ? {
|
|
446
|
+
systemPath: parsed === PLATFORM_ADMIN_ACCESS_KEY_SHORTHAND ? PLATFORM_ADMIN_ACCESS_KEY : parsed,
|
|
447
|
+
action: DEFAULT_ACCESS_ACTION
|
|
448
|
+
} : parsed;
|
|
449
|
+
return NormalizedAccessKeySchema.parse(normalized);
|
|
450
|
+
}
|
|
451
|
+
function accessKeyToString(input) {
|
|
452
|
+
const key = normalizeAccessKey(input);
|
|
453
|
+
return `${key.systemPath}:${key.action}`;
|
|
454
|
+
}
|
|
455
|
+
function rolePermissionForAccessKey(key) {
|
|
456
|
+
if (key.action === DEFAULT_ACCESS_ACTION) return void 0;
|
|
457
|
+
return `${key.systemPath}.${key.action}`;
|
|
458
|
+
}
|
|
459
|
+
function groupCatalogEntries(entries) {
|
|
460
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
461
|
+
for (const entry of entries) {
|
|
462
|
+
const existing = grouped.get(entry.key.systemPath) ?? [];
|
|
463
|
+
existing.push(entry);
|
|
464
|
+
grouped.set(entry.key.systemPath, existing);
|
|
465
|
+
}
|
|
466
|
+
return grouped;
|
|
467
|
+
}
|
|
468
|
+
function buildCatalogEntry(systemPath, action, source) {
|
|
469
|
+
const key = normalizeAccessKey({ systemPath, action });
|
|
470
|
+
return {
|
|
471
|
+
key,
|
|
472
|
+
source,
|
|
473
|
+
rolePermission: rolePermissionForAccessKey(key)
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
function deriveAccessKeyCatalog(organizationModel, options = {}) {
|
|
477
|
+
const { diagnosticKeys = DIAGNOSTIC_VIEW_ACCESS_KEYS, includeManageActions = true } = options;
|
|
478
|
+
const entries = [
|
|
479
|
+
buildCatalogEntry(PLATFORM_ADMIN_ACCESS_KEY, DEFAULT_ACCESS_ACTION, "platform")
|
|
480
|
+
];
|
|
481
|
+
for (const { path } of listAllSystems(organizationModel)) {
|
|
482
|
+
entries.push(buildCatalogEntry(path, DEFAULT_ACCESS_ACTION, "om-system"));
|
|
483
|
+
if (includeManageActions) {
|
|
484
|
+
entries.push(buildCatalogEntry(path, "manage", "om-system"));
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
for (const { key, rolePermission } of PERMISSION_ACCESS_KEY_DEFINITIONS) {
|
|
488
|
+
entries.push({
|
|
489
|
+
key,
|
|
490
|
+
source: "permission",
|
|
491
|
+
rolePermission
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
for (const systemPath of diagnosticKeys) {
|
|
495
|
+
entries.push(buildCatalogEntry(systemPath, DEFAULT_ACCESS_ACTION, "diagnostic"));
|
|
496
|
+
}
|
|
497
|
+
return {
|
|
498
|
+
bySystemPath: groupCatalogEntries(entries),
|
|
499
|
+
entries
|
|
500
|
+
};
|
|
501
|
+
}
|
|
502
|
+
function findAccessCatalogEntry(catalog, key) {
|
|
503
|
+
return catalog.bySystemPath.get(key.systemPath)?.find((entry) => entry.key.action === key.action);
|
|
504
|
+
}
|
|
505
|
+
function listAccessKeys(catalog) {
|
|
506
|
+
return catalog.entries.map((entry) => entry.key);
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
// src/auth/access-model.ts
|
|
510
|
+
var ALLOWED = { allowed: true, restrictedBy: null, reason: "allowed" };
|
|
511
|
+
var PLATFORM_ADMIN_BYPASS = {
|
|
512
|
+
allowed: true,
|
|
513
|
+
restrictedBy: null,
|
|
514
|
+
reason: "platform-admin-bypass"
|
|
515
|
+
};
|
|
516
|
+
function deny(restrictedBy, reason) {
|
|
517
|
+
return { allowed: false, restrictedBy, reason };
|
|
518
|
+
}
|
|
519
|
+
function isPlatformAdmin(profile) {
|
|
520
|
+
return profile?.isPlatformAdmin === true || profile?.is_platform_admin === true;
|
|
521
|
+
}
|
|
522
|
+
function diagnosticAllowlistHas(allowlist, systemPath) {
|
|
523
|
+
if (allowlist === void 0) return false;
|
|
524
|
+
return "has" in allowlist ? allowlist.has(systemPath) : allowlist.includes(systemPath);
|
|
525
|
+
}
|
|
526
|
+
function lifecycleAllowsAccess(lifecycle, ctx) {
|
|
527
|
+
if (lifecycle === "active") return true;
|
|
528
|
+
if (lifecycle === "beta") return ctx.betaAccessEnabled === true || ctx.isDevelopment === true;
|
|
529
|
+
return false;
|
|
530
|
+
}
|
|
531
|
+
function getPermissionSource(ctx) {
|
|
532
|
+
return ctx.permissions ?? ctx.membership?.effectivePermissions;
|
|
533
|
+
}
|
|
534
|
+
function hasRequiredPermission(key, rolePermission, ctx) {
|
|
535
|
+
const permissionSource = getPermissionSource(ctx);
|
|
536
|
+
if (permissionSource === void 0 || permissionSource === null) return true;
|
|
537
|
+
const requiredPermission = rolePermission ?? `${key.systemPath}.${key.action}`;
|
|
538
|
+
return permissionSource.includes(requiredPermission);
|
|
539
|
+
}
|
|
540
|
+
function hasExplicitRequiredPermission(rolePermission, ctx) {
|
|
541
|
+
const permissionSource = getPermissionSource(ctx);
|
|
542
|
+
return rolePermission !== void 0 && permissionSource !== void 0 && permissionSource !== null ? permissionSource.includes(rolePermission) : false;
|
|
543
|
+
}
|
|
544
|
+
function checkAccess(input, ctx) {
|
|
545
|
+
if (isPlatformAdmin(ctx.profile)) return PLATFORM_ADMIN_BYPASS;
|
|
546
|
+
const parsed = (() => {
|
|
547
|
+
try {
|
|
548
|
+
return normalizeAccessKey(input);
|
|
549
|
+
} catch {
|
|
550
|
+
return null;
|
|
551
|
+
}
|
|
552
|
+
})();
|
|
553
|
+
if (parsed === null) return deny("catalog", "invalid-access-key");
|
|
554
|
+
const catalog = ctx.accessCatalog ?? deriveAccessKeyCatalog(ctx.organizationModel);
|
|
555
|
+
const catalogEntry = findAccessCatalogEntry(catalog, parsed);
|
|
556
|
+
if (catalogEntry === void 0) return deny("catalog", "unknown-access-key");
|
|
557
|
+
const membership = ctx.membership;
|
|
558
|
+
if (membership === void 0 || membership === null) return deny("membership", "missing-membership");
|
|
559
|
+
if (membership.organizationId !== ctx.organizationId) return deny("membership", "organization-mismatch");
|
|
560
|
+
if (parsed.systemPath === PLATFORM_ADMIN_ACCESS_KEY) {
|
|
561
|
+
return deny("role-permission", "role-permission-denied");
|
|
562
|
+
}
|
|
563
|
+
if (catalogEntry.source === "diagnostic") {
|
|
564
|
+
if (!diagnosticAllowlistHas(ctx.diagnosticAllowlist, parsed.systemPath)) {
|
|
565
|
+
return deny("diagnostic-allowlist", "diagnostic-key-not-allowed");
|
|
566
|
+
}
|
|
567
|
+
if (parsed.action !== DEFAULT_ACCESS_ACTION && !hasRequiredPermission(parsed, catalogEntry.rolePermission, ctx)) {
|
|
568
|
+
return deny("role-permission", "role-permission-denied");
|
|
569
|
+
}
|
|
570
|
+
return ALLOWED;
|
|
571
|
+
}
|
|
572
|
+
if (catalogEntry.source === "permission") {
|
|
573
|
+
if (!hasExplicitRequiredPermission(catalogEntry.rolePermission, ctx)) {
|
|
574
|
+
return deny("role-permission", "role-permission-denied");
|
|
575
|
+
}
|
|
576
|
+
return ALLOWED;
|
|
577
|
+
}
|
|
578
|
+
const system = getSystem(ctx.organizationModel, parsed.systemPath);
|
|
579
|
+
if (system === void 0 || !lifecycleAllowsAccess(system.lifecycle, ctx)) {
|
|
580
|
+
return deny("system-lifecycle", "system-not-active");
|
|
581
|
+
}
|
|
582
|
+
if (parsed.action !== DEFAULT_ACCESS_ACTION && !hasRequiredPermission(parsed, catalogEntry.rolePermission, ctx)) {
|
|
583
|
+
return deny("role-permission", "role-permission-denied");
|
|
584
|
+
}
|
|
585
|
+
return ALLOWED;
|
|
586
|
+
}
|
|
587
|
+
function createAccessModel(organizationModel) {
|
|
588
|
+
const catalog = deriveAccessKeyCatalog(organizationModel);
|
|
589
|
+
return {
|
|
590
|
+
catalog,
|
|
591
|
+
checkAccess: (key, ctx) => checkAccess(key, { ...ctx, organizationModel, accessCatalog: catalog })
|
|
592
|
+
};
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
export { AcceptInvitationSchema, AccessActionSchema, AccessKeyInputSchema, AccessKeyObjectSchema, AccessKeySchema, AccessKeys, AssignMembershipRoleRequestSchema, CreateMembershipSchema, CreateOrgRoleRequestSchema, CreateOrganizationSchema, DEFAULT_ACCESS_ACTION, DIAGNOSTIC_VIEW_ACCESS_KEYS, ExternalIdParamSchema, InvitationIdParamSchema, ListInvitationsQuerySchema, ListMembershipsQuerySchema, ListOrganizationsQuerySchema, ListUsersQuerySchema, MEMBERSHIP_STATUS_COLORS, MembershipIdParamSchema, MembershipParamsSchema, MembershipRoleParamsSchema, MembershipRoleSchema, MembershipStatusSchema, MyOrgPermissionsResponseSchema, NormalizedAccessKeySchema, OrgIdParamSchema, OrgRoleParamsSchema, OrgRolesParamsSchema, OrganizationDomainSchema, OrganizationIdParamSchema, OrganizationNameSchema, PERMISSIONS, PERMISSION_CATALOG, PLATFORM_ADMIN_ACCESS_KEY, PLATFORM_ADMIN_ACCESS_KEY_SHORTHAND, SendInvitationSchema, THEME_PRESETS, ThemePresetEnum, UpdateMembershipSchema, UpdateMyProfileSchema, UpdateOrgRoleRequestSchema, UpdateOrganizationSchema, UpdateUserSchema, UserIdParamSchema, accessKeyToString, checkAccess, createAccessModel, deriveAccessKeyCatalog, findAccessCatalogEntry, isPermissionKey, listAccessKeys, normalizeAccessKey, rolePermissionForAccessKey, transformMembershipToTableRow, transformSupabaseToInvitation, transformSupabaseToMembership };
|