@canopy-io/node 0.10.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/README.md +47 -0
- package/dist/index.cjs +263 -45
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +308 -16
- package/dist/index.d.ts +308 -16
- package/dist/index.js +263 -46
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -748,7 +748,7 @@ interface paths {
|
|
|
748
748
|
};
|
|
749
749
|
/**
|
|
750
750
|
* Get identity's effective permissions
|
|
751
|
-
* @description Returns the identity's effective
|
|
751
|
+
* @description Returns the identity's effective permission keys for the current Environment as a flat `items` array of strings. Without `node_id`, the Application-wide answer: every permission its role assignments grant anywhere. With `node_id`, the answer at that node: the permissions granted by assignments on the node's lineage, the list a screen renders its controls from. Returns `404` when the identity has no membership in this Environment, or when `node_id` names no node in it.
|
|
752
752
|
*/
|
|
753
753
|
get: operations["ApiIdentitiesController_getIdentityPermissions"];
|
|
754
754
|
put?: never;
|
|
@@ -759,6 +759,26 @@ interface paths {
|
|
|
759
759
|
patch?: never;
|
|
760
760
|
trace?: never;
|
|
761
761
|
};
|
|
762
|
+
"/api/v1/identities/{id}/accessible-nodes": {
|
|
763
|
+
parameters: {
|
|
764
|
+
query?: never;
|
|
765
|
+
header?: never;
|
|
766
|
+
path?: never;
|
|
767
|
+
cookie?: never;
|
|
768
|
+
};
|
|
769
|
+
/**
|
|
770
|
+
* Get where an identity may act with one permission
|
|
771
|
+
* @description Returns where the identity may act with `permission`, for a list query: `roots`, the nodes it holds the permission at that no other such node already covers, and `nodes`, every node beneath those roots, the roots included, when there are at most `max_nodes` of them (default 10,000; at most 50,000). Past the cap `nodes` is null and `complete` false, and the caller filters by `roots` against its own closure table or materialised path. `env_wide` is true when a root is the Environment's own root, so every node qualifies and no filter is needed; `nodes` is null then too. The same rules as `evaluate` decide the roots, so a list filtered by this answer agrees with the check a write makes. Returns `404` when the identity has no membership in this Environment.
|
|
772
|
+
*/
|
|
773
|
+
get: operations["ApiIdentitiesController_getIdentityAccessibleNodes"];
|
|
774
|
+
put?: never;
|
|
775
|
+
post?: never;
|
|
776
|
+
delete?: never;
|
|
777
|
+
options?: never;
|
|
778
|
+
head?: never;
|
|
779
|
+
patch?: never;
|
|
780
|
+
trace?: never;
|
|
781
|
+
};
|
|
762
782
|
"/api/v1/identities/{id}/grants": {
|
|
763
783
|
parameters: {
|
|
764
784
|
query?: never;
|
|
@@ -768,7 +788,7 @@ interface paths {
|
|
|
768
788
|
};
|
|
769
789
|
/**
|
|
770
790
|
* Get where an identity holds each permission
|
|
771
|
-
* @description Returns, for the current Environment, one page of `(permission, node_id)` rows: every permission the identity holds, paired with each hierarchy node its grants were made at — the nodes themselves, **not** expanded through their descendants. Rows are ordered by permission, then node; a caller building the whole map walks every page. A grant already means "this node and everything beneath it", so a caller answers a node-scoped question by walking up from the node in question and looking for one of these roots, using a copy of the parent edges fetched once from `GET /api/v1/nodes/parents` and shared across identities. Answers both scopes without a further call: the permission appearing at all is the Application-wide answer, and the walk is the node-scoped one.
|
|
791
|
+
* @description Returns, for the current Environment, one page of `(permission, node_id)` rows: every permission the identity holds, paired with each hierarchy node its grants were made at — the nodes themselves, **not** expanded through their descendants. Rows are ordered by permission, then node; a caller building the whole map walks every page. A grant already means "this node and everything beneath it", so a caller answers a node-scoped question by walking up from the node in question and looking for one of these roots, using a copy of the parent edges fetched once from `GET /api/v1/nodes/parents` and shared across identities. Answers both scopes without a further call: the permission appearing at all is the Application-wide answer, and the walk is the node-scoped one. By default, assignments outside their effective window are excluded. With `include=windows`, every assignment is a row carrying `effective_from` and `effective_to` (null for an unbounded side), in effect or not, so a caller applies the window itself: it can say why a check fails (the grant has ended, or has not begun) and drop a grant at its boundary without reading again. Returns `404` when the identity has no membership in this Environment.
|
|
772
792
|
*/
|
|
773
793
|
get: operations["ApiIdentitiesController_getIdentityGrants"];
|
|
774
794
|
put?: never;
|
|
@@ -2739,6 +2759,18 @@ interface components {
|
|
|
2739
2759
|
/** Format: date-time */
|
|
2740
2760
|
updated_at: string;
|
|
2741
2761
|
};
|
|
2762
|
+
IdentityAccessibleNodesResponseDto: {
|
|
2763
|
+
/** @example reports.view */
|
|
2764
|
+
permission: string;
|
|
2765
|
+
/** @description True when a root is the Environment's own root, so every node qualifies and a list needs no filter. `nodes` is null then. */
|
|
2766
|
+
env_wide: boolean;
|
|
2767
|
+
/** @description The nodes the permission is granted at, in effect now, that no other such node already covers. Each reaches everything beneath it. */
|
|
2768
|
+
roots: string[];
|
|
2769
|
+
/** @description Every node beneath the roots, the roots included, for an `IN (...)` filter. Null when `env_wide`, or when there are more than `max_nodes` (then `complete` is false and the caller filters by `roots` instead). */
|
|
2770
|
+
nodes: string[] | null;
|
|
2771
|
+
/** @description False when the set beneath the roots was larger than `max_nodes` and was not sent. */
|
|
2772
|
+
complete: boolean;
|
|
2773
|
+
};
|
|
2742
2774
|
IdentityGrantResponseDto: {
|
|
2743
2775
|
/** @example reports.view */
|
|
2744
2776
|
permission: string;
|
|
@@ -2747,6 +2779,16 @@ interface components {
|
|
|
2747
2779
|
* @description The hierarchy node the permission was granted at. Not expanded through descendants.
|
|
2748
2780
|
*/
|
|
2749
2781
|
node_id: string;
|
|
2782
|
+
/**
|
|
2783
|
+
* Format: date-time
|
|
2784
|
+
* @description When the assignment behind this root begins, or null for no start. Present only with `include=windows`.
|
|
2785
|
+
*/
|
|
2786
|
+
effective_from?: string | null;
|
|
2787
|
+
/**
|
|
2788
|
+
* Format: date-time
|
|
2789
|
+
* @description When the assignment behind this root ends, or null for no end. Present only with `include=windows`.
|
|
2790
|
+
*/
|
|
2791
|
+
effective_to?: string | null;
|
|
2750
2792
|
};
|
|
2751
2793
|
IdentityInviteResponseDto: {
|
|
2752
2794
|
id: string;
|
|
@@ -8358,7 +8400,10 @@ interface operations {
|
|
|
8358
8400
|
};
|
|
8359
8401
|
ApiIdentitiesController_getIdentityPermissions: {
|
|
8360
8402
|
parameters: {
|
|
8361
|
-
query?:
|
|
8403
|
+
query?: {
|
|
8404
|
+
/** @description Answer at this node: the permissions granted by assignments on its lineage. Omit for the Application-wide answer. */
|
|
8405
|
+
node_id?: string;
|
|
8406
|
+
};
|
|
8362
8407
|
header?: never;
|
|
8363
8408
|
path: {
|
|
8364
8409
|
id: string;
|
|
@@ -8464,6 +8509,119 @@ interface operations {
|
|
|
8464
8509
|
};
|
|
8465
8510
|
};
|
|
8466
8511
|
};
|
|
8512
|
+
ApiIdentitiesController_getIdentityAccessibleNodes: {
|
|
8513
|
+
parameters: {
|
|
8514
|
+
query: {
|
|
8515
|
+
/** @description The permission to answer for, by key. */
|
|
8516
|
+
permission: string;
|
|
8517
|
+
/** @description The largest `nodes` set to return. Past it, `nodes` is null and `complete` false. */
|
|
8518
|
+
max_nodes?: number;
|
|
8519
|
+
};
|
|
8520
|
+
header?: never;
|
|
8521
|
+
path: {
|
|
8522
|
+
id: string;
|
|
8523
|
+
};
|
|
8524
|
+
cookie?: never;
|
|
8525
|
+
};
|
|
8526
|
+
requestBody?: never;
|
|
8527
|
+
responses: {
|
|
8528
|
+
/** @description The minimal grant roots for the permission, and the nodes beneath them up to the cap */
|
|
8529
|
+
200: {
|
|
8530
|
+
headers: {
|
|
8531
|
+
[name: string]: unknown;
|
|
8532
|
+
};
|
|
8533
|
+
content: {
|
|
8534
|
+
"application/json": {
|
|
8535
|
+
data: components["schemas"]["IdentityAccessibleNodesResponseDto"];
|
|
8536
|
+
};
|
|
8537
|
+
};
|
|
8538
|
+
};
|
|
8539
|
+
/** @description The request failed validation: a body field, a query parameter or an id in the path is missing, malformed or out of range; or the request sent both an `X-API-Key` and an `Authorization` header */
|
|
8540
|
+
400: {
|
|
8541
|
+
headers: {
|
|
8542
|
+
[name: string]: unknown;
|
|
8543
|
+
};
|
|
8544
|
+
content: {
|
|
8545
|
+
/**
|
|
8546
|
+
* @example {
|
|
8547
|
+
* "error": {
|
|
8548
|
+
* "statusCode": 400,
|
|
8549
|
+
* "code": null,
|
|
8550
|
+
* "message": "The request failed validation: a body field, a query parameter or an id in the path is missing, malformed or out of range; or the request sent both an `X-API-Key` and an `Authorization` header",
|
|
8551
|
+
* "timestamp": "2026-04-20T12:00:00.000Z",
|
|
8552
|
+
* "path": "/api/v1/identities/{id}/accessible-nodes",
|
|
8553
|
+
* "method": "GET"
|
|
8554
|
+
* }
|
|
8555
|
+
* }
|
|
8556
|
+
*/
|
|
8557
|
+
"application/json": components["schemas"]["ErrorResponseDto"];
|
|
8558
|
+
};
|
|
8559
|
+
};
|
|
8560
|
+
/** @description Invalid or expired token */
|
|
8561
|
+
401: {
|
|
8562
|
+
headers: {
|
|
8563
|
+
[name: string]: unknown;
|
|
8564
|
+
};
|
|
8565
|
+
content: {
|
|
8566
|
+
/**
|
|
8567
|
+
* @example {
|
|
8568
|
+
* "error": {
|
|
8569
|
+
* "statusCode": 401,
|
|
8570
|
+
* "code": null,
|
|
8571
|
+
* "message": "Invalid or expired token",
|
|
8572
|
+
* "timestamp": "2026-04-20T12:00:00.000Z",
|
|
8573
|
+
* "path": "/api/v1/identities",
|
|
8574
|
+
* "method": "GET"
|
|
8575
|
+
* }
|
|
8576
|
+
* }
|
|
8577
|
+
*/
|
|
8578
|
+
"application/json": components["schemas"]["ErrorResponseDto"];
|
|
8579
|
+
};
|
|
8580
|
+
};
|
|
8581
|
+
/** @description This token is not authorized for this endpoint (wrong principal type — e.g., admin token on identity-only endpoint, or vice versa) */
|
|
8582
|
+
403: {
|
|
8583
|
+
headers: {
|
|
8584
|
+
[name: string]: unknown;
|
|
8585
|
+
};
|
|
8586
|
+
content: {
|
|
8587
|
+
/**
|
|
8588
|
+
* @example {
|
|
8589
|
+
* "error": {
|
|
8590
|
+
* "statusCode": 403,
|
|
8591
|
+
* "code": null,
|
|
8592
|
+
* "message": "This token is not authorized for this endpoint (wrong principal type — e.g., admin token on identity-only endpoint, or vice versa)",
|
|
8593
|
+
* "timestamp": "2026-04-20T12:00:00.000Z",
|
|
8594
|
+
* "path": "/api/v1/identities",
|
|
8595
|
+
* "method": "GET"
|
|
8596
|
+
* }
|
|
8597
|
+
* }
|
|
8598
|
+
*/
|
|
8599
|
+
"application/json": components["schemas"]["ErrorResponseDto"];
|
|
8600
|
+
};
|
|
8601
|
+
};
|
|
8602
|
+
/** @description No identity with that id has a membership in this Environment */
|
|
8603
|
+
404: {
|
|
8604
|
+
headers: {
|
|
8605
|
+
[name: string]: unknown;
|
|
8606
|
+
};
|
|
8607
|
+
content: {
|
|
8608
|
+
/**
|
|
8609
|
+
* @example {
|
|
8610
|
+
* "error": {
|
|
8611
|
+
* "statusCode": 404,
|
|
8612
|
+
* "code": null,
|
|
8613
|
+
* "message": "No identity with that id has a membership in this Environment",
|
|
8614
|
+
* "timestamp": "2026-04-20T12:00:00.000Z",
|
|
8615
|
+
* "path": "/api/v1/identities/{id}/accessible-nodes",
|
|
8616
|
+
* "method": "GET"
|
|
8617
|
+
* }
|
|
8618
|
+
* }
|
|
8619
|
+
*/
|
|
8620
|
+
"application/json": components["schemas"]["ErrorResponseDto"];
|
|
8621
|
+
};
|
|
8622
|
+
};
|
|
8623
|
+
};
|
|
8624
|
+
};
|
|
8467
8625
|
ApiIdentitiesController_getIdentityGrants: {
|
|
8468
8626
|
parameters: {
|
|
8469
8627
|
query?: {
|
|
@@ -8471,6 +8629,8 @@ interface operations {
|
|
|
8471
8629
|
page?: number;
|
|
8472
8630
|
/** @description Items per page (1-100, default 20) */
|
|
8473
8631
|
take?: number;
|
|
8632
|
+
/** @description `windows` adds `effective_from` and `effective_to` to every row and includes assignments outside their window, so the caller applies the window itself. */
|
|
8633
|
+
include?: "windows";
|
|
8474
8634
|
};
|
|
8475
8635
|
header?: never;
|
|
8476
8636
|
path: {
|
|
@@ -21539,7 +21699,18 @@ declare class Identities {
|
|
|
21539
21699
|
* question at the node that matters. This is for showing a person what
|
|
21540
21700
|
* someone can do.
|
|
21541
21701
|
*/
|
|
21542
|
-
permissions(id: string): Promise<ResponseBody<"ApiIdentitiesController_getIdentityPermissions">>;
|
|
21702
|
+
permissions(id: string, query?: QueryParams<"ApiIdentitiesController_getIdentityPermissions">): Promise<ResponseBody<"ApiIdentitiesController_getIdentityPermissions">>;
|
|
21703
|
+
/**
|
|
21704
|
+
* Where the identity may act with one permission, answered by Canopy for a
|
|
21705
|
+
* list query: the minimal grant roots, and every node beneath them up to
|
|
21706
|
+
* `max_nodes` (null past it, with `complete: false`), or `env_wide` when a
|
|
21707
|
+
* root is the Environment's own root and no filter is needed.
|
|
21708
|
+
*
|
|
21709
|
+
* `LocalAuthorizer.accessibleNodes` answers the same question from its
|
|
21710
|
+
* cache without a call; this is the fresh answer, and the one for a process
|
|
21711
|
+
* that does not run the authorizer.
|
|
21712
|
+
*/
|
|
21713
|
+
accessibleNodes(id: string, query: QueryParams<"ApiIdentitiesController_getIdentityAccessibleNodes">): Promise<ResponseBody<"ApiIdentitiesController_getIdentityAccessibleNodes">>;
|
|
21543
21714
|
/**
|
|
21544
21715
|
* Every session the identity holds that is neither revoked nor expired,
|
|
21545
21716
|
* newest first: when and where it was signed in, when it expires, and the
|
|
@@ -21839,6 +22010,39 @@ declare class Canopy {
|
|
|
21839
22010
|
* delay is short, so a longer value is refused rather than honoured.
|
|
21840
22011
|
*/
|
|
21841
22012
|
declare const MAX_TTL_MS = 300000;
|
|
22013
|
+
/**
|
|
22014
|
+
* The largest node set `accessibleNodes` builds unless told otherwise. Past
|
|
22015
|
+
* it the answer carries the roots alone, and the caller filters against its
|
|
22016
|
+
* own closure table or materialised path instead of an `IN (...)` list.
|
|
22017
|
+
*/
|
|
22018
|
+
declare const DEFAULT_MAX_NODES = 10000;
|
|
22019
|
+
/**
|
|
22020
|
+
* Why a check was denied, as far as the grant roots and the tree can say.
|
|
22021
|
+
*
|
|
22022
|
+
* - `permission_not_held`: no assignment grants the permission anywhere, in
|
|
22023
|
+
* effect or not.
|
|
22024
|
+
* - `outside_grants`: it is granted, but at no node on the lineage of the
|
|
22025
|
+
* node asked about.
|
|
22026
|
+
* - `expired`: a grant that would answer has ended, and none is in effect.
|
|
22027
|
+
* - `not_yet_effective`: a grant that would answer has not begun, and none
|
|
22028
|
+
* is in effect.
|
|
22029
|
+
* - `node_missing`: a `node` check named no node, which is a denial rather
|
|
22030
|
+
* than an error, because a request whose subject cannot be established
|
|
22031
|
+
* must not pass.
|
|
22032
|
+
*/
|
|
22033
|
+
type LocalDenialReason = "permission_not_held" | "outside_grants" | "expired" | "not_yet_effective" | "node_missing";
|
|
22034
|
+
/** The answer to a check: the verdict, and why when it is no. */
|
|
22035
|
+
interface LocalDecision {
|
|
22036
|
+
allowed: boolean;
|
|
22037
|
+
reason: LocalDenialReason | null;
|
|
22038
|
+
}
|
|
22039
|
+
/** What a decision hook receives, after the verdict is in. */
|
|
22040
|
+
interface LocalDecisionEvent {
|
|
22041
|
+
query: LocalAuthorizationQuery;
|
|
22042
|
+
decision: LocalDecision;
|
|
22043
|
+
/** How long the check took, reads included. */
|
|
22044
|
+
durationMs: number;
|
|
22045
|
+
}
|
|
21842
22046
|
interface LocalAuthorizerOptions {
|
|
21843
22047
|
/**
|
|
21844
22048
|
* How long grant roots and the hierarchy are reused, in milliseconds.
|
|
@@ -21871,6 +22075,19 @@ interface LocalAuthorizerOptions {
|
|
|
21871
22075
|
timeoutMs?: number;
|
|
21872
22076
|
/** Retries for those reads. */
|
|
21873
22077
|
maxRetries?: number;
|
|
22078
|
+
/**
|
|
22079
|
+
* The largest node set {@link LocalAuthorizer.accessibleNodes} builds
|
|
22080
|
+
* unless a call says otherwise. Defaults to {@link DEFAULT_MAX_NODES}.
|
|
22081
|
+
*/
|
|
22082
|
+
maxNodes?: number;
|
|
22083
|
+
/**
|
|
22084
|
+
* Called after every decision, allowed or not, with the question and the
|
|
22085
|
+
* answer. Never awaited and never allowed to fail the check: a hook that
|
|
22086
|
+
* throws is ignored. For audit trails and metrics.
|
|
22087
|
+
*/
|
|
22088
|
+
onDecision?: (event: LocalDecisionEvent) => void;
|
|
22089
|
+
/** Called after every denial, the same way. For alerting on refusals. */
|
|
22090
|
+
onDenied?: (event: LocalDecisionEvent) => void;
|
|
21874
22091
|
/** Injectable clock, for tests. */
|
|
21875
22092
|
now?: () => number;
|
|
21876
22093
|
}
|
|
@@ -21881,6 +22098,26 @@ interface LocalAuthorizationQuery {
|
|
|
21881
22098
|
scope?: "node" | "app_wide";
|
|
21882
22099
|
node_id?: string;
|
|
21883
22100
|
}
|
|
22101
|
+
/** Where an identity may act with one permission, shaped for a list query. */
|
|
22102
|
+
interface AccessibleNodes {
|
|
22103
|
+
/**
|
|
22104
|
+
* The nodes the permission is granted at, in effect now, that no other
|
|
22105
|
+
* such node already covers. Each reaches everything beneath it.
|
|
22106
|
+
*/
|
|
22107
|
+
roots: string[];
|
|
22108
|
+
/**
|
|
22109
|
+
* Every node beneath the roots, the roots included, for an `IN (...)`
|
|
22110
|
+
* filter. Null when `envWide`, or when the set would exceed the cap.
|
|
22111
|
+
*/
|
|
22112
|
+
nodes: Set<string> | null;
|
|
22113
|
+
/** False when the set exceeded the cap and `nodes` was not built. */
|
|
22114
|
+
complete: boolean;
|
|
22115
|
+
/**
|
|
22116
|
+
* True when a root is the Environment's own root: every node qualifies, so
|
|
22117
|
+
* a list needs no filter at all.
|
|
22118
|
+
*/
|
|
22119
|
+
envWide: boolean;
|
|
22120
|
+
}
|
|
21884
22121
|
interface LocalAuthorizerStats {
|
|
21885
22122
|
/** Reads of an identity's grant roots, one per refresh however many pages it took. */
|
|
21886
22123
|
grantFetches: number;
|
|
@@ -21900,8 +22137,11 @@ interface LocalAuthorizerStats {
|
|
|
21900
22137
|
declare class LocalAuthorizer {
|
|
21901
22138
|
private readonly client;
|
|
21902
22139
|
private readonly ttlMs;
|
|
22140
|
+
private readonly maxNodes;
|
|
21903
22141
|
private readonly now;
|
|
21904
22142
|
private readonly readOptions;
|
|
22143
|
+
private readonly onDecision;
|
|
22144
|
+
private readonly onDenied;
|
|
21905
22145
|
private readonly grants;
|
|
21906
22146
|
private tree;
|
|
21907
22147
|
/**
|
|
@@ -21917,7 +22157,7 @@ declare class LocalAuthorizer {
|
|
|
21917
22157
|
private stats;
|
|
21918
22158
|
constructor(client: CanopyClient, options?: LocalAuthorizerOptions);
|
|
21919
22159
|
/**
|
|
21920
|
-
* Whether the identity holds the permission.
|
|
22160
|
+
* Whether the identity holds the permission, and why not when it does not.
|
|
21921
22161
|
*
|
|
21922
22162
|
* Shaped like the API's own evaluate so a caller can swap one for the other.
|
|
21923
22163
|
* A `node` check with no node is a denial rather than an error: a request
|
|
@@ -21925,9 +22165,51 @@ declare class LocalAuthorizer {
|
|
|
21925
22165
|
*/
|
|
21926
22166
|
evaluate(query: LocalAuthorizationQuery, options?: {
|
|
21927
22167
|
signal?: AbortSignal;
|
|
21928
|
-
}): Promise<
|
|
21929
|
-
|
|
22168
|
+
}): Promise<LocalDecision>;
|
|
22169
|
+
/**
|
|
22170
|
+
* The nodes the identity holds the permission at, in effect now, reduced to
|
|
22171
|
+
* the ones no other such node already covers, and whether one of them is
|
|
22172
|
+
* the Environment's root. The roots alone are enough for an application
|
|
22173
|
+
* that keeps a closure table or a materialised path: it filters by "beneath
|
|
22174
|
+
* one of these" without an expanded set.
|
|
22175
|
+
*
|
|
22176
|
+
* Reads the tree, since minimality and `envWide` are questions about it; a
|
|
22177
|
+
* root the readable hierarchy does not contain raises
|
|
22178
|
+
* `authorizer.hierarchy_incomplete`, as a check at such a node does.
|
|
22179
|
+
*/
|
|
22180
|
+
grantRoots(identityId: string, permission: string): Promise<{
|
|
22181
|
+
roots: string[];
|
|
22182
|
+
envWide: boolean;
|
|
21930
22183
|
}>;
|
|
22184
|
+
/**
|
|
22185
|
+
* Where the identity may act with the permission, as the set a list query
|
|
22186
|
+
* filters by: {@link grantRoots} expanded through their descendants, up to
|
|
22187
|
+
* `maxNodes` (the authorizer's default unless given here).
|
|
22188
|
+
*
|
|
22189
|
+
* Past the cap `nodes` is null and `complete` false: the set is too large
|
|
22190
|
+
* to be worth an `IN (...)` list, and the caller filters by `roots`
|
|
22191
|
+
* instead. An `envWide` answer carries no set either, since every node
|
|
22192
|
+
* qualifies and a filter would only slow the query down.
|
|
22193
|
+
*
|
|
22194
|
+
* Computed from the same cached grant roots and tree a check uses, so a
|
|
22195
|
+
* list filtered by it and the check on a write cannot disagree, and it goes
|
|
22196
|
+
* stale on the same `ttlMs`.
|
|
22197
|
+
*/
|
|
22198
|
+
accessibleNodes(identityId: string, permission: string, options?: {
|
|
22199
|
+
maxNodes?: number;
|
|
22200
|
+
}): Promise<AccessibleNodes>;
|
|
22201
|
+
/**
|
|
22202
|
+
* Every permission the identity holds at the node, by grant or by
|
|
22203
|
+
* inheritance: the list a screen renders its controls from, answered from
|
|
22204
|
+
* the same cached grant roots and tree a check uses.
|
|
22205
|
+
*/
|
|
22206
|
+
permissionsAt(identityId: string, nodeId: string): Promise<string[]>;
|
|
22207
|
+
/**
|
|
22208
|
+
* {@link permissionsAt} for several nodes at once, as node → permissions:
|
|
22209
|
+
* the rows of a list each with their own controls, answered from one read
|
|
22210
|
+
* of the grant roots and one walk per node.
|
|
22211
|
+
*/
|
|
22212
|
+
permissionsAtMany(identityId: string, nodeIds: readonly string[]): Promise<Map<string, string[]>>;
|
|
21931
22213
|
private decide;
|
|
21932
22214
|
/** Counters for observability — how much traffic the cache is actually saving. */
|
|
21933
22215
|
snapshot(): LocalAuthorizerStats;
|
|
@@ -21954,19 +22236,29 @@ declare class LocalAuthorizer {
|
|
|
21954
22236
|
* top of it — size the TTL to the revocation latency you can promise.
|
|
21955
22237
|
*/
|
|
21956
22238
|
invalidate(identityId?: string): void;
|
|
21957
|
-
/**
|
|
21958
|
-
private
|
|
22239
|
+
/** Fire the decision hooks, after the verdict and without waiting on them. */
|
|
22240
|
+
private announce;
|
|
22241
|
+
/** The node and its ancestors, from the tree this client can read. */
|
|
22242
|
+
private lineageOf;
|
|
22243
|
+
private hierarchyIncomplete;
|
|
21959
22244
|
private grantRootsFor;
|
|
21960
22245
|
/**
|
|
21961
|
-
* Every page of the identity's grant roots,
|
|
22246
|
+
* Every page of the identity's grant roots, with their windows, folded
|
|
22247
|
+
* into permission → roots.
|
|
22248
|
+
*
|
|
22249
|
+
* Each row is one permission at one node for one assignment, so a
|
|
22250
|
+
* permission granted at three nodes arrives as three rows, possibly on
|
|
22251
|
+
* different pages. Stopping after the first page would under-report what
|
|
22252
|
+
* the identity holds: a false deny, the safe direction, but a wrong answer
|
|
22253
|
+
* all the same.
|
|
21962
22254
|
*
|
|
21963
|
-
*
|
|
21964
|
-
*
|
|
21965
|
-
*
|
|
21966
|
-
*
|
|
22255
|
+
* Rows outside their window are kept: they are what lets a denial say
|
|
22256
|
+
* `expired` or `not_yet_effective`, and the entry expires at the nearest
|
|
22257
|
+
* window boundary when that comes before the TTL, so a grant takes effect
|
|
22258
|
+
* or ends on time rather than up to `ttlMs` late.
|
|
21967
22259
|
*/
|
|
21968
22260
|
private fetchGrantRoots;
|
|
21969
|
-
private
|
|
22261
|
+
private treeEntry;
|
|
21970
22262
|
/**
|
|
21971
22263
|
* Revalidate rather than re-read. The hierarchy is the expensive half and
|
|
21972
22264
|
* the one that changes least, so the common case is a `304` and no transfer
|
|
@@ -22432,4 +22724,4 @@ type WebhookEventData<Name extends WebhookEventName> = WebhookEvent<Name>["data"
|
|
|
22432
22724
|
*/
|
|
22433
22725
|
declare function isWebhookEvent<Name extends WebhookEventName>(delivery: WebhookDelivery, name: Name): delivery is WebhookEvent<Name>;
|
|
22434
22726
|
|
|
22435
|
-
export { type AssignmentItem, Assignments, type CallOptions, Canopy, CanopyAuthorizerError, CanopyClient, type CanopyClientOptions, CanopyConnectionError, CanopyError, type CanopyErrorBody, type CanopyOrgContext, type CanopyTokenClaims, CanopyTokenError, type Collection, type ConcurrencyOptions, type ConditionalResult, type CursorPagination, type FailureStatus, Identities, type IdentityAssignmentItem, type IdentityItem, type IdentitySessionItem, type LocalAuthorizationQuery, LocalAuthorizer, type LocalAuthorizerOptions, type LocalAuthorizerStats, MAX_TTL_MS, type OffsetPagination, type OperationId, type OrganizationDirectoryGroupItem, type OrganizationInviteItem, type OrganizationItem, type OrganizationMemberItem, Organizations, type PageFetcher, type PageParams, type PaginateOptions, type Pagination, Paginator, type PartialSuccess, type PermissionItem, Permissions, type QueryParams, type RequestBody, type RequestOptions, type ResponseBody, type RoleItem, Roles, type Schema, TokenVerifier, type TokenVerifierOptions, type WebhookDelivery, type WebhookEvent, type WebhookEventData, type WebhookEventName, authorizationErrorStatus, type components, isCanopyAuthorizerError, isCanopyConnectionError, isCanopyError, isCanopyTokenError, isCursorPagination, isWebhookEvent, type operations, orgContext, paginate, type paths, tokenErrorStatus, withConcurrency };
|
|
22727
|
+
export { type AccessibleNodes, type AssignmentItem, Assignments, type CallOptions, Canopy, CanopyAuthorizerError, CanopyClient, type CanopyClientOptions, CanopyConnectionError, CanopyError, type CanopyErrorBody, type CanopyOrgContext, type CanopyTokenClaims, CanopyTokenError, type Collection, type ConcurrencyOptions, type ConditionalResult, type CursorPagination, DEFAULT_MAX_NODES, type FailureStatus, Identities, type IdentityAssignmentItem, type IdentityItem, type IdentitySessionItem, type LocalAuthorizationQuery, LocalAuthorizer, type LocalAuthorizerOptions, type LocalAuthorizerStats, type LocalDecision, type LocalDecisionEvent, type LocalDenialReason, MAX_TTL_MS, type OffsetPagination, type OperationId, type OrganizationDirectoryGroupItem, type OrganizationInviteItem, type OrganizationItem, type OrganizationMemberItem, Organizations, type PageFetcher, type PageParams, type PaginateOptions, type Pagination, Paginator, type PartialSuccess, type PermissionItem, Permissions, type QueryParams, type RequestBody, type RequestOptions, type ResponseBody, type RoleItem, Roles, type Schema, TokenVerifier, type TokenVerifierOptions, type WebhookDelivery, type WebhookEvent, type WebhookEventData, type WebhookEventName, authorizationErrorStatus, type components, isCanopyAuthorizerError, isCanopyConnectionError, isCanopyError, isCanopyTokenError, isCursorPagination, isWebhookEvent, type operations, orgContext, paginate, type paths, tokenErrorStatus, withConcurrency };
|