@c1m-public/platform-auth 0.8.4 → 0.9.1
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/README.md +8 -0
- package/dist/access/index.d.ts +1 -148
- package/dist/access/index.js +307 -2
- package/dist/access/next.d.ts +2 -3
- package/dist/access/next.js +335 -5
- package/dist/index-BnchfjP_.d.ts +235 -0
- package/dist/index-CbsCWtku.d.ts +154 -0
- package/dist/index.d.ts +1 -230
- package/dist/index.js +534 -3
- package/dist/next.d.ts +2 -3
- package/dist/next.js +536 -7
- package/package.json +1 -1
- package/dist/chunk-73ZH67GM.js +0 -261
- package/dist/chunk-FBC2NUH7.js +0 -83
- package/dist/chunk-Q7YI7RMG.js +0 -404
- package/dist/chunk-ZWSJVE5S.js +0 -52
- package/dist/internal-D6RafCm2.d.ts +0 -6
package/README.md
CHANGED
|
@@ -116,6 +116,14 @@ await access.requireAccess("platform.tenants.manage");
|
|
|
116
116
|
Omit `getSubjectId` and the loader derives the cache-tag subject from the access
|
|
117
117
|
token, so per-user tagging cannot silently collapse to one shared tag.
|
|
118
118
|
|
|
119
|
+
### Pipeline stages
|
|
120
|
+
|
|
121
|
+
`PIPELINE_STAGES` / `PipelineStage` / `isPipelineStage` under `./access` are
|
|
122
|
+
the onboarding funnel strings in order (`created`, `awaiting_payment`,
|
|
123
|
+
`provisioning`, `attention`, `active`), generated from the backend's
|
|
124
|
+
`schemas/api/pipeline-stages.json` the same way the permission keys are.
|
|
125
|
+
Labels and descriptions stay with the app; the strings are the backend's.
|
|
126
|
+
|
|
119
127
|
## Keys drift guard
|
|
120
128
|
|
|
121
129
|
`src/access/generated/keys.ts` is generated from the backend authz catalog
|
package/dist/access/index.d.ts
CHANGED
|
@@ -1,148 +1 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
/** Platform-operator permission keys. */
|
|
4
|
-
declare const PLATFORM_PERMISSIONS: readonly ["affiliates.finance.manage", "affiliates.partners.manage", "affiliates.programmes.manage", "affiliates.view", "linkservice.domains.manage", "linkservice.links.manage", "linkservice.qr.manage", "linkservice.view", "platform.admin.view", "platform.audit.view", "platform.auth.manage", "platform.billing.manage", "platform.channels.manage", "platform.channels.view", "platform.config.manage", "platform.docs.manage", "platform.docs.view", "platform.observability.manage", "platform.observability.view", "platform.offboarding.view", "platform.ops.manage", "platform.support.impersonate", "platform.support.sessions", "platform.tenants.archive", "platform.tenants.manage", "platform.tenants.restore", "platform.users.deactivate", "platform.users.delete", "platform.users.invite", "platform.users.manage_access", "platform.users.reactivate", "platform.users.view"];
|
|
5
|
-
type PlatformPermissionKey = (typeof PLATFORM_PERMISSIONS)[number];
|
|
6
|
-
/** Dash-owner permission keys (granted at dash scope, mirrored from the membership role). */
|
|
7
|
-
declare const DASH_PERMISSIONS: readonly ["dash.apps.manage", "dash.billing.manage", "dash.channels.manage", "dash.customers.manage", "dash.domains.manage", "dash.members.manage", "dash.ownership.transfer", "dash.plans.manage", "dash.publish", "dash.settings.manage", "dash.workspaces.admin", "dash.workspaces.manage"];
|
|
8
|
-
type DashPermissionKey = (typeof DASH_PERMISSIONS)[number];
|
|
9
|
-
/** Membership-mirrored dash role bundles (grant keys at dash scope). */
|
|
10
|
-
declare const DASH_ROLES: readonly ["dash.admin", "dash.owner"];
|
|
11
|
-
type DashRole = (typeof DASH_ROLES)[number];
|
|
12
|
-
/** Lead-broker workload permission keys. */
|
|
13
|
-
declare const LEADBROKER_PERMISSIONS: readonly ["leadbroker.finance.manage", "leadbroker.leads.dispatch", "leadbroker.leads.review", "leadbroker.leads.view", "leadbroker.policy.manage"];
|
|
14
|
-
type LeadBrokerPermissionKey = (typeof LEADBROKER_PERMISSIONS)[number];
|
|
15
|
-
/** Workspace-staff permission keys. */
|
|
16
|
-
declare const WORKSPACE_PERMISSIONS: readonly ["workspace.channels.manage", "workspace.channels.secrets", "workspace.channels.view", "workspace.crm.manage", "workspace.crm.view", "workspace.customers.manage", "workspace.customers.view", "workspace.orders.manage", "workspace.orders.view", "workspace.payments.record", "workspace.site.edit", "workspace.site.view", "workspace.staff.manage"];
|
|
17
|
-
type WorkspacePermissionKey = (typeof WORKSPACE_PERMISSIONS)[number];
|
|
18
|
-
/** Assignable workspace-staff role bundles (grant keys at workspace scope). */
|
|
19
|
-
declare const WORKSPACE_ROLES: readonly ["workspace.editor", "workspace.frontdesk", "workspace.manager"];
|
|
20
|
-
type WorkspaceRole = (typeof WORKSPACE_ROLES)[number];
|
|
21
|
-
/** Every permission key the backend can grant, across all scopes. */
|
|
22
|
-
type PermissionKey = PlatformPermissionKey | DashPermissionKey | LeadBrokerPermissionKey | WorkspacePermissionKey;
|
|
23
|
-
|
|
24
|
-
type AccessErrorCode = "unauthenticated" | "forbidden" | "unavailable" | "invalid_snapshot";
|
|
25
|
-
/**
|
|
26
|
-
* Typed failure for loading or validating an access snapshot. `unauthenticated`
|
|
27
|
-
* = no/expired session (401); `forbidden` = the backend refused the snapshot
|
|
28
|
-
* (403; e.g. wrong audience); `unavailable` = transport/5xx; `invalid_snapshot`
|
|
29
|
-
* = the payload did not match the contract.
|
|
30
|
-
*/
|
|
31
|
-
declare class AccessSnapshotError extends Error {
|
|
32
|
-
readonly code: AccessErrorCode;
|
|
33
|
-
readonly status: number | undefined;
|
|
34
|
-
constructor(code: AccessErrorCode, message: string, status?: number);
|
|
35
|
-
}
|
|
36
|
-
declare function isAccessSnapshotError(error: unknown, code?: AccessErrorCode): error is AccessSnapshotError;
|
|
37
|
-
|
|
38
|
-
/** Scope a permission entry applies to. Mirrors c1m-server authz.Scope*. */
|
|
39
|
-
type AccessScopeType = "platform" | "saas_app" | "workspace";
|
|
40
|
-
interface AccessEntry {
|
|
41
|
-
/** Permission key, e.g. "platform.users.manage_access". */
|
|
42
|
-
key: string;
|
|
43
|
-
scope_type: AccessScopeType;
|
|
44
|
-
/** "" for platform scope; saas_app slug or "*" for saas_app scope; workspace id for workspace scope. */
|
|
45
|
-
scope_id: string;
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* The resolved authorization snapshot for one subject, as returned by
|
|
49
|
-
* `GET /api/v1/platform/access`. This is the *output* of the backend resolver
|
|
50
|
-
* (base-role grant ∪ scoped grants), never the grant rows themselves.
|
|
51
|
-
*/
|
|
52
|
-
interface AccessSnapshot {
|
|
53
|
-
subject_id: string;
|
|
54
|
-
/** Monotonic; bumps on any grant/role change. Use for optimistic concurrency and cache keys. */
|
|
55
|
-
access_version: number;
|
|
56
|
-
is_owner: boolean;
|
|
57
|
-
active: boolean;
|
|
58
|
-
/** Platform operator base role (derived; display only — never authorize on it). */
|
|
59
|
-
platform_role: string;
|
|
60
|
-
permissions: AccessEntry[];
|
|
61
|
-
/**
|
|
62
|
-
* Dash bundle (`dash.owner` / `dash.admin`) per saas_app id, mirrored from
|
|
63
|
-
* the subject's dash membership. This is the admission list for the dash
|
|
64
|
-
* area: the dashes this subject runs. What they may *do* on one is a normal
|
|
65
|
-
* `hasPermission(snapshot, key, { saasApp })` check — platform managers hold
|
|
66
|
-
* their authority platform-wide and so appear here not at all.
|
|
67
|
-
*/
|
|
68
|
-
dash_roles: Record<string, string>;
|
|
69
|
-
/** Effective workspace-staff permission keys per workspace id. */
|
|
70
|
-
workspace_permissions: Record<string, string[]>;
|
|
71
|
-
/** Workspace-staff role bundle per workspace id (display only). */
|
|
72
|
-
workspace_roles: Record<string, string>;
|
|
73
|
-
}
|
|
74
|
-
declare function emptySnapshot(subjectId?: string): AccessSnapshot;
|
|
75
|
-
/** Runtime validator for the wire payload. Throws AccessSnapshotError("invalid_snapshot"). */
|
|
76
|
-
declare function parseAccessSnapshot(input: unknown): AccessSnapshot;
|
|
77
|
-
|
|
78
|
-
interface PermissionCheckOptions {
|
|
79
|
-
/**
|
|
80
|
-
* SaaS app (dash) slug the check is about. Omit for "anywhere": platform
|
|
81
|
-
* entries and any saas_app entry satisfy the check.
|
|
82
|
-
*/
|
|
83
|
-
saasApp?: string;
|
|
84
|
-
}
|
|
85
|
-
/**
|
|
86
|
-
* Mirrors c1m-server authz.entryCoversApp: platform scope covers every app;
|
|
87
|
-
* saas_app scope covers "*" or a case-insensitive slug match; workspace-scoped
|
|
88
|
-
* entries never satisfy an app-level check.
|
|
89
|
-
*/
|
|
90
|
-
declare function entryCoversApp(entry: AccessEntry, saasApp: string | undefined): boolean;
|
|
91
|
-
/** True when the subject is active and holds `key` for the given app (or anywhere). */
|
|
92
|
-
declare function hasPermission(snapshot: AccessSnapshot, key: string, opts?: PermissionCheckOptions): boolean;
|
|
93
|
-
declare function hasAnyPermission(snapshot: AccessSnapshot, keys: readonly string[], opts?: PermissionCheckOptions): boolean;
|
|
94
|
-
declare function hasAllPermissions(snapshot: AccessSnapshot, keys: readonly string[], opts?: PermissionCheckOptions): boolean;
|
|
95
|
-
/**
|
|
96
|
-
* Workspace-staff check. Platform owners always pass (they own every
|
|
97
|
-
* workspace); otherwise the workspace's effective permission list decides.
|
|
98
|
-
* Platform/saas_app entries never confer workspace-staff permissions.
|
|
99
|
-
*/
|
|
100
|
-
declare function hasWorkspacePermission(snapshot: AccessSnapshot, workspaceId: string, key: string): boolean;
|
|
101
|
-
/**
|
|
102
|
-
* The dashes this subject runs, from the mirrored dash bundle grants — the
|
|
103
|
-
* admission list for the Admin Center's dash area. Platform managers are not
|
|
104
|
-
* listed: their reach is platform-wide, so admit them with
|
|
105
|
-
* `hasPermission(snapshot, "platform.tenants.manage")` instead.
|
|
106
|
-
*/
|
|
107
|
-
declare function managedDashes(snapshot: AccessSnapshot): Array<{
|
|
108
|
-
saasApp: string;
|
|
109
|
-
role: string;
|
|
110
|
-
}>;
|
|
111
|
-
/** The dash bundle held on one dash, or "" when the subject holds none there. */
|
|
112
|
-
declare function dashRole(snapshot: AccessSnapshot, saasApp: string): string;
|
|
113
|
-
declare function isPlatformOwner(snapshot: AccessSnapshot): boolean;
|
|
114
|
-
/**
|
|
115
|
-
* The saas_app scopes under which `key` is held. Returns `{ all: true }` when a
|
|
116
|
-
* platform-scoped or "*" entry grants it everywhere; otherwise the explicit
|
|
117
|
-
* app slugs (empty = nowhere). Mirrors authz.AppPermissionScope.
|
|
118
|
-
*/
|
|
119
|
-
declare function appScopes(snapshot: AccessSnapshot, key: string): {
|
|
120
|
-
all: boolean;
|
|
121
|
-
apps: string[];
|
|
122
|
-
};
|
|
123
|
-
/** Distinct permission keys held for `saasApp` (or anywhere). Handy for nav/badges. */
|
|
124
|
-
declare function permissionsFor(snapshot: AccessSnapshot, opts?: PermissionCheckOptions): string[];
|
|
125
|
-
|
|
126
|
-
declare const DEFAULT_ACCESS_ENDPOINT_PATH = "/api/v1/platform/access";
|
|
127
|
-
interface FetchAccessSnapshotOptions {
|
|
128
|
-
/** Backend origin, e.g. https://api.example.com (no trailing slash needed). */
|
|
129
|
-
apiUrl: Resolvable;
|
|
130
|
-
/** Bearer access token for the platform surface. */
|
|
131
|
-
accessToken: string;
|
|
132
|
-
/** Defaults to /api/v1/platform/access. */
|
|
133
|
-
endpointPath?: string;
|
|
134
|
-
fetchTimeoutMs?: number;
|
|
135
|
-
fetchImpl?: typeof fetch;
|
|
136
|
-
/** Extra headers. Authorization and Accept are always set here. */
|
|
137
|
-
headers?: HeadersInit;
|
|
138
|
-
/** Incoming BFF request; its client identity (IP, geo, UA) is forwarded to the backend. */
|
|
139
|
-
request?: Request;
|
|
140
|
-
}
|
|
141
|
-
/**
|
|
142
|
-
* Framework-neutral fetch of the resolved access snapshot. Throws
|
|
143
|
-
* AccessSnapshotError: 401 → unauthenticated, 403 → forbidden, other non-2xx or
|
|
144
|
-
* network failure → unavailable, malformed body → invalid_snapshot.
|
|
145
|
-
*/
|
|
146
|
-
declare function fetchAccessSnapshot(opts: FetchAccessSnapshotOptions): Promise<AccessSnapshot>;
|
|
147
|
-
|
|
148
|
-
export { type AccessEntry, type AccessErrorCode, type AccessScopeType, type AccessSnapshot, AccessSnapshotError, DASH_PERMISSIONS, DASH_ROLES, DEFAULT_ACCESS_ENDPOINT_PATH, type DashPermissionKey, type DashRole, type FetchAccessSnapshotOptions, LEADBROKER_PERMISSIONS, type LeadBrokerPermissionKey, PLATFORM_PERMISSIONS, type PermissionCheckOptions, type PermissionKey, type PlatformPermissionKey, WORKSPACE_PERMISSIONS, WORKSPACE_ROLES, type WorkspacePermissionKey, type WorkspaceRole, appScopes, dashRole, emptySnapshot, entryCoversApp, fetchAccessSnapshot, hasAllPermissions, hasAnyPermission, hasPermission, hasWorkspacePermission, isAccessSnapshotError, isPlatformOwner, managedDashes, parseAccessSnapshot, permissionsFor };
|
|
1
|
+
export { a as AccessEntry, b as AccessErrorCode, c as AccessScopeType, A as AccessSnapshot, d as AccessSnapshotError, D as DASH_PERMISSIONS, e as DASH_ROLES, f as DEFAULT_ACCESS_ENDPOINT_PATH, g as DashPermissionKey, h as DashRole, F as FetchAccessSnapshotOptions, L as LEADBROKER_PERMISSIONS, i as LeadBrokerPermissionKey, j as PIPELINE_STAGES, k as PLATFORM_PERMISSIONS, P as PermissionCheckOptions, l as PermissionKey, m as PipelineStage, n as PlatformPermissionKey, W as WORKSPACE_PERMISSIONS, o as WORKSPACE_ROLES, p as WorkspacePermissionKey, q as WorkspaceRole, r as appScopes, s as dashRole, t as emptySnapshot, u as entryCoversApp, v as fetchAccessSnapshot, w as hasAllPermissions, x as hasAnyPermission, y as hasPermission, z as hasWorkspacePermission, B as isAccessSnapshotError, C as isPipelineStage, E as isPlatformOwner, G as managedDashes, H as parseAccessSnapshot, I as permissionsFor } from '../index-CbsCWtku.js';
|
package/dist/access/index.js
CHANGED
|
@@ -1,2 +1,307 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
// src/access/generated/keys.ts
|
|
2
|
+
var PLATFORM_PERMISSIONS = [
|
|
3
|
+
"affiliates.finance.manage",
|
|
4
|
+
"affiliates.partners.manage",
|
|
5
|
+
"affiliates.programmes.manage",
|
|
6
|
+
"affiliates.view",
|
|
7
|
+
"linkservice.domains.manage",
|
|
8
|
+
"linkservice.links.manage",
|
|
9
|
+
"linkservice.qr.manage",
|
|
10
|
+
"linkservice.view",
|
|
11
|
+
"platform.admin.view",
|
|
12
|
+
"platform.audit.view",
|
|
13
|
+
"platform.auth.manage",
|
|
14
|
+
"platform.billing.manage",
|
|
15
|
+
"platform.channels.manage",
|
|
16
|
+
"platform.channels.view",
|
|
17
|
+
"platform.config.manage",
|
|
18
|
+
"platform.docs.manage",
|
|
19
|
+
"platform.docs.view",
|
|
20
|
+
"platform.observability.manage",
|
|
21
|
+
"platform.observability.view",
|
|
22
|
+
"platform.offboarding.view",
|
|
23
|
+
"platform.ops.manage",
|
|
24
|
+
"platform.support.impersonate",
|
|
25
|
+
"platform.support.sessions",
|
|
26
|
+
"platform.tenants.archive",
|
|
27
|
+
"platform.tenants.manage",
|
|
28
|
+
"platform.tenants.restore",
|
|
29
|
+
"platform.users.deactivate",
|
|
30
|
+
"platform.users.delete",
|
|
31
|
+
"platform.users.invite",
|
|
32
|
+
"platform.users.manage_access",
|
|
33
|
+
"platform.users.reactivate",
|
|
34
|
+
"platform.users.view"
|
|
35
|
+
];
|
|
36
|
+
var DASH_PERMISSIONS = [
|
|
37
|
+
"dash.apps.manage",
|
|
38
|
+
"dash.billing.manage",
|
|
39
|
+
"dash.channels.manage",
|
|
40
|
+
"dash.customers.manage",
|
|
41
|
+
"dash.domains.manage",
|
|
42
|
+
"dash.members.manage",
|
|
43
|
+
"dash.ownership.transfer",
|
|
44
|
+
"dash.plans.manage",
|
|
45
|
+
"dash.publish",
|
|
46
|
+
"dash.settings.manage",
|
|
47
|
+
"dash.workspaces.admin",
|
|
48
|
+
"dash.workspaces.manage"
|
|
49
|
+
];
|
|
50
|
+
var DASH_ROLES = [
|
|
51
|
+
"dash.admin",
|
|
52
|
+
"dash.owner"
|
|
53
|
+
];
|
|
54
|
+
var LEADBROKER_PERMISSIONS = [
|
|
55
|
+
"leadbroker.finance.manage",
|
|
56
|
+
"leadbroker.leads.dispatch",
|
|
57
|
+
"leadbroker.leads.review",
|
|
58
|
+
"leadbroker.leads.view",
|
|
59
|
+
"leadbroker.policy.manage"
|
|
60
|
+
];
|
|
61
|
+
var WORKSPACE_PERMISSIONS = [
|
|
62
|
+
"workspace.channels.manage",
|
|
63
|
+
"workspace.channels.secrets",
|
|
64
|
+
"workspace.channels.view",
|
|
65
|
+
"workspace.crm.manage",
|
|
66
|
+
"workspace.crm.view",
|
|
67
|
+
"workspace.customers.manage",
|
|
68
|
+
"workspace.customers.view",
|
|
69
|
+
"workspace.orders.manage",
|
|
70
|
+
"workspace.orders.view",
|
|
71
|
+
"workspace.payments.record",
|
|
72
|
+
"workspace.site.edit",
|
|
73
|
+
"workspace.site.view",
|
|
74
|
+
"workspace.staff.manage"
|
|
75
|
+
];
|
|
76
|
+
var WORKSPACE_ROLES = [
|
|
77
|
+
"workspace.editor",
|
|
78
|
+
"workspace.frontdesk",
|
|
79
|
+
"workspace.manager"
|
|
80
|
+
];
|
|
81
|
+
|
|
82
|
+
// src/access/generated/stages.ts
|
|
83
|
+
var PIPELINE_STAGES = ["created", "awaiting_payment", "provisioning", "attention", "active"];
|
|
84
|
+
function isPipelineStage(value) {
|
|
85
|
+
return typeof value === "string" && PIPELINE_STAGES.includes(value);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// src/access/errors.ts
|
|
89
|
+
var AccessSnapshotError = class extends Error {
|
|
90
|
+
code;
|
|
91
|
+
status;
|
|
92
|
+
constructor(code, message, status) {
|
|
93
|
+
super(message);
|
|
94
|
+
this.name = "AccessSnapshotError";
|
|
95
|
+
this.code = code;
|
|
96
|
+
this.status = status;
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
function isAccessSnapshotError(error, code) {
|
|
100
|
+
return error instanceof AccessSnapshotError && (code === void 0 || error.code === code);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// src/access/snapshot.ts
|
|
104
|
+
var SCOPE_TYPES = /* @__PURE__ */ new Set(["platform", "saas_app", "workspace"]);
|
|
105
|
+
function emptySnapshot(subjectId = "") {
|
|
106
|
+
return {
|
|
107
|
+
subject_id: subjectId,
|
|
108
|
+
access_version: 0,
|
|
109
|
+
is_owner: false,
|
|
110
|
+
active: false,
|
|
111
|
+
platform_role: "",
|
|
112
|
+
permissions: [],
|
|
113
|
+
dash_roles: {},
|
|
114
|
+
workspace_permissions: {},
|
|
115
|
+
workspace_roles: {}
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
function isRecord(value) {
|
|
119
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
120
|
+
}
|
|
121
|
+
function fail(field, why) {
|
|
122
|
+
throw new AccessSnapshotError("invalid_snapshot", `access snapshot: ${field} ${why}`);
|
|
123
|
+
}
|
|
124
|
+
function parseAccessSnapshot(input) {
|
|
125
|
+
if (!isRecord(input)) fail("payload", "is not an object");
|
|
126
|
+
const { subject_id, access_version, is_owner, active, platform_role, permissions, dash_roles, workspace_permissions, workspace_roles } = input;
|
|
127
|
+
if (typeof subject_id !== "string" || subject_id === "") fail("subject_id", "must be a non-empty string");
|
|
128
|
+
if (typeof access_version !== "number" || !Number.isInteger(access_version) || access_version < 0) fail("access_version", "must be a non-negative integer");
|
|
129
|
+
if (typeof is_owner !== "boolean") fail("is_owner", "must be a boolean");
|
|
130
|
+
if (typeof active !== "boolean") fail("active", "must be a boolean");
|
|
131
|
+
if (platform_role !== void 0 && platform_role !== null && typeof platform_role !== "string") fail("platform_role", "must be a string");
|
|
132
|
+
if (!Array.isArray(permissions)) fail("permissions", "must be an array");
|
|
133
|
+
const entries = permissions.map((raw, i) => {
|
|
134
|
+
if (!isRecord(raw)) fail(`permissions[${i}]`, "is not an object");
|
|
135
|
+
const { key, scope_type, scope_id } = raw;
|
|
136
|
+
if (typeof key !== "string" || key === "") fail(`permissions[${i}].key`, "must be a non-empty string");
|
|
137
|
+
if (typeof scope_type !== "string" || !SCOPE_TYPES.has(scope_type)) fail(`permissions[${i}].scope_type`, "must be platform|saas_app|workspace");
|
|
138
|
+
if (scope_id !== void 0 && scope_id !== null && typeof scope_id !== "string") fail(`permissions[${i}].scope_id`, "must be a string");
|
|
139
|
+
return { key, scope_type, scope_id: typeof scope_id === "string" ? scope_id : "" };
|
|
140
|
+
});
|
|
141
|
+
const dashRoles = {};
|
|
142
|
+
if (dash_roles !== void 0 && dash_roles !== null) {
|
|
143
|
+
if (!isRecord(dash_roles)) fail("dash_roles", "must be an object");
|
|
144
|
+
for (const [dash, role] of Object.entries(dash_roles)) {
|
|
145
|
+
if (typeof role !== "string") fail(`dash_roles[${dash}]`, "must be a string");
|
|
146
|
+
dashRoles[dash] = role;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
const wsPerms = {};
|
|
150
|
+
if (workspace_permissions !== void 0 && workspace_permissions !== null) {
|
|
151
|
+
if (!isRecord(workspace_permissions)) fail("workspace_permissions", "must be an object");
|
|
152
|
+
for (const [ws, keys] of Object.entries(workspace_permissions)) {
|
|
153
|
+
if (!Array.isArray(keys) || keys.some((k) => typeof k !== "string")) fail(`workspace_permissions[${ws}]`, "must be a string array");
|
|
154
|
+
wsPerms[ws] = [...keys];
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
const wsRoles = {};
|
|
158
|
+
if (workspace_roles !== void 0 && workspace_roles !== null) {
|
|
159
|
+
if (!isRecord(workspace_roles)) fail("workspace_roles", "must be an object");
|
|
160
|
+
for (const [ws, role] of Object.entries(workspace_roles)) {
|
|
161
|
+
if (typeof role !== "string") fail(`workspace_roles[${ws}]`, "must be a string");
|
|
162
|
+
wsRoles[ws] = role;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return {
|
|
166
|
+
subject_id,
|
|
167
|
+
access_version,
|
|
168
|
+
is_owner,
|
|
169
|
+
active,
|
|
170
|
+
platform_role: typeof platform_role === "string" ? platform_role : "",
|
|
171
|
+
permissions: entries,
|
|
172
|
+
dash_roles: dashRoles,
|
|
173
|
+
workspace_permissions: wsPerms,
|
|
174
|
+
workspace_roles: wsRoles
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// src/access/predicates.ts
|
|
179
|
+
function entryCoversApp(entry, saasApp) {
|
|
180
|
+
switch (entry.scope_type) {
|
|
181
|
+
case "platform":
|
|
182
|
+
return true;
|
|
183
|
+
case "saas_app":
|
|
184
|
+
if (saasApp === void 0) return true;
|
|
185
|
+
return entry.scope_id === "*" || entry.scope_id.toLowerCase() === saasApp.toLowerCase();
|
|
186
|
+
default:
|
|
187
|
+
return false;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
function hasPermission(snapshot, key, opts = {}) {
|
|
191
|
+
if (!snapshot.active) return false;
|
|
192
|
+
return snapshot.permissions.some((entry) => entry.key === key && entryCoversApp(entry, opts.saasApp));
|
|
193
|
+
}
|
|
194
|
+
function hasAnyPermission(snapshot, keys, opts = {}) {
|
|
195
|
+
return keys.some((key) => hasPermission(snapshot, key, opts));
|
|
196
|
+
}
|
|
197
|
+
function hasAllPermissions(snapshot, keys, opts = {}) {
|
|
198
|
+
return keys.every((key) => hasPermission(snapshot, key, opts));
|
|
199
|
+
}
|
|
200
|
+
function hasWorkspacePermission(snapshot, workspaceId, key) {
|
|
201
|
+
if (!snapshot.active) return false;
|
|
202
|
+
if (snapshot.is_owner) return true;
|
|
203
|
+
return (snapshot.workspace_permissions[workspaceId] ?? []).includes(key);
|
|
204
|
+
}
|
|
205
|
+
function managedDashes(snapshot) {
|
|
206
|
+
if (!snapshot.active) return [];
|
|
207
|
+
return Object.entries(snapshot.dash_roles).map(([saasApp, role]) => ({ saasApp, role })).sort((a, b) => a.saasApp.localeCompare(b.saasApp));
|
|
208
|
+
}
|
|
209
|
+
function dashRole(snapshot, saasApp) {
|
|
210
|
+
if (!snapshot.active) return "";
|
|
211
|
+
return snapshot.dash_roles[saasApp] ?? "";
|
|
212
|
+
}
|
|
213
|
+
function isPlatformOwner(snapshot) {
|
|
214
|
+
return snapshot.active && snapshot.is_owner;
|
|
215
|
+
}
|
|
216
|
+
function appScopes(snapshot, key) {
|
|
217
|
+
if (!snapshot.active) return { all: false, apps: [] };
|
|
218
|
+
const apps = /* @__PURE__ */ new Set();
|
|
219
|
+
for (const entry of snapshot.permissions) {
|
|
220
|
+
if (entry.key !== key) continue;
|
|
221
|
+
if (entry.scope_type === "platform") return { all: true, apps: [] };
|
|
222
|
+
if (entry.scope_type === "saas_app") {
|
|
223
|
+
if (entry.scope_id === "*") return { all: true, apps: [] };
|
|
224
|
+
apps.add(entry.scope_id.toLowerCase());
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return { all: false, apps: [...apps].sort() };
|
|
228
|
+
}
|
|
229
|
+
function permissionsFor(snapshot, opts = {}) {
|
|
230
|
+
if (!snapshot.active) return [];
|
|
231
|
+
const keys = /* @__PURE__ */ new Set();
|
|
232
|
+
for (const entry of snapshot.permissions) if (entryCoversApp(entry, opts.saasApp)) keys.add(entry.key);
|
|
233
|
+
return [...keys].sort();
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// ../transport/src/forwarding.ts
|
|
237
|
+
function applyForwardedClientHeaders(incoming, headers = new Headers()) {
|
|
238
|
+
const forwardedFor = incoming.get("x-forwarded-for");
|
|
239
|
+
const realIp = incoming.get("x-real-ip") ?? forwardedFor?.split(",")[0]?.trim();
|
|
240
|
+
if (realIp && !headers.has("X-Client-IP")) headers.set("X-Client-IP", realIp);
|
|
241
|
+
if (realIp && !headers.has("X-Real-IP")) headers.set("X-Real-IP", realIp);
|
|
242
|
+
if (forwardedFor && !headers.has("X-Forwarded-For")) headers.set("X-Forwarded-For", forwardedFor);
|
|
243
|
+
const country = incoming.get("cf-ipcountry") ?? incoming.get("x-vercel-ip-country") ?? incoming.get("x-geo-country") ?? incoming.get("x-country-code");
|
|
244
|
+
if (country && country !== "XX" && !headers.has("X-Geo-Country")) {
|
|
245
|
+
headers.set("X-Geo-Country", country);
|
|
246
|
+
}
|
|
247
|
+
const host = incoming.get("host");
|
|
248
|
+
if (host && !headers.has("X-Forwarded-Host")) headers.set("X-Forwarded-Host", host);
|
|
249
|
+
const proto = incoming.get("x-forwarded-proto");
|
|
250
|
+
if (proto && !headers.has("X-Forwarded-Proto")) headers.set("X-Forwarded-Proto", proto);
|
|
251
|
+
const userAgent = incoming.get("user-agent");
|
|
252
|
+
if (userAgent && !headers.has("X-Client-User-Agent")) headers.set("X-Client-User-Agent", userAgent);
|
|
253
|
+
return headers;
|
|
254
|
+
}
|
|
255
|
+
function forwardedRequestHeaders(request, init) {
|
|
256
|
+
return applyForwardedClientHeaders(request.headers, new Headers(init));
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// src/internal.ts
|
|
260
|
+
function resolveValue(value) {
|
|
261
|
+
return typeof value === "function" ? value() : value;
|
|
262
|
+
}
|
|
263
|
+
async function timedFetch(url, options = {}) {
|
|
264
|
+
const doFetch = options.fetch ?? options.fetchImpl ?? fetch;
|
|
265
|
+
const controller = options.timeoutMs ? new AbortController() : void 0;
|
|
266
|
+
const timer = controller ? setTimeout(() => controller.abort(), options.timeoutMs) : void 0;
|
|
267
|
+
try {
|
|
268
|
+
return await doFetch(url, {
|
|
269
|
+
...options.init,
|
|
270
|
+
cache: "no-store",
|
|
271
|
+
...controller ? { signal: controller.signal } : {}
|
|
272
|
+
});
|
|
273
|
+
} finally {
|
|
274
|
+
if (timer) clearTimeout(timer);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// src/access/fetch.ts
|
|
279
|
+
var DEFAULT_ACCESS_ENDPOINT_PATH = "/api/v1/platform/access";
|
|
280
|
+
async function fetchAccessSnapshot(opts) {
|
|
281
|
+
const url = `${resolveValue(opts.apiUrl).replace(/\/$/, "")}${opts.endpointPath ?? DEFAULT_ACCESS_ENDPOINT_PATH}`;
|
|
282
|
+
const headers = opts.request ? forwardedRequestHeaders(opts.request, opts.headers) : new Headers(opts.headers);
|
|
283
|
+
headers.set("Authorization", `Bearer ${opts.accessToken}`);
|
|
284
|
+
headers.set("Accept", "application/json");
|
|
285
|
+
let response;
|
|
286
|
+
try {
|
|
287
|
+
response = await timedFetch(url, {
|
|
288
|
+
init: { method: "GET", headers },
|
|
289
|
+
timeoutMs: opts.fetchTimeoutMs,
|
|
290
|
+
fetchImpl: opts.fetchImpl
|
|
291
|
+
});
|
|
292
|
+
} catch (error) {
|
|
293
|
+
throw new AccessSnapshotError("unavailable", `access snapshot request failed: ${error.message}`);
|
|
294
|
+
}
|
|
295
|
+
if (response.status === 401) throw new AccessSnapshotError("unauthenticated", "session expired", 401);
|
|
296
|
+
if (response.status === 403) throw new AccessSnapshotError("forbidden", "access snapshot refused", 403);
|
|
297
|
+
if (!response.ok) throw new AccessSnapshotError("unavailable", `access snapshot returned ${response.status}`, response.status);
|
|
298
|
+
let body;
|
|
299
|
+
try {
|
|
300
|
+
body = await response.json();
|
|
301
|
+
} catch {
|
|
302
|
+
throw new AccessSnapshotError("invalid_snapshot", "access snapshot body is not JSON", response.status);
|
|
303
|
+
}
|
|
304
|
+
return parseAccessSnapshot(body);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
export { AccessSnapshotError, DASH_PERMISSIONS, DASH_ROLES, DEFAULT_ACCESS_ENDPOINT_PATH, LEADBROKER_PERMISSIONS, PIPELINE_STAGES, PLATFORM_PERMISSIONS, WORKSPACE_PERMISSIONS, WORKSPACE_ROLES, appScopes, dashRole, emptySnapshot, entryCoversApp, fetchAccessSnapshot, hasAllPermissions, hasAnyPermission, hasPermission, hasWorkspacePermission, isAccessSnapshotError, isPipelineStage, isPlatformOwner, managedDashes, parseAccessSnapshot, permissionsFor };
|
package/dist/access/next.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { R as Resolvable } from '../
|
|
2
|
-
|
|
3
|
-
export { AccessEntry, AccessErrorCode, AccessScopeType, AccessSnapshotError, DASH_PERMISSIONS, DASH_ROLES, DEFAULT_ACCESS_ENDPOINT_PATH, DashPermissionKey, DashRole, FetchAccessSnapshotOptions, LEADBROKER_PERMISSIONS, LeadBrokerPermissionKey, PLATFORM_PERMISSIONS, PermissionKey, PlatformPermissionKey, WORKSPACE_PERMISSIONS, WORKSPACE_ROLES, WorkspacePermissionKey, WorkspaceRole, appScopes, dashRole, emptySnapshot, entryCoversApp, fetchAccessSnapshot, hasAllPermissions, hasAnyPermission, hasPermission, hasWorkspacePermission, isAccessSnapshotError, isPlatformOwner, managedDashes, parseAccessSnapshot, permissionsFor } from './index.js';
|
|
1
|
+
import { A as AccessSnapshot, P as PermissionCheckOptions, R as Resolvable } from '../index-CbsCWtku.js';
|
|
2
|
+
export { a as AccessEntry, b as AccessErrorCode, c as AccessScopeType, d as AccessSnapshotError, D as DASH_PERMISSIONS, e as DASH_ROLES, f as DEFAULT_ACCESS_ENDPOINT_PATH, g as DashPermissionKey, h as DashRole, F as FetchAccessSnapshotOptions, L as LEADBROKER_PERMISSIONS, i as LeadBrokerPermissionKey, j as PIPELINE_STAGES, k as PLATFORM_PERMISSIONS, l as PermissionKey, m as PipelineStage, n as PlatformPermissionKey, W as WORKSPACE_PERMISSIONS, o as WORKSPACE_ROLES, p as WorkspacePermissionKey, q as WorkspaceRole, r as appScopes, s as dashRole, t as emptySnapshot, u as entryCoversApp, v as fetchAccessSnapshot, w as hasAllPermissions, x as hasAnyPermission, y as hasPermission, z as hasWorkspacePermission, B as isAccessSnapshotError, C as isPipelineStage, E as isPlatformOwner, G as managedDashes, H as parseAccessSnapshot, I as permissionsFor } from '../index-CbsCWtku.js';
|
|
4
3
|
|
|
5
4
|
/** Default cache tag for one subject's snapshot: `access:<subjectId>`. */
|
|
6
5
|
declare function accessCacheTag(subjectId: string): string;
|