odgn-rights 0.2.0 → 0.5.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/README.md +368 -0
- package/dist/adapters/base-adapter.d.ts +83 -0
- package/dist/adapters/base-adapter.js +142 -0
- package/dist/adapters/factories.d.ts +31 -0
- package/dist/adapters/factories.js +48 -0
- package/dist/adapters/index.d.ts +11 -0
- package/dist/adapters/index.js +12 -0
- package/dist/adapters/postgres-adapter.d.ts +51 -0
- package/dist/adapters/postgres-adapter.js +469 -0
- package/dist/adapters/redis-adapter.d.ts +84 -0
- package/dist/adapters/redis-adapter.js +673 -0
- package/dist/adapters/schema.d.ts +25 -0
- package/dist/adapters/schema.js +186 -0
- package/dist/adapters/sqlite-adapter.d.ts +78 -0
- package/dist/adapters/sqlite-adapter.js +655 -0
- package/dist/adapters/types.d.ts +174 -0
- package/dist/adapters/types.js +1 -0
- package/dist/cli/commands/explain.js +13 -5
- package/dist/helpers.d.ts +16 -0
- package/dist/{utils.js → helpers.js} +22 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +3 -1
- package/dist/integrations/elysia.d.ts +235 -0
- package/dist/integrations/elysia.js +375 -0
- package/dist/right.d.ts +7 -0
- package/dist/right.js +63 -8
- package/dist/rights.d.ts +19 -0
- package/dist/rights.js +48 -2
- package/dist/role-registry.d.ts +9 -0
- package/dist/role-registry.js +12 -0
- package/dist/subject-registry.d.ts +77 -0
- package/dist/subject-registry.js +123 -0
- package/dist/subject.d.ts +4 -0
- package/dist/subject.js +3 -0
- package/package.json +41 -6
- package/dist/utils.d.ts +0 -2
package/dist/role-registry.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { DatabaseAdapter } from './adapters/types';
|
|
1
2
|
import { Rights, type RightJSON } from './rights';
|
|
2
3
|
import { Role } from './role';
|
|
3
4
|
export type RoleJSON = {
|
|
@@ -10,5 +11,13 @@ export declare class RoleRegistry {
|
|
|
10
11
|
define(name: string, rights?: Rights): Role;
|
|
11
12
|
get(name: string): Role | undefined;
|
|
12
13
|
toJSON(): RoleJSON[];
|
|
14
|
+
/**
|
|
15
|
+
* Load all roles and their relationships from a database adapter
|
|
16
|
+
*/
|
|
17
|
+
static loadFrom(adapter: DatabaseAdapter): Promise<RoleRegistry>;
|
|
18
|
+
/**
|
|
19
|
+
* Save all roles and their relationships to a database adapter
|
|
20
|
+
*/
|
|
21
|
+
saveTo(adapter: DatabaseAdapter): Promise<void>;
|
|
13
22
|
static fromJSON(data: RoleJSON[]): RoleRegistry;
|
|
14
23
|
}
|
package/dist/role-registry.js
CHANGED
|
@@ -23,6 +23,18 @@ export class RoleRegistry {
|
|
|
23
23
|
toJSON() {
|
|
24
24
|
return Array.from(this.roles.values()).map(r => r.toJSON());
|
|
25
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* Load all roles and their relationships from a database adapter
|
|
28
|
+
*/
|
|
29
|
+
static async loadFrom(adapter) {
|
|
30
|
+
return adapter.loadRegistry();
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Save all roles and their relationships to a database adapter
|
|
34
|
+
*/
|
|
35
|
+
async saveTo(adapter) {
|
|
36
|
+
await adapter.saveRegistry(this);
|
|
37
|
+
}
|
|
26
38
|
static fromJSON(data) {
|
|
27
39
|
const registry = new RoleRegistry();
|
|
28
40
|
// First pass: create all roles
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { DatabaseAdapter } from './adapters/types';
|
|
2
|
+
import { Flags } from './constants';
|
|
3
|
+
import type { ConditionContext } from './right';
|
|
4
|
+
import type { RoleRegistry } from './role-registry';
|
|
5
|
+
import { Subject, type SubjectJSON } from './subject';
|
|
6
|
+
export type SubjectRegistryJSON = {
|
|
7
|
+
[identifier: string]: SubjectJSON;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* In-memory registry for managing Subject instances.
|
|
11
|
+
* Provides an API similar to RoleRegistry for consistency.
|
|
12
|
+
*/
|
|
13
|
+
export declare class SubjectRegistry {
|
|
14
|
+
private subjects;
|
|
15
|
+
/**
|
|
16
|
+
* Register a subject with an identifier.
|
|
17
|
+
* If a subject with this identifier already exists, it will be replaced.
|
|
18
|
+
*/
|
|
19
|
+
register(identifier: string, subject: Subject): void;
|
|
20
|
+
/**
|
|
21
|
+
* Get a subject by its identifier.
|
|
22
|
+
*/
|
|
23
|
+
get(identifier: string): Subject | undefined;
|
|
24
|
+
/**
|
|
25
|
+
* Check if a subject with the given identifier exists.
|
|
26
|
+
*/
|
|
27
|
+
has(identifier: string): boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Delete a subject by its identifier.
|
|
30
|
+
* @returns true if the subject was deleted, false if not found
|
|
31
|
+
*/
|
|
32
|
+
delete(identifier: string): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Get all registered identifiers.
|
|
35
|
+
*/
|
|
36
|
+
identifiers(): string[];
|
|
37
|
+
/**
|
|
38
|
+
* Get the number of registered subjects.
|
|
39
|
+
*/
|
|
40
|
+
get size(): number;
|
|
41
|
+
/**
|
|
42
|
+
* Clear all registered subjects.
|
|
43
|
+
*/
|
|
44
|
+
clear(): void;
|
|
45
|
+
/**
|
|
46
|
+
* Iterate over all subjects.
|
|
47
|
+
*/
|
|
48
|
+
entries(): IterableIterator<[string, Subject]>;
|
|
49
|
+
/**
|
|
50
|
+
* Find all subject identifiers that have access to a specific path with given flags.
|
|
51
|
+
* @param pathPattern The path pattern to check (supports wildcards)
|
|
52
|
+
* @param flags The flags to check for
|
|
53
|
+
* @param context Optional condition context for ABAC-style checks
|
|
54
|
+
* @returns Array of subject identifiers that have access
|
|
55
|
+
*/
|
|
56
|
+
findSubjectsWithAccess(pathPattern: string, flags: Flags, context?: ConditionContext): string[];
|
|
57
|
+
/**
|
|
58
|
+
* Serialize the registry to JSON.
|
|
59
|
+
*/
|
|
60
|
+
toJSON(): SubjectRegistryJSON;
|
|
61
|
+
/**
|
|
62
|
+
* Create a SubjectRegistry from JSON data.
|
|
63
|
+
* @param data The serialized registry data
|
|
64
|
+
* @param roleRegistry Optional RoleRegistry for resolving role references
|
|
65
|
+
*/
|
|
66
|
+
static fromJSON(data: SubjectRegistryJSON, roleRegistry?: RoleRegistry): SubjectRegistry;
|
|
67
|
+
/**
|
|
68
|
+
* Save all subjects to a database adapter.
|
|
69
|
+
*/
|
|
70
|
+
saveTo(adapter: DatabaseAdapter): Promise<void>;
|
|
71
|
+
/**
|
|
72
|
+
* Load all subjects from a database adapter.
|
|
73
|
+
* Note: This requires the adapter to support getAllSubjectIdentifiers().
|
|
74
|
+
* For adapters that don't support this, use loadFromIdentifiers() instead.
|
|
75
|
+
*/
|
|
76
|
+
static loadFrom(adapter: DatabaseAdapter, identifiers: string[]): Promise<SubjectRegistry>;
|
|
77
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { Flags } from './constants';
|
|
2
|
+
import { Subject } from './subject';
|
|
3
|
+
/**
|
|
4
|
+
* In-memory registry for managing Subject instances.
|
|
5
|
+
* Provides an API similar to RoleRegistry for consistency.
|
|
6
|
+
*/
|
|
7
|
+
export class SubjectRegistry {
|
|
8
|
+
constructor() {
|
|
9
|
+
this.subjects = new Map();
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Register a subject with an identifier.
|
|
13
|
+
* If a subject with this identifier already exists, it will be replaced.
|
|
14
|
+
*/
|
|
15
|
+
register(identifier, subject) {
|
|
16
|
+
this.subjects.set(identifier, subject);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Get a subject by its identifier.
|
|
20
|
+
*/
|
|
21
|
+
get(identifier) {
|
|
22
|
+
return this.subjects.get(identifier);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Check if a subject with the given identifier exists.
|
|
26
|
+
*/
|
|
27
|
+
has(identifier) {
|
|
28
|
+
return this.subjects.has(identifier);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Delete a subject by its identifier.
|
|
32
|
+
* @returns true if the subject was deleted, false if not found
|
|
33
|
+
*/
|
|
34
|
+
delete(identifier) {
|
|
35
|
+
return this.subjects.delete(identifier);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Get all registered identifiers.
|
|
39
|
+
*/
|
|
40
|
+
identifiers() {
|
|
41
|
+
return Array.from(this.subjects.keys());
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Get the number of registered subjects.
|
|
45
|
+
*/
|
|
46
|
+
get size() {
|
|
47
|
+
return this.subjects.size;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Clear all registered subjects.
|
|
51
|
+
*/
|
|
52
|
+
clear() {
|
|
53
|
+
this.subjects.clear();
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Iterate over all subjects.
|
|
57
|
+
*/
|
|
58
|
+
entries() {
|
|
59
|
+
return this.subjects.entries();
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Find all subject identifiers that have access to a specific path with given flags.
|
|
63
|
+
* @param pathPattern The path pattern to check (supports wildcards)
|
|
64
|
+
* @param flags The flags to check for
|
|
65
|
+
* @param context Optional condition context for ABAC-style checks
|
|
66
|
+
* @returns Array of subject identifiers that have access
|
|
67
|
+
*/
|
|
68
|
+
findSubjectsWithAccess(pathPattern, flags, context) {
|
|
69
|
+
const matching = [];
|
|
70
|
+
for (const [identifier, subject] of this.subjects) {
|
|
71
|
+
if (subject.has(pathPattern, flags, context)) {
|
|
72
|
+
matching.push(identifier);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return matching;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Serialize the registry to JSON.
|
|
79
|
+
*/
|
|
80
|
+
toJSON() {
|
|
81
|
+
const result = {};
|
|
82
|
+
for (const [identifier, subject] of this.subjects) {
|
|
83
|
+
result[identifier] = subject.toJSON();
|
|
84
|
+
}
|
|
85
|
+
return result;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Create a SubjectRegistry from JSON data.
|
|
89
|
+
* @param data The serialized registry data
|
|
90
|
+
* @param roleRegistry Optional RoleRegistry for resolving role references
|
|
91
|
+
*/
|
|
92
|
+
static fromJSON(data, roleRegistry) {
|
|
93
|
+
const registry = new SubjectRegistry();
|
|
94
|
+
for (const [identifier, subjectData] of Object.entries(data)) {
|
|
95
|
+
const subject = Subject.fromJSON(subjectData, roleRegistry);
|
|
96
|
+
registry.register(identifier, subject);
|
|
97
|
+
}
|
|
98
|
+
return registry;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Save all subjects to a database adapter.
|
|
102
|
+
*/
|
|
103
|
+
async saveTo(adapter) {
|
|
104
|
+
for (const [identifier, subject] of this.subjects) {
|
|
105
|
+
await adapter.saveSubject(identifier, subject);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Load all subjects from a database adapter.
|
|
110
|
+
* Note: This requires the adapter to support getAllSubjectIdentifiers().
|
|
111
|
+
* For adapters that don't support this, use loadFromIdentifiers() instead.
|
|
112
|
+
*/
|
|
113
|
+
static async loadFrom(adapter, identifiers) {
|
|
114
|
+
const registry = new SubjectRegistry();
|
|
115
|
+
for (const id of identifiers) {
|
|
116
|
+
const subject = await adapter.loadSubject(id);
|
|
117
|
+
if (subject) {
|
|
118
|
+
registry.register(id, subject);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return registry;
|
|
122
|
+
}
|
|
123
|
+
}
|
package/dist/subject.d.ts
CHANGED
|
@@ -43,4 +43,8 @@ export declare class Subject {
|
|
|
43
43
|
delete(path: string, context?: ConditionContext): boolean;
|
|
44
44
|
create(path: string, context?: ConditionContext): boolean;
|
|
45
45
|
execute(path: string, context?: ConditionContext): boolean;
|
|
46
|
+
checkMany(requests: Array<{
|
|
47
|
+
flags: Flags;
|
|
48
|
+
path: string;
|
|
49
|
+
}>, context?: ConditionContext): boolean[];
|
|
46
50
|
}
|
package/dist/subject.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "odgn-rights",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Tiny TypeScript library for expressing and evaluating hierarchical rights with simple glob patterns.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"rights",
|
|
@@ -28,6 +28,26 @@
|
|
|
28
28
|
"./cli": {
|
|
29
29
|
"types": "./dist/cli/index.d.ts",
|
|
30
30
|
"import": "./dist/cli/index.js"
|
|
31
|
+
},
|
|
32
|
+
"./adapters": {
|
|
33
|
+
"types": "./dist/adapters/index.d.ts",
|
|
34
|
+
"import": "./dist/adapters/index.js"
|
|
35
|
+
},
|
|
36
|
+
"./adapters/sqlite": {
|
|
37
|
+
"types": "./dist/adapters/sqlite-adapter.d.ts",
|
|
38
|
+
"import": "./dist/adapters/sqlite-adapter.js"
|
|
39
|
+
},
|
|
40
|
+
"./adapters/postgres": {
|
|
41
|
+
"types": "./dist/adapters/postgres-adapter.d.ts",
|
|
42
|
+
"import": "./dist/adapters/postgres-adapter.js"
|
|
43
|
+
},
|
|
44
|
+
"./adapters/redis": {
|
|
45
|
+
"types": "./dist/adapters/redis-adapter.d.ts",
|
|
46
|
+
"import": "./dist/adapters/redis-adapter.js"
|
|
47
|
+
},
|
|
48
|
+
"./integrations/elysia": {
|
|
49
|
+
"types": "./dist/integrations/elysia.d.ts",
|
|
50
|
+
"import": "./dist/integrations/elysia.js"
|
|
31
51
|
}
|
|
32
52
|
},
|
|
33
53
|
"files": [
|
|
@@ -55,15 +75,25 @@
|
|
|
55
75
|
"@ianvs/prettier-plugin-sort-imports": "^4.7.0",
|
|
56
76
|
"@nkzw/eslint-config": "^3.3.0",
|
|
57
77
|
"@playwright/test": "^1.57.0",
|
|
78
|
+
"@testcontainers/postgresql": "^11.11.0",
|
|
79
|
+
"@testcontainers/valkey": "^11.11.0",
|
|
58
80
|
"@types/bun": "latest",
|
|
59
81
|
"@types/react": "^19.2.7",
|
|
60
82
|
"@types/react-dom": "^19.2.3",
|
|
83
|
+
"elysia": "^1.4.21",
|
|
61
84
|
"eslint": "^9",
|
|
62
|
-
"knip": "^5.
|
|
63
|
-
"prettier": "^3"
|
|
85
|
+
"knip": "^5.80.0",
|
|
86
|
+
"prettier": "^3",
|
|
87
|
+
"testcontainers": "^11.11.0"
|
|
64
88
|
},
|
|
65
89
|
"peerDependencies": {
|
|
66
|
-
"typescript": "^5"
|
|
90
|
+
"typescript": "^5",
|
|
91
|
+
"elysia": "^1.0.0"
|
|
92
|
+
},
|
|
93
|
+
"peerDependenciesMeta": {
|
|
94
|
+
"elysia": {
|
|
95
|
+
"optional": true
|
|
96
|
+
}
|
|
67
97
|
},
|
|
68
98
|
"publishConfig": {
|
|
69
99
|
"access": "public"
|
|
@@ -71,8 +101,13 @@
|
|
|
71
101
|
"sideEffects": false,
|
|
72
102
|
"dependencies": {
|
|
73
103
|
"commander": "^14.0.2",
|
|
74
|
-
"
|
|
104
|
+
"ioredis": "^5.9.0",
|
|
105
|
+
"jotai": "^2.16.1",
|
|
75
106
|
"react": "^19.2.3",
|
|
76
107
|
"react-dom": "^19.2.3"
|
|
77
|
-
}
|
|
108
|
+
},
|
|
109
|
+
"trustedDependencies": [
|
|
110
|
+
"protobufjs",
|
|
111
|
+
"unrs-resolver"
|
|
112
|
+
]
|
|
78
113
|
}
|
package/dist/utils.d.ts
DELETED