import { DataStore } from '@voltro/database'; import { PluginHttpRouteRequest } from '@voltro/protocol'; import { PluginHttpRouteResult } from '@voltro/protocol'; import { VoltroPlugin } from '@voltro/protocol'; /** Apply a SCIM PATCH to a Group — the membership add/remove/replace ops * Okta/Entra push after the initial create, plus displayName replace. * Supports the `members[value eq "id"]` remove-path form Entra sends. */ export declare const applyScimGroupPatch: (group: G, body: Record) => G; /** Apply a SCIM PATCH (the `active=false` deactivation IdPs send is the case * that matters most). Supports `replace`/`add` ops on `active` / `userName`, * path-targeted or as a pathless partial resource. */ export declare const applyScimPatch: (row: ScimUserRow, body: Record) => ScimUserRow; export declare const dataStoreScimStore: (store: DataStore) => ScimStore; /** SCIM User JSON (create/replace body) → the fields the store writes. */ export declare const fromScimUser: (body: Record) => Omit; /** Group row shape the patch helper deals in (mirrors `ScimGroupRow`). */ declare interface GroupLike { readonly displayName: string; readonly members: ReadonlyArray; } /** Dispatch one SCIM request. Pure over an injected store — unit-tested. */ declare const handleScim: (store: ScimStore, token: string, basePath: string, req: PluginHttpRouteRequest) => Promise; export { handleScim } export { handleScim as handleScimRequest } export declare const memoryScimStore: (now?: () => number) => ScimStore; /** RFC 7644 §3.4.2.4 pagination params (`startIndex` 1-based, `count`). */ export declare const parsePagination: (query: URLSearchParams) => { readonly startIndex: number; readonly count: number | null; }; /** Parse the ` eq "value"` equality filter IdPs send (`userName eq` from * Okta, `externalId eq` from Entra, `displayName eq` on Groups). * - `{ kind: 'none' }` — no filter present (list everything) * - `{ kind: 'filter', … }` — a supported equality filter * - `{ kind: 'invalid' }` — a filter we can't honour (the caller answers * `400 invalidFilter`; silently ignoring a filter could match the wrong * user during provisioning) */ export declare const parseScimFilter: (filter: string | null | undefined, allowed: ReadonlyArray) => { kind: "none"; } | { kind: "invalid"; } | ({ kind: "filter"; } & ScimFilter); /** RFC 7644 §4 /ResourceTypes. */ export declare const resourceTypes: (basePath: string) => ReadonlyArray>; export declare const SCIM_ERROR_SCHEMA = "urn:ietf:params:scim:api:messages:2.0:Error"; export declare const SCIM_GROUP_SCHEMA = "urn:ietf:params:scim:schemas:core:2.0:Group"; export declare const SCIM_LIST_SCHEMA = "urn:ietf:params:scim:api:messages:2.0:ListResponse"; export declare const SCIM_USER_SCHEMA = "urn:ietf:params:scim:schemas:core:2.0:User"; /** SCIM Error envelope. `scimType` is the RFC 7644 §3.12 error keyword * (`uniqueness`, `invalidFilter`, …) where one applies. */ export declare const scimError: (status: number, detail: string, scimType?: string) => Record; /** A parsed ` eq ""` filter. */ export declare interface ScimFilter { readonly attribute: string; readonly value: string; } declare interface ScimGroupRow { readonly id: string; readonly displayName: string; readonly externalId: string | null; readonly members: ReadonlyArray; readonly createdAt: number; readonly updatedAt: number; } /** ListResponse envelope. `page` carries RFC 7644 §3.4.2.4 pagination fields: * `totalResults` = the FULL (unpaged) match count, `startIndex` = the 1-based * offset the page starts at. Defaults reproduce an unpaged full list. */ export declare const scimList: (resources: ReadonlyArray>, page?: { readonly totalResults?: number; readonly startIndex?: number; }) => Record; export declare const scimPlugin: (options: ScimPluginOptions) => VoltroPlugin; export declare interface ScimPluginOptions { /** Bearer token the IdP presents. From your IdP's SCIM config. */ readonly token: string; /** Mount prefix. Default `/scim/v2`. */ readonly basePath?: string; /** * Namespace for this plugin's HTTP mount + inspect surface. Default `scim`. * * Set it when your app already publishes under that name — an exact tag * collision is fatal at codegen, and this is the way out. Orthogonal to * `name` below: `alias` REPLACES the namespace, `name` distinguishes two * installations within it. */ readonly alias?: string; /** * Discriminator for a SECOND installation of this plugin, when one app runs * two (`@voltro/plugin-scim#eu`). Not a rename — for that use `alias`. */ readonly name?: string; } /** RFC 7643 §7 /Schemas — the attributes this implementation persists. */ export declare const scimSchemas: (location: string) => ReadonlyArray>; export declare interface ScimStore { /** `filter` is an ` eq ""` equality (userName / externalId), or `null` = all. */ readonly listUsers: (filter: ScimFilter | null) => Promise>; readonly getUser: (id: string) => Promise; readonly createUser: (fields: Omit) => Promise; readonly putUser: (id: string, row: ScimUserRow) => Promise; readonly deleteUser: (id: string) => Promise; /** `filter` is displayName / externalId equality, or `null` = all. */ readonly listGroups: (filter: ScimFilter | null) => Promise>; readonly getGroup: (id: string) => Promise; readonly createGroup: (fields: Omit) => Promise; readonly putGroup: (id: string, row: ScimGroupRow) => Promise; readonly deleteGroup: (id: string) => Promise; } /** Internal user row shape the store deals in. */ export declare interface ScimUserRow { readonly id: string; readonly userName: string; readonly givenName: string | null; readonly familyName: string | null; readonly email: string | null; readonly active: boolean; readonly externalId: string | null; readonly createdAt: number; readonly updatedAt: number; } /** RFC 7643 §5 ServiceProviderConfig — what this implementation supports. */ export declare const serviceProviderConfig: (location: string) => Record; /** App row → SCIM User JSON. */ export declare const toScimUser: (row: ScimUserRow, location: string) => Record; export { }