@lucern/sdk 0.3.0-alpha.11 → 0.3.0-alpha.13
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 +51 -0
- package/dist/accessControl.d.ts +1 -0
- package/dist/accessControl.js +156 -22
- package/dist/accessControl.js.map +1 -1
- package/dist/adminClient.js.map +1 -1
- package/dist/answersClient.js.map +1 -1
- package/dist/audiencesClient.js.map +1 -1
- package/dist/auditClient.js.map +1 -1
- package/dist/authContext.d.ts +1 -1
- package/dist/authContext.js.map +1 -1
- package/dist/beliefs/index.d.ts +1 -0
- package/dist/beliefs/index.js +206 -40
- package/dist/beliefs/index.js.map +1 -1
- package/dist/beliefsClient.js.map +1 -1
- package/dist/client.d.ts +79 -31
- package/dist/client.js +206 -40
- package/dist/client.js.map +1 -1
- package/dist/contextClient.js.map +1 -1
- package/dist/contracts/auth-session.contract.d.ts +1 -1
- package/dist/contracts/auth-session.contract.js +13 -1
- package/dist/contracts/auth-session.contract.js.map +1 -1
- package/dist/contracts/index.js +13 -1
- package/dist/contracts/index.js.map +1 -1
- package/dist/contradictions/index.d.ts +1 -0
- package/dist/contradictions/index.js +206 -40
- package/dist/contradictions/index.js.map +1 -1
- package/dist/control-plane.d.ts +69 -0
- package/dist/control-plane.js +656 -0
- package/dist/control-plane.js.map +1 -0
- package/dist/coreClient.js.map +1 -1
- package/dist/decisions/index.d.ts +1 -0
- package/dist/decisions/index.js +206 -40
- package/dist/decisions/index.js.map +1 -1
- package/dist/decisionsClient.js.map +1 -1
- package/dist/edges/index.d.ts +1 -0
- package/dist/edges/index.js +206 -40
- package/dist/edges/index.js.map +1 -1
- package/dist/embeddingsClient.js.map +1 -1
- package/dist/eventingClient.js.map +1 -1
- package/dist/eventsCore.js.map +1 -1
- package/dist/evidence/index.d.ts +1 -0
- package/dist/evidence/index.js +206 -40
- package/dist/evidence/index.js.map +1 -1
- package/dist/evidenceClient.js.map +1 -1
- package/dist/functionSurface.d.ts +2 -1
- package/dist/functionSurface.js +5 -0
- package/dist/functionSurface.js.map +1 -1
- package/dist/functionSurfaceClient.js +5 -0
- package/dist/functionSurfaceClient.js.map +1 -1
- package/dist/gatewayFacades.d.ts +26 -2
- package/dist/gatewayFacades.js +135 -7
- package/dist/gatewayFacades.js.map +1 -1
- package/dist/graphAnalysisClient.js.map +1 -1
- package/dist/graphClient.js.map +1 -1
- package/dist/graphRecommendationsClient.js.map +1 -1
- package/dist/graphStateClassifierClient.js.map +1 -1
- package/dist/harnessClient.js.map +1 -1
- package/dist/identityClient.d.ts +19 -1
- package/dist/identityClient.js +133 -5
- package/dist/identityClient.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +232 -49
- package/dist/index.js.map +1 -1
- package/dist/jobsClient.js.map +1 -1
- package/dist/learningClient.js.map +1 -1
- package/dist/lenses/index.d.ts +1 -0
- package/dist/lenses/index.js +206 -40
- package/dist/lenses/index.js.map +1 -1
- package/dist/mcpClient.js.map +1 -1
- package/dist/modelRuntimeClient.js.map +1 -1
- package/dist/nodes/index.d.ts +1 -0
- package/dist/nodes/index.js +206 -40
- package/dist/nodes/index.js.map +1 -1
- package/dist/ontologies/index.d.ts +1 -0
- package/dist/ontologies/index.js +206 -40
- package/dist/ontologies/index.js.map +1 -1
- package/dist/ontologyClient.js.map +1 -1
- package/dist/ontologyLinksClient.js.map +1 -1
- package/dist/orgGraphSearchClient.js.map +1 -1
- package/dist/packsClient.js.map +1 -1
- package/dist/policyClient.js.map +1 -1
- package/dist/questions/index.d.ts +1 -0
- package/dist/questions/index.js +206 -40
- package/dist/questions/index.js.map +1 -1
- package/dist/reportsClient.js.map +1 -1
- package/dist/schemaClient.js.map +1 -1
- package/dist/sourcesClient.js.map +1 -1
- package/dist/telemetryClient.js.map +1 -1
- package/dist/toolRegistryClient.js.map +1 -1
- package/dist/topics/index.d.ts +1 -0
- package/dist/topics/index.js +206 -40
- package/dist/topics/index.js.map +1 -1
- package/dist/topicsClient.js.map +1 -1
- package/dist/types.d.ts +12 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/dist/workflowClient.js.map +1 -1
- package/dist/worktrees/index.d.ts +1 -0
- package/dist/worktrees/index.js +206 -40
- package/dist/worktrees/index.js.map +1 -1
- package/package.json +9 -5
package/README.md
CHANGED
|
@@ -67,6 +67,57 @@ const identity = await lucern.identity.whoami();
|
|
|
67
67
|
const principal: SdkPrincipalContext = identity.data;
|
|
68
68
|
```
|
|
69
69
|
|
|
70
|
+
### Control-Plane Tenant Bootstrap
|
|
71
|
+
|
|
72
|
+
Interactive tenant applications should resolve Clerk users through the Lucern
|
|
73
|
+
control-plane identity surface before making workspace-scoped graph calls. Clerk
|
|
74
|
+
proves the browser user's identity; Lucern authorization comes from the
|
|
75
|
+
Permit-backed control-plane projection.
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
import { createLucernClient } from "@lucern/sdk";
|
|
79
|
+
|
|
80
|
+
async function createLucernForClerkUser(args: {
|
|
81
|
+
clerkUserId: string;
|
|
82
|
+
getClerkToken: () => Promise<string | null>;
|
|
83
|
+
tenantId: string;
|
|
84
|
+
workspaceId: string;
|
|
85
|
+
clerkProjectId?: string;
|
|
86
|
+
}) {
|
|
87
|
+
const token = await args.getClerkToken();
|
|
88
|
+
if (!token) {
|
|
89
|
+
throw new Error("Clerk session token is required.");
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const getAuthHeaders = () => ({ Authorization: `Bearer ${token}` });
|
|
93
|
+
const lucern = createLucernClient({
|
|
94
|
+
baseUrl: "https://api.lucern.ai",
|
|
95
|
+
getAuthHeaders,
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
const principal =
|
|
99
|
+
await lucern.controlPlane.identity.resolveInteractivePrincipal({
|
|
100
|
+
clerkId: args.clerkUserId,
|
|
101
|
+
tenantId: args.tenantId,
|
|
102
|
+
workspaceId: args.workspaceId,
|
|
103
|
+
providerProjectId: args.clerkProjectId,
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
return createLucernClient({
|
|
107
|
+
baseUrl: "https://api.lucern.ai",
|
|
108
|
+
getAuthHeaders,
|
|
109
|
+
authContext: principal.data,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Use `authContext.principalId`, roles, scopes, groups, permitted tools, and
|
|
115
|
+
Permit subject data as the runtime Lucern principal context. Tenant apps must
|
|
116
|
+
not read legacy `users.mcRole` / `defaultTenantId` fields as authorization, and
|
|
117
|
+
they must not call `components.controlPlane.migration` from application code.
|
|
118
|
+
Provisioning and backfills can use migration APIs; runtime bootstrapping uses
|
|
119
|
+
`controlPlane.identity.resolveInteractivePrincipal(...)`.
|
|
120
|
+
|
|
70
121
|
## The Full Developer Journey
|
|
71
122
|
|
|
72
123
|
This walkthrough mirrors what a developer building an AI-powered code review system would experience in a real coding session. Every API call is something you would actually use.
|
package/dist/accessControl.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { GatewayClientConfig } from './coreClient.js';
|
|
|
3
3
|
import { PolicyEvaluationInput, PolicyDecisionRecord } from './identityClient.js';
|
|
4
4
|
import { SessionPrincipalType } from './contracts/auth-session.contract.js';
|
|
5
5
|
import { JsonObject } from './types.js';
|
|
6
|
+
import './control-plane.js';
|
|
6
7
|
import './contracts/workflow-runtime.contract.js';
|
|
7
8
|
import './contracts/lens-workflow.contract.js';
|
|
8
9
|
import './contracts/lens-filter.contract.js';
|
package/dist/accessControl.js
CHANGED
|
@@ -29,14 +29,14 @@ function requireString(value, reason, label) {
|
|
|
29
29
|
}
|
|
30
30
|
return normalized;
|
|
31
31
|
}
|
|
32
|
-
function requirePrincipalType(
|
|
33
|
-
if (!
|
|
32
|
+
function requirePrincipalType(principalType2) {
|
|
33
|
+
if (!principalType2) {
|
|
34
34
|
throw new LucernSdkAuthContextError(
|
|
35
35
|
"principal_missing",
|
|
36
36
|
"Canonical Lucern SDK auth context is missing principalType."
|
|
37
37
|
);
|
|
38
38
|
}
|
|
39
|
-
return
|
|
39
|
+
return principalType2;
|
|
40
40
|
}
|
|
41
41
|
function requireAuthMode(authMode) {
|
|
42
42
|
if (!authMode) {
|
|
@@ -82,7 +82,7 @@ function normalizeCanonicalLucernAuthContext(input) {
|
|
|
82
82
|
);
|
|
83
83
|
const roles = cleanStringList(input.roles);
|
|
84
84
|
const scopes = cleanStringList(input.scopes);
|
|
85
|
-
const
|
|
85
|
+
const principalType2 = requirePrincipalType(input.principalType);
|
|
86
86
|
const authMode = requireAuthMode(input.authMode);
|
|
87
87
|
const roleBasedInteractiveAuth = authMode === "interactive_user" && roles.length > 0;
|
|
88
88
|
if (roles.length === 0 || scopes.length === 0 && !roleBasedInteractiveAuth) {
|
|
@@ -111,7 +111,7 @@ function normalizeCanonicalLucernAuthContext(input) {
|
|
|
111
111
|
principalId,
|
|
112
112
|
tenantId,
|
|
113
113
|
workspaceId,
|
|
114
|
-
principalType,
|
|
114
|
+
principalType: principalType2,
|
|
115
115
|
authMode,
|
|
116
116
|
roles,
|
|
117
117
|
scopes,
|
|
@@ -600,6 +600,109 @@ function listResultFromEnvelope(data, legacyKey) {
|
|
|
600
600
|
);
|
|
601
601
|
}
|
|
602
602
|
|
|
603
|
+
// src/control-plane.ts
|
|
604
|
+
var LucernControlPlaneIdentityError = class extends Error {
|
|
605
|
+
reason;
|
|
606
|
+
principalStatus;
|
|
607
|
+
tenantStatus;
|
|
608
|
+
workspaceStatus;
|
|
609
|
+
details;
|
|
610
|
+
constructor(failure) {
|
|
611
|
+
super(failure.message);
|
|
612
|
+
this.name = "LucernControlPlaneIdentityError";
|
|
613
|
+
this.reason = failure.reason;
|
|
614
|
+
this.principalStatus = failure.principalStatus;
|
|
615
|
+
this.tenantStatus = failure.tenantStatus;
|
|
616
|
+
this.workspaceStatus = failure.workspaceStatus;
|
|
617
|
+
this.details = failure.details;
|
|
618
|
+
}
|
|
619
|
+
};
|
|
620
|
+
function cleanString2(value) {
|
|
621
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
|
|
622
|
+
}
|
|
623
|
+
function stringList(value) {
|
|
624
|
+
if (!Array.isArray(value)) {
|
|
625
|
+
return [];
|
|
626
|
+
}
|
|
627
|
+
return [
|
|
628
|
+
...new Set(
|
|
629
|
+
value.filter((entry) => typeof entry === "string").map((entry) => entry.trim()).filter(Boolean)
|
|
630
|
+
)
|
|
631
|
+
];
|
|
632
|
+
}
|
|
633
|
+
function principalType(value) {
|
|
634
|
+
switch (value) {
|
|
635
|
+
case "service":
|
|
636
|
+
case "service_principal":
|
|
637
|
+
return "service";
|
|
638
|
+
case "agent":
|
|
639
|
+
return "agent";
|
|
640
|
+
case "group":
|
|
641
|
+
return "group";
|
|
642
|
+
case "external_viewer":
|
|
643
|
+
case "external_stakeholder":
|
|
644
|
+
return "external_viewer";
|
|
645
|
+
default:
|
|
646
|
+
return "human";
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
function adminFlags(roles) {
|
|
650
|
+
const normalized = roles.map((role) => role.toLowerCase());
|
|
651
|
+
const isPlatformAdmin = normalized.includes("platform_admin");
|
|
652
|
+
const isTenantAdmin = isPlatformAdmin || normalized.includes("tenant_admin");
|
|
653
|
+
const isWorkspaceAdmin = isTenantAdmin || normalized.includes("workspace_admin") || normalized.includes("workspace_owner");
|
|
654
|
+
return { isPlatformAdmin, isTenantAdmin, isWorkspaceAdmin };
|
|
655
|
+
}
|
|
656
|
+
function normalizeResolvedInteractivePrincipal(payload) {
|
|
657
|
+
if ("ok" in payload && payload.ok === false) {
|
|
658
|
+
throw new LucernControlPlaneIdentityError(payload);
|
|
659
|
+
}
|
|
660
|
+
const principalId = cleanString2(payload.principalId);
|
|
661
|
+
const clerkId = cleanString2(payload.clerkId);
|
|
662
|
+
const tenantId = cleanString2(payload.tenantId);
|
|
663
|
+
if (!principalId || !clerkId || !tenantId) {
|
|
664
|
+
throw new LucernControlPlaneIdentityError({
|
|
665
|
+
ok: false,
|
|
666
|
+
reason: "resolver_unavailable",
|
|
667
|
+
message: "Control-plane principal resolver returned an incomplete principal context.",
|
|
668
|
+
principalStatus: payload.principalStatus ?? "missing",
|
|
669
|
+
tenantStatus: payload.tenantStatus,
|
|
670
|
+
workspaceStatus: payload.workspaceStatus
|
|
671
|
+
});
|
|
672
|
+
}
|
|
673
|
+
const roles = stringList(payload.roles);
|
|
674
|
+
const scopes = stringList(payload.scopes);
|
|
675
|
+
const workspaceId = cleanString2(payload.workspaceId) ?? null;
|
|
676
|
+
const flags = adminFlags(roles);
|
|
677
|
+
return {
|
|
678
|
+
principalId,
|
|
679
|
+
principalType: principalType(payload.principalType),
|
|
680
|
+
clerkId,
|
|
681
|
+
tenantId,
|
|
682
|
+
workspaceId,
|
|
683
|
+
roles,
|
|
684
|
+
scopes,
|
|
685
|
+
groupIds: stringList(payload.groupIds),
|
|
686
|
+
permittedToolNames: stringList(payload.permittedToolNames),
|
|
687
|
+
permittedPackKeys: stringList(payload.permittedPackKeys),
|
|
688
|
+
principalStatus: cleanString2(payload.principalStatus) ?? "active",
|
|
689
|
+
tenantStatus: cleanString2(payload.tenantStatus) ?? "active",
|
|
690
|
+
workspaceStatus: cleanString2(payload.workspaceStatus) ?? (workspaceId ? "active" : "none"),
|
|
691
|
+
isPlatformAdmin: typeof payload.isPlatformAdmin === "boolean" ? payload.isPlatformAdmin : flags.isPlatformAdmin,
|
|
692
|
+
isTenantAdmin: typeof payload.isTenantAdmin === "boolean" ? payload.isTenantAdmin : flags.isTenantAdmin,
|
|
693
|
+
isWorkspaceAdmin: typeof payload.isWorkspaceAdmin === "boolean" ? payload.isWorkspaceAdmin : flags.isWorkspaceAdmin,
|
|
694
|
+
permit: {
|
|
695
|
+
subject: cleanString2(payload.permit?.subject) ?? principalId,
|
|
696
|
+
tenant: cleanString2(payload.permit?.tenant) ?? tenantId,
|
|
697
|
+
...workspaceId ? { workspace: cleanString2(payload.permit?.workspace) ?? workspaceId } : {}
|
|
698
|
+
},
|
|
699
|
+
authMode: "interactive_user",
|
|
700
|
+
sessionId: payload.sessionId,
|
|
701
|
+
delegatedBy: payload.delegatedBy,
|
|
702
|
+
expiresAt: payload.expiresAt
|
|
703
|
+
};
|
|
704
|
+
}
|
|
705
|
+
|
|
603
706
|
// src/identityClient.ts
|
|
604
707
|
function createIdentityWhoamiClient(config = {}) {
|
|
605
708
|
const gateway = createGatewayRequestClient(config);
|
|
@@ -667,13 +770,25 @@ function createIdentityClient(config = {}) {
|
|
|
667
770
|
(response) => mapGatewayData(response, (data) => ({
|
|
668
771
|
principalId: data.principalId,
|
|
669
772
|
principalType: data.principalType,
|
|
773
|
+
clerkId: data.clerkId,
|
|
670
774
|
tenantId: data.tenantId ?? null,
|
|
671
775
|
workspaceId: data.workspaceId ?? null,
|
|
672
776
|
scopes: Array.isArray(data.scopes) ? data.scopes : [],
|
|
673
777
|
roles: Array.isArray(data.roles) ? data.roles : [],
|
|
778
|
+
groupIds: Array.isArray(data.groupIds) ? data.groupIds : [],
|
|
779
|
+
permittedToolNames: Array.isArray(data.permittedToolNames) ? data.permittedToolNames : [],
|
|
780
|
+
permittedPackKeys: Array.isArray(data.permittedPackKeys) ? data.permittedPackKeys : [],
|
|
781
|
+
principalStatus: data.principalStatus,
|
|
782
|
+
tenantStatus: data.tenantStatus,
|
|
783
|
+
workspaceStatus: data.workspaceStatus,
|
|
674
784
|
isPlatformAdmin: data.isPlatformAdmin === true,
|
|
675
785
|
isTenantAdmin: data.isTenantAdmin === true,
|
|
676
786
|
isWorkspaceAdmin: data.isWorkspaceAdmin === true,
|
|
787
|
+
permit: data.permit ?? (data.tenantId ? {
|
|
788
|
+
subject: data.principalId,
|
|
789
|
+
tenant: data.tenantId,
|
|
790
|
+
...data.workspaceId ? { workspace: data.workspaceId } : {}
|
|
791
|
+
} : void 0),
|
|
677
792
|
authMode: data.authMode,
|
|
678
793
|
sessionId: data.sessionId,
|
|
679
794
|
delegatedBy: data.delegatedBy,
|
|
@@ -681,6 +796,19 @@ function createIdentityClient(config = {}) {
|
|
|
681
796
|
}))
|
|
682
797
|
);
|
|
683
798
|
},
|
|
799
|
+
/**
|
|
800
|
+
* Resolve a Clerk subject through the tenant control-plane Permit projection.
|
|
801
|
+
* @deprecated Prefer lucern.controlPlane.identity.resolveInteractivePrincipal().
|
|
802
|
+
*/
|
|
803
|
+
async resolveInteractivePrincipal(input) {
|
|
804
|
+
return gateway.request({
|
|
805
|
+
path: "/api/platform/v1/control-plane/identity/resolve-interactive-principal",
|
|
806
|
+
method: "POST",
|
|
807
|
+
body: input
|
|
808
|
+
}).then(
|
|
809
|
+
(response) => mapGatewayData(response, normalizeResolvedInteractivePrincipal)
|
|
810
|
+
);
|
|
811
|
+
},
|
|
684
812
|
/**
|
|
685
813
|
* List principals in the current identity scope.
|
|
686
814
|
*/
|
|
@@ -877,7 +1005,7 @@ var LucernAccessControlError = class extends LucernSdkAuthContextError {
|
|
|
877
1005
|
this.policyDecision = policyDecision;
|
|
878
1006
|
}
|
|
879
1007
|
};
|
|
880
|
-
function
|
|
1008
|
+
function cleanString3(value) {
|
|
881
1009
|
const normalized = value?.trim();
|
|
882
1010
|
return normalized ? normalized : void 0;
|
|
883
1011
|
}
|
|
@@ -892,7 +1020,7 @@ function cleanStringList2(values) {
|
|
|
892
1020
|
];
|
|
893
1021
|
}
|
|
894
1022
|
function requireString2(value, reason, label) {
|
|
895
|
-
const normalized =
|
|
1023
|
+
const normalized = cleanString3(value);
|
|
896
1024
|
if (!normalized) {
|
|
897
1025
|
throw new LucernAccessControlError(
|
|
898
1026
|
reason,
|
|
@@ -901,13 +1029,19 @@ function requireString2(value, reason, label) {
|
|
|
901
1029
|
}
|
|
902
1030
|
return normalized;
|
|
903
1031
|
}
|
|
904
|
-
function normalizePrincipalType(
|
|
905
|
-
if (
|
|
1032
|
+
function normalizePrincipalType(principalType2) {
|
|
1033
|
+
if (principalType2 === "agent") {
|
|
906
1034
|
return "agent";
|
|
907
1035
|
}
|
|
908
|
-
if (
|
|
1036
|
+
if (principalType2 === "service") {
|
|
909
1037
|
return "service";
|
|
910
1038
|
}
|
|
1039
|
+
if (principalType2 === "group") {
|
|
1040
|
+
return "group";
|
|
1041
|
+
}
|
|
1042
|
+
if (principalType2 === "external_viewer") {
|
|
1043
|
+
return "external_viewer";
|
|
1044
|
+
}
|
|
911
1045
|
return "human";
|
|
912
1046
|
}
|
|
913
1047
|
function aliasKey(alias) {
|
|
@@ -916,15 +1050,15 @@ function aliasKey(alias) {
|
|
|
916
1050
|
function normalizeAliases(input, canonicalClerkUserId) {
|
|
917
1051
|
const aliases = /* @__PURE__ */ new Map();
|
|
918
1052
|
for (const alias of input ?? []) {
|
|
919
|
-
const externalSubjectId =
|
|
1053
|
+
const externalSubjectId = cleanString3(alias.externalSubjectId);
|
|
920
1054
|
if (!externalSubjectId) {
|
|
921
1055
|
continue;
|
|
922
1056
|
}
|
|
923
1057
|
const normalized = {
|
|
924
|
-
provider:
|
|
925
|
-
providerProjectId:
|
|
1058
|
+
provider: cleanString3(alias.provider) ?? "clerk",
|
|
1059
|
+
providerProjectId: cleanString3(alias.providerProjectId),
|
|
926
1060
|
externalSubjectId,
|
|
927
|
-
status:
|
|
1061
|
+
status: cleanString3(alias.status)
|
|
928
1062
|
};
|
|
929
1063
|
aliases.set(aliasKey(normalized), normalized);
|
|
930
1064
|
}
|
|
@@ -969,10 +1103,10 @@ function normalizeCanonicalPrincipalIdentity(input, options = {}) {
|
|
|
969
1103
|
"principal_missing",
|
|
970
1104
|
"principalId"
|
|
971
1105
|
);
|
|
972
|
-
const
|
|
973
|
-
const observedClerkId =
|
|
974
|
-
const canonicalClerkUserId =
|
|
975
|
-
if (
|
|
1106
|
+
const principalType2 = normalizePrincipalType(principalInput.principalType);
|
|
1107
|
+
const observedClerkId = cleanString3(options.observedClerkId);
|
|
1108
|
+
const canonicalClerkUserId = cleanString3(principalInput.canonicalClerkUserId) ?? cleanString3(principalInput.clerkId);
|
|
1109
|
+
if (principalType2 === "human" && !canonicalClerkUserId) {
|
|
976
1110
|
throw new LucernAccessControlError(
|
|
977
1111
|
"clerk_alias_missing",
|
|
978
1112
|
"Human principals require one canonical Clerk user id."
|
|
@@ -994,11 +1128,11 @@ function normalizeCanonicalPrincipalIdentity(input, options = {}) {
|
|
|
994
1128
|
}
|
|
995
1129
|
return {
|
|
996
1130
|
principalId,
|
|
997
|
-
principalType,
|
|
1131
|
+
principalType: principalType2,
|
|
998
1132
|
canonicalClerkUserId,
|
|
999
1133
|
clerkIdentityAliases: aliases,
|
|
1000
|
-
tenantId:
|
|
1001
|
-
workspaceId:
|
|
1134
|
+
tenantId: cleanString3(principalInput.tenantId),
|
|
1135
|
+
workspaceId: cleanString3(principalInput.workspaceId),
|
|
1002
1136
|
roles: cleanStringList2(principalInput.roles),
|
|
1003
1137
|
scopes: cleanStringList2(principalInput.scopes)
|
|
1004
1138
|
};
|
|
@@ -1023,7 +1157,7 @@ function buildPolicyInput(identity, input) {
|
|
|
1023
1157
|
"tenant_missing",
|
|
1024
1158
|
"tenantId"
|
|
1025
1159
|
);
|
|
1026
|
-
const workspaceId =
|
|
1160
|
+
const workspaceId = cleanString3(input.workspaceId ?? identity.workspaceId);
|
|
1027
1161
|
if (resourceRequiresWorkspace(input.resource) && !workspaceId) {
|
|
1028
1162
|
throw new LucernAccessControlError(
|
|
1029
1163
|
"workspace_missing",
|