@l3dev/abac 0.1.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/LICENSE +21 -0
- package/README.md +98 -0
- package/build/abac.d.ts +72 -0
- package/build/abac.js +65 -0
- package/build/abac.js.map +1 -0
- package/build/action.d.ts +61 -0
- package/build/action.js +54 -0
- package/build/action.js.map +1 -0
- package/build/actionGroup.d.ts +44 -0
- package/build/actionGroup.js +47 -0
- package/build/actionGroup.js.map +1 -0
- package/build/actionVisitor.d.ts +29 -0
- package/build/actionVisitor.js +64 -0
- package/build/actionVisitor.js.map +1 -0
- package/build/index.d.ts +9 -0
- package/build/index.js +3 -0
- package/build/index.js.map +1 -0
- package/build/internal/collections.d.ts +12 -0
- package/build/internal/collections.js +2 -0
- package/build/internal/collections.js.map +1 -0
- package/build/internal/core.d.ts +19 -0
- package/build/internal/core.js +2 -0
- package/build/internal/core.js.map +1 -0
- package/build/internal/paths.d.ts +45 -0
- package/build/internal/paths.js +2 -0
- package/build/internal/paths.js.map +1 -0
- package/build/permission.d.ts +53 -0
- package/build/permission.js +153 -0
- package/build/permission.js.map +1 -0
- package/build/policy.d.ts +49 -0
- package/build/policy.js +72 -0
- package/build/policy.js.map +1 -0
- package/build/policyFilters.d.ts +9 -0
- package/build/policyFilters.js +21 -0
- package/build/policyFilters.js.map +1 -0
- package/build/resource.d.ts +75 -0
- package/build/resource.js +71 -0
- package/build/resource.js.map +1 -0
- package/package.json +28 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Action } from "../action.js";
|
|
2
|
+
import type { ActionGroup } from "../actionGroup.js";
|
|
3
|
+
import type { Resource } from "../resource.js";
|
|
4
|
+
import type { UnionToIntersection } from "./core.js";
|
|
5
|
+
export type ActionMap = Record<string, Action<any, any> | ActionGroup<any, any>>;
|
|
6
|
+
export type ToActionMap<TActions extends Action<any, any> | ActionGroup<any, any>> = UnionToIntersection<TActions extends Action<infer TName, any> | ActionGroup<infer TName, any> ? {
|
|
7
|
+
[name in TName]: TActions;
|
|
8
|
+
} : never, ActionMap>;
|
|
9
|
+
export type ResourceMap = Record<string, Resource<any, any>>;
|
|
10
|
+
export type UnionToResourceMap<TResources extends Resource<any, any>> = UnionToIntersection<TResources extends Resource<infer TName, any> ? {
|
|
11
|
+
[name in TName]: TResources;
|
|
12
|
+
} : never, ResourceMap>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collections.js","sourceRoot":"","sources":["../../src/internal/collections.ts"],"names":[],"mappings":"","sourcesContent":["import type { Action } from \"../action.js\";\nimport type { ActionGroup } from \"../actionGroup.js\";\nimport type { Resource } from \"../resource.js\";\nimport type { UnionToIntersection } from \"./core.js\";\n\nexport type ActionMap = Record<string, Action<any, any> | ActionGroup<any, any>>;\n\nexport type ToActionMap<TActions extends Action<any, any> | ActionGroup<any, any>> =\n\tUnionToIntersection<\n\t\tTActions extends Action<infer TName, any> | ActionGroup<infer TName, any>\n\t\t\t? {\n\t\t\t\t\t[name in TName]: TActions;\n\t\t\t\t}\n\t\t\t: never,\n\t\tActionMap\n\t>;\n\nexport type ResourceMap = Record<string, Resource<any, any>>;\n\nexport type UnionToResourceMap<TResources extends Resource<any, any>> = UnionToIntersection<\n\tTResources extends Resource<infer TName, any>\n\t\t? {\n\t\t\t\t[name in TName]: TResources;\n\t\t\t}\n\t\t: never,\n\tResourceMap\n>;\n"]}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export type UnsetMarker = "unset" & {
|
|
2
|
+
__brand: "unsetMarker";
|
|
3
|
+
};
|
|
4
|
+
export type OptionalMarker = "optional" & {
|
|
5
|
+
__brand: "optionalMarker";
|
|
6
|
+
};
|
|
7
|
+
export type MakeOptional<T> = T extends UnsetMarker ? OptionalMarker : T | undefined;
|
|
8
|
+
export type Expand<T> = T extends Record<any, any> ? {
|
|
9
|
+
[K in keyof T]: T[K];
|
|
10
|
+
} : never;
|
|
11
|
+
export type KeysEqual<A, B> = A extends Record<keyof B, any> ? (B extends Record<keyof A, any> ? true : false) : false;
|
|
12
|
+
export type UnionToIntersection<U, T> = (U extends any ? (k: U) => void : never) extends (k: infer I extends T) => void ? I : never;
|
|
13
|
+
export type OptionalUndefinedFields<T> = Expand<Partial<T> & Pick<T, {
|
|
14
|
+
[K in keyof T]: T[K] extends Exclude<T[K], undefined> ? K : never;
|
|
15
|
+
}[keyof T]>>;
|
|
16
|
+
export type EmptyObject = {
|
|
17
|
+
object: void;
|
|
18
|
+
};
|
|
19
|
+
export type IsOnlyEmptyObject<T> = KeysEqual<EmptyObject, T> extends true ? (T extends EmptyObject ? true : false) : false;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core.js","sourceRoot":"","sources":["../../src/internal/core.ts"],"names":[],"mappings":"","sourcesContent":["export type UnsetMarker = \"unset\" & {\n\t__brand: \"unsetMarker\";\n};\n\nexport type OptionalMarker = \"optional\" & {\n\t__brand: \"optionalMarker\";\n};\n\nexport type MakeOptional<T> = T extends UnsetMarker ? OptionalMarker : T | undefined;\n\nexport type Expand<T> = T extends Record<any, any> ? { [K in keyof T]: T[K] } : never;\n\nexport type KeysEqual<A, B> =\n\tA extends Record<keyof B, any> ? (B extends Record<keyof A, any> ? true : false) : false;\n\nexport type UnionToIntersection<U, T> = (U extends any ? (k: U) => void : never) extends (\n\tk: infer I extends T\n) => void\n\t? I\n\t: never;\n\nexport type OptionalUndefinedFields<T> = Expand<\n\tPartial<T> &\n\t\tPick<\n\t\t\tT,\n\t\t\t{\n\t\t\t\t[K in keyof T]: T[K] extends Exclude<T[K], undefined> ? K : never;\n\t\t\t}[keyof T]\n\t\t>\n>;\n\nexport type EmptyObject = { object: void };\n\nexport type IsOnlyEmptyObject<T> =\n\tKeysEqual<EmptyObject, T> extends true ? (T extends EmptyObject ? true : false) : false;\n"]}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { Action, RuleCtx } from "../action.js";
|
|
2
|
+
import type { ActionGroup } from "../actionGroup.js";
|
|
3
|
+
import type { Resource } from "../resource.js";
|
|
4
|
+
import type { ResourceMap } from "./collections.js";
|
|
5
|
+
import type { EmptyObject, Expand, MakeOptional, OptionalMarker, UnsetMarker } from "./core.js";
|
|
6
|
+
type ActionNames<TActionMap, TExcludeEmptyObjects extends boolean = false> = {
|
|
7
|
+
[actionName in keyof TActionMap]: TActionMap[actionName] extends Action<infer TActionName, infer TActionContext> ? TActionContext["ctx"] extends EmptyObject ? TExcludeEmptyObjects extends true ? never : `:${TActionName}` : `:${TActionName}` : TActionMap[actionName] extends ActionGroup<infer TGroupName, infer TGroupContext> ? `.${TGroupName}${ActionNames<TGroupContext["actions"], TExcludeEmptyObjects>}` : never;
|
|
8
|
+
}[keyof TActionMap];
|
|
9
|
+
export type OwnableActionPaths<TResources extends ResourceMap> = {
|
|
10
|
+
[resourceName in Extract<keyof TResources, string>]: (TResources[resourceName]["subResources"] extends UnsetMarker ? never : `${resourceName}.${OwnableActionPaths<TResources[resourceName]["subResources"]>}`) | (TResources[resourceName] extends Resource<any, infer TResourceContext> ? TResourceContext["ownable"] extends true ? TResources[resourceName]["actions"] extends UnsetMarker ? never : `${resourceName}${ActionNames<TResources[resourceName]["actions"], true>}` : never : never);
|
|
11
|
+
}[Extract<keyof TResources, string>];
|
|
12
|
+
export type ActionPaths<TResources extends ResourceMap> = {
|
|
13
|
+
[resourceName in Extract<keyof TResources, string>]: (TResources[resourceName]["subResources"] extends UnsetMarker ? never : `${resourceName}.${ActionPaths<TResources[resourceName]["subResources"]>}`) | (TResources[resourceName]["actions"] extends UnsetMarker ? never : `${resourceName}${ActionNames<TResources[resourceName]["actions"]>}`);
|
|
14
|
+
}[Extract<keyof TResources, string>] | `${OwnableActionPaths<TResources>}.own`;
|
|
15
|
+
type ActionNamesWithWildcards<TActionMap> = {
|
|
16
|
+
[actionName in keyof TActionMap]: TActionMap[actionName] extends ActionGroup<infer TGroupName, infer TGroupContext> ? `.${TGroupName}${ActionNamesWithWildcards<TGroupContext["actions"]>}` | `.${TGroupName}:*` : never;
|
|
17
|
+
}[keyof TActionMap];
|
|
18
|
+
export type OwnableWildcardActionPaths<TResources extends ResourceMap> = {
|
|
19
|
+
[resourceName in Extract<keyof TResources, string>]: TResources[resourceName] extends Resource<any, infer TResourceContext> ? TResourceContext["ownable"] extends true ? `${resourceName}:*` | (TResources[resourceName]["subResources"] extends UnsetMarker ? never : `${resourceName}.${OwnableWildcardActionPaths<TResources[resourceName]["subResources"]>}`) | (TResources[resourceName]["actions"] extends UnsetMarker ? never : `${resourceName}${ActionNamesWithWildcards<TResources[resourceName]["actions"]>}`) : never : never;
|
|
20
|
+
}[Extract<keyof TResources, string>];
|
|
21
|
+
export type WildcardActionPaths<TResources extends ResourceMap> = {
|
|
22
|
+
[resourceName in Extract<keyof TResources, string>]: (TResources[resourceName]["subResources"] extends UnsetMarker ? never : `${resourceName}.${WildcardActionPaths<TResources[resourceName]["subResources"]>}`) | (TResources[resourceName]["actions"] extends UnsetMarker ? never : `${resourceName}${ActionNamesWithWildcards<TResources[resourceName]["actions"]>}`) | `${resourceName}:*` | `${resourceName}.*`;
|
|
23
|
+
}[Extract<keyof TResources, string>] | `${OwnableWildcardActionPaths<TResources>}.own`;
|
|
24
|
+
type MergeRuleCtx<A extends RuleCtx, B extends RuleCtx> = {
|
|
25
|
+
subject: A["subject"] extends UnsetMarker ? B["subject"] : A["subject"];
|
|
26
|
+
object: A["object"] extends UnsetMarker ? B["object"] : A["object"] extends OptionalMarker ? MakeOptional<B["object"]> : A["object"];
|
|
27
|
+
};
|
|
28
|
+
type ResolveAction<TParentCtx extends RuleCtx, TAction extends Action<any, any>> = TAction extends Action<infer TName, infer TActionContext> ? Action<TName, Expand<{
|
|
29
|
+
ctx: Expand<MergeRuleCtx<TActionContext["ctx"], TParentCtx>>;
|
|
30
|
+
configurable: TActionContext["configurable"];
|
|
31
|
+
}>> : never;
|
|
32
|
+
type IndexActionGroupWithPath<TPath extends string, TActionGroup extends ActionGroup<any, any>, TParentCtx extends RuleCtx> = TPath extends keyof TActionGroup["actions"] ? ResolveAction<TParentCtx, TActionGroup["actions"][TPath]> : TPath extends `${infer ActionGroupName}.${infer Rest}` ? TActionGroup["actions"][ActionGroupName] extends ActionGroup<any, infer TActionGroupContext> ? IndexActionGroupWithPath<Rest, TActionGroup["actions"][ActionGroupName], MergeRuleCtx<TActionGroupContext["ctx"], TParentCtx>> : never : TPath extends `${infer ActionGroupName}:${infer ActionName}` ? TActionGroup["actions"][ActionGroupName] extends ActionGroup<any, any> ? ResolveAction<TParentCtx, TActionGroup["actions"][ActionGroupName]["actions"][ActionName]> : never : never;
|
|
33
|
+
type IndexResourceWithPath<TPath extends string, TResource extends Resource<any, any>, TParentCtx extends RuleCtx> = TPath extends keyof TResource["actions"] ? TResource["actions"][TPath] extends Action<any, any> ? ResolveAction<TParentCtx, TResource["actions"][TPath]> : never : TPath extends `${infer ResourceOrGroupName}.${infer Rest}` ? ResourceOrGroupName extends keyof TResource["actions"] ? TResource["actions"][ResourceOrGroupName] extends ActionGroup<any, infer TActionGroupContext> ? IndexActionGroupWithPath<Rest, TResource["actions"][ResourceOrGroupName], MergeRuleCtx<TActionGroupContext["ctx"], TParentCtx>> : never : TResource["subResources"][ResourceOrGroupName] extends Resource<any, infer TResourceContext> ? IndexResourceWithPath<Rest, TResource["subResources"][ResourceOrGroupName], MergeRuleCtx<TResourceContext["ctx"], TParentCtx>> : never : TPath extends `${infer ResourceOrGroupName}:${infer ActionName}` ? TResource["actions"][ResourceOrGroupName] extends ActionGroup<any, infer TActionGroupContext> ? ResolveAction<MergeRuleCtx<TActionGroupContext["ctx"], TParentCtx>, TResource["actions"][ResourceOrGroupName]["actions"][ActionName]> : TResource["subResources"][ResourceOrGroupName] extends Resource<any, infer TResourceContext> ? ResolveAction<MergeRuleCtx<TResourceContext["ctx"], TParentCtx>, TResource["subResources"][ResourceOrGroupName]["actions"][ActionName]> : never : never;
|
|
34
|
+
export type ActionFromPath<TResources extends ResourceMap, TPath extends string> = TPath extends `${infer TActualPath}.own` ? ActionFromPath<TResources, TActualPath> : TPath extends `${infer ResourceName extends Extract<keyof TResources, string>}.${infer Rest}` ? TResources[ResourceName] extends Resource<any, infer TResourceContext> ? IndexResourceWithPath<Rest, TResources[ResourceName], TResourceContext["ctx"]> : never : TPath extends `${infer ResourceName extends Extract<keyof TResources, string>}:${infer ActionName}` ? TResources[ResourceName] extends Resource<any, infer TResourceContext> ? ResolveAction<TResourceContext["ctx"], TResources[ResourceName]["actions"][ActionName]> : never : never;
|
|
35
|
+
type ActionPathsWithAction<TResources extends ResourceMap, TPaths extends string> = {
|
|
36
|
+
[path in TPaths]: {
|
|
37
|
+
path: path;
|
|
38
|
+
action: ActionFromPath<TResources, path>;
|
|
39
|
+
};
|
|
40
|
+
}[TPaths];
|
|
41
|
+
export type FilterActionPaths<TResources extends ResourceMap, TCtxPattern, TConfigurable extends boolean = boolean, TPathsAndActions = ActionPathsWithAction<TResources, ActionPaths<TResources>>> = TPathsAndActions extends {
|
|
42
|
+
path: string;
|
|
43
|
+
action: Action<any, infer TActionContext>;
|
|
44
|
+
} ? TActionContext["ctx"] extends TCtxPattern ? TActionContext["configurable"] extends TConfigurable ? TPathsAndActions["path"] : never : never : never;
|
|
45
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/internal/paths.ts"],"names":[],"mappings":"","sourcesContent":["import type { Action, RuleCtx } from \"../action.js\";\nimport type { ActionGroup } from \"../actionGroup.js\";\nimport type { Resource } from \"../resource.js\";\nimport type { ResourceMap } from \"./collections.js\";\nimport type { EmptyObject, Expand, MakeOptional, OptionalMarker, UnsetMarker } from \"./core.js\";\n\ntype ActionNames<TActionMap, TExcludeEmptyObjects extends boolean = false> = {\n\t[actionName in keyof TActionMap]: TActionMap[actionName] extends Action<\n\t\tinfer TActionName,\n\t\tinfer TActionContext\n\t>\n\t\t? TActionContext[\"ctx\"] extends EmptyObject\n\t\t\t? TExcludeEmptyObjects extends true\n\t\t\t\t? never\n\t\t\t\t: `:${TActionName}`\n\t\t\t: `:${TActionName}`\n\t\t: TActionMap[actionName] extends ActionGroup<infer TGroupName, infer TGroupContext>\n\t\t\t? `.${TGroupName}${ActionNames<TGroupContext[\"actions\"], TExcludeEmptyObjects>}`\n\t\t\t: never;\n}[keyof TActionMap];\n\nexport type OwnableActionPaths<TResources extends ResourceMap> = {\n\t[resourceName in Extract<keyof TResources, string>]:\n\t\t| (TResources[resourceName][\"subResources\"] extends UnsetMarker\n\t\t\t\t? never\n\t\t\t\t: `${resourceName}.${OwnableActionPaths<TResources[resourceName][\"subResources\"]>}`)\n\t\t| (TResources[resourceName] extends Resource<any, infer TResourceContext>\n\t\t\t\t? TResourceContext[\"ownable\"] extends true\n\t\t\t\t\t? TResources[resourceName][\"actions\"] extends UnsetMarker\n\t\t\t\t\t\t? never\n\t\t\t\t\t\t: `${resourceName}${ActionNames<TResources[resourceName][\"actions\"], true>}`\n\t\t\t\t\t: never\n\t\t\t\t: never);\n}[Extract<keyof TResources, string>];\n\nexport type ActionPaths<TResources extends ResourceMap> =\n\t| {\n\t\t\t[resourceName in Extract<keyof TResources, string>]:\n\t\t\t\t| (TResources[resourceName][\"subResources\"] extends UnsetMarker\n\t\t\t\t\t\t? never\n\t\t\t\t\t\t: `${resourceName}.${ActionPaths<TResources[resourceName][\"subResources\"]>}`)\n\t\t\t\t| (TResources[resourceName][\"actions\"] extends UnsetMarker\n\t\t\t\t\t\t? never\n\t\t\t\t\t\t: `${resourceName}${ActionNames<TResources[resourceName][\"actions\"]>}`);\n\t }[Extract<keyof TResources, string>]\n\t| `${OwnableActionPaths<TResources>}.own`;\n\ntype ActionNamesWithWildcards<TActionMap> = {\n\t[actionName in keyof TActionMap]: TActionMap[actionName] extends ActionGroup<\n\t\tinfer TGroupName,\n\t\tinfer TGroupContext\n\t>\n\t\t? `.${TGroupName}${ActionNamesWithWildcards<TGroupContext[\"actions\"]>}` | `.${TGroupName}:*`\n\t\t: never;\n}[keyof TActionMap];\n\nexport type OwnableWildcardActionPaths<TResources extends ResourceMap> = {\n\t[resourceName in Extract<keyof TResources, string>]: TResources[resourceName] extends Resource<\n\t\tany,\n\t\tinfer TResourceContext\n\t>\n\t\t? TResourceContext[\"ownable\"] extends true\n\t\t\t?\n\t\t\t\t\t| `${resourceName}:*`\n\t\t\t\t\t| (TResources[resourceName][\"subResources\"] extends UnsetMarker\n\t\t\t\t\t\t\t? never\n\t\t\t\t\t\t\t: `${resourceName}.${OwnableWildcardActionPaths<TResources[resourceName][\"subResources\"]>}`)\n\t\t\t\t\t| (TResources[resourceName][\"actions\"] extends UnsetMarker\n\t\t\t\t\t\t\t? never\n\t\t\t\t\t\t\t: `${resourceName}${ActionNamesWithWildcards<TResources[resourceName][\"actions\"]>}`)\n\t\t\t: never\n\t\t: never;\n}[Extract<keyof TResources, string>];\n\nexport type WildcardActionPaths<TResources extends ResourceMap> =\n\t| {\n\t\t\t[resourceName in Extract<keyof TResources, string>]:\n\t\t\t\t| (TResources[resourceName][\"subResources\"] extends UnsetMarker\n\t\t\t\t\t\t? never\n\t\t\t\t\t\t: `${resourceName}.${WildcardActionPaths<TResources[resourceName][\"subResources\"]>}`)\n\t\t\t\t| (TResources[resourceName][\"actions\"] extends UnsetMarker\n\t\t\t\t\t\t? never\n\t\t\t\t\t\t: `${resourceName}${ActionNamesWithWildcards<TResources[resourceName][\"actions\"]>}`)\n\t\t\t\t| `${resourceName}:*`\n\t\t\t\t| `${resourceName}.*`;\n\t }[Extract<keyof TResources, string>]\n\t| `${OwnableWildcardActionPaths<TResources>}.own`;\n\ntype MergeRuleCtx<A extends RuleCtx, B extends RuleCtx> = {\n\tsubject: A[\"subject\"] extends UnsetMarker ? B[\"subject\"] : A[\"subject\"];\n\tobject: A[\"object\"] extends UnsetMarker\n\t\t? B[\"object\"]\n\t\t: A[\"object\"] extends OptionalMarker\n\t\t\t? MakeOptional<B[\"object\"]>\n\t\t\t: A[\"object\"];\n};\n\ntype ResolveAction<TParentCtx extends RuleCtx, TAction extends Action<any, any>> =\n\tTAction extends Action<infer TName, infer TActionContext>\n\t\t? Action<\n\t\t\t\tTName,\n\t\t\t\tExpand<{\n\t\t\t\t\tctx: Expand<MergeRuleCtx<TActionContext[\"ctx\"], TParentCtx>>;\n\t\t\t\t\tconfigurable: TActionContext[\"configurable\"];\n\t\t\t\t}>\n\t\t\t>\n\t\t: never;\n\ntype IndexActionGroupWithPath<\n\tTPath extends string,\n\tTActionGroup extends ActionGroup<any, any>,\n\tTParentCtx extends RuleCtx\n> = TPath extends keyof TActionGroup[\"actions\"] // Check fast path first\n\t? ResolveAction<TParentCtx, TActionGroup[\"actions\"][TPath]>\n\t: TPath extends `${infer ActionGroupName}.${infer Rest}`\n\t\t? TActionGroup[\"actions\"][ActionGroupName] extends ActionGroup<any, infer TActionGroupContext>\n\t\t\t? IndexActionGroupWithPath<\n\t\t\t\t\tRest,\n\t\t\t\t\tTActionGroup[\"actions\"][ActionGroupName],\n\t\t\t\t\tMergeRuleCtx<TActionGroupContext[\"ctx\"], TParentCtx>\n\t\t\t\t>\n\t\t\t: never\n\t\t: TPath extends `${infer ActionGroupName}:${infer ActionName}`\n\t\t\t? TActionGroup[\"actions\"][ActionGroupName] extends ActionGroup<any, any>\n\t\t\t\t? ResolveAction<TParentCtx, TActionGroup[\"actions\"][ActionGroupName][\"actions\"][ActionName]>\n\t\t\t\t: never\n\t\t\t: never;\n\ntype IndexResourceWithPath<\n\tTPath extends string,\n\tTResource extends Resource<any, any>,\n\tTParentCtx extends RuleCtx\n> = TPath extends keyof TResource[\"actions\"] // Check fast path first\n\t? TResource[\"actions\"][TPath] extends Action<any, any>\n\t\t? ResolveAction<TParentCtx, TResource[\"actions\"][TPath]>\n\t\t: never\n\t: TPath extends `${infer ResourceOrGroupName}.${infer Rest}`\n\t\t? ResourceOrGroupName extends keyof TResource[\"actions\"]\n\t\t\t? TResource[\"actions\"][ResourceOrGroupName] extends ActionGroup<\n\t\t\t\t\tany,\n\t\t\t\t\tinfer TActionGroupContext\n\t\t\t\t>\n\t\t\t\t? IndexActionGroupWithPath<\n\t\t\t\t\t\tRest,\n\t\t\t\t\t\tTResource[\"actions\"][ResourceOrGroupName],\n\t\t\t\t\t\tMergeRuleCtx<TActionGroupContext[\"ctx\"], TParentCtx>\n\t\t\t\t\t>\n\t\t\t\t: never\n\t\t\t: TResource[\"subResources\"][ResourceOrGroupName] extends Resource<any, infer TResourceContext>\n\t\t\t\t? IndexResourceWithPath<\n\t\t\t\t\t\tRest,\n\t\t\t\t\t\tTResource[\"subResources\"][ResourceOrGroupName],\n\t\t\t\t\t\tMergeRuleCtx<TResourceContext[\"ctx\"], TParentCtx>\n\t\t\t\t\t>\n\t\t\t\t: never\n\t\t: TPath extends `${infer ResourceOrGroupName}:${infer ActionName}`\n\t\t\t? TResource[\"actions\"][ResourceOrGroupName] extends ActionGroup<\n\t\t\t\t\tany,\n\t\t\t\t\tinfer TActionGroupContext\n\t\t\t\t>\n\t\t\t\t? ResolveAction<\n\t\t\t\t\t\tMergeRuleCtx<TActionGroupContext[\"ctx\"], TParentCtx>,\n\t\t\t\t\t\tTResource[\"actions\"][ResourceOrGroupName][\"actions\"][ActionName]\n\t\t\t\t\t>\n\t\t\t\t: TResource[\"subResources\"][ResourceOrGroupName] extends Resource<\n\t\t\t\t\t\t\tany,\n\t\t\t\t\t\t\tinfer TResourceContext\n\t\t\t\t\t >\n\t\t\t\t\t? ResolveAction<\n\t\t\t\t\t\t\tMergeRuleCtx<TResourceContext[\"ctx\"], TParentCtx>,\n\t\t\t\t\t\t\tTResource[\"subResources\"][ResourceOrGroupName][\"actions\"][ActionName]\n\t\t\t\t\t\t>\n\t\t\t\t\t: never\n\t\t\t: never;\n\nexport type ActionFromPath<\n\tTResources extends ResourceMap,\n\tTPath extends string\n> = TPath extends `${infer TActualPath}.own`\n\t? ActionFromPath<TResources, TActualPath>\n\t: TPath extends `${infer ResourceName extends Extract<keyof TResources, string>}.${infer Rest}`\n\t\t? TResources[ResourceName] extends Resource<any, infer TResourceContext>\n\t\t\t? IndexResourceWithPath<Rest, TResources[ResourceName], TResourceContext[\"ctx\"]>\n\t\t\t: never\n\t\t: TPath extends `${infer ResourceName extends Extract<keyof TResources, string>}:${infer ActionName}`\n\t\t\t? TResources[ResourceName] extends Resource<any, infer TResourceContext>\n\t\t\t\t? ResolveAction<TResourceContext[\"ctx\"], TResources[ResourceName][\"actions\"][ActionName]>\n\t\t\t\t: never\n\t\t\t: never;\n\ntype ActionPathsWithAction<TResources extends ResourceMap, TPaths extends string> = {\n\t[path in TPaths]: {\n\t\tpath: path;\n\t\taction: ActionFromPath<TResources, path>;\n\t};\n}[TPaths];\n\nexport type FilterActionPaths<\n\tTResources extends ResourceMap,\n\tTCtxPattern,\n\tTConfigurable extends boolean = boolean,\n\tTPathsAndActions = ActionPathsWithAction<TResources, ActionPaths<TResources>>\n> = TPathsAndActions extends { path: string; action: Action<any, infer TActionContext> }\n\t? TActionContext[\"ctx\"] extends TCtxPattern\n\t\t? TActionContext[\"configurable\"] extends TConfigurable\n\t\t\t? TPathsAndActions[\"path\"]\n\t\t\t: never\n\t\t: never\n\t: never;\n"]}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { Action, InferActionAdditionalContext, InferActionObject, InferActionRuleCtx, InferActionSubject, RuleCtx } from "./action.js";
|
|
2
|
+
import type { ResourceMap } from "./internal/collections.js";
|
|
3
|
+
import type { EmptyObject, Expand, IsOnlyEmptyObject, OptionalUndefinedFields } from "./internal/core.js";
|
|
4
|
+
import type { ActionFromPath } from "./internal/paths.js";
|
|
5
|
+
import { Specitivity, type Policy, type Rule } from "./policy.js";
|
|
6
|
+
import type { Resource } from "./resource.js";
|
|
7
|
+
type PermissionTestResult = {
|
|
8
|
+
granted: boolean;
|
|
9
|
+
specitivity: Specitivity;
|
|
10
|
+
policy: Policy<any> | null;
|
|
11
|
+
rule: Rule | null;
|
|
12
|
+
};
|
|
13
|
+
type Granted<TAction extends Action<any, any>, TCtx extends Omit<InferActionRuleCtx<TAction>, "subject"> = Omit<InferActionRuleCtx<TAction>, "subject">> = IsOnlyEmptyObject<TCtx> extends true ? {
|
|
14
|
+
granted(): boolean;
|
|
15
|
+
test(): PermissionTestResult;
|
|
16
|
+
} : {
|
|
17
|
+
granted(ctx: OptionalUndefinedFields<TCtx extends EmptyObject ? Omit<TCtx, "object"> : TCtx>): boolean;
|
|
18
|
+
test(ctx: OptionalUndefinedFields<TCtx extends EmptyObject ? Omit<TCtx, "object"> : TCtx>): PermissionTestResult;
|
|
19
|
+
};
|
|
20
|
+
type Filter<TAction extends Action<any, any>, TObjects extends InferActionObject<TAction>[] = InferActionObject<TAction>[]> = InferActionRuleCtx<TAction> extends EmptyObject ? {} : InferActionAdditionalContext<TAction> extends {} ? {
|
|
21
|
+
filter(objects: TObjects): TObjects;
|
|
22
|
+
} : {
|
|
23
|
+
filter(objects: TObjects, ctx: OptionalUndefinedFields<InferActionAdditionalContext<TAction>>): TObjects;
|
|
24
|
+
};
|
|
25
|
+
type ResolvedPermission<TActionPath extends string, TAction extends Action<any, any>> = Omit<Permission<TActionPath, TAction>, "granted" | "filter"> & Granted<TAction> & Filter<TAction>;
|
|
26
|
+
export type PermissionCtx<TAction extends Action<any, any>, TCtx extends RuleCtx = InferActionRuleCtx<TAction>> = TCtx extends EmptyObject ? Expand<Omit<TCtx, "object">> : Expand<TCtx>;
|
|
27
|
+
export type GrantedPermissionsResolver<TActionPath extends string, TAction extends Action<any, any>> = (object: InferActionObject<TAction>, permission: ResolvedPermission<TActionPath, TAction>) => string[] | Set<string>;
|
|
28
|
+
export type PermissionOptions<TActionPath extends string, TAction extends Action<any, any>> = {
|
|
29
|
+
grantedPermissions?: "all" | string[] | Set<string> | GrantedPermissionsResolver<TActionPath, TAction>;
|
|
30
|
+
checkOwnership?: (ctx: InferActionRuleCtx<TAction>) => boolean;
|
|
31
|
+
};
|
|
32
|
+
export declare class Permission<TActionPath extends string, TAction extends Action<any, any>> {
|
|
33
|
+
private _subject;
|
|
34
|
+
private _path;
|
|
35
|
+
private _resource;
|
|
36
|
+
private _action;
|
|
37
|
+
private _policies;
|
|
38
|
+
private _options;
|
|
39
|
+
private _grantedPermissionsCache;
|
|
40
|
+
constructor(subject: InferActionSubject<TAction>, path: TActionPath, resource: Resource<any, any>, action: TAction, policies: Policy<any>[], options?: PermissionOptions<TActionPath, TAction>);
|
|
41
|
+
get path(): TActionPath;
|
|
42
|
+
get resource(): Resource<any, any>;
|
|
43
|
+
get action(): TAction;
|
|
44
|
+
get policies(): Policy<any>[];
|
|
45
|
+
test(ctx?: OptionalUndefinedFields<Omit<InferActionRuleCtx<TAction>, "subject">>): PermissionTestResult;
|
|
46
|
+
granted(ctx?: OptionalUndefinedFields<Omit<InferActionRuleCtx<TAction>, "subject">>): boolean;
|
|
47
|
+
filter(objects: InferActionObject<TAction>[], ctx?: InferActionAdditionalContext<TAction>): Exclude<InferActionRuleCtx<TAction>["object"], void | undefined>[];
|
|
48
|
+
private isGranted;
|
|
49
|
+
private checkOwnership;
|
|
50
|
+
private static matchPathPattern;
|
|
51
|
+
static create<TResources extends ResourceMap, TActionPath extends string, TAction extends Action<any, any> = ActionFromPath<TResources, TActionPath>>(subject: InferActionSubject<TAction>, path: TActionPath, resource: Resource<any, any>, action: TAction, policies: Policy<TResources>[], options?: PermissionOptions<TActionPath, TAction>): ResolvedPermission<TActionPath, TAction>;
|
|
52
|
+
}
|
|
53
|
+
export {};
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { Specitivity } from "./policy.js";
|
|
2
|
+
const VoidObjectCacheKey = Symbol("void-object");
|
|
3
|
+
export class Permission {
|
|
4
|
+
_subject;
|
|
5
|
+
_path;
|
|
6
|
+
_resource;
|
|
7
|
+
_action;
|
|
8
|
+
_policies;
|
|
9
|
+
_options;
|
|
10
|
+
_grantedPermissionsCache = new Map();
|
|
11
|
+
constructor(subject, path, resource, action, policies, options) {
|
|
12
|
+
this._subject = subject;
|
|
13
|
+
this._path = path;
|
|
14
|
+
this._resource = resource;
|
|
15
|
+
this._action = action;
|
|
16
|
+
this._policies = policies;
|
|
17
|
+
this._options = options ?? {};
|
|
18
|
+
}
|
|
19
|
+
get path() {
|
|
20
|
+
return this._path;
|
|
21
|
+
}
|
|
22
|
+
get resource() {
|
|
23
|
+
return this._resource;
|
|
24
|
+
}
|
|
25
|
+
get action() {
|
|
26
|
+
return this._action;
|
|
27
|
+
}
|
|
28
|
+
get policies() {
|
|
29
|
+
return this._policies;
|
|
30
|
+
}
|
|
31
|
+
test(ctx) {
|
|
32
|
+
const fullCtx = {
|
|
33
|
+
...(ctx ?? {}),
|
|
34
|
+
subject: this._subject
|
|
35
|
+
};
|
|
36
|
+
let granted = false;
|
|
37
|
+
let specitivity = -1;
|
|
38
|
+
let grantPolicy = null;
|
|
39
|
+
let grantRule = null;
|
|
40
|
+
const ownsObject = this.checkOwnership(fullCtx);
|
|
41
|
+
const isGranted = !this._action.configurable ||
|
|
42
|
+
this.isGranted(this._path, fullCtx.object) ||
|
|
43
|
+
(ownsObject && this.isGranted(this._path + ".own", fullCtx.object));
|
|
44
|
+
for (const policy of this._policies) {
|
|
45
|
+
for (const rule of policy.rules) {
|
|
46
|
+
const match = Permission.matchPathPattern(this._path, rule.action);
|
|
47
|
+
if (!match.matches || specitivity > match.specitivity) {
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
const result = rule.predicate(fullCtx, this._action);
|
|
51
|
+
granted = result === "ifgranted" ? isGranted : result;
|
|
52
|
+
specitivity = rule.specitivity ?? match.specitivity;
|
|
53
|
+
grantPolicy = policy;
|
|
54
|
+
grantRule = rule;
|
|
55
|
+
if (specitivity === Specitivity.Exact) {
|
|
56
|
+
// This rule is an exact match, so we can stop and return early
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (specitivity === Specitivity.Exact)
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
granted,
|
|
65
|
+
specitivity,
|
|
66
|
+
policy: grantPolicy,
|
|
67
|
+
rule: grantRule
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
granted(ctx) {
|
|
71
|
+
const { granted } = this.test(ctx);
|
|
72
|
+
return granted;
|
|
73
|
+
}
|
|
74
|
+
filter(objects, ctx) {
|
|
75
|
+
return objects
|
|
76
|
+
.map((object) => ({
|
|
77
|
+
object,
|
|
78
|
+
granted: this.granted({
|
|
79
|
+
object,
|
|
80
|
+
...(ctx ?? {})
|
|
81
|
+
})
|
|
82
|
+
}))
|
|
83
|
+
.filter(({ granted }) => granted)
|
|
84
|
+
.map(({ object }) => object);
|
|
85
|
+
}
|
|
86
|
+
isGranted(path, object) {
|
|
87
|
+
if (this._options.grantedPermissions === "all") {
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
const cacheKey = object ? object : VoidObjectCacheKey;
|
|
91
|
+
let grantedPermissions;
|
|
92
|
+
if (this._grantedPermissionsCache.has(cacheKey)) {
|
|
93
|
+
grantedPermissions = this._grantedPermissionsCache.get(cacheKey);
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
if (typeof this._options.grantedPermissions === "function") {
|
|
97
|
+
grantedPermissions = new Set(this._options.grantedPermissions(object, this));
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
grantedPermissions = new Set(this._options.grantedPermissions ?? []);
|
|
101
|
+
}
|
|
102
|
+
this._grantedPermissionsCache.set(cacheKey, grantedPermissions);
|
|
103
|
+
}
|
|
104
|
+
return grantedPermissions.has(path);
|
|
105
|
+
}
|
|
106
|
+
checkOwnership(ctx) {
|
|
107
|
+
if (!this._resource.ownable) {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
if (typeof this._options.checkOwnership === "function") {
|
|
111
|
+
return this._options.checkOwnership(ctx);
|
|
112
|
+
}
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
static matchPathPattern(path, pattern) {
|
|
116
|
+
if (pattern === "*") {
|
|
117
|
+
// Any action
|
|
118
|
+
return {
|
|
119
|
+
matches: true,
|
|
120
|
+
specitivity: Specitivity.Any
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
if (pattern.endsWith(".*")) {
|
|
124
|
+
// Any action, sub-resource or action group on this resource
|
|
125
|
+
return {
|
|
126
|
+
matches: path.startsWith(pattern.slice(0, -2)),
|
|
127
|
+
specitivity: Specitivity.ActionsAndNested
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
if (pattern.endsWith(":*")) {
|
|
131
|
+
// Any action on this resource (excluding action groups)
|
|
132
|
+
return {
|
|
133
|
+
matches: path.startsWith(pattern.slice(0, -1)),
|
|
134
|
+
specitivity: Specitivity.ActionsOnly
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
if (pattern.endsWith(":*.own")) {
|
|
138
|
+
return {
|
|
139
|
+
matches: path.startsWith(pattern.slice(0, -6)) && path.endsWith(".own"),
|
|
140
|
+
specitivity: Specitivity.OwnedActionsOnly
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
// Specific action
|
|
144
|
+
return {
|
|
145
|
+
matches: path === pattern,
|
|
146
|
+
specitivity: Specitivity.Exact
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
static create(subject, path, resource, action, policies, options) {
|
|
150
|
+
return new Permission(subject, path, resource, action, policies, options);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
//# sourceMappingURL=permission.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"permission.js","sourceRoot":"","sources":["../src/permission.ts"],"names":[],"mappings":"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","sourcesContent":["/* eslint-disable @typescript-eslint/no-empty-object-type */\nimport type {\n\tAction,\n\tInferActionAdditionalContext,\n\tInferActionObject,\n\tInferActionRuleCtx,\n\tInferActionSubject,\n\tRuleCtx\n} from \"./action.js\";\nimport type { ResourceMap } from \"./internal/collections.js\";\nimport type {\n\tEmptyObject,\n\tExpand,\n\tIsOnlyEmptyObject,\n\tOptionalUndefinedFields\n} from \"./internal/core.js\";\nimport type { ActionFromPath } from \"./internal/paths.js\";\nimport { Specitivity, type Policy, type Rule } from \"./policy.js\";\nimport type { Resource } from \"./resource.js\";\n\ntype PermissionTestResult = {\n\tgranted: boolean;\n\tspecitivity: Specitivity;\n\tpolicy: Policy<any> | null;\n\trule: Rule | null;\n};\n\ntype Granted<\n\tTAction extends Action<any, any>,\n\tTCtx extends Omit<InferActionRuleCtx<TAction>, \"subject\"> = Omit<\n\t\tInferActionRuleCtx<TAction>,\n\t\t\"subject\"\n\t>\n> =\n\tIsOnlyEmptyObject<TCtx> extends true\n\t\t? {\n\t\t\t\tgranted(): boolean;\n\t\t\t\ttest(): PermissionTestResult;\n\t\t\t}\n\t\t: {\n\t\t\t\tgranted(\n\t\t\t\t\tctx: OptionalUndefinedFields<TCtx extends EmptyObject ? Omit<TCtx, \"object\"> : TCtx>\n\t\t\t\t): boolean;\n\t\t\t\ttest(\n\t\t\t\t\tctx: OptionalUndefinedFields<TCtx extends EmptyObject ? Omit<TCtx, \"object\"> : TCtx>\n\t\t\t\t): PermissionTestResult;\n\t\t\t};\n\ntype Filter<\n\tTAction extends Action<any, any>,\n\tTObjects extends InferActionObject<TAction>[] = InferActionObject<TAction>[]\n> =\n\tInferActionRuleCtx<TAction> extends EmptyObject\n\t\t? {}\n\t\t: InferActionAdditionalContext<TAction> extends {}\n\t\t\t? {\n\t\t\t\t\tfilter(objects: TObjects): TObjects;\n\t\t\t\t}\n\t\t\t: {\n\t\t\t\t\tfilter(\n\t\t\t\t\t\tobjects: TObjects,\n\t\t\t\t\t\tctx: OptionalUndefinedFields<InferActionAdditionalContext<TAction>>\n\t\t\t\t\t): TObjects;\n\t\t\t\t};\n\ntype ResolvedPermission<TActionPath extends string, TAction extends Action<any, any>> = Omit<\n\tPermission<TActionPath, TAction>,\n\t\"granted\" | \"filter\"\n> &\n\tGranted<TAction> &\n\tFilter<TAction>;\n\nexport type PermissionCtx<\n\tTAction extends Action<any, any>,\n\tTCtx extends RuleCtx = InferActionRuleCtx<TAction>\n> = TCtx extends EmptyObject ? Expand<Omit<TCtx, \"object\">> : Expand<TCtx>;\n\nexport type GrantedPermissionsResolver<\n\tTActionPath extends string,\n\tTAction extends Action<any, any>\n> = (\n\tobject: InferActionObject<TAction>,\n\tpermission: ResolvedPermission<TActionPath, TAction>\n) => string[] | Set<string>;\n\nexport type PermissionOptions<TActionPath extends string, TAction extends Action<any, any>> = {\n\tgrantedPermissions?:\n\t\t| \"all\"\n\t\t| string[]\n\t\t| Set<string>\n\t\t| GrantedPermissionsResolver<TActionPath, TAction>;\n\tcheckOwnership?: (ctx: InferActionRuleCtx<TAction>) => boolean;\n};\n\nconst VoidObjectCacheKey = Symbol(\"void-object\");\n\nexport class Permission<TActionPath extends string, TAction extends Action<any, any>> {\n\tprivate _subject: InferActionSubject<TAction>;\n\tprivate _path: TActionPath;\n\tprivate _resource: Resource<any, any>;\n\tprivate _action: TAction;\n\tprivate _policies: Policy<any>[];\n\tprivate _options: PermissionOptions<TActionPath, TAction>;\n\n\tprivate _grantedPermissionsCache: Map<any, Set<string>> = new Map();\n\n\tconstructor(\n\t\tsubject: InferActionSubject<TAction>,\n\t\tpath: TActionPath,\n\t\tresource: Resource<any, any>,\n\t\taction: TAction,\n\t\tpolicies: Policy<any>[],\n\t\toptions?: PermissionOptions<TActionPath, TAction>\n\t) {\n\t\tthis._subject = subject;\n\t\tthis._path = path;\n\t\tthis._resource = resource;\n\t\tthis._action = action;\n\t\tthis._policies = policies;\n\n\t\tthis._options = options ?? {};\n\t}\n\n\tpublic get path() {\n\t\treturn this._path;\n\t}\n\n\tpublic get resource() {\n\t\treturn this._resource;\n\t}\n\n\tpublic get action() {\n\t\treturn this._action;\n\t}\n\n\tpublic get policies() {\n\t\treturn this._policies;\n\t}\n\n\tpublic test(\n\t\tctx?: OptionalUndefinedFields<Omit<InferActionRuleCtx<TAction>, \"subject\">>\n\t): PermissionTestResult {\n\t\tconst fullCtx = {\n\t\t\t...(ctx ?? {}),\n\t\t\tsubject: this._subject\n\t\t} as unknown as InferActionRuleCtx<TAction>;\n\n\t\tlet granted = false;\n\t\tlet specitivity = -1;\n\t\tlet grantPolicy: Policy<any> | null = null;\n\t\tlet grantRule: Rule | null = null;\n\n\t\tconst ownsObject = this.checkOwnership(fullCtx);\n\t\tconst isGranted =\n\t\t\t!this._action.configurable ||\n\t\t\tthis.isGranted(this._path, fullCtx.object) ||\n\t\t\t(ownsObject && this.isGranted(this._path + \".own\", fullCtx.object));\n\n\t\tfor (const policy of this._policies) {\n\t\t\tfor (const rule of policy.rules) {\n\t\t\t\tconst match = Permission.matchPathPattern(this._path, rule.action);\n\t\t\t\tif (!match.matches || specitivity > match.specitivity) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst result = rule.predicate(fullCtx, this._action);\n\t\t\t\tgranted = result === \"ifgranted\" ? isGranted : result;\n\t\t\t\tspecitivity = rule.specitivity ?? match.specitivity;\n\t\t\t\tgrantPolicy = policy;\n\t\t\t\tgrantRule = rule;\n\n\t\t\t\tif (specitivity === Specitivity.Exact) {\n\t\t\t\t\t// This rule is an exact match, so we can stop and return early\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (specitivity === Specitivity.Exact) break;\n\t\t}\n\n\t\treturn {\n\t\t\tgranted,\n\t\t\tspecitivity,\n\t\t\tpolicy: grantPolicy,\n\t\t\trule: grantRule\n\t\t};\n\t}\n\n\tpublic granted(ctx?: OptionalUndefinedFields<Omit<InferActionRuleCtx<TAction>, \"subject\">>) {\n\t\tconst { granted } = this.test(ctx);\n\t\treturn granted;\n\t}\n\n\tpublic filter(\n\t\tobjects: InferActionObject<TAction>[],\n\t\tctx?: InferActionAdditionalContext<TAction>\n\t) {\n\t\treturn objects\n\t\t\t.map((object) => ({\n\t\t\t\tobject,\n\t\t\t\tgranted: this.granted({\n\t\t\t\t\tobject,\n\t\t\t\t\t...(ctx ?? {})\n\t\t\t\t} as any)\n\t\t\t}))\n\t\t\t.filter(({ granted }) => granted)\n\t\t\t.map(({ object }) => object);\n\t}\n\n\tprivate isGranted(path: string, object: InferActionObject<TAction>) {\n\t\tif (this._options.grantedPermissions === \"all\") {\n\t\t\treturn true;\n\t\t}\n\n\t\tconst cacheKey: any = object ? object : VoidObjectCacheKey;\n\t\tlet grantedPermissions: Set<string>;\n\t\tif (this._grantedPermissionsCache.has(cacheKey)) {\n\t\t\tgrantedPermissions = this._grantedPermissionsCache.get(cacheKey)!;\n\t\t} else {\n\t\t\tif (typeof this._options.grantedPermissions === \"function\") {\n\t\t\t\tgrantedPermissions = new Set(\n\t\t\t\t\tthis._options.grantedPermissions(\n\t\t\t\t\t\tobject,\n\t\t\t\t\t\tthis as unknown as ResolvedPermission<TActionPath, TAction>\n\t\t\t\t\t)\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tgrantedPermissions = new Set(this._options.grantedPermissions ?? []);\n\t\t\t}\n\n\t\t\tthis._grantedPermissionsCache.set(cacheKey, grantedPermissions);\n\t\t}\n\n\t\treturn grantedPermissions.has(path);\n\t}\n\n\tprivate checkOwnership(ctx: InferActionRuleCtx<TAction>) {\n\t\tif (!this._resource.ownable) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (typeof this._options.checkOwnership === \"function\") {\n\t\t\treturn this._options.checkOwnership(ctx);\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tprivate static matchPathPattern(path: string, pattern: string) {\n\t\tif (pattern === \"*\") {\n\t\t\t// Any action\n\t\t\treturn {\n\t\t\t\tmatches: true,\n\t\t\t\tspecitivity: Specitivity.Any\n\t\t\t};\n\t\t}\n\n\t\tif (pattern.endsWith(\".*\")) {\n\t\t\t// Any action, sub-resource or action group on this resource\n\t\t\treturn {\n\t\t\t\tmatches: path.startsWith(pattern.slice(0, -2)),\n\t\t\t\tspecitivity: Specitivity.ActionsAndNested\n\t\t\t};\n\t\t}\n\n\t\tif (pattern.endsWith(\":*\")) {\n\t\t\t// Any action on this resource (excluding action groups)\n\t\t\treturn {\n\t\t\t\tmatches: path.startsWith(pattern.slice(0, -1)),\n\t\t\t\tspecitivity: Specitivity.ActionsOnly\n\t\t\t};\n\t\t}\n\n\t\tif (pattern.endsWith(\":*.own\")) {\n\t\t\treturn {\n\t\t\t\tmatches: path.startsWith(pattern.slice(0, -6)) && path.endsWith(\".own\"),\n\t\t\t\tspecitivity: Specitivity.OwnedActionsOnly\n\t\t\t};\n\t\t}\n\n\t\t// Specific action\n\t\treturn {\n\t\t\tmatches: path === pattern,\n\t\t\tspecitivity: Specitivity.Exact\n\t\t};\n\t}\n\n\tpublic static create<\n\t\tTResources extends ResourceMap,\n\t\tTActionPath extends string,\n\t\tTAction extends Action<any, any> = ActionFromPath<TResources, TActionPath>\n\t>(\n\t\tsubject: InferActionSubject<TAction>,\n\t\tpath: TActionPath,\n\t\tresource: Resource<any, any>,\n\t\taction: TAction,\n\t\tpolicies: Policy<TResources>[],\n\t\toptions?: PermissionOptions<TActionPath, TAction>\n\t) {\n\t\treturn new Permission(\n\t\t\tsubject,\n\t\t\tpath,\n\t\t\tresource,\n\t\t\taction,\n\t\t\tpolicies,\n\t\t\toptions\n\t\t) as unknown as ResolvedPermission<TActionPath, TAction>;\n\t}\n}\n"]}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { Action, RuleCtx, InferActionRuleCtx, ActionContext } from "./action.js";
|
|
2
|
+
import type { ResourceMap } from "./internal/collections.js";
|
|
3
|
+
import type { ActionFromPath, ActionPaths, OwnableActionPaths, WildcardActionPaths } from "./internal/paths.js";
|
|
4
|
+
export type MatchCtx = {
|
|
5
|
+
subject: any;
|
|
6
|
+
};
|
|
7
|
+
export type PolicyMatchPredicate = (ctx: MatchCtx) => boolean;
|
|
8
|
+
type InferRulePredicateReturn<TAction> = TAction extends Action<any, infer TContext extends ActionContext> ? TContext["configurable"] extends true ? "ifgranted" | boolean : boolean : boolean;
|
|
9
|
+
type SimplifyPredicate<T> = T extends (ctx: infer TCtx) => infer TReturn ? (ctx: TCtx) => TReturn : T;
|
|
10
|
+
export type RulePredicate<TResources extends ResourceMap, TActionPath extends string> = (ctx: InferActionRuleCtx<ActionFromPath<TResources, TActionPath>>, action: ActionFromPath<TResources, TActionPath>) => InferRulePredicateReturn<ActionFromPath<TResources, TActionPath>>;
|
|
11
|
+
type AnyRulePredicate = (ctx: RuleCtx, action: Action<any, any>) => boolean | "ifgranted";
|
|
12
|
+
export type OwnershipPredicate<TResources extends ResourceMap, TActionPath extends string> = (ctx: InferActionRuleCtx<ActionFromPath<TResources, TActionPath>>) => boolean;
|
|
13
|
+
export declare enum Specitivity {
|
|
14
|
+
Any = -1,
|
|
15
|
+
ActionsAndNested = 0,
|
|
16
|
+
ActionsOnly = 1,
|
|
17
|
+
OwnedActionsOnly = 2,
|
|
18
|
+
Exact = 3
|
|
19
|
+
}
|
|
20
|
+
export type Rule = {
|
|
21
|
+
action: string;
|
|
22
|
+
predicate: AnyRulePredicate;
|
|
23
|
+
specitivity?: Specitivity;
|
|
24
|
+
};
|
|
25
|
+
export declare class Policy<TResources extends ResourceMap> {
|
|
26
|
+
private _name;
|
|
27
|
+
private _match;
|
|
28
|
+
private _rules;
|
|
29
|
+
constructor();
|
|
30
|
+
get name(): string | null;
|
|
31
|
+
setName(name: string): this;
|
|
32
|
+
get match(): PolicyMatchPredicate;
|
|
33
|
+
setMatch(predicate: PolicyMatchPredicate): this;
|
|
34
|
+
get rules(): Rule[];
|
|
35
|
+
allowAll({ force }?: {
|
|
36
|
+
force?: boolean;
|
|
37
|
+
}): this;
|
|
38
|
+
denyAll({ force }?: {
|
|
39
|
+
force?: boolean;
|
|
40
|
+
}): this;
|
|
41
|
+
allow<TActionPath extends WildcardActionPaths<TResources>>(action: TActionPath, predicate?: AnyRulePredicate): this;
|
|
42
|
+
allow<TActionPath extends ActionPaths<TResources>>(action: TActionPath, predicate: SimplifyPredicate<RulePredicate<TResources, TActionPath>>): this;
|
|
43
|
+
allowOwn<TActionPath extends OwnableActionPaths<TResources>>(action: TActionPath, checkOwnership: SimplifyPredicate<OwnershipPredicate<TResources, TActionPath>>, predicate?: SimplifyPredicate<RulePredicate<TResources, TActionPath>>): this;
|
|
44
|
+
deny<TActionPath extends WildcardActionPaths<TResources>>(action: TActionPath): this;
|
|
45
|
+
deny<TActionPath extends ActionPaths<TResources>>(action: TActionPath): this;
|
|
46
|
+
denyOwn<TActionPath extends OwnableActionPaths<TResources>>(action: TActionPath, checkOwnership: SimplifyPredicate<OwnershipPredicate<TResources, TActionPath>>, otherPredicate?: SimplifyPredicate<RulePredicate<TResources, TActionPath>>): this;
|
|
47
|
+
private pushRule;
|
|
48
|
+
}
|
|
49
|
+
export {};
|
package/build/policy.js
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { ABAC } from "./abac.js";
|
|
2
|
+
export var Specitivity;
|
|
3
|
+
(function (Specitivity) {
|
|
4
|
+
Specitivity[Specitivity["Any"] = -1] = "Any";
|
|
5
|
+
Specitivity[Specitivity["ActionsAndNested"] = 0] = "ActionsAndNested";
|
|
6
|
+
Specitivity[Specitivity["ActionsOnly"] = 1] = "ActionsOnly";
|
|
7
|
+
Specitivity[Specitivity["OwnedActionsOnly"] = 2] = "OwnedActionsOnly";
|
|
8
|
+
Specitivity[Specitivity["Exact"] = 3] = "Exact";
|
|
9
|
+
})(Specitivity || (Specitivity = {}));
|
|
10
|
+
export class Policy {
|
|
11
|
+
_name = null;
|
|
12
|
+
_match = ABAC.Filter.allow();
|
|
13
|
+
_rules = [];
|
|
14
|
+
constructor() { }
|
|
15
|
+
get name() {
|
|
16
|
+
return this._name;
|
|
17
|
+
}
|
|
18
|
+
setName(name) {
|
|
19
|
+
this._name = name;
|
|
20
|
+
return this;
|
|
21
|
+
}
|
|
22
|
+
get match() {
|
|
23
|
+
return this._match;
|
|
24
|
+
}
|
|
25
|
+
setMatch(predicate) {
|
|
26
|
+
this._match = predicate;
|
|
27
|
+
return this;
|
|
28
|
+
}
|
|
29
|
+
get rules() {
|
|
30
|
+
return this._rules;
|
|
31
|
+
}
|
|
32
|
+
allowAll({ force = false } = {}) {
|
|
33
|
+
this.pushRule("*", ABAC.Filter.allow(), force ? Specitivity.Exact : undefined);
|
|
34
|
+
return this;
|
|
35
|
+
}
|
|
36
|
+
denyAll({ force = false } = {}) {
|
|
37
|
+
this.pushRule("*", ABAC.Filter.deny(), force ? Specitivity.Exact : undefined);
|
|
38
|
+
return this;
|
|
39
|
+
}
|
|
40
|
+
allow(action, predicate) {
|
|
41
|
+
this.pushRule(action, predicate ?? ABAC.Filter.allow());
|
|
42
|
+
return this;
|
|
43
|
+
}
|
|
44
|
+
allowOwn(action, checkOwnership, predicate) {
|
|
45
|
+
this.pushRule(`${action}.own`, ABAC.Filter.allow());
|
|
46
|
+
this.pushRule(action, (ctx, action) => {
|
|
47
|
+
if (!checkOwnership(ctx)) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
return (predicate ?? ABAC.Filter.allow())(ctx, action);
|
|
51
|
+
});
|
|
52
|
+
return this;
|
|
53
|
+
}
|
|
54
|
+
deny(action) {
|
|
55
|
+
this.pushRule(action, ABAC.Filter.deny());
|
|
56
|
+
return this;
|
|
57
|
+
}
|
|
58
|
+
denyOwn(action, checkOwnership, otherPredicate) {
|
|
59
|
+
this.pushRule(`${action}.own`, ABAC.Filter.deny());
|
|
60
|
+
this.pushRule(action, (ctx, action) => {
|
|
61
|
+
if (checkOwnership(ctx)) {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
return (otherPredicate ?? ABAC.Filter.allow())(ctx, action);
|
|
65
|
+
});
|
|
66
|
+
return this;
|
|
67
|
+
}
|
|
68
|
+
pushRule(action, predicate, specitivity) {
|
|
69
|
+
this._rules.push({ action, predicate, specitivity });
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=policy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy.js","sourceRoot":"","sources":["../src/policy.ts"],"names":[],"mappings":"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","sourcesContent":["import { ABAC } from \"./abac.js\";\nimport type { Action, RuleCtx, InferActionRuleCtx, ActionContext } from \"./action.js\";\nimport type { ResourceMap } from \"./internal/collections.js\";\nimport type {\n\tActionFromPath,\n\tActionPaths,\n\tOwnableActionPaths,\n\tWildcardActionPaths\n} from \"./internal/paths.js\";\n\nexport type MatchCtx = {\n\tsubject: any;\n};\n\nexport type PolicyMatchPredicate = (ctx: MatchCtx) => boolean;\n\ntype InferRulePredicateReturn<TAction> =\n\tTAction extends Action<any, infer TContext extends ActionContext>\n\t\t? TContext[\"configurable\"] extends true\n\t\t\t? \"ifgranted\" | boolean\n\t\t\t: boolean\n\t\t: boolean;\n\ntype SimplifyPredicate<T> = T extends (ctx: infer TCtx) => infer TReturn\n\t? (ctx: TCtx) => TReturn\n\t: T;\n\nexport type RulePredicate<TResources extends ResourceMap, TActionPath extends string> = (\n\tctx: InferActionRuleCtx<ActionFromPath<TResources, TActionPath>>,\n\taction: ActionFromPath<TResources, TActionPath>\n) => InferRulePredicateReturn<ActionFromPath<TResources, TActionPath>>;\n\ntype AnyRulePredicate = (ctx: RuleCtx, action: Action<any, any>) => boolean | \"ifgranted\";\n\nexport type OwnershipPredicate<TResources extends ResourceMap, TActionPath extends string> = (\n\tctx: InferActionRuleCtx<ActionFromPath<TResources, TActionPath>>\n) => boolean;\n\nexport enum Specitivity {\n\tAny = -1,\n\tActionsAndNested,\n\tActionsOnly,\n\tOwnedActionsOnly,\n\tExact\n}\n\nexport type Rule = {\n\taction: string;\n\tpredicate: AnyRulePredicate;\n\tspecitivity?: Specitivity;\n};\n\nexport class Policy<TResources extends ResourceMap> {\n\tprivate _name: string | null = null;\n\tprivate _match: PolicyMatchPredicate = ABAC.Filter.allow();\n\tprivate _rules: Rule[] = [];\n\n\tconstructor() {}\n\n\tpublic get name() {\n\t\treturn this._name;\n\t}\n\n\tpublic setName(name: string) {\n\t\tthis._name = name;\n\t\treturn this;\n\t}\n\n\tpublic get match() {\n\t\treturn this._match;\n\t}\n\n\tpublic setMatch(predicate: PolicyMatchPredicate) {\n\t\tthis._match = predicate;\n\t\treturn this;\n\t}\n\n\tpublic get rules() {\n\t\treturn this._rules;\n\t}\n\n\tpublic allowAll({ force = false }: { force?: boolean } = {}) {\n\t\tthis.pushRule(\"*\", ABAC.Filter.allow(), force ? Specitivity.Exact : undefined);\n\t\treturn this;\n\t}\n\n\tpublic denyAll({ force = false }: { force?: boolean } = {}) {\n\t\tthis.pushRule(\"*\", ABAC.Filter.deny(), force ? Specitivity.Exact : undefined);\n\t\treturn this;\n\t}\n\n\tpublic allow<TActionPath extends WildcardActionPaths<TResources>>(\n\t\taction: TActionPath,\n\t\tpredicate?: AnyRulePredicate\n\t): this;\n\tpublic allow<TActionPath extends ActionPaths<TResources>>(\n\t\taction: TActionPath,\n\t\tpredicate: SimplifyPredicate<RulePredicate<TResources, TActionPath>>\n\t): this;\n\tpublic allow(action: string, predicate?: any) {\n\t\tthis.pushRule(action, predicate ?? ABAC.Filter.allow());\n\t\treturn this;\n\t}\n\n\tpublic allowOwn<TActionPath extends OwnableActionPaths<TResources>>(\n\t\taction: TActionPath,\n\t\tcheckOwnership: SimplifyPredicate<OwnershipPredicate<TResources, TActionPath>>,\n\t\tpredicate?: SimplifyPredicate<RulePredicate<TResources, TActionPath>>\n\t) {\n\t\tthis.pushRule(`${action}.own`, ABAC.Filter.allow());\n\t\tthis.pushRule(action, (ctx: any, action: any) => {\n\t\t\tif (!checkOwnership(ctx)) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\treturn (predicate ?? ABAC.Filter.allow())(ctx, action);\n\t\t});\n\t\treturn this;\n\t}\n\n\tpublic deny<TActionPath extends WildcardActionPaths<TResources>>(action: TActionPath): this;\n\tpublic deny<TActionPath extends ActionPaths<TResources>>(action: TActionPath): this;\n\tpublic deny(action: string) {\n\t\tthis.pushRule(action, ABAC.Filter.deny());\n\t\treturn this;\n\t}\n\n\tpublic denyOwn<TActionPath extends OwnableActionPaths<TResources>>(\n\t\taction: TActionPath,\n\t\tcheckOwnership: SimplifyPredicate<OwnershipPredicate<TResources, TActionPath>>,\n\t\totherPredicate?: SimplifyPredicate<RulePredicate<TResources, TActionPath>>\n\t) {\n\t\tthis.pushRule(`${action}.own`, ABAC.Filter.deny());\n\t\tthis.pushRule(action, (ctx: any, action: any) => {\n\t\t\tif (checkOwnership(ctx)) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\treturn (otherPredicate ?? ABAC.Filter.allow())(ctx, action);\n\t\t});\n\t\treturn this;\n\t}\n\n\tprivate pushRule(action: string, predicate: AnyRulePredicate, specitivity?: Specitivity) {\n\t\tthis._rules.push({ action, predicate, specitivity });\n\t}\n}\n"]}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
declare function allowPredicate(): boolean;
|
|
2
|
+
declare function denyPredicate(): boolean;
|
|
3
|
+
declare function ifgrantedPredicate(): "ifgranted";
|
|
4
|
+
export declare abstract class PolicyFilters {
|
|
5
|
+
static allow(): typeof allowPredicate;
|
|
6
|
+
static deny(): typeof denyPredicate;
|
|
7
|
+
static ifgranted(): typeof ifgrantedPredicate;
|
|
8
|
+
}
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
function allowPredicate() {
|
|
2
|
+
return true;
|
|
3
|
+
}
|
|
4
|
+
function denyPredicate() {
|
|
5
|
+
return false;
|
|
6
|
+
}
|
|
7
|
+
function ifgrantedPredicate() {
|
|
8
|
+
return "ifgranted";
|
|
9
|
+
}
|
|
10
|
+
export class PolicyFilters {
|
|
11
|
+
static allow() {
|
|
12
|
+
return allowPredicate;
|
|
13
|
+
}
|
|
14
|
+
static deny() {
|
|
15
|
+
return denyPredicate;
|
|
16
|
+
}
|
|
17
|
+
static ifgranted() {
|
|
18
|
+
return ifgrantedPredicate;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=policyFilters.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policyFilters.js","sourceRoot":"","sources":["../src/policyFilters.ts"],"names":[],"mappings":"AAAA,SAAS,cAAc;IACtB,OAAO,IAAI,CAAC;AACb,CAAC;AAED,SAAS,aAAa;IACrB,OAAO,KAAK,CAAC;AACd,CAAC;AAED,SAAS,kBAAkB;IAC1B,OAAO,WAAoB,CAAC;AAC7B,CAAC;AAED,MAAM,OAAgB,aAAa;IAC3B,MAAM,CAAC,KAAK;QAClB,OAAO,cAAc,CAAC;IACvB,CAAC;IAEM,MAAM,CAAC,IAAI;QACjB,OAAO,aAAa,CAAC;IACtB,CAAC;IAEM,MAAM,CAAC,SAAS;QACtB,OAAO,kBAAkB,CAAC;IAC3B,CAAC;CACD","sourcesContent":["function allowPredicate() {\n\treturn true;\n}\n\nfunction denyPredicate() {\n\treturn false;\n}\n\nfunction ifgrantedPredicate() {\n\treturn \"ifgranted\" as const;\n}\n\nexport abstract class PolicyFilters {\n\tpublic static allow() {\n\t\treturn allowPredicate;\n\t}\n\n\tpublic static deny() {\n\t\treturn denyPredicate;\n\t}\n\n\tpublic static ifgranted() {\n\t\treturn ifgrantedPredicate;\n\t}\n}\n"]}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { type Action, type RuleCtx } from "./action.js";
|
|
2
|
+
import type { ActionGroup } from "./actionGroup.js";
|
|
3
|
+
import type { ActionMap, ResourceMap, ToActionMap, UnionToResourceMap } from "./internal/collections.js";
|
|
4
|
+
import type { Expand, UnsetMarker } from "./internal/core.js";
|
|
5
|
+
export type ResourceContext = {
|
|
6
|
+
ctx: RuleCtx;
|
|
7
|
+
actions: ActionMap | UnsetMarker;
|
|
8
|
+
subResources: ResourceMap | UnsetMarker;
|
|
9
|
+
ownable: boolean;
|
|
10
|
+
};
|
|
11
|
+
export type AnyResourceContext = {
|
|
12
|
+
ctx: RuleCtx;
|
|
13
|
+
actions: ActionMap;
|
|
14
|
+
subResources: ResourceMap;
|
|
15
|
+
ownable: boolean;
|
|
16
|
+
};
|
|
17
|
+
export declare class Resource<TName extends string, TContext extends ResourceContext = AnyResourceContext> {
|
|
18
|
+
private _name;
|
|
19
|
+
private _title;
|
|
20
|
+
private _description;
|
|
21
|
+
private _ownable;
|
|
22
|
+
private _ownConfigurable;
|
|
23
|
+
private _actions;
|
|
24
|
+
private _subResources;
|
|
25
|
+
constructor(name: TName);
|
|
26
|
+
get name(): TName;
|
|
27
|
+
get title(): string | null;
|
|
28
|
+
setTitle(title: string): this;
|
|
29
|
+
get description(): string | null;
|
|
30
|
+
setDescription(description: string): this;
|
|
31
|
+
get ownable(): boolean;
|
|
32
|
+
get ownConfigurable(): boolean;
|
|
33
|
+
setOwnable<TOwnable extends boolean>(ownable: TOwnable, configurable?: boolean): Resource<TName, {
|
|
34
|
+
ctx: TContext["ctx"];
|
|
35
|
+
actions: TContext["actions"];
|
|
36
|
+
subResources: TContext["subResources"];
|
|
37
|
+
ownable: TOwnable;
|
|
38
|
+
}>;
|
|
39
|
+
withSubject<TSubject>(): Resource<TName, {
|
|
40
|
+
ctx: Expand<Omit<TContext["ctx"], "subject"> & {
|
|
41
|
+
subject: TSubject;
|
|
42
|
+
}>;
|
|
43
|
+
actions: TContext["actions"];
|
|
44
|
+
subResources: TContext["subResources"];
|
|
45
|
+
ownable: TContext["ownable"];
|
|
46
|
+
}>;
|
|
47
|
+
withObject<TObject>(): Resource<TName, {
|
|
48
|
+
ctx: Expand<Omit<TContext["ctx"], "object"> & {
|
|
49
|
+
object: TObject;
|
|
50
|
+
}>;
|
|
51
|
+
actions: TContext["actions"];
|
|
52
|
+
subResources: TContext["subResources"];
|
|
53
|
+
ownable: TContext["ownable"];
|
|
54
|
+
}>;
|
|
55
|
+
withAdditionalContext<TAdditionalContext extends object>(): Resource<TName, {
|
|
56
|
+
ctx: TContext["ctx"] & TAdditionalContext;
|
|
57
|
+
actions: TContext["actions"];
|
|
58
|
+
subResources: TContext["subResources"];
|
|
59
|
+
ownable: TContext["ownable"];
|
|
60
|
+
}>;
|
|
61
|
+
get actions(): TContext["actions"];
|
|
62
|
+
setActions<TActions extends Action<any, any> | ActionGroup<any, any>>(actions: TActions[]): Resource<TName, {
|
|
63
|
+
ctx: TContext["ctx"];
|
|
64
|
+
actions: Expand<ToActionMap<TActions>>;
|
|
65
|
+
subResources: TContext["subResources"];
|
|
66
|
+
ownable: TContext["ownable"];
|
|
67
|
+
}>;
|
|
68
|
+
get subResources(): TContext["subResources"];
|
|
69
|
+
setSubResources<TResources extends Resource<any, any>>(resources: TResources[]): Resource<TName, {
|
|
70
|
+
ctx: TContext["ctx"];
|
|
71
|
+
actions: TContext["actions"];
|
|
72
|
+
subResources: Expand<UnionToResourceMap<TResources>>;
|
|
73
|
+
ownable: TContext["ownable"];
|
|
74
|
+
}>;
|
|
75
|
+
}
|