odgn-rights 0.2.0 → 0.5.1
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 +144 -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 +471 -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 +25 -0
- package/dist/rights.js +63 -2
- package/dist/role-registry.d.ts +9 -0
- package/dist/role-registry.js +12 -0
- package/dist/role.d.ts +5 -0
- package/dist/role.js +17 -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
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { Flags } from '../constants';
|
|
2
|
+
import type { Right } from '../right';
|
|
3
|
+
import type { Rights } from '../rights';
|
|
4
|
+
import type { Role } from '../role';
|
|
5
|
+
import type { RoleRegistry } from '../role-registry';
|
|
6
|
+
import type { Subject } from '../subject';
|
|
7
|
+
export type RightsRow = {
|
|
8
|
+
allow_mask: number;
|
|
9
|
+
created_at: string;
|
|
10
|
+
deny_mask: number;
|
|
11
|
+
description: string | null;
|
|
12
|
+
id: number;
|
|
13
|
+
path: string;
|
|
14
|
+
priority: number;
|
|
15
|
+
tags: string | null;
|
|
16
|
+
updated_at: string;
|
|
17
|
+
valid_from: string | null;
|
|
18
|
+
valid_until: string | null;
|
|
19
|
+
};
|
|
20
|
+
export type RoleRow = {
|
|
21
|
+
created_at: string;
|
|
22
|
+
id: number;
|
|
23
|
+
name: string;
|
|
24
|
+
updated_at: string;
|
|
25
|
+
};
|
|
26
|
+
export type RoleRightRow = {
|
|
27
|
+
right_id: number;
|
|
28
|
+
role_id: number;
|
|
29
|
+
};
|
|
30
|
+
export type RoleInheritanceRow = {
|
|
31
|
+
child_role_id: number;
|
|
32
|
+
parent_role_id: number;
|
|
33
|
+
};
|
|
34
|
+
export type SubjectRow = {
|
|
35
|
+
created_at: string;
|
|
36
|
+
id: number;
|
|
37
|
+
identifier: string;
|
|
38
|
+
updated_at: string;
|
|
39
|
+
};
|
|
40
|
+
export type SubjectRoleRow = {
|
|
41
|
+
role_id: number;
|
|
42
|
+
subject_id: number;
|
|
43
|
+
};
|
|
44
|
+
export type SubjectRightRow = {
|
|
45
|
+
right_id: number;
|
|
46
|
+
subject_id: number;
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* Base configuration shared by all adapters
|
|
50
|
+
*/
|
|
51
|
+
export type BaseAdapterOptions = {
|
|
52
|
+
/**
|
|
53
|
+
* Prefix for all table names. Defaults to 'tbl_'.
|
|
54
|
+
* Set to empty string '' for no prefix.
|
|
55
|
+
*/
|
|
56
|
+
tablePrefix?: string;
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Table names with the configured prefix applied
|
|
60
|
+
*/
|
|
61
|
+
export type TableNames = {
|
|
62
|
+
rights: string;
|
|
63
|
+
roleInheritance: string;
|
|
64
|
+
roleRights: string;
|
|
65
|
+
roles: string;
|
|
66
|
+
subjectRights: string;
|
|
67
|
+
subjectRoles: string;
|
|
68
|
+
subjects: string;
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* Common interface for all database adapters.
|
|
72
|
+
* Supports both synchronous (SQLite) and asynchronous (PostgreSQL) patterns
|
|
73
|
+
* by using Promise-based methods throughout.
|
|
74
|
+
*/
|
|
75
|
+
export type DatabaseAdapter = {
|
|
76
|
+
/**
|
|
77
|
+
* Clear all data from the database (useful for testing)
|
|
78
|
+
*/
|
|
79
|
+
clear(): Promise<void>;
|
|
80
|
+
/**
|
|
81
|
+
* Connect to the database
|
|
82
|
+
*/
|
|
83
|
+
connect(): Promise<void>;
|
|
84
|
+
/**
|
|
85
|
+
* Delete a right by its database ID
|
|
86
|
+
* @returns true if the right was deleted, false if not found
|
|
87
|
+
*/
|
|
88
|
+
deleteRight(id: number): Promise<boolean>;
|
|
89
|
+
/**
|
|
90
|
+
* Delete a role by name
|
|
91
|
+
* @returns true if the role was deleted, false if not found
|
|
92
|
+
*/
|
|
93
|
+
deleteRole(name: string): Promise<boolean>;
|
|
94
|
+
/**
|
|
95
|
+
* Delete a subject by its external identifier
|
|
96
|
+
* @returns true if the subject was deleted, false if not found
|
|
97
|
+
*/
|
|
98
|
+
deleteSubject(identifier: string): Promise<boolean>;
|
|
99
|
+
/**
|
|
100
|
+
* Disconnect from the database
|
|
101
|
+
*/
|
|
102
|
+
disconnect(): Promise<void>;
|
|
103
|
+
/**
|
|
104
|
+
* Find all subject identifiers that have access to a specific path with given flags
|
|
105
|
+
* @param pathPattern The path pattern to check (supports wildcards)
|
|
106
|
+
* @param flags The flags to check for
|
|
107
|
+
* @returns Array of subject identifiers that have access
|
|
108
|
+
*/
|
|
109
|
+
findSubjectsWithAccess(pathPattern: string, flags: Flags): Promise<string[]>;
|
|
110
|
+
/**
|
|
111
|
+
* Load all roles into a new RoleRegistry
|
|
112
|
+
*/
|
|
113
|
+
loadRegistry(): Promise<RoleRegistry>;
|
|
114
|
+
/**
|
|
115
|
+
* Load a right by its database ID
|
|
116
|
+
*/
|
|
117
|
+
loadRight(id: number): Promise<Right | null>;
|
|
118
|
+
/**
|
|
119
|
+
* Load all rights from the database
|
|
120
|
+
*/
|
|
121
|
+
loadRights(): Promise<Rights>;
|
|
122
|
+
/**
|
|
123
|
+
* Load rights matching a path pattern
|
|
124
|
+
*/
|
|
125
|
+
loadRightsByPath(pathPattern: string): Promise<Rights>;
|
|
126
|
+
/**
|
|
127
|
+
* Load a role by name
|
|
128
|
+
*/
|
|
129
|
+
loadRole(name: string): Promise<Role | null>;
|
|
130
|
+
/**
|
|
131
|
+
* Load all roles from the database
|
|
132
|
+
*/
|
|
133
|
+
loadRoles(): Promise<Role[]>;
|
|
134
|
+
/**
|
|
135
|
+
* Load a subject by its external identifier
|
|
136
|
+
*/
|
|
137
|
+
loadSubject(identifier: string): Promise<Subject | null>;
|
|
138
|
+
/**
|
|
139
|
+
* Run database migrations to create or update schema
|
|
140
|
+
*/
|
|
141
|
+
migrate(): Promise<void>;
|
|
142
|
+
/**
|
|
143
|
+
* Save an entire role registry to the database
|
|
144
|
+
*/
|
|
145
|
+
saveRegistry(registry: RoleRegistry): Promise<void>;
|
|
146
|
+
/**
|
|
147
|
+
* Save a single right to the database
|
|
148
|
+
* @returns The database ID of the saved right
|
|
149
|
+
*/
|
|
150
|
+
saveRight(right: Right): Promise<number>;
|
|
151
|
+
/**
|
|
152
|
+
* Save multiple rights to the database
|
|
153
|
+
* @returns Array of database IDs for the saved rights
|
|
154
|
+
*/
|
|
155
|
+
saveRights(rights: Rights): Promise<number[]>;
|
|
156
|
+
/**
|
|
157
|
+
* Save a role and its rights to the database
|
|
158
|
+
* @returns The database ID of the saved role
|
|
159
|
+
*/
|
|
160
|
+
saveRole(role: Role): Promise<number>;
|
|
161
|
+
/**
|
|
162
|
+
* Save a subject with its roles and direct rights
|
|
163
|
+
* @param identifier External identifier for the subject (e.g., user ID)
|
|
164
|
+
* @param subject The subject to save
|
|
165
|
+
* @returns The database ID of the saved subject
|
|
166
|
+
*/
|
|
167
|
+
saveSubject(identifier: string, subject: Subject): Promise<number>;
|
|
168
|
+
/**
|
|
169
|
+
* Execute operations within a transaction.
|
|
170
|
+
* The transaction will be committed if the function succeeds,
|
|
171
|
+
* or rolled back if it throws an error.
|
|
172
|
+
*/
|
|
173
|
+
transaction<T>(fn: (adapter: DatabaseAdapter) => Promise<T>): Promise<T>;
|
|
174
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import { Flags } from '../constants';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* eslint-disable no-console */
|
|
2
2
|
import { Command } from 'commander';
|
|
3
|
-
import { lettersFromMask } from '
|
|
3
|
+
import { lettersFromMask } from '@/helpers';
|
|
4
4
|
import { loadConfig } from '../helpers/config-loader';
|
|
5
5
|
import { parseFlags } from '../helpers/flag-parser';
|
|
6
6
|
import { colors, flagName, formatResult } from '../helpers/output';
|
|
@@ -30,7 +30,9 @@ export const explainCommand = new Command('explain')
|
|
|
30
30
|
details: explanation.details.map(d => ({
|
|
31
31
|
allowed: d.allowed,
|
|
32
32
|
flag: flagName(d.bit),
|
|
33
|
-
|
|
33
|
+
priority: d.right?.priority,
|
|
34
|
+
rule: d.right?.toString(),
|
|
35
|
+
specificity: d.right?.specificity()
|
|
34
36
|
})),
|
|
35
37
|
flag: options.flag,
|
|
36
38
|
path: options.path
|
|
@@ -55,12 +57,18 @@ export const explainCommand = new Command('explain')
|
|
|
55
57
|
// Show all matching rules
|
|
56
58
|
const allRights = rights.allRights().filter(r => r.matches(options.path));
|
|
57
59
|
if (allRights.length > 0) {
|
|
58
|
-
console.log(`\n${colors.bold('Matching rules (by specificity):')}`);
|
|
60
|
+
console.log(`\n${colors.bold('Matching rules (by priority, then specificity):')}`);
|
|
59
61
|
allRights
|
|
60
|
-
.sort((a, b) =>
|
|
62
|
+
.sort((a, b) => {
|
|
63
|
+
const pDiff = b.priority - a.priority;
|
|
64
|
+
if (pDiff !== 0) {
|
|
65
|
+
return pDiff;
|
|
66
|
+
}
|
|
67
|
+
return b.specificity() - a.specificity();
|
|
68
|
+
})
|
|
61
69
|
.forEach((r, i) => {
|
|
62
70
|
console.log(` ${i + 1}. ${colors.cyan(r.toString())}`);
|
|
63
|
-
console.log(` Specificity: ${r.specificity()}`);
|
|
71
|
+
console.log(` Priority: ${r.priority}, Specificity: ${r.specificity()}`);
|
|
64
72
|
if (r.tags.length > 0) {
|
|
65
73
|
console.log(` Tags: ${r.tags.join(', ')}`);
|
|
66
74
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export type ParsedPath = {
|
|
2
|
+
negated: boolean;
|
|
3
|
+
path: string;
|
|
4
|
+
};
|
|
5
|
+
/**
|
|
6
|
+
* Parse a path string, detecting and stripping negation prefix.
|
|
7
|
+
* Handles double negation (!! becomes positive).
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* parsePath('!/api/internal') // { path: '/api/internal', negated: true }
|
|
11
|
+
* parsePath('/api/public') // { path: '/api/public', negated: false }
|
|
12
|
+
* parsePath('!!/api/path') // { path: '/api/path', negated: false }
|
|
13
|
+
*/
|
|
14
|
+
export declare const parsePath: (p: string) => ParsedPath;
|
|
15
|
+
export declare const normalizePath: (p: string) => string;
|
|
16
|
+
export declare const lettersFromMask: (mask: number) => string;
|
|
@@ -1,4 +1,26 @@
|
|
|
1
1
|
import { Flags, hasBit } from './constants';
|
|
2
|
+
/**
|
|
3
|
+
* Parse a path string, detecting and stripping negation prefix.
|
|
4
|
+
* Handles double negation (!! becomes positive).
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* parsePath('!/api/internal') // { path: '/api/internal', negated: true }
|
|
8
|
+
* parsePath('/api/public') // { path: '/api/public', negated: false }
|
|
9
|
+
* parsePath('!!/api/path') // { path: '/api/path', negated: false }
|
|
10
|
+
*/
|
|
11
|
+
export const parsePath = (p) => {
|
|
12
|
+
let trimmed = p.trim();
|
|
13
|
+
let negated = false;
|
|
14
|
+
// Handle negation prefix(es) - !! cancels out
|
|
15
|
+
while (trimmed.startsWith('!')) {
|
|
16
|
+
negated = !negated;
|
|
17
|
+
trimmed = trimmed.slice(1).trimStart();
|
|
18
|
+
}
|
|
19
|
+
return {
|
|
20
|
+
negated,
|
|
21
|
+
path: normalizePath(trimmed)
|
|
22
|
+
};
|
|
23
|
+
};
|
|
2
24
|
export const normalizePath = (p) => {
|
|
3
25
|
if (!p) {
|
|
4
26
|
return '/';
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ElysiaJS integration for odgn-rights
|
|
3
|
+
*
|
|
4
|
+
* @module odgn-rights/integrations/elysia
|
|
5
|
+
*
|
|
6
|
+
* This module provides middleware and utilities for integrating
|
|
7
|
+
* odgn-rights with ElysiaJS applications.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* import { Elysia } from 'elysia';
|
|
12
|
+
* import { elysiaRights } from 'odgn-rights/integrations/elysia';
|
|
13
|
+
*
|
|
14
|
+
* const app = new Elysia()
|
|
15
|
+
* .use(elysiaRights({
|
|
16
|
+
* registry: subjectRegistry,
|
|
17
|
+
* getSubject: ({ headers }) => headers.get('x-user-id')
|
|
18
|
+
* }))
|
|
19
|
+
* .get('/users', () => 'list users')
|
|
20
|
+
* .listen(3000);
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
import type { Flags } from '../constants';
|
|
24
|
+
import type { ConditionContext } from '../right';
|
|
25
|
+
import type { Rights } from '../rights';
|
|
26
|
+
import type { Subject } from '../subject';
|
|
27
|
+
import type { SubjectRegistry } from '../subject-registry';
|
|
28
|
+
/**
|
|
29
|
+
* Configuration options for the Elysia rights middleware
|
|
30
|
+
*/
|
|
31
|
+
export type ElysiaRightsOptions<Store = unknown, Derive = unknown> = {
|
|
32
|
+
/**
|
|
33
|
+
* Function to determine the required flags based on the request.
|
|
34
|
+
* Defaults to mapping HTTP methods to flags:
|
|
35
|
+
* - GET, HEAD, OPTIONS -> READ
|
|
36
|
+
* - POST -> CREATE
|
|
37
|
+
* - PUT, PATCH -> WRITE
|
|
38
|
+
* - DELETE -> DELETE
|
|
39
|
+
*/
|
|
40
|
+
flagMapper?: (ctx: {
|
|
41
|
+
method: string;
|
|
42
|
+
path: string;
|
|
43
|
+
request: Request;
|
|
44
|
+
}) => Flags;
|
|
45
|
+
/**
|
|
46
|
+
* Function to build the condition context for ABAC-style checks.
|
|
47
|
+
* The returned context is passed to the permission check.
|
|
48
|
+
*/
|
|
49
|
+
getContext?: (ctx: {
|
|
50
|
+
headers: Headers;
|
|
51
|
+
path: string;
|
|
52
|
+
request: Request;
|
|
53
|
+
store: Store;
|
|
54
|
+
subject: Subject;
|
|
55
|
+
} & Derive) => ConditionContext | Promise<ConditionContext>;
|
|
56
|
+
/**
|
|
57
|
+
* Function to extract the subject or subject identifier from the request context.
|
|
58
|
+
* If registry is provided, this should return a string identifier.
|
|
59
|
+
* Otherwise, it should return a Subject instance directly.
|
|
60
|
+
*/
|
|
61
|
+
getSubject: (ctx: {
|
|
62
|
+
headers: Headers;
|
|
63
|
+
path: string;
|
|
64
|
+
request: Request;
|
|
65
|
+
store: Store;
|
|
66
|
+
} & Derive) => string | Subject | undefined | null | Promise<string | Subject | undefined | null>;
|
|
67
|
+
/**
|
|
68
|
+
* Custom handler when no subject is found.
|
|
69
|
+
* Defaults to returning 401 status.
|
|
70
|
+
*/
|
|
71
|
+
onNoSubject?: (ctx: {
|
|
72
|
+
path: string;
|
|
73
|
+
request: Request;
|
|
74
|
+
}) => Response | Promise<Response>;
|
|
75
|
+
/**
|
|
76
|
+
* Custom handler for unauthorized responses.
|
|
77
|
+
* Defaults to returning 403 status.
|
|
78
|
+
*/
|
|
79
|
+
onUnauthorized?: (ctx: {
|
|
80
|
+
path: string;
|
|
81
|
+
request: Request;
|
|
82
|
+
requiredFlags: Flags;
|
|
83
|
+
subject?: Subject;
|
|
84
|
+
}) => Response | Promise<Response>;
|
|
85
|
+
/**
|
|
86
|
+
* Function to map the request path to a rights path.
|
|
87
|
+
* Useful for stripping prefixes or transforming paths.
|
|
88
|
+
* Defaults to using the request path as-is.
|
|
89
|
+
*/
|
|
90
|
+
pathMapper?: (ctx: {
|
|
91
|
+
method: string;
|
|
92
|
+
path: string;
|
|
93
|
+
request: Request;
|
|
94
|
+
}) => string;
|
|
95
|
+
/**
|
|
96
|
+
* The SubjectRegistry to look up subjects from.
|
|
97
|
+
* If provided, getSubject should return the subject identifier.
|
|
98
|
+
*/
|
|
99
|
+
registry?: SubjectRegistry;
|
|
100
|
+
};
|
|
101
|
+
/**
|
|
102
|
+
* Options for standalone rights-based checks (without subjects)
|
|
103
|
+
*/
|
|
104
|
+
export type ElysiaRightsStandaloneOptions<Store = unknown, Derive = unknown> = Omit<ElysiaRightsOptions<Store, Derive>, 'registry' | 'getSubject'> & {
|
|
105
|
+
/**
|
|
106
|
+
* A Rights instance to check against directly.
|
|
107
|
+
* Use this when you don't need subject/role-based access control.
|
|
108
|
+
*/
|
|
109
|
+
rights: Rights;
|
|
110
|
+
};
|
|
111
|
+
type ElysiaContext = {
|
|
112
|
+
[key: string]: unknown;
|
|
113
|
+
path: string;
|
|
114
|
+
request: Request;
|
|
115
|
+
set: {
|
|
116
|
+
status?: number | string;
|
|
117
|
+
};
|
|
118
|
+
};
|
|
119
|
+
/**
|
|
120
|
+
* Create an Elysia plugin for rights-based authorization.
|
|
121
|
+
*
|
|
122
|
+
* @example Using with SubjectRegistry
|
|
123
|
+
* ```typescript
|
|
124
|
+
* import { Elysia } from 'elysia';
|
|
125
|
+
* import { elysiaRights } from 'odgn-rights/integrations/elysia';
|
|
126
|
+
*
|
|
127
|
+
* const app = new Elysia()
|
|
128
|
+
* .use(elysiaRights({
|
|
129
|
+
* registry: subjectRegistry,
|
|
130
|
+
* getSubject: ({ headers }) => headers.get('x-user-id')
|
|
131
|
+
* }))
|
|
132
|
+
* .get('/users', () => 'list users')
|
|
133
|
+
* .listen(3000);
|
|
134
|
+
* ```
|
|
135
|
+
*
|
|
136
|
+
* @example Using with direct Subject
|
|
137
|
+
* ```typescript
|
|
138
|
+
* const app = new Elysia()
|
|
139
|
+
* .derive(({ headers }) => ({
|
|
140
|
+
* user: getUserFromToken(headers.get('authorization'))
|
|
141
|
+
* }))
|
|
142
|
+
* .use(elysiaRights({
|
|
143
|
+
* getSubject: ({ user }) => user?.subject
|
|
144
|
+
* }))
|
|
145
|
+
* .get('/users', () => 'list users')
|
|
146
|
+
* .listen(3000);
|
|
147
|
+
* ```
|
|
148
|
+
*/
|
|
149
|
+
export declare const elysiaRights: <Store = unknown, Derive = unknown>(options: ElysiaRightsOptions<Store, Derive>) => any;
|
|
150
|
+
/**
|
|
151
|
+
* Create an Elysia plugin for rights-based authorization using a Rights instance directly.
|
|
152
|
+
* Use this when you don't need subject/role-based access control.
|
|
153
|
+
*
|
|
154
|
+
* @example
|
|
155
|
+
* ```typescript
|
|
156
|
+
* import { Elysia } from 'elysia';
|
|
157
|
+
* import { elysiaRightsStandalone } from 'odgn-rights/integrations/elysia';
|
|
158
|
+
* import { Rights, Flags } from 'odgn-rights';
|
|
159
|
+
*
|
|
160
|
+
* const rights = new Rights();
|
|
161
|
+
* rights.allow('/api/**', Flags.READ);
|
|
162
|
+
* rights.allow('/api/admin/**', Flags.ALL);
|
|
163
|
+
*
|
|
164
|
+
* const app = new Elysia()
|
|
165
|
+
* .use(elysiaRightsStandalone({ rights }))
|
|
166
|
+
* .get('/api/users', () => 'list users')
|
|
167
|
+
* .listen(3000);
|
|
168
|
+
* ```
|
|
169
|
+
*/
|
|
170
|
+
export declare const elysiaRightsStandalone: <Store = unknown, Derive = unknown>(options: ElysiaRightsStandaloneOptions<Store, Derive>) => any;
|
|
171
|
+
/**
|
|
172
|
+
* Type for beforeHandle guard configuration
|
|
173
|
+
*/
|
|
174
|
+
export type RightsGuardConfig = {
|
|
175
|
+
beforeHandle: (ctx: ElysiaContext) => Promise<Response | object | void>;
|
|
176
|
+
};
|
|
177
|
+
/**
|
|
178
|
+
* Create a guard configuration for use with Elysia's .guard() method.
|
|
179
|
+
* This allows more fine-grained control over which routes are protected.
|
|
180
|
+
*
|
|
181
|
+
* @example
|
|
182
|
+
* ```typescript
|
|
183
|
+
* import { Elysia } from 'elysia';
|
|
184
|
+
* import { createRightsGuard } from 'odgn-rights/integrations/elysia';
|
|
185
|
+
*
|
|
186
|
+
* const guard = createRightsGuard({
|
|
187
|
+
* registry: subjectRegistry,
|
|
188
|
+
* getSubject: ({ headers }) => headers.get('x-user-id')
|
|
189
|
+
* });
|
|
190
|
+
*
|
|
191
|
+
* const app = new Elysia()
|
|
192
|
+
* .get('/public', () => 'anyone can see this')
|
|
193
|
+
* .guard(guard, (app) =>
|
|
194
|
+
* app
|
|
195
|
+
* .get('/protected', () => 'only authorized users')
|
|
196
|
+
* .post('/protected', () => 'create something')
|
|
197
|
+
* )
|
|
198
|
+
* .listen(3000);
|
|
199
|
+
* ```
|
|
200
|
+
*/
|
|
201
|
+
export declare const createRightsGuard: <Store = unknown, Derive = unknown>(options: ElysiaRightsOptions<Store, Derive>) => {
|
|
202
|
+
beforeHandle({ path, request, set, ...rest }: ElysiaContext): Promise<Response | {
|
|
203
|
+
error: string;
|
|
204
|
+
message: string;
|
|
205
|
+
} | undefined>;
|
|
206
|
+
};
|
|
207
|
+
/**
|
|
208
|
+
* Create a macro for declarative per-route authorization.
|
|
209
|
+
* This allows you to specify rights requirements directly on route definitions.
|
|
210
|
+
*
|
|
211
|
+
* @example
|
|
212
|
+
* ```typescript
|
|
213
|
+
* import { Elysia } from 'elysia';
|
|
214
|
+
* import { createRightsMacro } from 'odgn-rights/integrations/elysia';
|
|
215
|
+
* import { Flags } from 'odgn-rights';
|
|
216
|
+
*
|
|
217
|
+
* const rightsMacro = createRightsMacro({
|
|
218
|
+
* registry: subjectRegistry,
|
|
219
|
+
* getSubject: ({ headers }) => headers.get('x-user-id')
|
|
220
|
+
* });
|
|
221
|
+
*
|
|
222
|
+
* const app = new Elysia()
|
|
223
|
+
* .use(rightsMacro)
|
|
224
|
+
* .get('/public', () => 'anyone')
|
|
225
|
+
* .get('/users', () => 'list users', {
|
|
226
|
+
* rights: { path: '/users', flags: Flags.READ }
|
|
227
|
+
* })
|
|
228
|
+
* .delete('/users/:id', () => 'delete user', {
|
|
229
|
+
* rights: { path: '/users/*', flags: Flags.DELETE }
|
|
230
|
+
* })
|
|
231
|
+
* .listen(3000);
|
|
232
|
+
* ```
|
|
233
|
+
*/
|
|
234
|
+
export declare const createRightsMacro: <Store = unknown, Derive = unknown>(options: ElysiaRightsOptions<Store, Derive>) => any;
|
|
235
|
+
export {};
|