@ionite/server 0.0.24-beta.20260907.3 → 0.0.24-beta.20260910.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/dist/index.d.ts +14 -10
- package/dist/index.js +219 -65
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -384,7 +384,7 @@ declare class InMemoryCustomerStore<TExtended = Record<string, never>> implement
|
|
|
384
384
|
delete(id: string): Promise<number>;
|
|
385
385
|
list(options?: ListOptions3): Promise<ListResult3<BaseCustomer<TExtended>>>;
|
|
386
386
|
}
|
|
387
|
-
import { AccessAssignmentStore as AccessAssignmentStore2, AccessCatalogStore as AccessCatalogStore2, BaseUser as BaseUser2, CodeCatalog, CustomerStore as CustomerStore2, IamDeltaEmitter, IamDeltaMode, Logger as Logger4, ScimUser, UserStore, Workload as Workload2, WorkloadIdentityStore } from "@ionite/core";
|
|
387
|
+
import { AccessAssignmentStore as AccessAssignmentStore2, AccessCatalogStore as AccessCatalogStore2, BaseCustomer as BaseCustomer2, BaseUser as BaseUser2, BaseWorkloadIdentity, CodeCatalog, CustomerStore as CustomerStore2, IamDeltaEmitter, IamDeltaMode, Logger as Logger4, ScimCustomer, ScimUser, ScimWorkload, UserStore, Workload as Workload2, WorkloadIdentityStore } from "@ionite/core";
|
|
388
388
|
type FetchLike = (input: string, init?: RequestInit) => Promise<Response>;
|
|
389
389
|
/** RemoteConfig-derived destination descriptor for IAM delta egress. */
|
|
390
390
|
type IamDeltaConfig = {
|
|
@@ -408,6 +408,10 @@ type IamDeltaDeps = {
|
|
|
408
408
|
codeCatalog?: CodeCatalog;
|
|
409
409
|
/** Optional app mapper shared with the inbound IAM Users read surface. */
|
|
410
410
|
mapUserToScim?: (stored: BaseUser2) => ScimUser | Promise<ScimUser>;
|
|
411
|
+
/** Optional app mapper shared with the inbound IAM Customers read surface. */
|
|
412
|
+
mapCustomerToScim?: (stored: BaseCustomer2) => ScimCustomer | Promise<ScimCustomer>;
|
|
413
|
+
/** Optional app mapper shared with the inbound IAM Workloads read surface. */
|
|
414
|
+
mapWorkloadToScim?: (stored: BaseWorkloadIdentity) => ScimWorkload | Promise<ScimWorkload>;
|
|
411
415
|
/** Injectable fetch (defaults to global fetch); enables testing the patch path in-process. */
|
|
412
416
|
fetchImpl?: FetchLike;
|
|
413
417
|
};
|
|
@@ -518,13 +522,13 @@ declare const ION_CAPABILITY_CATALOG: readonly [{
|
|
|
518
522
|
readonly requires: "AccessAssignmentStore + AccessCatalogStore";
|
|
519
523
|
readonly incapableReason: "OAA projection is configured by RemoteConfig but no access/catalog store capability is wired by the application.";
|
|
520
524
|
}];
|
|
521
|
-
import { BaseCustomer as
|
|
525
|
+
import { BaseCustomer as BaseCustomer3, MagicLink, MagicLinkStore } from "@ionite/core";
|
|
522
526
|
/**
|
|
523
527
|
* In-memory implementation of MagicLinkStore from @ionite/core.
|
|
524
528
|
*/
|
|
525
529
|
declare class InMemoryMagicLinkStore<TExtended = Record<string, never>> implements MagicLinkStore<TExtended> {
|
|
526
530
|
private magicLinks;
|
|
527
|
-
create(token: string, customer:
|
|
531
|
+
create(token: string, customer: BaseCustomer3, expiresAt: Date): Promise<void>;
|
|
528
532
|
get(token: string): Promise<MagicLink<TExtended> | null>;
|
|
529
533
|
getAndDelete(token: string): Promise<MagicLink<TExtended> | null>;
|
|
530
534
|
delete(token: string): Promise<void>;
|
|
@@ -851,7 +855,7 @@ declare function closeAllVaultSockets(): void;
|
|
|
851
855
|
*
|
|
852
856
|
* Keep this synchronized with packages/server/package.json via publish.ts.
|
|
853
857
|
*/
|
|
854
|
-
declare const ION_SDK_VERSION = "0.0.24-beta.
|
|
858
|
+
declare const ION_SDK_VERSION = "0.0.24-beta.20260910.1";
|
|
855
859
|
import { Workload as CoreWorkload, FrameworkWorkloadConfig, Ionite as Ionite5, JWTAssertionClaims, Logger as Logger7, TokenValidationResult, TrustedIdp, WorkloadConfigMap, WorkloadGetToken, WorkloadIdentityStore as WorkloadIdentityStore2, WorkloadIncomingOutgoing, WorkloadTokenResponse, WorkloadTokenStore } from "@ionite/core";
|
|
856
860
|
import { StandardSchemaV1 } from "@standard-schema/spec";
|
|
857
861
|
/**
|
|
@@ -1114,7 +1118,7 @@ declare function revokeWorkloadToken(token: string, ion: Ionite5): Promise<void>
|
|
|
1114
1118
|
* Framework-agnostic request handler for the Workload module (token, validate, jwks, refresh routes).
|
|
1115
1119
|
*/
|
|
1116
1120
|
declare function workloadHandler(request: Request, ion: Ionite5): Promise<Response>;
|
|
1117
|
-
import { BaseWorkloadIdentity, ListOptions as ListOptions5, ListResult as ListResult5, WorkloadIdentityStore as WorkloadIdentityStore3 } from "@ionite/core";
|
|
1121
|
+
import { BaseWorkloadIdentity as BaseWorkloadIdentity2, ListOptions as ListOptions5, ListResult as ListResult5, WorkloadIdentityStore as WorkloadIdentityStore3 } from "@ionite/core";
|
|
1118
1122
|
/**
|
|
1119
1123
|
* In-memory {@link WorkloadIdentityStore} implementation using Maps. Suitable for development and
|
|
1120
1124
|
* testing; use a durable store (e.g. Redis/SQL) in production.
|
|
@@ -1123,12 +1127,12 @@ declare class InMemoryWorkloadIdentityStore<TExtended = Record<string, never>> i
|
|
|
1123
1127
|
private identities;
|
|
1124
1128
|
private clientIdIndex;
|
|
1125
1129
|
private externalIdIndex;
|
|
1126
|
-
get(id: string): Promise<
|
|
1127
|
-
lookup(identity:
|
|
1128
|
-
getByExternalId(externalId: string): Promise<
|
|
1129
|
-
upsert(identity:
|
|
1130
|
+
get(id: string): Promise<BaseWorkloadIdentity2<TExtended> | undefined>;
|
|
1131
|
+
lookup(identity: BaseWorkloadIdentity2<TExtended>): Promise<BaseWorkloadIdentity2<TExtended> | undefined>;
|
|
1132
|
+
getByExternalId(externalId: string): Promise<BaseWorkloadIdentity2<TExtended> | undefined>;
|
|
1133
|
+
upsert(identity: BaseWorkloadIdentity2<TExtended>): Promise<BaseWorkloadIdentity2<TExtended>>;
|
|
1130
1134
|
delete(id: string): Promise<number>;
|
|
1131
|
-
list(options?: ListOptions5): Promise<ListResult5<
|
|
1135
|
+
list(options?: ListOptions5): Promise<ListResult5<BaseWorkloadIdentity2<TExtended>>>;
|
|
1132
1136
|
}
|
|
1133
1137
|
import { CachedWorkloadToken, WorkloadTokenStore as WorkloadTokenStore2 } from "@ionite/core";
|
|
1134
1138
|
/**
|
package/dist/index.js
CHANGED
|
@@ -504,7 +504,12 @@ import {
|
|
|
504
504
|
|
|
505
505
|
// packages/server/src/oaa.ts
|
|
506
506
|
import { accessRefId, accessRootId, ION_AUTHZ_GROUP_SCHEMA, mergeCodeCatalogEntity } from "@ionite/core";
|
|
507
|
-
import {
|
|
507
|
+
import {
|
|
508
|
+
baseCustomerToScim,
|
|
509
|
+
baseUserToScimUser,
|
|
510
|
+
baseWorkloadToScim,
|
|
511
|
+
collectReferencedDefinitions
|
|
512
|
+
} from "@ionite/core/server";
|
|
508
513
|
|
|
509
514
|
// packages/server/src/readiness-latch.ts
|
|
510
515
|
function createReadinessLatch(name, isActive, makeTimeoutError = (timeout) => new Error(`${name} ready timed out after ${timeout}ms`)) {
|
|
@@ -589,7 +594,9 @@ function isOaaConfigured(getConfig) {
|
|
|
589
594
|
const config = getConfig();
|
|
590
595
|
return Boolean(config?.url && config.providerId && config.datasourceId);
|
|
591
596
|
}
|
|
592
|
-
function createOaaClient(log, getConfig, getTransforms,
|
|
597
|
+
function createOaaClient(log, getConfig, getTransforms, getPrincipalMappers = () => ({}), getAccessStore = () => {
|
|
598
|
+
return;
|
|
599
|
+
}, getTrustedClientGrants, getCatalog) {
|
|
593
600
|
const pushIncremental = async (payload) => {
|
|
594
601
|
await pushPayload(log, getRequiredConfig(getConfig), payload, { mode: "incremental" });
|
|
595
602
|
};
|
|
@@ -598,9 +605,29 @@ function createOaaClient(log, getConfig, getTransforms, getTrustedClientGrants,
|
|
|
598
605
|
isReady: latch.isReady,
|
|
599
606
|
ready: latch.ready,
|
|
600
607
|
refreshReadiness: latch.refresh,
|
|
608
|
+
population(kind) {
|
|
609
|
+
return principalPopulation(getConfig(), kind);
|
|
610
|
+
},
|
|
601
611
|
async pushUser(user) {
|
|
602
612
|
const config = getRequiredConfig(getConfig);
|
|
603
|
-
await
|
|
613
|
+
if (!await shouldPushPrincipal(config, "user", accessRefId(user), getAccessStore()))
|
|
614
|
+
return;
|
|
615
|
+
const mapped = await projectUser(user, getPrincipalMappers());
|
|
616
|
+
await pushIncremental(singleApplicationPayload(config, { local_users: [mapScimPrincipal(mapped, "user", "modify")] }, true));
|
|
617
|
+
},
|
|
618
|
+
async pushCustomer(customer) {
|
|
619
|
+
const config = getRequiredConfig(getConfig);
|
|
620
|
+
if (!await shouldPushPrincipal(config, "customer", accessRefId(customer), getAccessStore()))
|
|
621
|
+
return;
|
|
622
|
+
const mapped = await projectCustomer(customer, getPrincipalMappers());
|
|
623
|
+
await pushIncremental(singleApplicationPayload(config, { local_users: [mapScimPrincipal(mapped, "customer", "modify")] }, true));
|
|
624
|
+
},
|
|
625
|
+
async pushWorkload(workload) {
|
|
626
|
+
const config = getRequiredConfig(getConfig);
|
|
627
|
+
if (!await shouldPushPrincipal(config, "workload", accessRefId(workload), getAccessStore()))
|
|
628
|
+
return;
|
|
629
|
+
const mapped = await projectWorkload(workload, getPrincipalMappers());
|
|
630
|
+
await pushIncremental(singleApplicationPayload(config, { local_users: [mapScimPrincipal(mapped, "workload", "modify")] }, true));
|
|
604
631
|
},
|
|
605
632
|
async pushGroup(group) {
|
|
606
633
|
const config = getRequiredConfig(getConfig);
|
|
@@ -654,6 +681,14 @@ function createOaaClient(log, getConfig, getTransforms, getTrustedClientGrants,
|
|
|
654
681
|
const config = getRequiredConfig(getConfig);
|
|
655
682
|
await pushIncremental(singleApplicationPayload(config, { local_users: [{ name: id, unique_id: id, operation: "delete" }] }, true));
|
|
656
683
|
},
|
|
684
|
+
async deleteCustomer(id) {
|
|
685
|
+
const config = getRequiredConfig(getConfig);
|
|
686
|
+
await pushIncremental(singleApplicationPayload(config, { local_users: [deletedPrincipal(id, "customer")] }, true));
|
|
687
|
+
},
|
|
688
|
+
async deleteWorkload(id) {
|
|
689
|
+
const config = getRequiredConfig(getConfig);
|
|
690
|
+
await pushIncremental(singleApplicationPayload(config, { local_users: [deletedPrincipal(id, "workload")] }, true));
|
|
691
|
+
},
|
|
657
692
|
async deleteGroup(id) {
|
|
658
693
|
const config = getRequiredConfig(getConfig);
|
|
659
694
|
await pushIncremental(singleApplicationPayload(config, { local_groups: [{ name: id, unique_id: id, operation: "delete" }] }, true));
|
|
@@ -674,7 +709,7 @@ function createOaaClient(log, getConfig, getTransforms, getTrustedClientGrants,
|
|
|
674
709
|
const config = getRequiredConfig(getConfig);
|
|
675
710
|
const incremental = options?.mode === "incremental";
|
|
676
711
|
const extraAccess = incremental ? undefined : getTrustedClientGrants?.();
|
|
677
|
-
const payload = await buildSyncPayload(config, input, getTransforms(), incremental, extraAccess);
|
|
712
|
+
const payload = await buildSyncPayload(config, input, getTransforms(), getPrincipalMappers(), incremental, extraAccess);
|
|
678
713
|
return pushPayload(log, config, payload, options);
|
|
679
714
|
}
|
|
680
715
|
};
|
|
@@ -686,7 +721,7 @@ function getRequiredConfig(getConfig) {
|
|
|
686
721
|
}
|
|
687
722
|
return config;
|
|
688
723
|
}
|
|
689
|
-
async function buildSyncPayload(config, input, transforms, incremental, extraAccess) {
|
|
724
|
+
async function buildSyncPayload(config, input, transforms, principalMappers, incremental, extraAccess) {
|
|
690
725
|
const localUsers = [];
|
|
691
726
|
const localGroups = [];
|
|
692
727
|
const resources = [];
|
|
@@ -695,9 +730,28 @@ async function buildSyncPayload(config, input, transforms, incremental, extraAcc
|
|
|
695
730
|
const identityToPermissions = [];
|
|
696
731
|
const op = incremental ? "modify" : undefined;
|
|
697
732
|
const seenAccessKeys = new Set;
|
|
733
|
+
const accessChanges = [];
|
|
734
|
+
const accessSubjects = new Set;
|
|
698
735
|
const accessKey = (change) => accessRootId(change);
|
|
736
|
+
for await (const edge of iterate(input.access)) {
|
|
737
|
+
accessChanges.push(edge);
|
|
738
|
+
accessSubjects.add(`${edge.subjectType}:${accessRefId(edge.subject)}`);
|
|
739
|
+
}
|
|
740
|
+
const includePrincipal = (kind, id) => principalPopulation(config, kind) === "all" || Boolean(id && accessSubjects.has(`${kind}:${id}`));
|
|
699
741
|
for await (const user of iterate(input.users)) {
|
|
700
|
-
|
|
742
|
+
if (includePrincipal("user", accessRefId(user))) {
|
|
743
|
+
localUsers.push(mapScimPrincipal(await projectUser(user, principalMappers), "user", op));
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
for await (const customer of iterate(input.customers)) {
|
|
747
|
+
if (includePrincipal("customer", accessRefId(customer))) {
|
|
748
|
+
localUsers.push(mapScimPrincipal(await projectCustomer(customer, principalMappers), "customer", op));
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
for await (const workload of iterate(input.workloads)) {
|
|
752
|
+
if (includePrincipal("workload", accessRefId(workload))) {
|
|
753
|
+
localUsers.push(mapScimPrincipal(await projectWorkload(workload, principalMappers), "workload", op));
|
|
754
|
+
}
|
|
701
755
|
}
|
|
702
756
|
for await (const group of iterate(input.groups)) {
|
|
703
757
|
const source = transformGroup(group, transforms);
|
|
@@ -719,7 +773,7 @@ async function buildSyncPayload(config, input, transforms, incremental, extraAcc
|
|
|
719
773
|
collectedResources.push(source);
|
|
720
774
|
}
|
|
721
775
|
resources.push(...buildNestedResourceTree(collectedResources, op));
|
|
722
|
-
for
|
|
776
|
+
for (const edge of accessChanges) {
|
|
723
777
|
seenAccessKeys.add(accessKey(edge));
|
|
724
778
|
applyAccessChange(edge, identityToPermissions, localUsers, applicationName(config), incremental ? "add" : undefined);
|
|
725
779
|
if (typeof edge.target !== "string") {
|
|
@@ -933,17 +987,17 @@ function applyAccessChange(change, identityToPermissions, localUsers, applicatio
|
|
|
933
987
|
const identity = accessRefId(change.subject);
|
|
934
988
|
const target = accessRefId(change.target);
|
|
935
989
|
let localUser = localUsers.find((user) => accessRefId(user.unique_id ?? user.name) === identity);
|
|
936
|
-
if (change.subjectType
|
|
990
|
+
if (change.subjectType !== "user") {
|
|
937
991
|
if (localUser) {
|
|
938
992
|
localUser.custom_properties = {
|
|
939
993
|
...localUser.custom_properties,
|
|
940
|
-
identity_type:
|
|
994
|
+
identity_type: change.subjectType
|
|
941
995
|
};
|
|
942
996
|
} else {
|
|
943
997
|
localUser = {
|
|
944
998
|
name: identity,
|
|
945
999
|
unique_id: identity,
|
|
946
|
-
custom_properties: { identity_type:
|
|
1000
|
+
custom_properties: { identity_type: change.subjectType },
|
|
947
1001
|
operation
|
|
948
1002
|
};
|
|
949
1003
|
localUsers.push(localUser);
|
|
@@ -996,30 +1050,64 @@ function applyAccessChange(change, identityToPermissions, localUsers, applicatio
|
|
|
996
1050
|
}
|
|
997
1051
|
identityToPermissions.push(access);
|
|
998
1052
|
}
|
|
999
|
-
function
|
|
1053
|
+
async function projectUser(user, mappers) {
|
|
1054
|
+
const mapped = await (mappers.mapUserToScim?.(user) ?? baseUserToScimUser(user));
|
|
1055
|
+
return { ...mapped, id: mapped.id ?? user.id };
|
|
1056
|
+
}
|
|
1057
|
+
async function projectCustomer(customer, mappers) {
|
|
1058
|
+
const mapped = await (mappers.mapCustomerToScim?.(customer) ?? baseCustomerToScim(customer));
|
|
1059
|
+
return { ...mapped, id: mapped.id ?? customer.id };
|
|
1060
|
+
}
|
|
1061
|
+
async function projectWorkload(workload, mappers) {
|
|
1062
|
+
const mapped = await (mappers.mapWorkloadToScim?.(workload) ?? baseWorkloadToScim(workload));
|
|
1063
|
+
return { ...mapped, id: mapped.id ?? workload.id };
|
|
1064
|
+
}
|
|
1065
|
+
function mapScimPrincipal(principal, identityType, operation) {
|
|
1066
|
+
const { schemas: _schemas, id, displayName, userName, active, emails, groups, meta, ...custom } = principal;
|
|
1067
|
+
const uniqueId = stringValue(id) ?? stringValue(principal.externalId) ?? stringValue(userName) ?? "principal";
|
|
1000
1068
|
return omitEmpty({
|
|
1001
|
-
name:
|
|
1002
|
-
unique_id:
|
|
1003
|
-
identities:
|
|
1004
|
-
groups: user.groups
|
|
1005
|
-
is_active:
|
|
1006
|
-
created_at:
|
|
1069
|
+
name: stringValue(displayName) ?? stringValue(userName) ?? uniqueId,
|
|
1070
|
+
unique_id: uniqueId,
|
|
1071
|
+
identities: identityType === "customer" ? undefined : buildProjectedIdentities(principal),
|
|
1072
|
+
groups: identityType === "user" && Array.isArray(groups) ? groups.map((group) => typeof group === "string" ? group : stringField(group, "value")).filter((value) => Boolean(value)) : undefined,
|
|
1073
|
+
is_active: typeof active === "boolean" ? active : undefined,
|
|
1074
|
+
created_at: meta && typeof meta === "object" ? stringField(meta, "created") : undefined,
|
|
1007
1075
|
custom_properties: omitEmpty({
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1076
|
+
...custom,
|
|
1077
|
+
ecid: identityType === "customer" ? undefined : projectedEcid(principal),
|
|
1078
|
+
accountClass: projectedAccountClass(principal),
|
|
1079
|
+
externalId: principal.externalId,
|
|
1080
|
+
userName,
|
|
1081
|
+
emails,
|
|
1082
|
+
identity_type: identityType === "user" ? undefined : identityType
|
|
1013
1083
|
}),
|
|
1014
1084
|
tags: [],
|
|
1015
1085
|
operation
|
|
1016
1086
|
});
|
|
1017
1087
|
}
|
|
1018
|
-
function
|
|
1019
|
-
const
|
|
1020
|
-
const
|
|
1088
|
+
function buildProjectedIdentities(principal) {
|
|
1089
|
+
const ecid = projectedEcid(principal);
|
|
1090
|
+
const projectedEmails = principal.emails;
|
|
1091
|
+
const emails = Array.isArray(projectedEmails) ? projectedEmails.map((email) => email && typeof email === "object" ? stringField(email, "value") : undefined).filter((value) => Boolean(value)) : [];
|
|
1092
|
+
const identities = [...ecid ? [ecid] : [], ...emails];
|
|
1021
1093
|
return identities.length > 0 ? identities : undefined;
|
|
1022
1094
|
}
|
|
1095
|
+
function projectedEcid(principal) {
|
|
1096
|
+
const extension = principal["urn:ionite:scim:schemas:extension:ion:2.0:User"];
|
|
1097
|
+
return stringValue(principal.ecid) ?? (extension && typeof extension === "object" ? stringField(extension, "ecid") : undefined);
|
|
1098
|
+
}
|
|
1099
|
+
function projectedAccountClass(principal) {
|
|
1100
|
+
const extension = principal["urn:ionite:scim:schemas:extension:ion:2.0:User"];
|
|
1101
|
+
return extension && typeof extension === "object" ? stringField(extension, "accountClass") : undefined;
|
|
1102
|
+
}
|
|
1103
|
+
function deletedPrincipal(id, identityType) {
|
|
1104
|
+
return {
|
|
1105
|
+
name: id,
|
|
1106
|
+
unique_id: id,
|
|
1107
|
+
custom_properties: { identity_type: identityType },
|
|
1108
|
+
operation: "delete"
|
|
1109
|
+
};
|
|
1110
|
+
}
|
|
1023
1111
|
function mapGroup(group, operation) {
|
|
1024
1112
|
const extension = authzGroupExtension(group);
|
|
1025
1113
|
const id = stringField(group, "id") ?? stringField(group, "name") ?? stringField(group, "displayName");
|
|
@@ -1416,17 +1504,15 @@ function chunkString(value, size) {
|
|
|
1416
1504
|
function byteLength(value) {
|
|
1417
1505
|
return Buffer.byteLength(value, "utf8");
|
|
1418
1506
|
}
|
|
1419
|
-
function dateToIso(value) {
|
|
1420
|
-
if (!value)
|
|
1421
|
-
return;
|
|
1422
|
-
return value instanceof Date ? value.toISOString() : value;
|
|
1423
|
-
}
|
|
1424
1507
|
function stringField(value, field) {
|
|
1425
1508
|
if (!value || typeof value !== "object")
|
|
1426
1509
|
return;
|
|
1427
1510
|
const fieldValue = value[field];
|
|
1428
1511
|
return typeof fieldValue === "string" && fieldValue.length > 0 ? fieldValue : undefined;
|
|
1429
1512
|
}
|
|
1513
|
+
function stringValue(value) {
|
|
1514
|
+
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
1515
|
+
}
|
|
1430
1516
|
function booleanField(value, field) {
|
|
1431
1517
|
if (!value || typeof value !== "object")
|
|
1432
1518
|
return;
|
|
@@ -1476,6 +1562,21 @@ function trimTrailingSlash2(value) {
|
|
|
1476
1562
|
function applicationName(config) {
|
|
1477
1563
|
return config.applicationName ?? DEFAULT_APPLICATION_NAME;
|
|
1478
1564
|
}
|
|
1565
|
+
function principalPopulation(config, kind) {
|
|
1566
|
+
if (kind === "user")
|
|
1567
|
+
return config?.users?.population ?? "all";
|
|
1568
|
+
if (kind === "customer")
|
|
1569
|
+
return config?.customers?.population ?? "access";
|
|
1570
|
+
return config?.workloads?.population ?? "access";
|
|
1571
|
+
}
|
|
1572
|
+
async function shouldPushPrincipal(config, kind, id, accessStore) {
|
|
1573
|
+
if (principalPopulation(config, kind) === "all")
|
|
1574
|
+
return true;
|
|
1575
|
+
if (!accessStore || !id)
|
|
1576
|
+
return false;
|
|
1577
|
+
const result = await accessStore.listBySubject(id, kind, { start: 0, limit: 1 });
|
|
1578
|
+
return result.items.length > 0;
|
|
1579
|
+
}
|
|
1479
1580
|
function delay(ms) {
|
|
1480
1581
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1481
1582
|
}
|
|
@@ -1494,7 +1595,7 @@ function createManagedAuthz(log, getIamDelta = () => {
|
|
|
1494
1595
|
return;
|
|
1495
1596
|
}, getIam = () => {
|
|
1496
1597
|
return;
|
|
1497
|
-
}) {
|
|
1598
|
+
}, getPrincipalMappers = () => ({})) {
|
|
1498
1599
|
let current;
|
|
1499
1600
|
let closed = false;
|
|
1500
1601
|
let codeCatalog;
|
|
@@ -1504,7 +1605,7 @@ function createManagedAuthz(log, getIamDelta = () => {
|
|
|
1504
1605
|
mapGroup: current?.mapGroup,
|
|
1505
1606
|
mapRole: current?.mapRole,
|
|
1506
1607
|
mapResource: current?.mapResource
|
|
1507
|
-
}), () => configTrustedClientGrants(current), () => {
|
|
1608
|
+
}), getPrincipalMappers, () => current?.accessStore, () => configTrustedClientGrants(current), () => {
|
|
1508
1609
|
const catalog = current?.catalogStore;
|
|
1509
1610
|
return catalog || codeCatalog ? { catalog, code: codeCatalog, resources: current?.resources, cache: adjacencyCache } : undefined;
|
|
1510
1611
|
});
|
|
@@ -3444,9 +3545,9 @@ import {
|
|
|
3444
3545
|
unionCatalogRead as unionCatalogRead2
|
|
3445
3546
|
} from "@ionite/core";
|
|
3446
3547
|
import {
|
|
3447
|
-
baseCustomerToScim,
|
|
3448
|
-
baseUserToScimUser,
|
|
3449
|
-
baseWorkloadToScim,
|
|
3548
|
+
baseCustomerToScim as baseCustomerToScim2,
|
|
3549
|
+
baseUserToScimUser as baseUserToScimUser2,
|
|
3550
|
+
baseWorkloadToScim as baseWorkloadToScim2,
|
|
3450
3551
|
entitlementToScimGroup
|
|
3451
3552
|
} from "@ionite/core/server";
|
|
3452
3553
|
|
|
@@ -4099,7 +4200,7 @@ function createManagedIamDelta(log) {
|
|
|
4099
4200
|
return {
|
|
4100
4201
|
externalId: externalId2,
|
|
4101
4202
|
resourceType: "workload",
|
|
4102
|
-
create:
|
|
4203
|
+
create: await (deps.mapWorkloadToScim?.({ ...workload, externalId: externalId2 }) ?? baseWorkloadToScim2({ ...workload, externalId: externalId2 }))
|
|
4103
4204
|
};
|
|
4104
4205
|
}
|
|
4105
4206
|
if (change.subjectType === "customer") {
|
|
@@ -4110,7 +4211,7 @@ function createManagedIamDelta(log) {
|
|
|
4110
4211
|
const externalId2 = customer?.externalId ?? customer?.id ?? id;
|
|
4111
4212
|
if (!externalId2)
|
|
4112
4213
|
return;
|
|
4113
|
-
const body = customer ?
|
|
4214
|
+
const body = customer ? await (deps.mapCustomerToScim?.({ ...customer, externalId: externalId2 }) ?? baseCustomerToScim2({ ...customer, externalId: externalId2 })) : {
|
|
4114
4215
|
schemas: ["urn:ionite:scim:schemas:core:2.0:Customer"],
|
|
4115
4216
|
externalId: externalId2,
|
|
4116
4217
|
displayName: externalId2,
|
|
@@ -4119,12 +4220,7 @@ function createManagedIamDelta(log) {
|
|
|
4119
4220
|
return {
|
|
4120
4221
|
externalId: externalId2,
|
|
4121
4222
|
resourceType: "customer",
|
|
4122
|
-
create:
|
|
4123
|
-
schemas: body.schemas ?? ["urn:ionite:scim:schemas:core:2.0:Customer"],
|
|
4124
|
-
externalId: externalId2,
|
|
4125
|
-
displayName: body.displayName,
|
|
4126
|
-
active: body.active ?? true
|
|
4127
|
-
}
|
|
4223
|
+
create: body
|
|
4128
4224
|
};
|
|
4129
4225
|
}
|
|
4130
4226
|
const inline = refObject(change.subject);
|
|
@@ -4137,7 +4233,7 @@ function createManagedIamDelta(log) {
|
|
|
4137
4233
|
const externalId = user?.externalId;
|
|
4138
4234
|
if (!user || !externalId)
|
|
4139
4235
|
return;
|
|
4140
|
-
return { externalId, resourceType: "user", create: await (deps.mapUserToScim?.(user) ??
|
|
4236
|
+
return { externalId, resourceType: "user", create: await (deps.mapUserToScim?.(user) ?? baseUserToScimUser2(user)) };
|
|
4141
4237
|
};
|
|
4142
4238
|
const resolveTarget = async (change) => {
|
|
4143
4239
|
const inline = refObject(change.target);
|
|
@@ -4255,7 +4351,7 @@ function createManagedIamDelta(log) {
|
|
|
4255
4351
|
}
|
|
4256
4352
|
return members.length > 0 ? members : undefined;
|
|
4257
4353
|
};
|
|
4258
|
-
const
|
|
4354
|
+
const projectUser2 = async (user) => {
|
|
4259
4355
|
if (!isReady() || !kindDeltaOn("user"))
|
|
4260
4356
|
return "skipped";
|
|
4261
4357
|
const externalId = user.externalId;
|
|
@@ -4271,10 +4367,10 @@ function createManagedIamDelta(log) {
|
|
|
4271
4367
|
const scim = ensureClient();
|
|
4272
4368
|
if (!scim)
|
|
4273
4369
|
return "skipped";
|
|
4274
|
-
const body = await (deps.mapUserToScim?.(user) ??
|
|
4370
|
+
const body = await (deps.mapUserToScim?.(user) ?? baseUserToScimUser2(user));
|
|
4275
4371
|
return scim.upsertResource("user", externalId, body);
|
|
4276
4372
|
};
|
|
4277
|
-
const
|
|
4373
|
+
const projectWorkload2 = async (workload) => {
|
|
4278
4374
|
if (!isReady() || !kindDeltaOn("workload"))
|
|
4279
4375
|
return "skipped";
|
|
4280
4376
|
const externalId = workload.externalId ?? workload.id;
|
|
@@ -4283,9 +4379,10 @@ function createManagedIamDelta(log) {
|
|
|
4283
4379
|
const scim = ensureClient();
|
|
4284
4380
|
if (!scim)
|
|
4285
4381
|
return "skipped";
|
|
4286
|
-
|
|
4382
|
+
const projected = await (deps.mapWorkloadToScim?.({ ...workload, externalId }) ?? baseWorkloadToScim2({ ...workload, externalId }));
|
|
4383
|
+
return scim.upsertResource("workload", externalId, projected);
|
|
4287
4384
|
};
|
|
4288
|
-
const
|
|
4385
|
+
const projectCustomer2 = async (customer) => {
|
|
4289
4386
|
if (!isReady() || !kindDeltaOn("customer"))
|
|
4290
4387
|
return "skipped";
|
|
4291
4388
|
const externalId = customer.externalId ?? customer.id;
|
|
@@ -4294,13 +4391,8 @@ function createManagedIamDelta(log) {
|
|
|
4294
4391
|
const scim = ensureClient();
|
|
4295
4392
|
if (!scim)
|
|
4296
4393
|
return "skipped";
|
|
4297
|
-
const projected =
|
|
4298
|
-
return scim.upsertResource("customer", externalId,
|
|
4299
|
-
schemas: projected.schemas,
|
|
4300
|
-
externalId,
|
|
4301
|
-
displayName: projected.displayName,
|
|
4302
|
-
active: projected.active
|
|
4303
|
-
});
|
|
4394
|
+
const projected = await (deps.mapCustomerToScim?.({ ...customer, externalId }) ?? baseCustomerToScim2({ ...customer, externalId }));
|
|
4395
|
+
return scim.upsertResource("customer", externalId, projected);
|
|
4304
4396
|
};
|
|
4305
4397
|
const projectEntitlement = async (entity, type, restoreRoots = true) => {
|
|
4306
4398
|
if (!isReady() || !anyDeltaPatch())
|
|
@@ -4336,7 +4428,7 @@ function createManagedIamDelta(log) {
|
|
|
4336
4428
|
};
|
|
4337
4429
|
const emitter = {
|
|
4338
4430
|
async onUserUpsert(user) {
|
|
4339
|
-
requirePushed(await
|
|
4431
|
+
requirePushed(await projectUser2(user), "user upsert");
|
|
4340
4432
|
},
|
|
4341
4433
|
async onUserDelete(externalId) {
|
|
4342
4434
|
if (!isReady() || !kindDeltaOn("user"))
|
|
@@ -4346,6 +4438,28 @@ function createManagedIamDelta(log) {
|
|
|
4346
4438
|
return;
|
|
4347
4439
|
requirePushed(await scim.deleteResource("user", externalId), "user delete");
|
|
4348
4440
|
},
|
|
4441
|
+
async onWorkloadUpsert(workload) {
|
|
4442
|
+
requirePushed(await projectWorkload2(workload), "workload upsert");
|
|
4443
|
+
},
|
|
4444
|
+
async onWorkloadDelete(externalId) {
|
|
4445
|
+
if (!isReady() || !kindDeltaOn("workload"))
|
|
4446
|
+
return;
|
|
4447
|
+
const scim = ensureClient();
|
|
4448
|
+
if (!scim)
|
|
4449
|
+
return;
|
|
4450
|
+
requirePushed(await scim.deleteResource("workload", externalId), "workload delete");
|
|
4451
|
+
},
|
|
4452
|
+
async onCustomerUpsert(customer) {
|
|
4453
|
+
requirePushed(await projectCustomer2(customer), "customer upsert");
|
|
4454
|
+
},
|
|
4455
|
+
async onCustomerDelete(externalId) {
|
|
4456
|
+
if (!isReady() || !kindDeltaOn("customer"))
|
|
4457
|
+
return;
|
|
4458
|
+
const scim = ensureClient();
|
|
4459
|
+
if (!scim)
|
|
4460
|
+
return;
|
|
4461
|
+
requirePushed(await scim.deleteResource("customer", externalId), "customer delete");
|
|
4462
|
+
},
|
|
4349
4463
|
async onEntitlementUpsert(entity, type) {
|
|
4350
4464
|
requirePushed(await projectEntitlement(entity, type), `${type} upsert`);
|
|
4351
4465
|
},
|
|
@@ -4417,13 +4531,13 @@ function createManagedIamDelta(log) {
|
|
|
4417
4531
|
await run(input.permissions, (value) => projectEntitlement(value, "permission", false), (value) => {
|
|
4418
4532
|
return `permission upsert for ${value.id}`;
|
|
4419
4533
|
});
|
|
4420
|
-
await run(input.users, (value) =>
|
|
4534
|
+
await run(input.users, (value) => projectUser2(value), (value) => {
|
|
4421
4535
|
return `user upsert for ${value.id ?? "unknown"}`;
|
|
4422
4536
|
});
|
|
4423
|
-
await run(input.workloads, (value) =>
|
|
4537
|
+
await run(input.workloads, (value) => projectWorkload2(value), (value) => {
|
|
4424
4538
|
return `workload upsert for ${value.id}`;
|
|
4425
4539
|
});
|
|
4426
|
-
await run(input.customers, (value) =>
|
|
4540
|
+
await run(input.customers, (value) => projectCustomer2(value), (value) => {
|
|
4427
4541
|
return `customer upsert for ${value.id ?? "unknown"}`;
|
|
4428
4542
|
});
|
|
4429
4543
|
await run(input.access, (value) => project(value, "add"), (value) => {
|
|
@@ -9901,7 +10015,7 @@ function tenantManager(ion, options) {
|
|
|
9901
10015
|
}
|
|
9902
10016
|
|
|
9903
10017
|
// packages/server/src/version.ts
|
|
9904
|
-
var ION_SDK_VERSION = "0.0.24-beta.
|
|
10018
|
+
var ION_SDK_VERSION = "0.0.24-beta.20260910.1";
|
|
9905
10019
|
|
|
9906
10020
|
// packages/server/src/index.ts
|
|
9907
10021
|
export * from "@ionite/core";
|
|
@@ -12464,6 +12578,34 @@ async function buildCapabilitiezResponse(request, ion, capabilitiez) {
|
|
|
12464
12578
|
return denied;
|
|
12465
12579
|
return Response.json(capabilitiez.build(), { status: 200 });
|
|
12466
12580
|
}
|
|
12581
|
+
async function collectOaaAccess(accessStore) {
|
|
12582
|
+
if (!accessStore)
|
|
12583
|
+
return [];
|
|
12584
|
+
const roots = [];
|
|
12585
|
+
for await (const root of listGenerator3((start, limit) => accessStore.list({ start, limit }))) {
|
|
12586
|
+
roots.push(root);
|
|
12587
|
+
}
|
|
12588
|
+
return roots;
|
|
12589
|
+
}
|
|
12590
|
+
function oaaPrincipalIds(access, kind) {
|
|
12591
|
+
return [
|
|
12592
|
+
...new Set(access.filter((root) => root.subjectType === kind).map((root) => typeof root.subject === "string" ? root.subject : root.subject.id).filter((id) => Boolean(id)))
|
|
12593
|
+
];
|
|
12594
|
+
}
|
|
12595
|
+
function oaaPrincipalIterable(store, population, accessIds) {
|
|
12596
|
+
if (!store)
|
|
12597
|
+
return;
|
|
12598
|
+
if (population === "all") {
|
|
12599
|
+
return listGenerator3((start, limit) => store.list({ start, limit }));
|
|
12600
|
+
}
|
|
12601
|
+
return async function* accessPrincipals() {
|
|
12602
|
+
for (const id of accessIds) {
|
|
12603
|
+
const principal = await store.get(id);
|
|
12604
|
+
if (principal)
|
|
12605
|
+
yield principal;
|
|
12606
|
+
}
|
|
12607
|
+
}();
|
|
12608
|
+
}
|
|
12467
12609
|
async function buildAuthzOaaSyncResponse(request, ion, frameworkConfig) {
|
|
12468
12610
|
if (!ion.isReady()) {
|
|
12469
12611
|
return createNotReadyResponse();
|
|
@@ -12480,16 +12622,22 @@ async function buildAuthzOaaSyncResponse(request, ion, frameworkConfig) {
|
|
|
12480
12622
|
}
|
|
12481
12623
|
const reconcile = ion.authz ? await ion.authz.reconcile() : undefined;
|
|
12482
12624
|
const userStore = frameworkConfig.iam?.userStore;
|
|
12625
|
+
const customerStore = frameworkConfig.iam?.customerStore ?? frameworkConfig.ciam?.customerStore;
|
|
12626
|
+
const configuredWorkload = frameworkConfig.workload;
|
|
12627
|
+
const workloadIdentityStore = frameworkConfig.iam?.workloadIdentityStore ?? (configuredWorkload && "workloadIdentityStore" in configuredWorkload ? configuredWorkload.workloadIdentityStore : configuredWorkload && ("incoming" in configuredWorkload) && configuredWorkload.incoming && !Array.isArray(configuredWorkload.incoming) && ("workloadIdentityStore" in configuredWorkload.incoming) ? configuredWorkload.incoming.workloadIdentityStore : undefined);
|
|
12483
12628
|
const catalogStore = unionCatalogRead3(frameworkConfig.authz?.catalog ? toCodeCatalog2(frameworkConfig.authz.catalog) : undefined, frameworkConfig.authz?.catalogStore);
|
|
12484
12629
|
const accessStore = frameworkConfig.authz?.accessStore;
|
|
12485
12630
|
const resourceProvider = frameworkConfig.authz?.resources;
|
|
12631
|
+
const access = await collectOaaAccess(accessStore);
|
|
12486
12632
|
const result = await oaa.sync({
|
|
12487
|
-
users: userStore
|
|
12633
|
+
users: oaaPrincipalIterable(userStore, oaa.population("user"), oaaPrincipalIds(access, "user")),
|
|
12634
|
+
customers: oaaPrincipalIterable(customerStore, oaa.population("customer"), oaaPrincipalIds(access, "customer")),
|
|
12635
|
+
workloads: oaaPrincipalIterable(workloadIdentityStore, oaa.population("workload"), oaaPrincipalIds(access, "workload")),
|
|
12488
12636
|
groups: catalogStore ? listGenerator3((start, limit) => catalogStore.groups.list({ start, limit })) : undefined,
|
|
12489
12637
|
roles: catalogStore ? listGenerator3((start, limit) => catalogStore.roles.list({ start, limit })) : undefined,
|
|
12490
12638
|
permissions: catalogStore ? listGenerator3((start, limit) => catalogStore.permissions.list({ start, limit })) : undefined,
|
|
12491
12639
|
resources: resourceProvider ? listGenerator3((start, limit) => resourceProvider.list({ start, limit })) : undefined,
|
|
12492
|
-
access
|
|
12640
|
+
access
|
|
12493
12641
|
}, { mode: "snapshot", compression: "gzip", multipart: "auto" });
|
|
12494
12642
|
return Response.json({ ...result, reconcile });
|
|
12495
12643
|
}
|
|
@@ -12691,7 +12839,9 @@ function buildModules(remoteConfig, frameworkConfig, log, defaultSecret, kms, au
|
|
|
12691
12839
|
accessStore: frameworkConfig.authz?.accessStore,
|
|
12692
12840
|
catalogStore: frameworkConfig.authz?.catalogStore,
|
|
12693
12841
|
codeCatalog: authzCodeCatalog,
|
|
12694
|
-
mapUserToScim: mergedIamConfig.inboundUsers?.mapBaseUserToScim
|
|
12842
|
+
mapUserToScim: mergedIamConfig.inboundUsers?.mapBaseUserToScim,
|
|
12843
|
+
mapCustomerToScim: mergedIamConfig.inboundCustomers?.mapBaseCustomerToScim,
|
|
12844
|
+
mapWorkloadToScim: mergedIamConfig.inboundWorkloads?.mapBaseWorkloadToScim
|
|
12695
12845
|
});
|
|
12696
12846
|
let iamHandle;
|
|
12697
12847
|
if (requiresIamWorkload && !workloadHandle) {
|
|
@@ -12800,7 +12950,11 @@ function ionite(source, config) {
|
|
|
12800
12950
|
kms: createManagedKms(defaultLog),
|
|
12801
12951
|
workload: createManagedWorkload(defaultLog),
|
|
12802
12952
|
otel: createManagedOtel(defaultLog),
|
|
12803
|
-
authz: createManagedAuthz(defaultLog, () => iamDeltaModule.handle(), () => resultRef?.iam)
|
|
12953
|
+
authz: createManagedAuthz(defaultLog, () => iamDeltaModule.handle(), () => resultRef?.iam, () => ({
|
|
12954
|
+
mapUserToScim: state.frameworkConfig.iam?.inboundUsers?.mapBaseUserToScim,
|
|
12955
|
+
mapCustomerToScim: state.frameworkConfig.iam?.inboundCustomers?.mapBaseCustomerToScim,
|
|
12956
|
+
mapWorkloadToScim: state.frameworkConfig.iam?.inboundWorkloads?.mapBaseWorkloadToScim
|
|
12957
|
+
})),
|
|
12804
12958
|
iamDelta: iamDeltaModule,
|
|
12805
12959
|
sso: createManagedReadiness("sso"),
|
|
12806
12960
|
iam: createManagedReadiness("iam"),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ionite/server",
|
|
3
|
-
"version": "0.0.24-beta.
|
|
3
|
+
"version": "0.0.24-beta.20260910.1",
|
|
4
4
|
"description": "ionite Server (Node/server-only)",
|
|
5
5
|
"private": false,
|
|
6
6
|
"author": "ionite",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"./package.json": "./package.json"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@ionite/core": "0.0.24-beta.
|
|
27
|
+
"@ionite/core": "0.0.24-beta.20260910.1",
|
|
28
28
|
"@opentelemetry/api": "^1.9.1",
|
|
29
29
|
"@opentelemetry/api-logs": "^0.220.0",
|
|
30
30
|
"@opentelemetry/exporter-logs-otlp-http": "^0.220.0",
|