@zakyyudha/node-authzkit 1.0.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/.eslintrc.cjs +17 -0
- package/.prettierrc.json +10 -0
- package/.release-it.json +24 -0
- package/README.md +221 -0
- package/dist/src/classes/Authzkit.d.ts +110 -0
- package/dist/src/classes/Authzkit.js +189 -0
- package/dist/src/classes/Authzkit.js.map +1 -0
- package/dist/src/dashboard/router.d.ts +19 -0
- package/dist/src/dashboard/router.js +89 -0
- package/dist/src/dashboard/router.js.map +1 -0
- package/dist/src/dashboard/routes/permissions.d.ts +3 -0
- package/dist/src/dashboard/routes/permissions.js +39 -0
- package/dist/src/dashboard/routes/permissions.js.map +1 -0
- package/dist/src/dashboard/routes/roles.d.ts +3 -0
- package/dist/src/dashboard/routes/roles.js +39 -0
- package/dist/src/dashboard/routes/roles.js.map +1 -0
- package/dist/src/dashboard/routes/users.d.ts +3 -0
- package/dist/src/dashboard/routes/users.js +81 -0
- package/dist/src/dashboard/routes/users.js.map +1 -0
- package/dist/src/drivers/mongodb/mongo-connection.d.ts +15 -0
- package/dist/src/drivers/mongodb/mongo-connection.js +89 -0
- package/dist/src/drivers/mongodb/mongo-connection.js.map +1 -0
- package/dist/src/drivers/postgres/pg-connection.d.ts +17 -0
- package/dist/src/drivers/postgres/pg-connection.js +145 -0
- package/dist/src/drivers/postgres/pg-connection.js.map +1 -0
- package/dist/src/index.d.ts +19 -0
- package/dist/src/index.js +36 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/interfaces/Authorizable.d.ts +7 -0
- package/dist/src/interfaces/Authorizable.js +3 -0
- package/dist/src/interfaces/Authorizable.js.map +1 -0
- package/dist/src/interfaces/IAuthzkitConfig.d.ts +18 -0
- package/dist/src/interfaces/IAuthzkitConfig.js +3 -0
- package/dist/src/interfaces/IAuthzkitConfig.js.map +1 -0
- package/dist/src/interfaces/Permission.d.ts +4 -0
- package/dist/src/interfaces/Permission.js +3 -0
- package/dist/src/interfaces/Permission.js.map +1 -0
- package/dist/src/interfaces/Role.d.ts +5 -0
- package/dist/src/interfaces/Role.js +3 -0
- package/dist/src/interfaces/Role.js.map +1 -0
- package/dist/src/middleware/authzMiddleware.d.ts +17 -0
- package/dist/src/middleware/authzMiddleware.js +52 -0
- package/dist/src/middleware/authzMiddleware.js.map +1 -0
- package/dist/src/stores/IAuthzkitStore.d.ts +23 -0
- package/dist/src/stores/IAuthzkitStore.js +3 -0
- package/dist/src/stores/IAuthzkitStore.js.map +1 -0
- package/dist/src/stores/InMemoryAuthzkitStore.d.ts +28 -0
- package/dist/src/stores/InMemoryAuthzkitStore.js +83 -0
- package/dist/src/stores/InMemoryAuthzkitStore.js.map +1 -0
- package/dist/src/stores/MongoAuthzkitStore.d.ts +31 -0
- package/dist/src/stores/MongoAuthzkitStore.js +127 -0
- package/dist/src/stores/MongoAuthzkitStore.js.map +1 -0
- package/dist/src/stores/PgAuthzkitStore.d.ts +31 -0
- package/dist/src/stores/PgAuthzkitStore.js +133 -0
- package/dist/src/stores/PgAuthzkitStore.js.map +1 -0
- package/dist/src/utils/envConfig.d.ts +2 -0
- package/dist/src/utils/envConfig.js +68 -0
- package/dist/src/utils/envConfig.js.map +1 -0
- package/dist/tests/Authzkit.test.d.ts +1 -0
- package/dist/tests/Authzkit.test.js +126 -0
- package/dist/tests/Authzkit.test.js.map +1 -0
- package/dist/tests/MongoAuthzkitStore.test.d.ts +1 -0
- package/dist/tests/MongoAuthzkitStore.test.js +161 -0
- package/dist/tests/MongoAuthzkitStore.test.js.map +1 -0
- package/dist/tests/MongoAuthzkitStoreCustom.test.d.ts +1 -0
- package/dist/tests/MongoAuthzkitStoreCustom.test.js +65 -0
- package/dist/tests/MongoAuthzkitStoreCustom.test.js.map +1 -0
- package/dist/tests/PgAuthzkitStore.test.d.ts +1 -0
- package/dist/tests/PgAuthzkitStore.test.js +163 -0
- package/dist/tests/PgAuthzkitStore.test.js.map +1 -0
- package/dist/tests/PgAuthzkitStoreCustom.test.d.ts +1 -0
- package/dist/tests/PgAuthzkitStoreCustom.test.js +74 -0
- package/dist/tests/PgAuthzkitStoreCustom.test.js.map +1 -0
- package/examples/express-app.ts +65 -0
- package/jest.config.js +9 -0
- package/package.json +57 -0
- package/src/classes/Authzkit.ts +214 -0
- package/src/dashboard/router.ts +79 -0
- package/src/dashboard/routes/permissions.ts +38 -0
- package/src/dashboard/routes/roles.ts +38 -0
- package/src/dashboard/routes/users.ts +81 -0
- package/src/dashboard/web/README.md +73 -0
- package/src/dashboard/web/eslint.config.js +23 -0
- package/src/dashboard/web/index.html +13 -0
- package/src/dashboard/web/package.json +31 -0
- package/src/dashboard/web/pnpm-lock.yaml +2094 -0
- package/src/dashboard/web/public/vite.svg +1 -0
- package/src/dashboard/web/src/App.css +42 -0
- package/src/dashboard/web/src/App.tsx +26 -0
- package/src/dashboard/web/src/assets/react.svg +1 -0
- package/src/dashboard/web/src/components/Navbar.tsx +53 -0
- package/src/dashboard/web/src/index.css +138 -0
- package/src/dashboard/web/src/main.tsx +10 -0
- package/src/dashboard/web/src/pages/PermissionsPage.tsx +87 -0
- package/src/dashboard/web/src/pages/RolesPage.tsx +98 -0
- package/src/dashboard/web/src/pages/UsersPage.tsx +146 -0
- package/src/dashboard/web/src/services/api.ts +59 -0
- package/src/dashboard/web/tsconfig.app.json +28 -0
- package/src/dashboard/web/tsconfig.json +7 -0
- package/src/dashboard/web/tsconfig.node.json +26 -0
- package/src/dashboard/web/vite.config.ts +8 -0
- package/src/drivers/mongodb/mongo-connection.ts +98 -0
- package/src/drivers/postgres/pg-connection.ts +159 -0
- package/src/index.ts +19 -0
- package/src/interfaces/Authorizable.ts +8 -0
- package/src/interfaces/IAuthzkitConfig.ts +19 -0
- package/src/interfaces/Permission.ts +4 -0
- package/src/interfaces/Role.ts +5 -0
- package/src/middleware/authzMiddleware.ts +60 -0
- package/src/stores/IAuthzkitStore.ts +33 -0
- package/src/stores/InMemoryAuthzkitStore.ts +101 -0
- package/src/stores/MongoAuthzkitStore.ts +171 -0
- package/src/stores/PgAuthzkitStore.ts +191 -0
- package/src/utils/envConfig.ts +70 -0
- package/tests/Authzkit.test.ts +157 -0
- package/tests/MongoAuthzkitStore.test.ts +204 -0
- package/tests/MongoAuthzkitStoreCustom.test.ts +75 -0
- package/tests/PgAuthzkitStore.test.ts +207 -0
- package/tests/PgAuthzkitStoreCustom.test.ts +90 -0
- package/tsconfig.json +37 -0
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const Authzkit_1 = require("../src/classes/Authzkit");
|
|
4
|
+
const pg_connection_1 = require("../src/drivers/postgres/pg-connection");
|
|
5
|
+
const pg_mem_1 = require("pg-mem");
|
|
6
|
+
const PgAuthzkitStore_1 = require("../src/stores/PgAuthzkitStore");
|
|
7
|
+
// Increase Jest timeout for PostgreSQL operations
|
|
8
|
+
jest.setTimeout(30000); // 30 seconds
|
|
9
|
+
describe('PgAuthzkitStore', () => {
|
|
10
|
+
let pgMemDb; // Use any because IMemoryDb doesn't have clone method directly
|
|
11
|
+
let pgConnection;
|
|
12
|
+
let pgAuthzkitStore;
|
|
13
|
+
let authzkit;
|
|
14
|
+
let user;
|
|
15
|
+
// No actual connection string needed for in-memory db
|
|
16
|
+
const CONNECTION_STRING = 'in-memory-db';
|
|
17
|
+
beforeAll(async () => {
|
|
18
|
+
// pg-mem's newDb() method creates a fresh, isolated database instance.
|
|
19
|
+
// We create one for the whole test suite as pg-mem can be reset efficiently.
|
|
20
|
+
pgMemDb = (0, pg_mem_1.newDb)();
|
|
21
|
+
const config = {
|
|
22
|
+
connection: {
|
|
23
|
+
type: 'postgres',
|
|
24
|
+
uri: CONNECTION_STRING,
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
pgConnection = pg_connection_1.PgConnection.getInstance(config, pgMemDb);
|
|
28
|
+
// No need to call pgConnection.connect() for pg-mem, as it's mocked
|
|
29
|
+
await pgConnection.initSchema(); // Initialize schema for tests
|
|
30
|
+
});
|
|
31
|
+
beforeEach(async () => {
|
|
32
|
+
// For pg-mem, pgMemDb.public.query('TRUNCATE ...') is the way to reset data.
|
|
33
|
+
// This is handled by authzkit.reset() which calls pgAuthzkitStore.reset().
|
|
34
|
+
// We need to re-initialize PgConnection for each test to ensure its internal state is clean.
|
|
35
|
+
const config = {
|
|
36
|
+
connection: {
|
|
37
|
+
type: 'postgres',
|
|
38
|
+
uri: CONNECTION_STRING + Math.random(),
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
pgConnection = pg_connection_1.PgConnection.getInstance(config, pgMemDb); // Unique connectionString for singleton instance tracking
|
|
42
|
+
pgAuthzkitStore = new PgAuthzkitStore_1.PgAuthzkitStore(pgConnection);
|
|
43
|
+
Authzkit_1.Authzkit.getInstance(pgAuthzkitStore); // Initialize Authzkit with the Pg store
|
|
44
|
+
authzkit = Authzkit_1.Authzkit.getInstance(); // Get the instance, ensure it's the one we just set
|
|
45
|
+
await authzkit.reset(); // Clear all data for current test using the clean schema
|
|
46
|
+
// Define some permissions and roles
|
|
47
|
+
await authzkit.definePermission('create_pg_post');
|
|
48
|
+
await authzkit.definePermission('edit_pg_post');
|
|
49
|
+
await authzkit.definePermission('delete_pg_post');
|
|
50
|
+
await authzkit.definePermission('view_pg_dashboard');
|
|
51
|
+
await authzkit.defineRole('pg_admin', [
|
|
52
|
+
'create_pg_post',
|
|
53
|
+
'edit_pg_post',
|
|
54
|
+
'delete_pg_post',
|
|
55
|
+
'view_pg_dashboard',
|
|
56
|
+
]);
|
|
57
|
+
await authzkit.defineRole('pg_editor', ['create_pg_post', 'edit_pg_post']);
|
|
58
|
+
await authzkit.defineRole('pg_viewer', ['view_pg_dashboard']);
|
|
59
|
+
user = { id: 'pg_user1', roles: [], permissions: [] }; // Using string ID
|
|
60
|
+
});
|
|
61
|
+
afterAll(async () => {
|
|
62
|
+
// No explicit disconnect needed for pg-mem
|
|
63
|
+
});
|
|
64
|
+
// --- Permission Definition Tests ---
|
|
65
|
+
test('should define a permission', async () => {
|
|
66
|
+
const perm = await authzkit.definePermission('new_pg_permission');
|
|
67
|
+
expect(perm).toEqual({ name: 'new_pg_permission', guard_name: undefined });
|
|
68
|
+
await expect(authzkit.definePermission('new_pg_permission')).rejects.toThrow(/Permission 'new_pg_permission' already exists./);
|
|
69
|
+
});
|
|
70
|
+
// --- Role Definition Tests ---
|
|
71
|
+
test('should define a role with permissions', async () => {
|
|
72
|
+
const role = await authzkit.defineRole('pg_moderator', ['edit_pg_post']);
|
|
73
|
+
expect(role).toEqual({
|
|
74
|
+
name: 'pg_moderator',
|
|
75
|
+
permissions: ['edit_pg_post'],
|
|
76
|
+
guard_name: undefined,
|
|
77
|
+
});
|
|
78
|
+
await expect(authzkit.defineRole('pg_admin')).rejects.toThrow(/Role 'pg_admin' already exists./);
|
|
79
|
+
});
|
|
80
|
+
test('should throw error if role is defined with non-existent permission', async () => {
|
|
81
|
+
await expect(authzkit.defineRole('bad_pg_role', ['non_existent_pg_permission'])).rejects.toThrow(/Permission 'non_existent_pg_permission' not found when defining role 'bad_pg_role'./);
|
|
82
|
+
});
|
|
83
|
+
// --- Assignment Tests ---
|
|
84
|
+
test('should assign a role to a user', async () => {
|
|
85
|
+
await authzkit.assignRole(user, 'pg_editor');
|
|
86
|
+
expect(await authzkit.hasRole(user, 'pg_editor')).toBe(true);
|
|
87
|
+
expect(await authzkit.hasRole(user, 'pg_admin')).toBe(false);
|
|
88
|
+
});
|
|
89
|
+
test('should assign a direct permission to a user', async () => {
|
|
90
|
+
await authzkit.assignPermission(user, 'create_pg_post');
|
|
91
|
+
expect(await authzkit.hasPermission(user, 'create_pg_post')).toBe(true);
|
|
92
|
+
expect(await authzkit.hasPermission(user, 'edit_pg_post')).toBe(false);
|
|
93
|
+
});
|
|
94
|
+
test('should throw error if assigning non-existent role', async () => {
|
|
95
|
+
await expect(authzkit.assignRole(user, 'non_existent_pg_role')).rejects.toThrow(/Role 'non_existent_pg_role' not found./);
|
|
96
|
+
});
|
|
97
|
+
test('should throw error if assigning non-existent permission', async () => {
|
|
98
|
+
await expect(authzkit.assignPermission(user, 'non_existent_pg_permission')).rejects.toThrow(/Permission 'non_existent_pg_permission' not found./);
|
|
99
|
+
});
|
|
100
|
+
// --- Revocation Tests ---
|
|
101
|
+
test('should revoke a role from a user', async () => {
|
|
102
|
+
await authzkit.assignRole(user, 'pg_editor');
|
|
103
|
+
expect(await authzkit.hasRole(user, 'pg_editor')).toBe(true);
|
|
104
|
+
await authzkit.revokeRole(user, 'pg_editor');
|
|
105
|
+
expect(await authzkit.hasRole(user, 'pg_editor')).toBe(false);
|
|
106
|
+
});
|
|
107
|
+
test('should revoke a direct permission from a user', async () => {
|
|
108
|
+
await authzkit.assignPermission(user, 'create_pg_post');
|
|
109
|
+
expect(await authzkit.hasPermission(user, 'create_pg_post')).toBe(true);
|
|
110
|
+
await authzkit.revokePermission(user, 'create_pg_post');
|
|
111
|
+
expect(await authzkit.hasPermission(user, 'create_pg_post')).toBe(false);
|
|
112
|
+
});
|
|
113
|
+
// --- Has Role/Permission Tests ---
|
|
114
|
+
test('user with role should have role', async () => {
|
|
115
|
+
await authzkit.assignRole(user, 'pg_admin');
|
|
116
|
+
expect(await authzkit.hasRole(user, 'pg_admin')).toBe(true);
|
|
117
|
+
});
|
|
118
|
+
test('user without role should not have role', async () => {
|
|
119
|
+
expect(await authzkit.hasRole(user, 'pg_admin')).toBe(false);
|
|
120
|
+
});
|
|
121
|
+
test('user with direct permission should have permission', async () => {
|
|
122
|
+
await authzkit.assignPermission(user, 'edit_pg_post');
|
|
123
|
+
expect(await authzkit.hasPermission(user, 'edit_pg_post')).toBe(true);
|
|
124
|
+
});
|
|
125
|
+
test('user with role having permission should have permission', async () => {
|
|
126
|
+
await authzkit.assignRole(user, 'pg_editor'); // pg_editor has create_pg_post, edit_pg_post
|
|
127
|
+
expect(await authzkit.hasPermission(user, 'create_pg_post')).toBe(true);
|
|
128
|
+
expect(await authzkit.hasPermission(user, 'edit_pg_post')).toBe(true);
|
|
129
|
+
expect(await authzkit.hasPermission(user, 'delete_pg_post')).toBe(false);
|
|
130
|
+
});
|
|
131
|
+
test('user with admin role should have all admin permissions', async () => {
|
|
132
|
+
await authzkit.assignRole(user, 'pg_admin');
|
|
133
|
+
expect(await authzkit.hasPermission(user, 'create_pg_post')).toBe(true);
|
|
134
|
+
expect(await authzkit.hasPermission(user, 'edit_pg_post')).toBe(true);
|
|
135
|
+
expect(await authzkit.hasPermission(user, 'delete_pg_post')).toBe(true);
|
|
136
|
+
expect(await authzkit.hasPermission(user, 'view_pg_dashboard')).toBe(true);
|
|
137
|
+
});
|
|
138
|
+
test('user with no permissions/roles should not have any permissions', async () => {
|
|
139
|
+
expect(await authzkit.hasPermission(user, 'create_pg_post')).toBe(false);
|
|
140
|
+
expect(await authzkit.hasRole(user, 'pg_admin')).toBe(false);
|
|
141
|
+
});
|
|
142
|
+
test('roleHasPermission should work correctly', async () => {
|
|
143
|
+
expect(await authzkit.roleHasPermission('pg_admin', 'create_pg_post')).toBe(true);
|
|
144
|
+
expect(await authzkit.roleHasPermission('pg_editor', 'delete_pg_post')).toBe(false);
|
|
145
|
+
expect(await authzkit.roleHasPermission('pg_viewer', 'view_pg_dashboard')).toBe(true);
|
|
146
|
+
expect(await authzkit.roleHasPermission('non_existent_pg_role', 'some_permission')).toBe(false);
|
|
147
|
+
});
|
|
148
|
+
// --- Reset Tests ---
|
|
149
|
+
test('reset should clear all permissions, roles, and assignments', async () => {
|
|
150
|
+
await authzkit.definePermission('test_pg_perm');
|
|
151
|
+
await authzkit.defineRole('test_pg_role', ['test_pg_perm']);
|
|
152
|
+
await authzkit.assignRole(user, 'test_pg_role');
|
|
153
|
+
await authzkit.assignPermission(user, 'test_pg_perm');
|
|
154
|
+
await authzkit.reset();
|
|
155
|
+
// After reset, defining again should not throw an error (as they were cleared)
|
|
156
|
+
await expect(authzkit.definePermission('test_pg_perm')).resolves.not.toThrow();
|
|
157
|
+
await expect(authzkit.defineRole('test_pg_role', ['test_pg_perm'])).resolves.not.toThrow();
|
|
158
|
+
// User should no longer have the role/permission
|
|
159
|
+
expect(await authzkit.hasRole(user, 'test_pg_role')).toBe(false);
|
|
160
|
+
expect(await authzkit.hasPermission(user, 'test_pg_perm')).toBe(false);
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
//# sourceMappingURL=PgAuthzkitStore.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PgAuthzkitStore.test.js","sourceRoot":"","sources":["../../tests/PgAuthzkitStore.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const Authzkit_1 = require("../src/classes/Authzkit");
|
|
4
|
+
const pg_connection_1 = require("../src/drivers/postgres/pg-connection");
|
|
5
|
+
const pg_mem_1 = require("pg-mem");
|
|
6
|
+
const PgAuthzkitStore_1 = require("../src/stores/PgAuthzkitStore");
|
|
7
|
+
// Increase Jest timeout
|
|
8
|
+
jest.setTimeout(30000);
|
|
9
|
+
describe('PgAuthzkitStore (Custom Tables)', () => {
|
|
10
|
+
let pgMemDb;
|
|
11
|
+
let pgConnection;
|
|
12
|
+
let pgAuthzkitStore;
|
|
13
|
+
let authzkit;
|
|
14
|
+
const CONNECTION_STRING = 'in-memory-db-custom';
|
|
15
|
+
beforeAll(async () => {
|
|
16
|
+
pgMemDb = (0, pg_mem_1.newDb)();
|
|
17
|
+
// We need to initialize schema with custom names
|
|
18
|
+
const config = {
|
|
19
|
+
connection: {
|
|
20
|
+
type: 'postgres',
|
|
21
|
+
uri: CONNECTION_STRING,
|
|
22
|
+
},
|
|
23
|
+
models: {
|
|
24
|
+
users: 'custom_users',
|
|
25
|
+
roles: 'custom_roles',
|
|
26
|
+
permissions: 'custom_permissions',
|
|
27
|
+
user_roles: 'custom_user_roles',
|
|
28
|
+
user_permissions: 'custom_user_permissions',
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
pgConnection = pg_connection_1.PgConnection.getInstance(config, pgMemDb);
|
|
32
|
+
await pgConnection.initSchema();
|
|
33
|
+
});
|
|
34
|
+
beforeEach(async () => {
|
|
35
|
+
// Config matching the one used in beforeAll (for singleton to return same instance)
|
|
36
|
+
// Actually, if we pass same config object (by value/content), getInstance should return same instance.
|
|
37
|
+
// But we used random string in other test. Here we can stick to one instance since we just want to verify table names.
|
|
38
|
+
// Or we can create a new instance if we want, but we need to pass pgMemDb again.
|
|
39
|
+
const config = {
|
|
40
|
+
connection: {
|
|
41
|
+
type: 'postgres',
|
|
42
|
+
uri: CONNECTION_STRING,
|
|
43
|
+
},
|
|
44
|
+
models: {
|
|
45
|
+
users: 'custom_users',
|
|
46
|
+
roles: 'custom_roles',
|
|
47
|
+
permissions: 'custom_permissions',
|
|
48
|
+
user_roles: 'custom_user_roles',
|
|
49
|
+
user_permissions: 'custom_user_permissions',
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
// We reuse the instance created in beforeAll
|
|
53
|
+
pgConnection = pg_connection_1.PgConnection.getInstance(config, pgMemDb);
|
|
54
|
+
pgAuthzkitStore = new PgAuthzkitStore_1.PgAuthzkitStore(pgConnection);
|
|
55
|
+
Authzkit_1.Authzkit.getInstance(pgAuthzkitStore);
|
|
56
|
+
authzkit = Authzkit_1.Authzkit.getInstance();
|
|
57
|
+
// We don't call reset() here because it might truncate tables we just created?
|
|
58
|
+
// Actually reset() calls truncateTables(). PgConnection.truncateTables() uses getTableName().
|
|
59
|
+
// So if config is correct, it should truncate custom tables.
|
|
60
|
+
await authzkit.reset();
|
|
61
|
+
await authzkit.definePermission('custom_pg_perm');
|
|
62
|
+
});
|
|
63
|
+
test('should store data in custom tables', async () => {
|
|
64
|
+
// Query pg-mem directly to see if data is in custom table
|
|
65
|
+
// pg-mem exposes the DB state via pgMemDb
|
|
66
|
+
// we can use pgConnection.query acting as a backdoor too, but let's try to verify via "direct" SQL on the connection
|
|
67
|
+
const res = await pgConnection.query('SELECT count(*) FROM custom_permissions WHERE name = $1', ['custom_pg_perm']);
|
|
68
|
+
expect(res.rows[0].count).toBe(1);
|
|
69
|
+
// Verify default table does not exist or is empty
|
|
70
|
+
// In pg-mem, querying non-existent table throws error.
|
|
71
|
+
await expect(pgConnection.query('SELECT count(*) FROM permissions')).rejects.toThrow();
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
//# sourceMappingURL=PgAuthzkitStoreCustom.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PgAuthzkitStoreCustom.test.js","sourceRoot":"","sources":["../../tests/PgAuthzkitStoreCustom.test.ts"],"names":[],"mappings":";;AAAA,sDAAmD;AACnD,yEAAqE;AACrE,mCAA+B;AAC/B,mEAAgE;AAGhE,wBAAwB;AACxB,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;AAEvB,QAAQ,CAAC,iCAAiC,EAAE,GAAG,EAAE;IAC/C,IAAI,OAAY,CAAC;IACjB,IAAI,YAA0B,CAAC;IAC/B,IAAI,eAAgC,CAAC;IACrC,IAAI,QAAkB,CAAC;IAEvB,MAAM,iBAAiB,GAAG,qBAAqB,CAAC;IAEhD,SAAS,CAAC,KAAK,IAAI,EAAE;QACnB,OAAO,GAAG,IAAA,cAAK,GAAE,CAAC;QAElB,iDAAiD;QACjD,MAAM,MAAM,GAAoB;YAC9B,UAAU,EAAE;gBACV,IAAI,EAAE,UAAU;gBAChB,GAAG,EAAE,iBAAiB;aACvB;YACD,MAAM,EAAE;gBACN,KAAK,EAAE,cAAc;gBACrB,KAAK,EAAE,cAAc;gBACrB,WAAW,EAAE,oBAAoB;gBACjC,UAAU,EAAE,mBAAmB;gBAC/B,gBAAgB,EAAE,yBAAyB;aAC5C;SACF,CAAC;QAEF,YAAY,GAAG,4BAAY,CAAC,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACzD,MAAM,YAAY,CAAC,UAAU,EAAE,CAAC;IAClC,CAAC,CAAC,CAAC;IAEH,UAAU,CAAC,KAAK,IAAI,EAAE;QACpB,oFAAoF;QACpF,uGAAuG;QACvG,uHAAuH;QACvH,iFAAiF;QAEjF,MAAM,MAAM,GAAoB;YAC9B,UAAU,EAAE;gBACV,IAAI,EAAE,UAAU;gBAChB,GAAG,EAAE,iBAAiB;aACvB;YACD,MAAM,EAAE;gBACN,KAAK,EAAE,cAAc;gBACrB,KAAK,EAAE,cAAc;gBACrB,WAAW,EAAE,oBAAoB;gBACjC,UAAU,EAAE,mBAAmB;gBAC/B,gBAAgB,EAAE,yBAAyB;aAC5C;SACF,CAAC;QAEF,6CAA6C;QAC7C,YAAY,GAAG,4BAAY,CAAC,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACzD,eAAe,GAAG,IAAI,iCAAe,CAAC,YAAY,CAAC,CAAC;QACpD,mBAAQ,CAAC,WAAW,CAAC,eAAe,CAAC,CAAC;QACtC,QAAQ,GAAG,mBAAQ,CAAC,WAAW,EAAE,CAAC;QAElC,+EAA+E;QAC/E,8FAA8F;QAC9F,6DAA6D;QAC7D,MAAM,QAAQ,CAAC,KAAK,EAAE,CAAC;QAEvB,MAAM,QAAQ,CAAC,gBAAgB,CAAC,gBAAgB,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,oCAAoC,EAAE,KAAK,IAAI,EAAE;QACpD,0DAA0D;QAC1D,0CAA0C;QAE1C,qHAAqH;QAErH,MAAM,GAAG,GAAG,MAAM,YAAY,CAAC,KAAK,CAClC,yDAAyD,EACzD,CAAC,gBAAgB,CAAC,CACnB,CAAC;QACF,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAElC,kDAAkD;QAClD,uDAAuD;QACvD,MAAM,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC,kCAAkC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;IACzF,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import {
|
|
3
|
+
Authzkit,
|
|
4
|
+
InMemoryAuthzkitStore,
|
|
5
|
+
createDashboardRouter,
|
|
6
|
+
loadConfigFromEnv,
|
|
7
|
+
} from '@zakyyudha/node-authzkit';
|
|
8
|
+
|
|
9
|
+
let authzkit;
|
|
10
|
+
|
|
11
|
+
const app = express();
|
|
12
|
+
const port = 3000;
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
// Initialize Authzkit
|
|
16
|
+
// Example: Load from env or default to InMemory
|
|
17
|
+
const config = loadConfigFromEnv();
|
|
18
|
+
if (config) {
|
|
19
|
+
console.log('Using configured store from environment');
|
|
20
|
+
// Here you would initialize Mongo/Pg connection based on config
|
|
21
|
+
// For this example we default to InMemory to keep it runnable without DB
|
|
22
|
+
} else {
|
|
23
|
+
console.log('Using In-Memory Store');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
authzkit = Authzkit.getInstance(new InMemoryAuthzkitStore());
|
|
27
|
+
|
|
28
|
+
// Setup Dashboard
|
|
29
|
+
// Mount at /authzkit
|
|
30
|
+
// Basic Auth: admin / mysecret
|
|
31
|
+
app.use(
|
|
32
|
+
'/authzkit',
|
|
33
|
+
createDashboardRouter({
|
|
34
|
+
authzkit,
|
|
35
|
+
secret: 'mysecret',
|
|
36
|
+
username: 'admin',
|
|
37
|
+
})
|
|
38
|
+
);
|
|
39
|
+
} catch (error) {
|
|
40
|
+
console.error('Failed to initialize Authzkit or Dashboard:', error);
|
|
41
|
+
process.exit(1); // Exit the process if initialization fails
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Global error handlers
|
|
45
|
+
process.on('uncaughtException', (err) => {
|
|
46
|
+
console.error('Uncaught Exception:', err);
|
|
47
|
+
});
|
|
48
|
+
process.on('unhandledRejection', (reason, promise) => {
|
|
49
|
+
console.error('Unhandled Rejection at:', promise, 'reason:', reason);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
app.get('/', (req, res) => {
|
|
53
|
+
res.send(
|
|
54
|
+
'Go to <a href="/authzkit/">/authzkit/</a> to manage permissions. (User: admin, Pass: mysecret)'
|
|
55
|
+
);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const server = app.listen(port, () => {
|
|
59
|
+
console.log(`Example app listening at http://localhost:${port}`);
|
|
60
|
+
console.log(`Dashboard available at http://localhost:${port}/authzkit/`);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
server.on('error', (err) => {
|
|
64
|
+
console.error('Server error:', err);
|
|
65
|
+
});
|
package/jest.config.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@zakyyudha/node-authzkit",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A very simple Node.js authorization library",
|
|
5
|
+
"main": "dist/src/index.js",
|
|
6
|
+
"types": "dist/src/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"prebuild": "rm -rf dist",
|
|
9
|
+
"build:web": "cd src/dashboard/web && pnpm install && pnpm build",
|
|
10
|
+
"build": "pnpm build:web && tsc && copyfiles -u 3 \"src/dashboard/web/dist/**/*\" dist/src/dashboard/web/",
|
|
11
|
+
"test": "jest --coverage",
|
|
12
|
+
"lint": "eslint \"src/**/*.ts\" \"tests/**/*.ts\" --fix",
|
|
13
|
+
"format": "prettier --write \"src/**/*.ts\" \"tests/**/*.ts\" \"examples/**/*.ts\"",
|
|
14
|
+
"start:dev": "ts-node-dev --respawn --transpile-only -r tsconfig-paths/register examples/express-app.ts",
|
|
15
|
+
"clean": "rm -rf dist node_modules",
|
|
16
|
+
"release": "release-it"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"authorization",
|
|
20
|
+
"permissions",
|
|
21
|
+
"roles",
|
|
22
|
+
"node",
|
|
23
|
+
"typescript"
|
|
24
|
+
],
|
|
25
|
+
"license": "ISC",
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@eslint/js": "^9.39.2",
|
|
28
|
+
"@types/basic-auth": "^1.1.8",
|
|
29
|
+
"@types/jest": "^29.5.12",
|
|
30
|
+
"@types/mongodb-memory-server": "^2.3.0",
|
|
31
|
+
"@typescript-eslint/eslint-plugin": "^7.16.0",
|
|
32
|
+
"@typescript-eslint/parser": "^7.16.0",
|
|
33
|
+
"copyfiles": "^2.4.1",
|
|
34
|
+
"eslint": "^8.57.0",
|
|
35
|
+
"globals": "^17.2.0",
|
|
36
|
+
"jest": "^29.7.0",
|
|
37
|
+
"mongodb-memory-server": "^11.0.1",
|
|
38
|
+
"pg-mem": "^3.0.8",
|
|
39
|
+
"prettier": "^3.3.3",
|
|
40
|
+
"release-it": "^19.2.4",
|
|
41
|
+
"ts-jest": "^29.4.6",
|
|
42
|
+
"ts-node": "^10.9.2",
|
|
43
|
+
"ts-node-dev": "^2.0.0",
|
|
44
|
+
"tsconfig-paths": "^4.2.0",
|
|
45
|
+
"typescript": "^5.5.3",
|
|
46
|
+
"typescript-eslint": "^8.54.0"
|
|
47
|
+
},
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"@types/express": "^5.0.6",
|
|
50
|
+
"@types/mongodb": "^4.0.7",
|
|
51
|
+
"@types/pg": "^8.16.0",
|
|
52
|
+
"basic-auth": "^2.0.1",
|
|
53
|
+
"express": "^5.2.1",
|
|
54
|
+
"mongodb": "^7.0.0",
|
|
55
|
+
"pg": "^8.17.2"
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import { Authorizable } from '../interfaces/Authorizable';
|
|
2
|
+
import { Permission } from '../interfaces/Permission';
|
|
3
|
+
import { Role } from '../interfaces/Role';
|
|
4
|
+
import { IAuthzkitStore } from '../stores/IAuthzkitStore';
|
|
5
|
+
import { InMemoryAuthzkitStore } from '../stores/InMemoryAuthzkitStore';
|
|
6
|
+
|
|
7
|
+
export class Authzkit {
|
|
8
|
+
private static instance: Authzkit;
|
|
9
|
+
private store: IAuthzkitStore;
|
|
10
|
+
|
|
11
|
+
private constructor(store?: IAuthzkitStore) {
|
|
12
|
+
this.store = store || new InMemoryAuthzkitStore();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Returns the singleton instance of Authzkit.
|
|
17
|
+
* Optionally accepts a store implementation to use. If no store is provided,
|
|
18
|
+
* an `InMemoryAuthzkitStore` will be used by default.
|
|
19
|
+
*/
|
|
20
|
+
public static getInstance(store?: IAuthzkitStore): Authzkit {
|
|
21
|
+
if (!Authzkit.instance) {
|
|
22
|
+
Authzkit.instance = new Authzkit(store);
|
|
23
|
+
}
|
|
24
|
+
return Authzkit.instance;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Defines a new permission.
|
|
29
|
+
* @param name The name of the permission.
|
|
30
|
+
* @param guard_name Optional guard name.
|
|
31
|
+
*/
|
|
32
|
+
public async definePermission(name: string, guard_name?: string): Promise<Permission> {
|
|
33
|
+
if (await this.store.hasPermission(name)) {
|
|
34
|
+
throw new Error(`Permission '${name}' already exists.`);
|
|
35
|
+
}
|
|
36
|
+
const permission: Permission = { name, guard_name };
|
|
37
|
+
await this.store.setPermission(permission);
|
|
38
|
+
return permission;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Retrieves all defined permissions.
|
|
43
|
+
* @returns An array of Permission objects.
|
|
44
|
+
*/
|
|
45
|
+
public async getPermissions(): Promise<Permission[]> {
|
|
46
|
+
return await this.store.getPermissions();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Retrieves all defined roles.
|
|
51
|
+
* @returns An array of Role objects.
|
|
52
|
+
*/
|
|
53
|
+
public async getRoles(): Promise<Role[]> {
|
|
54
|
+
return await this.store.getRoles();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Defines a new role with associated permissions.
|
|
59
|
+
* @param name The name of the role.
|
|
60
|
+
* @param permissions An array of permission names.
|
|
61
|
+
* @param guard_name Optional guard name.
|
|
62
|
+
*/
|
|
63
|
+
public async defineRole(
|
|
64
|
+
name: string,
|
|
65
|
+
permissions: string[] = [],
|
|
66
|
+
guard_name?: string
|
|
67
|
+
): Promise<Role> {
|
|
68
|
+
if (await this.store.hasRole(name)) {
|
|
69
|
+
throw new Error(`Role '${name}' already exists.`);
|
|
70
|
+
}
|
|
71
|
+
// Ensure all provided permissions exist
|
|
72
|
+
for (const pName of permissions) {
|
|
73
|
+
if (!(await this.store.hasPermission(pName))) {
|
|
74
|
+
throw new Error(`Permission '${pName}' not found when defining role '${name}'.`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const role: Role = { name, permissions, guard_name }; // Store permission names
|
|
79
|
+
await this.store.setRole(role);
|
|
80
|
+
return role;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Deletes a permission.
|
|
85
|
+
* @param name The name of the permission to delete.
|
|
86
|
+
*/
|
|
87
|
+
public async deletePermission(name: string): Promise<void> {
|
|
88
|
+
await this.store.deletePermission(name);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Deletes a role.
|
|
93
|
+
* @param name The name of the role to delete.
|
|
94
|
+
*/
|
|
95
|
+
public async deleteRole(name: string): Promise<void> {
|
|
96
|
+
await this.store.deleteRole(name);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Retrieves roles assigned to a user.
|
|
101
|
+
* @param authorizable The user for whom to retrieve roles.
|
|
102
|
+
* @returns A Promise resolving to a Set of role names.
|
|
103
|
+
*/
|
|
104
|
+
public async getUserRoles(authorizable: Authorizable): Promise<Set<string>> {
|
|
105
|
+
return await this.store.getUserRoles(authorizable.id);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Retrieves permissions assigned directly to a user.
|
|
110
|
+
* @param authorizable The user for whom to retrieve permissions.
|
|
111
|
+
* @returns A Promise resolving to a Set of permission names.
|
|
112
|
+
*/
|
|
113
|
+
public async getUserPermissions(authorizable: Authorizable): Promise<Set<string>> {
|
|
114
|
+
return await this.store.getUserPermissions(authorizable.id);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Assigns a role to an authorizable entity.
|
|
119
|
+
* @param authorizable The entity to assign the role to.
|
|
120
|
+
* @param roleName The name of the role to assign.
|
|
121
|
+
*/
|
|
122
|
+
public async assignRole(authorizable: Authorizable, roleName: string): Promise<void> {
|
|
123
|
+
if (!(await this.store.hasRole(roleName))) {
|
|
124
|
+
throw new Error(`Role '${roleName}' not found.`);
|
|
125
|
+
}
|
|
126
|
+
await this.store.addUserRole(authorizable.id, roleName);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Assigns a direct permission to an authorizable entity.
|
|
131
|
+
* @param authorizable The entity to assign the permission to.
|
|
132
|
+
* @param permissionName The name of the permission to assign.
|
|
133
|
+
*/
|
|
134
|
+
public async assignPermission(authorizable: Authorizable, permissionName: string): Promise<void> {
|
|
135
|
+
if (!(await this.store.hasPermission(permissionName))) {
|
|
136
|
+
throw new Error(`Permission '${permissionName}' not found.`);
|
|
137
|
+
}
|
|
138
|
+
await this.store.addUserPermission(authorizable.id, permissionName);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Revokes a role from an authorizable entity.
|
|
143
|
+
* @param authorizable The entity to revoke the role from.
|
|
144
|
+
* @param roleName The name of the role to revoke.
|
|
145
|
+
*/
|
|
146
|
+
public async revokeRole(authorizable: Authorizable, roleName: string): Promise<void> {
|
|
147
|
+
await this.store.removeUserRole(authorizable.id, roleName);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Revokes a direct permission from an authorizable entity.
|
|
152
|
+
* @param authorizable The entity to revoke the permission from.
|
|
153
|
+
* @param permissionName The name of the permission to revoke.
|
|
154
|
+
*/
|
|
155
|
+
public async revokePermission(authorizable: Authorizable, permissionName: string): Promise<void> {
|
|
156
|
+
await this.store.removeUserPermission(authorizable.id, permissionName);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Checks if an authorizable entity has a given role.
|
|
161
|
+
* @param authorizable The entity to check.
|
|
162
|
+
* @param roleName The name of the role to check for.
|
|
163
|
+
* @returns True if the entity has the role, false otherwise.
|
|
164
|
+
*/
|
|
165
|
+
public async hasRole(authorizable: Authorizable, roleName: string): Promise<boolean> {
|
|
166
|
+
return await this.store.hasUserRole(authorizable.id, roleName);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Checks if an authorizable entity has a given permission, either directly or through a role.
|
|
171
|
+
* @param authorizable The entity to check.
|
|
172
|
+
* @param permissionName The name of the permission to check for.
|
|
173
|
+
* @returns True if the entity has the permission, false otherwise.
|
|
174
|
+
*/
|
|
175
|
+
public async hasPermission(authorizable: Authorizable, permissionName: string): Promise<boolean> {
|
|
176
|
+
// Check direct permissions from store
|
|
177
|
+
if (await this.store.hasUserPermission(authorizable.id, permissionName)) {
|
|
178
|
+
return true;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Check permissions via roles
|
|
182
|
+
const roles = await this.store.getUserRoles(authorizable.id);
|
|
183
|
+
if (roles) {
|
|
184
|
+
for (const roleName of roles) {
|
|
185
|
+
const role = await this.store.getRole(roleName);
|
|
186
|
+
// Check if the role has the permission
|
|
187
|
+
if (role && role.permissions.includes(permissionName)) {
|
|
188
|
+
return true;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return false;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Checks if a role has a specific permission.
|
|
198
|
+
* @param roleName The name of the role.
|
|
199
|
+
* @param permissionName The name of the permission.
|
|
200
|
+
* @returns True if the role has the permission, false otherwise.
|
|
201
|
+
*/
|
|
202
|
+
public async roleHasPermission(roleName: string, permissionName: string): Promise<boolean> {
|
|
203
|
+
const role = await this.store.getRole(roleName);
|
|
204
|
+
return role ? role.permissions.includes(permissionName) : false;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Resets all defined permissions, roles, and assigned user roles/permissions.
|
|
209
|
+
* Useful for testing.
|
|
210
|
+
*/
|
|
211
|
+
public async reset(): Promise<void> {
|
|
212
|
+
await this.store.reset();
|
|
213
|
+
}
|
|
214
|
+
}
|