@owlmeans/iam 0.1.5 → 0.1.7
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/build/errors.d.ts +8 -0
- package/build/errors.d.ts.map +1 -1
- package/build/errors.js +16 -0
- package/build/errors.js.map +1 -1
- package/build/index.d.ts +1 -0
- package/build/index.d.ts.map +1 -1
- package/build/index.js +1 -0
- package/build/index.js.map +1 -1
- package/build/types.d.ts +41 -0
- package/build/types.d.ts.map +1 -1
- package/build/utils.d.ts +13 -0
- package/build/utils.d.ts.map +1 -0
- package/build/utils.js +8 -0
- package/build/utils.js.map +1 -0
- package/package.json +5 -4
- package/src/errors.ts +20 -0
- package/src/index.ts +1 -0
- package/src/types.ts +62 -0
- package/src/utils.ts +22 -0
- package/tests/utils.spec.ts +39 -0
package/build/errors.d.ts
CHANGED
|
@@ -11,4 +11,12 @@ export declare class IamResourceError extends IamError {
|
|
|
11
11
|
static typeName: string;
|
|
12
12
|
constructor(message?: string);
|
|
13
13
|
}
|
|
14
|
+
export declare class IamGrantError extends IamError {
|
|
15
|
+
static typeName: string;
|
|
16
|
+
constructor(message?: string);
|
|
17
|
+
}
|
|
18
|
+
export declare class IamUnsupported extends IamError {
|
|
19
|
+
static typeName: string;
|
|
20
|
+
constructor(message?: string);
|
|
21
|
+
}
|
|
14
22
|
//# sourceMappingURL=errors.d.ts.map
|
package/build/errors.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAEhD,qBAAa,QAAS,SAAQ,cAAc;IAC1C,OAAuB,QAAQ,SAAkC;gBAErD,OAAO,SAAU;CAI9B;AAED,qBAAa,cAAe,SAAQ,QAAQ;IAC1C,OAAuB,QAAQ,SAAwC;gBAE3D,OAAO,SAAU;CAI9B;AAED,qBAAa,gBAAiB,SAAQ,QAAQ;IAC5C,OAAuB,QAAQ,SAA0C;gBAE7D,OAAO,SAAU;CAI9B"}
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAEhD,qBAAa,QAAS,SAAQ,cAAc;IAC1C,OAAuB,QAAQ,SAAkC;gBAErD,OAAO,SAAU;CAI9B;AAED,qBAAa,cAAe,SAAQ,QAAQ;IAC1C,OAAuB,QAAQ,SAAwC;gBAE3D,OAAO,SAAU;CAI9B;AAED,qBAAa,gBAAiB,SAAQ,QAAQ;IAC5C,OAAuB,QAAQ,SAA0C;gBAE7D,OAAO,SAAU;CAI9B;AAED,qBAAa,aAAc,SAAQ,QAAQ;IACzC,OAAuB,QAAQ,SAAuC;gBAE1D,OAAO,SAAU;CAI9B;AAED,qBAAa,cAAe,SAAQ,QAAQ;IAC1C,OAAuB,QAAQ,SAA6C;gBAEhE,OAAO,SAAU;CAI9B"}
|
package/build/errors.js
CHANGED
|
@@ -20,7 +20,23 @@ export class IamResourceError extends IamError {
|
|
|
20
20
|
this.type = IamResourceError.typeName;
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
|
+
export class IamGrantError extends IamError {
|
|
24
|
+
static typeName = `IamGrant${ResilientError.typeName}`;
|
|
25
|
+
constructor(message = 'error') {
|
|
26
|
+
super(`grant:${message}`);
|
|
27
|
+
this.type = IamGrantError.typeName;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
export class IamUnsupported extends IamError {
|
|
31
|
+
static typeName = `IamUnsupported${ResilientError.typeName}`;
|
|
32
|
+
constructor(message = 'error') {
|
|
33
|
+
super(`unsupported:${message}`);
|
|
34
|
+
this.type = IamUnsupported.typeName;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
23
37
|
ResilientError.registerErrorClass(IamError);
|
|
24
38
|
ResilientError.registerErrorClass(IamClientError);
|
|
25
39
|
ResilientError.registerErrorClass(IamResourceError);
|
|
40
|
+
ResilientError.registerErrorClass(IamGrantError);
|
|
41
|
+
ResilientError.registerErrorClass(IamUnsupported);
|
|
26
42
|
//# sourceMappingURL=errors.js.map
|
package/build/errors.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAEhD,MAAM,OAAO,QAAS,SAAQ,cAAc;IACnC,MAAM,CAAU,QAAQ,GAAG,MAAM,cAAc,CAAC,QAAQ,EAAE,CAAA;IAEjE,YAAY,OAAO,GAAG,OAAO;QAC3B,KAAK,CAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,OAAO,EAAE,CAAC,CAAA;QAC1C,IAAI,CAAC,IAAI,GAAG,QAAQ,CAAC,QAAQ,CAAA;IAC/B,CAAC;;AAGH,MAAM,OAAO,cAAe,SAAQ,QAAQ;IACnC,MAAM,CAAU,QAAQ,GAAG,YAAY,cAAc,CAAC,QAAQ,EAAE,CAAA;IAEvE,YAAY,OAAO,GAAG,OAAO;QAC3B,KAAK,CAAC,UAAU,OAAO,EAAE,CAAC,CAAA;QAC1B,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC,QAAQ,CAAA;IACrC,CAAC;;AAGH,MAAM,OAAO,gBAAiB,SAAQ,QAAQ;IACrC,MAAM,CAAU,QAAQ,GAAG,cAAc,cAAc,CAAC,QAAQ,EAAE,CAAA;IAEzE,YAAY,OAAO,GAAG,OAAO;QAC3B,KAAK,CAAC,YAAY,OAAO,EAAE,CAAC,CAAA;QAC5B,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC,QAAQ,CAAA;IACvC,CAAC;;AAGH,cAAc,CAAC,kBAAkB,CAAC,QAAQ,CAAC,CAAA;AAC3C,cAAc,CAAC,kBAAkB,CAAC,cAAc,CAAC,CAAA;AACjD,cAAc,CAAC,kBAAkB,CAAC,gBAAgB,CAAC,CAAA"}
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAEhD,MAAM,OAAO,QAAS,SAAQ,cAAc;IACnC,MAAM,CAAU,QAAQ,GAAG,MAAM,cAAc,CAAC,QAAQ,EAAE,CAAA;IAEjE,YAAY,OAAO,GAAG,OAAO;QAC3B,KAAK,CAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,OAAO,EAAE,CAAC,CAAA;QAC1C,IAAI,CAAC,IAAI,GAAG,QAAQ,CAAC,QAAQ,CAAA;IAC/B,CAAC;;AAGH,MAAM,OAAO,cAAe,SAAQ,QAAQ;IACnC,MAAM,CAAU,QAAQ,GAAG,YAAY,cAAc,CAAC,QAAQ,EAAE,CAAA;IAEvE,YAAY,OAAO,GAAG,OAAO;QAC3B,KAAK,CAAC,UAAU,OAAO,EAAE,CAAC,CAAA;QAC1B,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC,QAAQ,CAAA;IACrC,CAAC;;AAGH,MAAM,OAAO,gBAAiB,SAAQ,QAAQ;IACrC,MAAM,CAAU,QAAQ,GAAG,cAAc,cAAc,CAAC,QAAQ,EAAE,CAAA;IAEzE,YAAY,OAAO,GAAG,OAAO;QAC3B,KAAK,CAAC,YAAY,OAAO,EAAE,CAAC,CAAA;QAC5B,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC,QAAQ,CAAA;IACvC,CAAC;;AAGH,MAAM,OAAO,aAAc,SAAQ,QAAQ;IAClC,MAAM,CAAU,QAAQ,GAAG,WAAW,cAAc,CAAC,QAAQ,EAAE,CAAA;IAEtE,YAAY,OAAO,GAAG,OAAO;QAC3B,KAAK,CAAC,SAAS,OAAO,EAAE,CAAC,CAAA;QACzB,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC,QAAQ,CAAA;IACpC,CAAC;;AAGH,MAAM,OAAO,cAAe,SAAQ,QAAQ;IACnC,MAAM,CAAU,QAAQ,GAAG,iBAAiB,cAAc,CAAC,QAAQ,EAAE,CAAA;IAE5E,YAAY,OAAO,GAAG,OAAO;QAC3B,KAAK,CAAC,eAAe,OAAO,EAAE,CAAC,CAAA;QAC/B,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC,QAAQ,CAAA;IACrC,CAAC;;AAGH,cAAc,CAAC,kBAAkB,CAAC,QAAQ,CAAC,CAAA;AAC3C,cAAc,CAAC,kBAAkB,CAAC,cAAc,CAAC,CAAA;AACjD,cAAc,CAAC,kBAAkB,CAAC,gBAAgB,CAAC,CAAA;AACnD,cAAc,CAAC,kBAAkB,CAAC,aAAa,CAAC,CAAA;AAChD,cAAc,CAAC,kBAAkB,CAAC,cAAc,CAAC,CAAA"}
|
package/build/index.d.ts
CHANGED
package/build/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,mBAAmB,YAAY,CAAA;AAC/B,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,mBAAmB,YAAY,CAAA;AAC/B,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,YAAY,CAAA"}
|
package/build/index.js
CHANGED
package/build/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,YAAY,CAAA"}
|
package/build/types.d.ts
CHANGED
|
@@ -15,11 +15,38 @@ export interface IamCredentialsPair {
|
|
|
15
15
|
export interface IamPermissionArgs {
|
|
16
16
|
/** Action name. When absent the permission is unscoped (project-wide). */
|
|
17
17
|
permission?: string;
|
|
18
|
+
/** Declares that grants of this permission may be bound to specific resource ids. */
|
|
19
|
+
resourceScoped?: boolean;
|
|
20
|
+
/** Optional human-readable title for the permission definition. */
|
|
21
|
+
title?: string;
|
|
18
22
|
}
|
|
19
23
|
export interface IamResourceSpec {
|
|
20
24
|
name: string;
|
|
21
25
|
displayName?: string;
|
|
22
26
|
}
|
|
27
|
+
/** A permission declared for an entity's client (project). */
|
|
28
|
+
export interface IamPermissionDefinition {
|
|
29
|
+
/** Canonical name: "res--action" or bare "res" when unscoped. */
|
|
30
|
+
name: string;
|
|
31
|
+
resource: string;
|
|
32
|
+
action?: string;
|
|
33
|
+
resourceScoped?: boolean;
|
|
34
|
+
title?: string;
|
|
35
|
+
}
|
|
36
|
+
export interface IamGrantArgs {
|
|
37
|
+
/** Resource ids for the resource-scoped grant form; omit for an unscoped (project-wide) grant. */
|
|
38
|
+
resources?: string[];
|
|
39
|
+
}
|
|
40
|
+
/** A permission granted to an end-user subject of an entity's client. */
|
|
41
|
+
export interface IamGrant {
|
|
42
|
+
/** Backend-specific subject id: integrated = IdentityProfile.profileId, keycloak = KC user id. */
|
|
43
|
+
profileId: string;
|
|
44
|
+
clientId: string;
|
|
45
|
+
/** Canonical permission name. */
|
|
46
|
+
permission: string;
|
|
47
|
+
/** Present only for resource-scoped grants. */
|
|
48
|
+
resources?: string[];
|
|
49
|
+
}
|
|
23
50
|
/** Unified IAM provider interface — all platform/agent code calls only this, never a backend directly */
|
|
24
51
|
export interface IamService extends InitializedService {
|
|
25
52
|
getEntityAdminConfig: (entityId: string) => Promise<OidcProviderConfig>;
|
|
@@ -35,5 +62,19 @@ export interface IamService extends InitializedService {
|
|
|
35
62
|
* the entity-owner role. Used by payment provisioning for account/project/wl resources.
|
|
36
63
|
*/
|
|
37
64
|
ensureResourceOwnership: (entityId: string, clientId: string, resource: IamResourceSpec) => Promise<void>;
|
|
65
|
+
/** Lists permission definitions registered for the entity's client. */
|
|
66
|
+
listPermissions: (entityId: string, clientId: string) => Promise<IamPermissionDefinition[]>;
|
|
67
|
+
/**
|
|
68
|
+
* Grants a permission to an end-user subject. With args.resources the grant is
|
|
69
|
+
* resource-scoped (bound to those resource ids); without it the grant is project-wide.
|
|
70
|
+
*/
|
|
71
|
+
grantPermission: (entityId: string, clientId: string, profileId: string, permission: string, args?: IamGrantArgs) => Promise<IamGrant>;
|
|
72
|
+
/**
|
|
73
|
+
* Revokes a grant. With args.resources only those resource ids are removed;
|
|
74
|
+
* without it the whole grant is removed.
|
|
75
|
+
*/
|
|
76
|
+
revokePermission: (entityId: string, clientId: string, profileId: string, permission: string, args?: IamGrantArgs) => Promise<void>;
|
|
77
|
+
/** Lists grants for the entity's client, optionally for a single subject. */
|
|
78
|
+
listGrants: (entityId: string, clientId: string, profileId?: string) => Promise<IamGrant[]>;
|
|
38
79
|
}
|
|
39
80
|
//# sourceMappingURL=types.d.ts.map
|
package/build/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AAC3D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AAExD,MAAM,WAAW,SAAS;IACxB,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,6FAA6F;IAC7F,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,iBAAiB;IAChC,0EAA0E;IAC1E,UAAU,CAAC,EAAE,MAAM,CAAA;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/build/utils.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Authorization } from '@owlmeans/auth';
|
|
2
|
+
export interface HasPermissionOptions {
|
|
3
|
+
/** Restrict the check to PermissionSets of this scope (the project's clientId). */
|
|
4
|
+
scope?: string;
|
|
5
|
+
/** Resource-scoped check: the set must list this id — unless the set itself is unscoped. */
|
|
6
|
+
resourceId?: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* True when any PermissionSet grants `permission`. An unscoped set (no resources)
|
|
10
|
+
* satisfies a resourceId check too — a project-wide grant covers every resource.
|
|
11
|
+
*/
|
|
12
|
+
export declare const hasPermission: (auth: Authorization, permission: string, opts?: HasPermissionOptions) => boolean;
|
|
13
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAEnD,MAAM,WAAW,oBAAoB;IACnC,mFAAmF;IACnF,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,4FAA4F;IAC5F,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED;;;GAGG;AACH,eAAO,MAAM,aAAa,GACxB,MAAM,aAAa,EACnB,YAAY,MAAM,EAClB,OAAO,oBAAoB,KAC1B,OAIO,CAAA"}
|
package/build/utils.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* True when any PermissionSet grants `permission`. An unscoped set (no resources)
|
|
3
|
+
* satisfies a resourceId check too — a project-wide grant covers every resource.
|
|
4
|
+
*/
|
|
5
|
+
export const hasPermission = (auth, permission, opts) => auth.permissions?.some(set => (opts?.scope == null || set.scope === opts.scope)
|
|
6
|
+
&& set.permissions[permission] === true
|
|
7
|
+
&& (opts?.resourceId == null || set.resources == null || set.resources.includes(opts.resourceId))) ?? false;
|
|
8
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AASA;;;GAGG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAC3B,IAAmB,EACnB,UAAkB,EAClB,IAA2B,EAClB,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,CACzC,CAAC,IAAI,EAAE,KAAK,IAAI,IAAI,IAAI,GAAG,CAAC,KAAK,KAAK,IAAI,CAAC,KAAK,CAAC;OAC9C,GAAG,CAAC,WAAW,CAAC,UAAU,CAAC,KAAK,IAAI;OACpC,CAAC,IAAI,EAAE,UAAU,IAAI,IAAI,IAAI,GAAG,CAAC,SAAS,IAAI,IAAI,IAAI,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAClG,IAAI,KAAK,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@owlmeans/iam",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -22,9 +22,10 @@
|
|
|
22
22
|
}
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@owlmeans/
|
|
26
|
-
"@owlmeans/
|
|
27
|
-
"@owlmeans/
|
|
25
|
+
"@owlmeans/auth": "^0.1.7",
|
|
26
|
+
"@owlmeans/context": "^0.1.7",
|
|
27
|
+
"@owlmeans/error": "^0.1.7",
|
|
28
|
+
"@owlmeans/oidc": "^0.1.7"
|
|
28
29
|
},
|
|
29
30
|
"devDependencies": {
|
|
30
31
|
"@owlmeans/dep-config": "workspace:*",
|
package/src/errors.ts
CHANGED
|
@@ -27,6 +27,26 @@ export class IamResourceError extends IamError {
|
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
export class IamGrantError extends IamError {
|
|
31
|
+
public static override typeName = `IamGrant${ResilientError.typeName}`
|
|
32
|
+
|
|
33
|
+
constructor(message = 'error') {
|
|
34
|
+
super(`grant:${message}`)
|
|
35
|
+
this.type = IamGrantError.typeName
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export class IamUnsupported extends IamError {
|
|
40
|
+
public static override typeName = `IamUnsupported${ResilientError.typeName}`
|
|
41
|
+
|
|
42
|
+
constructor(message = 'error') {
|
|
43
|
+
super(`unsupported:${message}`)
|
|
44
|
+
this.type = IamUnsupported.typeName
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
30
48
|
ResilientError.registerErrorClass(IamError)
|
|
31
49
|
ResilientError.registerErrorClass(IamClientError)
|
|
32
50
|
ResilientError.registerErrorClass(IamResourceError)
|
|
51
|
+
ResilientError.registerErrorClass(IamGrantError)
|
|
52
|
+
ResilientError.registerErrorClass(IamUnsupported)
|
package/src/index.ts
CHANGED
package/src/types.ts
CHANGED
|
@@ -18,6 +18,10 @@ export interface IamCredentialsPair {
|
|
|
18
18
|
export interface IamPermissionArgs {
|
|
19
19
|
/** Action name. When absent the permission is unscoped (project-wide). */
|
|
20
20
|
permission?: string
|
|
21
|
+
/** Declares that grants of this permission may be bound to specific resource ids. */
|
|
22
|
+
resourceScoped?: boolean
|
|
23
|
+
/** Optional human-readable title for the permission definition. */
|
|
24
|
+
title?: string
|
|
21
25
|
}
|
|
22
26
|
|
|
23
27
|
export interface IamResourceSpec {
|
|
@@ -25,6 +29,32 @@ export interface IamResourceSpec {
|
|
|
25
29
|
displayName?: string
|
|
26
30
|
}
|
|
27
31
|
|
|
32
|
+
/** A permission declared for an entity's client (project). */
|
|
33
|
+
export interface IamPermissionDefinition {
|
|
34
|
+
/** Canonical name: "res--action" or bare "res" when unscoped. */
|
|
35
|
+
name: string
|
|
36
|
+
resource: string
|
|
37
|
+
action?: string
|
|
38
|
+
resourceScoped?: boolean
|
|
39
|
+
title?: string
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface IamGrantArgs {
|
|
43
|
+
/** Resource ids for the resource-scoped grant form; omit for an unscoped (project-wide) grant. */
|
|
44
|
+
resources?: string[]
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** A permission granted to an end-user subject of an entity's client. */
|
|
48
|
+
export interface IamGrant {
|
|
49
|
+
/** Backend-specific subject id: integrated = IdentityProfile.profileId, keycloak = KC user id. */
|
|
50
|
+
profileId: string
|
|
51
|
+
clientId: string
|
|
52
|
+
/** Canonical permission name. */
|
|
53
|
+
permission: string
|
|
54
|
+
/** Present only for resource-scoped grants. */
|
|
55
|
+
resources?: string[]
|
|
56
|
+
}
|
|
57
|
+
|
|
28
58
|
/** Unified IAM provider interface — all platform/agent code calls only this, never a backend directly */
|
|
29
59
|
export interface IamService extends InitializedService {
|
|
30
60
|
// --- Admin config (backend → OIDC RP config, payment provisioning) ---
|
|
@@ -54,4 +84,36 @@ export interface IamService extends InitializedService {
|
|
|
54
84
|
clientId: string,
|
|
55
85
|
resource: IamResourceSpec
|
|
56
86
|
) => Promise<void>
|
|
87
|
+
|
|
88
|
+
// --- Authorization (permission definitions & grants) ---
|
|
89
|
+
|
|
90
|
+
/** Lists permission definitions registered for the entity's client. */
|
|
91
|
+
listPermissions: (entityId: string, clientId: string) => Promise<IamPermissionDefinition[]>
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Grants a permission to an end-user subject. With args.resources the grant is
|
|
95
|
+
* resource-scoped (bound to those resource ids); without it the grant is project-wide.
|
|
96
|
+
*/
|
|
97
|
+
grantPermission: (
|
|
98
|
+
entityId: string,
|
|
99
|
+
clientId: string,
|
|
100
|
+
profileId: string,
|
|
101
|
+
permission: string,
|
|
102
|
+
args?: IamGrantArgs
|
|
103
|
+
) => Promise<IamGrant>
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Revokes a grant. With args.resources only those resource ids are removed;
|
|
107
|
+
* without it the whole grant is removed.
|
|
108
|
+
*/
|
|
109
|
+
revokePermission: (
|
|
110
|
+
entityId: string,
|
|
111
|
+
clientId: string,
|
|
112
|
+
profileId: string,
|
|
113
|
+
permission: string,
|
|
114
|
+
args?: IamGrantArgs
|
|
115
|
+
) => Promise<void>
|
|
116
|
+
|
|
117
|
+
/** Lists grants for the entity's client, optionally for a single subject. */
|
|
118
|
+
listGrants: (entityId: string, clientId: string, profileId?: string) => Promise<IamGrant[]>
|
|
57
119
|
}
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Authorization } from '@owlmeans/auth'
|
|
2
|
+
|
|
3
|
+
export interface HasPermissionOptions {
|
|
4
|
+
/** Restrict the check to PermissionSets of this scope (the project's clientId). */
|
|
5
|
+
scope?: string
|
|
6
|
+
/** Resource-scoped check: the set must list this id — unless the set itself is unscoped. */
|
|
7
|
+
resourceId?: string
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* True when any PermissionSet grants `permission`. An unscoped set (no resources)
|
|
12
|
+
* satisfies a resourceId check too — a project-wide grant covers every resource.
|
|
13
|
+
*/
|
|
14
|
+
export const hasPermission = (
|
|
15
|
+
auth: Authorization,
|
|
16
|
+
permission: string,
|
|
17
|
+
opts?: HasPermissionOptions
|
|
18
|
+
): boolean => auth.permissions?.some(set =>
|
|
19
|
+
(opts?.scope == null || set.scope === opts.scope)
|
|
20
|
+
&& set.permissions[permission] === true
|
|
21
|
+
&& (opts?.resourceId == null || set.resources == null || set.resources.includes(opts.resourceId))
|
|
22
|
+
) ?? false
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test'
|
|
2
|
+
import { hasPermission } from '@owlmeans/iam'
|
|
3
|
+
import type { Authorization } from '@owlmeans/auth'
|
|
4
|
+
|
|
5
|
+
const auth: Authorization = {
|
|
6
|
+
scopes: [],
|
|
7
|
+
permissions: [
|
|
8
|
+
// unscoped (project-wide) grant — shared set per client
|
|
9
|
+
{ scope: 'my-project', permissions: { 'article--modify': true } },
|
|
10
|
+
// resource-scoped grant — dedicated set carrying its resource ids
|
|
11
|
+
{
|
|
12
|
+
scope: 'my-project',
|
|
13
|
+
title: 'department--modify',
|
|
14
|
+
permissions: { 'department--modify': true },
|
|
15
|
+
resources: ['dep-12345678']
|
|
16
|
+
}
|
|
17
|
+
]
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
describe('@owlmeans/iam — hasPermission', () => {
|
|
21
|
+
test('grants an unscoped permission', () => {
|
|
22
|
+
expect(hasPermission(auth, 'article--modify')).toBe(true)
|
|
23
|
+
expect(hasPermission(auth, 'article--delete')).toBe(false)
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
test('grants a resource-scoped permission only for listed resource ids', () => {
|
|
27
|
+
expect(hasPermission(auth, 'department--modify', { resourceId: 'dep-12345678' })).toBe(true)
|
|
28
|
+
expect(hasPermission(auth, 'department--modify', { resourceId: 'dep-87654321' })).toBe(false)
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
test('an unscoped set satisfies a resourceId check (project-wide grant covers every resource)', () => {
|
|
32
|
+
expect(hasPermission(auth, 'article--modify', { resourceId: 'art-12345678' })).toBe(true)
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
test('scope option restricts the check to one client', () => {
|
|
36
|
+
expect(hasPermission(auth, 'article--modify', { scope: 'my-project' })).toBe(true)
|
|
37
|
+
expect(hasPermission(auth, 'article--modify', { scope: 'other-project' })).toBe(false)
|
|
38
|
+
})
|
|
39
|
+
})
|