@xeplr/auth 1.0.0 → 1.0.2
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/bin/migrate.js +58 -25
- package/bin/server.js +12 -30
- package/index.js +188 -48
- package/lib/adminRouter.js +20 -92
- package/lib/authHelper.js +26 -3
- package/lib/authMiddleware.js +39 -13
- package/lib/authRouter.js +144 -12
- package/lib/authService.js +448 -16
- package/lib/hooks.js +50 -0
- package/lib/mtMembershipMiddleware.js +61 -0
- package/lib/seed.js +74 -0
- package/lib/sessionService.js +54 -6
- package/lib/ticketService.js +88 -0
- package/lib/tokenDecision.js +45 -0
- package/migrations/0001_extensions.sql +9 -0
- package/migrations/0002_users.sql +31 -0
- package/migrations/0003_catalog_tables.sql +82 -0
- package/migrations/0004_role_mappings.sql +78 -0
- package/migrations/0005_user_tenants_mapping.sql +39 -0
- package/migrations/0006_seed_catalog.sql +120 -0
- package/models/Api.js +3 -0
- package/models/ApisRolesMapping.js +3 -0
- package/models/Menu.js +3 -0
- package/models/MenuRolesMapping.js +3 -0
- package/models/Role.js +8 -0
- package/models/UiElement.js +3 -0
- package/models/UiElementsRolesMapping.js +3 -0
- package/models/UiPage.js +3 -0
- package/models/UiPagesRolesMapping.js +3 -0
- package/models/User.js +3 -13
- package/models/UserRolesMapping.js +3 -0
- package/models/UserTenantsMapping.js +20 -2
- package/models/index.js +0 -2
- package/package.json +26 -5
- package/migrations/0001_users.js +0 -22
- package/migrations/0002_menus.js +0 -16
- package/migrations/0003_apis.js +0 -16
- package/migrations/0004_uiPages.js +0 -16
- package/migrations/0005_uiElements.js +0 -16
- package/migrations/0006_roles.js +0 -15
- package/migrations/0007_apisRolesMapping.js +0 -16
- package/migrations/0008_uiPagesRolesMapping.js +0 -16
- package/migrations/0009_uiElementsRolesMapping.js +0 -16
- package/migrations/0010_menuRolesMapping.js +0 -16
- package/migrations/0011_userRolesMapping.js +0 -16
- package/migrations/0012_users_add_reset_token.js +0 -13
- package/migrations/0013_add_isPublic.js +0 -27
- package/migrations/0014_users_add_activation_token.js +0 -13
- package/migrations/0015_add_mt_columns.js +0 -41
- package/migrations/0016_tenants.js +0 -23
- package/migrations/0017_userTenantsMapping.js +0 -20
- package/models/Tenant.js +0 -63
- package/seeds/001_roles.js +0 -26
- package/seeds/002_apis.js +0 -70
- package/seeds/003_pages.js +0 -41
- package/seeds/004_elements.js +0 -46
- package/seeds/005_menus.js +0 -35
- package/seeds/006_default_tenant.js +0 -50
- package/seeds/zzz_admin_access.js +0 -49
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
-- 0006_seed_catalog.sql
|
|
2
|
+
-- Base roles + the full apis/uiPages/uiElements/menus catalog for every route
|
|
3
|
+
-- xeplr-auth ships (authRouter.js + adminRouter.js), plus the "god-mode"
|
|
4
|
+
-- mapping of Super Admin/Admin to everything that exists at this point.
|
|
5
|
+
--
|
|
6
|
+
-- ids use pgcrypto's gen_random_bytes (enabled in 0001) to match the app's
|
|
7
|
+
-- own generateId() shape: a 24-char lowercase hex string.
|
|
8
|
+
--
|
|
9
|
+
-- isPublic:true rows need no role mapping — accessService.getUserAccess()
|
|
10
|
+
-- unions every isPublic:true row into every authenticated user's access,
|
|
11
|
+
-- regardless of role (see @xeplr/auth/lib/accessService.js getPublicItems()).
|
|
12
|
+
|
|
13
|
+
INSERT INTO "roles" (id, name, "isActive", "mtId1", "recordCreatedDate", "recordModifiedDate") VALUES
|
|
14
|
+
(encode(gen_random_bytes(12), 'hex'), 'Super Admin', true, '*', now(), now()),
|
|
15
|
+
(encode(gen_random_bytes(12), 'hex'), 'Admin', true, '*', now(), now()),
|
|
16
|
+
(encode(gen_random_bytes(12), 'hex'), 'Editor', true, '*', now(), now()),
|
|
17
|
+
(encode(gen_random_bytes(12), 'hex'), 'Viewer', true, '*', now(), now());
|
|
18
|
+
|
|
19
|
+
INSERT INTO "apis" (id, name, "apiGroup", "isPublic", "isActive", "mtId1", "recordCreatedDate", "recordModifiedDate") VALUES
|
|
20
|
+
(encode(gen_random_bytes(12), 'hex'), '/auth/api/register', '', true, true, '*', now(), now()),
|
|
21
|
+
(encode(gen_random_bytes(12), 'hex'), '/auth/api/activate', '', true, true, '*', now(), now()),
|
|
22
|
+
(encode(gen_random_bytes(12), 'hex'), '/auth/api/login', '', true, true, '*', now(), now()),
|
|
23
|
+
(encode(gen_random_bytes(12), 'hex'), '/auth/api/forgot-password', '', true, true, '*', now(), now()),
|
|
24
|
+
(encode(gen_random_bytes(12), 'hex'), '/auth/api/reset-password', '', true, true, '*', now(), now()),
|
|
25
|
+
(encode(gen_random_bytes(12), 'hex'), '/auth/api/refresh', '', true, true, '*', now(), now()),
|
|
26
|
+
(encode(gen_random_bytes(12), 'hex'), '/auth/api/logout', '', true, true, '*', now(), now()),
|
|
27
|
+
(encode(gen_random_bytes(12), 'hex'), '/auth/api/me', 'account:view', false, true, '*', now(), now()),
|
|
28
|
+
(encode(gen_random_bytes(12), 'hex'), '/auth/api/profile', 'account:view', false, true, '*', now(), now()),
|
|
29
|
+
(encode(gen_random_bytes(12), 'hex'), '/auth/api/profile/avatar', 'account:edit', false, true, '*', now(), now()),
|
|
30
|
+
(encode(gen_random_bytes(12), 'hex'), '/auth/api/change-password', 'account:edit', false, true, '*', now(), now()),
|
|
31
|
+
(encode(gen_random_bytes(12), 'hex'), '/auth/api/request-phone-otp', 'account:edit', false, true, '*', now(), now()),
|
|
32
|
+
(encode(gen_random_bytes(12), 'hex'), '/auth/api/verify-phone-otp', 'account:edit', false, true, '*', now(), now()),
|
|
33
|
+
(encode(gen_random_bytes(12), 'hex'), '/auth/api/admin/users', 'users:view', false, true, '*', now(), now()),
|
|
34
|
+
(encode(gen_random_bytes(12), 'hex'), '/auth/api/admin/roles', 'users:view', false, true, '*', now(), now()),
|
|
35
|
+
(encode(gen_random_bytes(12), 'hex'), '/auth/api/admin/user-role', 'users:edit', false, true, '*', now(), now()),
|
|
36
|
+
(encode(gen_random_bytes(12), 'hex'), '/auth/api/admin/access-items', 'access:view', false, true, '*', now(), now()),
|
|
37
|
+
(encode(gen_random_bytes(12), 'hex'), '/auth/api/admin/access-role', 'access:edit', false, true, '*', now(), now()),
|
|
38
|
+
(encode(gen_random_bytes(12), 'hex'), '/auth/api/admin/module-role', 'access:edit', false, true, '*', now(), now()),
|
|
39
|
+
(encode(gen_random_bytes(12), 'hex'), '/auth/api/admin/master/roles', 'settings:view', false, true, '*', now(), now()),
|
|
40
|
+
(encode(gen_random_bytes(12), 'hex'), '/auth/api/admin/master/roles/save', 'settings:edit', false, true, '*', now(), now()),
|
|
41
|
+
(encode(gen_random_bytes(12), 'hex'), '/auth/api/admin/master/roles/delete', 'settings:delete', false, true, '*', now(), now()),
|
|
42
|
+
(encode(gen_random_bytes(12), 'hex'), '/auth/api/admin/master/apis', 'settings:view', false, true, '*', now(), now()),
|
|
43
|
+
(encode(gen_random_bytes(12), 'hex'), '/auth/api/admin/master/apis/save', 'settings:edit', false, true, '*', now(), now()),
|
|
44
|
+
(encode(gen_random_bytes(12), 'hex'), '/auth/api/admin/master/apis/delete', 'settings:delete', false, true, '*', now(), now()),
|
|
45
|
+
(encode(gen_random_bytes(12), 'hex'), '/auth/api/admin/master/pages', 'settings:view', false, true, '*', now(), now()),
|
|
46
|
+
(encode(gen_random_bytes(12), 'hex'), '/auth/api/admin/master/pages/save', 'settings:edit', false, true, '*', now(), now()),
|
|
47
|
+
(encode(gen_random_bytes(12), 'hex'), '/auth/api/admin/master/pages/delete', 'settings:delete', false, true, '*', now(), now()),
|
|
48
|
+
(encode(gen_random_bytes(12), 'hex'), '/auth/api/admin/master/elements', 'settings:view', false, true, '*', now(), now()),
|
|
49
|
+
(encode(gen_random_bytes(12), 'hex'), '/auth/api/admin/master/elements/save', 'settings:edit', false, true, '*', now(), now()),
|
|
50
|
+
(encode(gen_random_bytes(12), 'hex'), '/auth/api/admin/master/elements/delete', 'settings:delete', false, true, '*', now(), now()),
|
|
51
|
+
(encode(gen_random_bytes(12), 'hex'), '/auth/api/admin/master/menus', 'settings:view', false, true, '*', now(), now()),
|
|
52
|
+
(encode(gen_random_bytes(12), 'hex'), '/auth/api/admin/master/menus/save', 'settings:edit', false, true, '*', now(), now()),
|
|
53
|
+
(encode(gen_random_bytes(12), 'hex'), '/auth/api/admin/master/menus/delete', 'settings:delete', false, true, '*', now(), now());
|
|
54
|
+
|
|
55
|
+
INSERT INTO "uiPages" (id, name, "uiPagesGroup", "isPublic", "isActive", "mtId1", "recordCreatedDate", "recordModifiedDate") VALUES
|
|
56
|
+
(encode(gen_random_bytes(12), 'hex'), 'Login', '', true, true, '*', now(), now()),
|
|
57
|
+
(encode(gen_random_bytes(12), 'hex'), 'Register', '', true, true, '*', now(), now()),
|
|
58
|
+
(encode(gen_random_bytes(12), 'hex'), 'Forgot Password', '', true, true, '*', now(), now()),
|
|
59
|
+
(encode(gen_random_bytes(12), 'hex'), 'Reset Password', '', true, true, '*', now(), now()),
|
|
60
|
+
(encode(gen_random_bytes(12), 'hex'), 'Activate Account', '', true, true, '*', now(), now()),
|
|
61
|
+
(encode(gen_random_bytes(12), 'hex'), 'Not Activated', '', true, true, '*', now(), now()),
|
|
62
|
+
(encode(gen_random_bytes(12), 'hex'), 'Profile', 'account:view', true, true, '*', now(), now()),
|
|
63
|
+
(encode(gen_random_bytes(12), 'hex'), 'Change Password', 'account:edit', true, true, '*', now(), now()),
|
|
64
|
+
(encode(gen_random_bytes(12), 'hex'), 'User Roles', 'users:view', false, true, '*', now(), now()),
|
|
65
|
+
(encode(gen_random_bytes(12), 'hex'), 'Access Matrix', 'access:view', false, true, '*', now(), now()),
|
|
66
|
+
(encode(gen_random_bytes(12), 'hex'), 'Master Settings', 'settings:view', false, true, '*', now(), now());
|
|
67
|
+
|
|
68
|
+
INSERT INTO "uiElements" (id, name, "uiElementsGroup", "isActive", "mtId1", "recordCreatedDate", "recordModifiedDate") VALUES
|
|
69
|
+
(encode(gen_random_bytes(12), 'hex'), 'Assign Role Button', 'users:edit', true, '*', now(), now()),
|
|
70
|
+
(encode(gen_random_bytes(12), 'hex'), 'Remove Role Button', 'users:edit', true, '*', now(), now()),
|
|
71
|
+
(encode(gen_random_bytes(12), 'hex'), 'Toggle Access Checkbox', 'access:edit', true, '*', now(), now()),
|
|
72
|
+
(encode(gen_random_bytes(12), 'hex'), 'Toggle Module Access Checkbox', 'access:edit', true, '*', now(), now()),
|
|
73
|
+
(encode(gen_random_bytes(12), 'hex'), 'Add Role Button', 'settings:edit', true, '*', now(), now()),
|
|
74
|
+
(encode(gen_random_bytes(12), 'hex'), 'Delete Role Button', 'settings:delete', true, '*', now(), now()),
|
|
75
|
+
(encode(gen_random_bytes(12), 'hex'), 'Add API Button', 'settings:edit', true, '*', now(), now()),
|
|
76
|
+
(encode(gen_random_bytes(12), 'hex'), 'Delete API Button', 'settings:delete', true, '*', now(), now()),
|
|
77
|
+
(encode(gen_random_bytes(12), 'hex'), 'Add Page Button', 'settings:edit', true, '*', now(), now()),
|
|
78
|
+
(encode(gen_random_bytes(12), 'hex'), 'Delete Page Button', 'settings:delete', true, '*', now(), now()),
|
|
79
|
+
(encode(gen_random_bytes(12), 'hex'), 'Add Element Button', 'settings:edit', true, '*', now(), now()),
|
|
80
|
+
(encode(gen_random_bytes(12), 'hex'), 'Delete Element Button', 'settings:delete', true, '*', now(), now()),
|
|
81
|
+
(encode(gen_random_bytes(12), 'hex'), 'Add Menu Button', 'settings:edit', true, '*', now(), now()),
|
|
82
|
+
(encode(gen_random_bytes(12), 'hex'), 'Delete Menu Button', 'settings:delete', true, '*', now(), now());
|
|
83
|
+
|
|
84
|
+
INSERT INTO "menus" (id, name, "menuGroup", "isPublic", "isActive", "mtId1", "recordCreatedDate", "recordModifiedDate") VALUES
|
|
85
|
+
(encode(gen_random_bytes(12), 'hex'), 'Login', '', true, true, '*', now(), now()),
|
|
86
|
+
(encode(gen_random_bytes(12), 'hex'), 'Register', '', true, true, '*', now(), now()),
|
|
87
|
+
(encode(gen_random_bytes(12), 'hex'), 'Forgot Password', '', true, true, '*', now(), now()),
|
|
88
|
+
(encode(gen_random_bytes(12), 'hex'), 'Reset Password', '', true, true, '*', now(), now()),
|
|
89
|
+
(encode(gen_random_bytes(12), 'hex'), 'Activate Account', '', true, true, '*', now(), now()),
|
|
90
|
+
(encode(gen_random_bytes(12), 'hex'), 'Not Activated', '', true, true, '*', now(), now()),
|
|
91
|
+
(encode(gen_random_bytes(12), 'hex'), 'Notifications', 'account:view', true, true, '*', now(), now()),
|
|
92
|
+
(encode(gen_random_bytes(12), 'hex'), 'Profile', 'account:view', true, true, '*', now(), now()),
|
|
93
|
+
(encode(gen_random_bytes(12), 'hex'), 'Change Password', 'account:edit', true, true, '*', now(), now()),
|
|
94
|
+
(encode(gen_random_bytes(12), 'hex'), 'User Roles', 'users:view', false, true, '*', now(), now()),
|
|
95
|
+
(encode(gen_random_bytes(12), 'hex'), 'Access Matrix', 'access:view', false, true, '*', now(), now()),
|
|
96
|
+
(encode(gen_random_bytes(12), 'hex'), 'Master Settings', 'settings:view', false, true, '*', now(), now()),
|
|
97
|
+
(encode(gen_random_bytes(12), 'hex'), 'Admin', 'admin:view', false, true, '*', now(), now());
|
|
98
|
+
|
|
99
|
+
-- God-mode: Super Admin + Admin see everything that exists at this point,
|
|
100
|
+
-- public or not (isPublic:true rows would reach them anyway — this also
|
|
101
|
+
-- covers the non-public ones without hand-picking which).
|
|
102
|
+
INSERT INTO "apisRolesMapping" (id, "roleId", "apiId", "isActive", "mtId1", "recordCreatedDate", "recordModifiedDate")
|
|
103
|
+
SELECT encode(gen_random_bytes(12), 'hex'), r.id, a.id, true, '*', now(), now()
|
|
104
|
+
FROM "roles" r CROSS JOIN "apis" a
|
|
105
|
+
WHERE r.name IN ('Super Admin', 'Admin');
|
|
106
|
+
|
|
107
|
+
INSERT INTO "uiPagesRolesMapping" (id, "roleId", "uiPageId", "isActive", "mtId1", "recordCreatedDate", "recordModifiedDate")
|
|
108
|
+
SELECT encode(gen_random_bytes(12), 'hex'), r.id, p.id, true, '*', now(), now()
|
|
109
|
+
FROM "roles" r CROSS JOIN "uiPages" p
|
|
110
|
+
WHERE r.name IN ('Super Admin', 'Admin');
|
|
111
|
+
|
|
112
|
+
INSERT INTO "uiElementsRolesMapping" (id, "roleId", "uiElementId", "isActive", "mtId1", "recordCreatedDate", "recordModifiedDate")
|
|
113
|
+
SELECT encode(gen_random_bytes(12), 'hex'), r.id, e.id, true, '*', now(), now()
|
|
114
|
+
FROM "roles" r CROSS JOIN "uiElements" e
|
|
115
|
+
WHERE r.name IN ('Super Admin', 'Admin');
|
|
116
|
+
|
|
117
|
+
INSERT INTO "menuRolesMapping" (id, "menuId", "roleId", "isActive", "mtId1", "recordCreatedDate", "recordModifiedDate")
|
|
118
|
+
SELECT encode(gen_random_bytes(12), 'hex'), m.id, r.id, true, '*', now(), now()
|
|
119
|
+
FROM "roles" r CROSS JOIN "menus" m
|
|
120
|
+
WHERE r.name IN ('Super Admin', 'Admin');
|
package/models/Api.js
CHANGED
|
@@ -4,6 +4,9 @@ class Api extends BaseModel {
|
|
|
4
4
|
static get tableName() { return 'apis'; }
|
|
5
5
|
static get idColumn() { return 'id'; }
|
|
6
6
|
|
|
7
|
+
// Shared RBAC catalog, not per-tenant data — see Role.js for why.
|
|
8
|
+
static get multiTenant() { return false; }
|
|
9
|
+
|
|
7
10
|
static get jsonSchema() {
|
|
8
11
|
return {
|
|
9
12
|
type: 'object',
|
|
@@ -4,6 +4,9 @@ class ApisRolesMapping extends BaseModel {
|
|
|
4
4
|
static get tableName() { return 'apisRolesMapping'; }
|
|
5
5
|
static get idColumn() { return 'id'; }
|
|
6
6
|
|
|
7
|
+
// Shared RBAC catalog, not per-tenant data — see Role.js for why.
|
|
8
|
+
static get multiTenant() { return false; }
|
|
9
|
+
|
|
7
10
|
static get jsonSchema() {
|
|
8
11
|
return {
|
|
9
12
|
type: 'object',
|
package/models/Menu.js
CHANGED
|
@@ -4,6 +4,9 @@ class Menu extends BaseModel {
|
|
|
4
4
|
static get tableName() { return 'menus'; }
|
|
5
5
|
static get idColumn() { return 'id'; }
|
|
6
6
|
|
|
7
|
+
// Shared RBAC catalog, not per-tenant data — see Role.js for why.
|
|
8
|
+
static get multiTenant() { return false; }
|
|
9
|
+
|
|
7
10
|
static get jsonSchema() {
|
|
8
11
|
return {
|
|
9
12
|
type: 'object',
|
|
@@ -4,6 +4,9 @@ class MenuRolesMapping extends BaseModel {
|
|
|
4
4
|
static get tableName() { return 'menuRolesMapping'; }
|
|
5
5
|
static get idColumn() { return 'id'; }
|
|
6
6
|
|
|
7
|
+
// Shared RBAC catalog, not per-tenant data — see Role.js for why.
|
|
8
|
+
static get multiTenant() { return false; }
|
|
9
|
+
|
|
7
10
|
static get jsonSchema() {
|
|
8
11
|
return {
|
|
9
12
|
type: 'object',
|
package/models/Role.js
CHANGED
|
@@ -9,6 +9,14 @@ class Role extends BaseModel {
|
|
|
9
9
|
return 'id';
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
// Shared RBAC catalog (role definitions), not per-tenant data — tenant
|
|
13
|
+
// scoping happens on userTenantsMapping.roleId, not here. Must opt out:
|
|
14
|
+
// a brand-new user creating their first company has no MT context yet, and
|
|
15
|
+
// BaseModel's fail-closed tenant modifier would otherwise return zero rows.
|
|
16
|
+
static get multiTenant() {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
|
|
12
20
|
static get jsonSchema() {
|
|
13
21
|
return {
|
|
14
22
|
type: 'object',
|
package/models/UiElement.js
CHANGED
|
@@ -4,6 +4,9 @@ class UiElement extends BaseModel {
|
|
|
4
4
|
static get tableName() { return 'uiElements'; }
|
|
5
5
|
static get idColumn() { return 'id'; }
|
|
6
6
|
|
|
7
|
+
// Shared RBAC catalog, not per-tenant data — see Role.js for why.
|
|
8
|
+
static get multiTenant() { return false; }
|
|
9
|
+
|
|
7
10
|
static get jsonSchema() {
|
|
8
11
|
return {
|
|
9
12
|
type: 'object',
|
|
@@ -4,6 +4,9 @@ class UiElementsRolesMapping extends BaseModel {
|
|
|
4
4
|
static get tableName() { return 'uiElementsRolesMapping'; }
|
|
5
5
|
static get idColumn() { return 'id'; }
|
|
6
6
|
|
|
7
|
+
// Shared RBAC catalog, not per-tenant data — see Role.js for why.
|
|
8
|
+
static get multiTenant() { return false; }
|
|
9
|
+
|
|
7
10
|
static get jsonSchema() {
|
|
8
11
|
return {
|
|
9
12
|
type: 'object',
|
package/models/UiPage.js
CHANGED
|
@@ -4,6 +4,9 @@ class UiPage extends BaseModel {
|
|
|
4
4
|
static get tableName() { return 'uiPages'; }
|
|
5
5
|
static get idColumn() { return 'id'; }
|
|
6
6
|
|
|
7
|
+
// Shared RBAC catalog, not per-tenant data — see Role.js for why.
|
|
8
|
+
static get multiTenant() { return false; }
|
|
9
|
+
|
|
7
10
|
static get jsonSchema() {
|
|
8
11
|
return {
|
|
9
12
|
type: 'object',
|
|
@@ -4,6 +4,9 @@ class UiPagesRolesMapping extends BaseModel {
|
|
|
4
4
|
static get tableName() { return 'uiPagesRolesMapping'; }
|
|
5
5
|
static get idColumn() { return 'id'; }
|
|
6
6
|
|
|
7
|
+
// Shared RBAC catalog, not per-tenant data — see Role.js for why.
|
|
8
|
+
static get multiTenant() { return false; }
|
|
9
|
+
|
|
7
10
|
static get jsonSchema() {
|
|
8
11
|
return {
|
|
9
12
|
type: 'object',
|
package/models/User.js
CHANGED
|
@@ -18,9 +18,12 @@ class User extends BaseModel {
|
|
|
18
18
|
id: { type: 'string', maxLength: 25 },
|
|
19
19
|
email: { type: 'string', maxLength: 255 },
|
|
20
20
|
phoneNumber: { type: ['string', 'null'], maxLength: 255 },
|
|
21
|
+
phoneVerified: { type: 'boolean' },
|
|
22
|
+
phoneVerifiedOn: { type: ['string', 'null'] },
|
|
21
23
|
name: { type: ['string', 'null'], maxLength: 255 },
|
|
22
24
|
pwd: { type: ['string', 'null'], maxLength: 255 },
|
|
23
25
|
pwdSalt: { type: ['string', 'null'], maxLength: 100 },
|
|
26
|
+
profilePicUrl: { type: ['string', 'null'], maxLength: 500 },
|
|
24
27
|
isActive: { type: 'boolean' },
|
|
25
28
|
mtId1: { type: ['string', 'null'], maxLength: 25 },
|
|
26
29
|
mtId2: { type: ['string', 'null'], maxLength: 25 },
|
|
@@ -43,7 +46,6 @@ class User extends BaseModel {
|
|
|
43
46
|
|
|
44
47
|
static get relationMappings() {
|
|
45
48
|
const Role = require('./Role');
|
|
46
|
-
const Tenant = require('./Tenant');
|
|
47
49
|
return {
|
|
48
50
|
roles: {
|
|
49
51
|
relation: BaseModel.ManyToManyRelation,
|
|
@@ -56,18 +58,6 @@ class User extends BaseModel {
|
|
|
56
58
|
},
|
|
57
59
|
to: 'roles.id'
|
|
58
60
|
}
|
|
59
|
-
},
|
|
60
|
-
tenants: {
|
|
61
|
-
relation: BaseModel.ManyToManyRelation,
|
|
62
|
-
modelClass: Tenant,
|
|
63
|
-
join: {
|
|
64
|
-
from: 'users.id',
|
|
65
|
-
through: {
|
|
66
|
-
from: 'userTenantsMapping.userId',
|
|
67
|
-
to: 'userTenantsMapping.tenantId'
|
|
68
|
-
},
|
|
69
|
-
to: 'tenants.id'
|
|
70
|
-
}
|
|
71
61
|
}
|
|
72
62
|
};
|
|
73
63
|
}
|
|
@@ -4,6 +4,9 @@ class UserRolesMapping extends BaseModel {
|
|
|
4
4
|
static get tableName() { return 'userRolesMapping'; }
|
|
5
5
|
static get idColumn() { return 'id'; }
|
|
6
6
|
|
|
7
|
+
// Shared RBAC catalog, not per-tenant data — see Role.js for why.
|
|
8
|
+
static get multiTenant() { return false; }
|
|
9
|
+
|
|
7
10
|
static get jsonSchema() {
|
|
8
11
|
return {
|
|
9
12
|
type: 'object',
|
|
@@ -8,11 +8,16 @@ class UserTenantsMapping extends BaseModel {
|
|
|
8
8
|
static get jsonSchema() {
|
|
9
9
|
return {
|
|
10
10
|
type: 'object',
|
|
11
|
-
required: ['id', 'userId', '
|
|
11
|
+
required: ['id', 'userId', 'level', 'value'],
|
|
12
12
|
properties: {
|
|
13
13
|
id: { type: 'string', maxLength: 25 },
|
|
14
14
|
userId: { type: 'string', maxLength: 25 },
|
|
15
|
-
|
|
15
|
+
// Generic MT slot (l1-l4, matching mtId1-4) — NOT the app-defined name
|
|
16
|
+
// (companyId/workspaceId/...); that mapping lives only in the app's
|
|
17
|
+
// own registerMTs() config (see @xeplr/db BaseModel.registerMTs).
|
|
18
|
+
level: { type: 'string', enum: ['l1', 'l2', 'l3', 'l4'] },
|
|
19
|
+
value: { type: 'string', maxLength: 255 }, // the app's own id at that level
|
|
20
|
+
roleId: { type: ['string', 'null'], maxLength: 25 }, // scoped role for this grant (null = access only)
|
|
16
21
|
mtId1: { type: ['string', 'null'], maxLength: 25 },
|
|
17
22
|
mtId2: { type: ['string', 'null'], maxLength: 25 },
|
|
18
23
|
mtId3: { type: ['string', 'null'], maxLength: 25 },
|
|
@@ -25,6 +30,19 @@ class UserTenantsMapping extends BaseModel {
|
|
|
25
30
|
}
|
|
26
31
|
};
|
|
27
32
|
}
|
|
33
|
+
|
|
34
|
+
static get relationMappings() {
|
|
35
|
+
const Role = require('./Role');
|
|
36
|
+
return {
|
|
37
|
+
// no `tenant` relation on purpose: auth does not own a tenant tree,
|
|
38
|
+
// `value` is a bare, app-interpreted id at the given level.
|
|
39
|
+
role: {
|
|
40
|
+
relation: BaseModel.BelongsToOneRelation,
|
|
41
|
+
modelClass: Role,
|
|
42
|
+
join: { from: 'userTenantsMapping.roleId', to: 'roles.id' }
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
}
|
|
28
46
|
}
|
|
29
47
|
|
|
30
48
|
module.exports = UserTenantsMapping;
|
package/models/index.js
CHANGED
|
@@ -9,7 +9,6 @@ const UiPagesRolesMapping = require('./UiPagesRolesMapping');
|
|
|
9
9
|
const UiElementsRolesMapping = require('./UiElementsRolesMapping');
|
|
10
10
|
const MenuRolesMapping = require('./MenuRolesMapping');
|
|
11
11
|
const UserRolesMapping = require('./UserRolesMapping');
|
|
12
|
-
const Tenant = require('./Tenant');
|
|
13
12
|
const UserTenantsMapping = require('./UserTenantsMapping');
|
|
14
13
|
|
|
15
14
|
module.exports = {
|
|
@@ -24,6 +23,5 @@ module.exports = {
|
|
|
24
23
|
UiElementsRolesMapping,
|
|
25
24
|
MenuRolesMapping,
|
|
26
25
|
UserRolesMapping,
|
|
27
|
-
Tenant,
|
|
28
26
|
UserTenantsMapping
|
|
29
27
|
};
|
package/package.json
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xeplr/auth",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Pluggable authentication service: register, login, forgot/reset password, RBAC, multi-tenancy",
|
|
5
5
|
"main": "index.js",
|
|
6
|
-
"files": [
|
|
6
|
+
"files": [
|
|
7
|
+
"index.js",
|
|
8
|
+
"lib/",
|
|
9
|
+
"bin/",
|
|
10
|
+
"models/",
|
|
11
|
+
"migrations/"
|
|
12
|
+
],
|
|
7
13
|
"bin": {
|
|
8
14
|
"xeplr-auth-migrate": "./bin/migrate.js",
|
|
9
15
|
"xeplr-auth-server": "./bin/server.js"
|
|
@@ -13,14 +19,29 @@
|
|
|
13
19
|
"./bin/server": "./bin/server.js",
|
|
14
20
|
"./package.json": "./package.json"
|
|
15
21
|
},
|
|
16
|
-
"keywords": [
|
|
22
|
+
"keywords": [
|
|
23
|
+
"auth",
|
|
24
|
+
"authentication",
|
|
25
|
+
"jwt",
|
|
26
|
+
"rbac",
|
|
27
|
+
"express",
|
|
28
|
+
"middleware",
|
|
29
|
+
"multi-tenancy"
|
|
30
|
+
],
|
|
17
31
|
"author": "xeplr",
|
|
18
32
|
"license": "MIT",
|
|
19
|
-
"repository": {
|
|
20
|
-
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "https://github.com/Xeplr/xeplr-auth"
|
|
36
|
+
},
|
|
37
|
+
"publishConfig": {
|
|
38
|
+
"access": "public"
|
|
39
|
+
},
|
|
21
40
|
"dependencies": {
|
|
22
41
|
"bcrypt": "^6.0.0",
|
|
42
|
+
"ioredis": "^5.6.1",
|
|
23
43
|
"jsonwebtoken": "^9.0.3",
|
|
44
|
+
"multer": "^1.4.5-lts.1",
|
|
24
45
|
"@xeplr/base-apis": "^1.0.0",
|
|
25
46
|
"@xeplr/db": "^1.0.0",
|
|
26
47
|
"@xeplr/utils": "^1.0.0"
|
package/migrations/0001_users.js
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
exports.up = function(knex) {
|
|
2
|
-
return knex.schema.createTable('users', function(table) {
|
|
3
|
-
table.string('id', 25).primary();
|
|
4
|
-
table.string('email', 255).notNullable();
|
|
5
|
-
table.string('phoneNumber', 255);
|
|
6
|
-
table.string('name', 255);
|
|
7
|
-
table.string('pwd', 255);
|
|
8
|
-
table.string('pwdSalt', 100);
|
|
9
|
-
table.boolean('isActive').defaultTo(false);
|
|
10
|
-
table.boolean('isActivated').defaultTo(false);
|
|
11
|
-
table.timestamp('activatedOn');
|
|
12
|
-
table.string('activatedBy', 25);
|
|
13
|
-
table.timestamp('recordCreatedDate');
|
|
14
|
-
table.timestamp('recordModifiedDate');
|
|
15
|
-
table.string('recordCreatedBy', 25);
|
|
16
|
-
table.string('recordModifiedBy', 25);
|
|
17
|
-
});
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
exports.down = function(knex) {
|
|
21
|
-
return knex.schema.dropTableIfExists('users');
|
|
22
|
-
};
|
package/migrations/0002_menus.js
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
exports.up = function(knex) {
|
|
2
|
-
return knex.schema.createTable('menus', function(table) {
|
|
3
|
-
table.string('id', 25).primary();
|
|
4
|
-
table.string('name', 255);
|
|
5
|
-
table.string('menuGroup', 255);
|
|
6
|
-
table.boolean('isActive').defaultTo(false);
|
|
7
|
-
table.timestamp('recordCreatedDate');
|
|
8
|
-
table.timestamp('recordModifiedDate');
|
|
9
|
-
table.string('recordCreatedBy', 25);
|
|
10
|
-
table.string('recordModifiedBy', 25);
|
|
11
|
-
});
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
exports.down = function(knex) {
|
|
15
|
-
return knex.schema.dropTableIfExists('menus');
|
|
16
|
-
};
|
package/migrations/0003_apis.js
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
exports.up = function(knex) {
|
|
2
|
-
return knex.schema.createTable('apis', function(table) {
|
|
3
|
-
table.string('id', 25).primary();
|
|
4
|
-
table.string('name', 255);
|
|
5
|
-
table.string('apiGroup', 255);
|
|
6
|
-
table.boolean('isActive').defaultTo(false);
|
|
7
|
-
table.timestamp('recordCreatedDate');
|
|
8
|
-
table.timestamp('recordModifiedDate');
|
|
9
|
-
table.string('recordCreatedBy', 25);
|
|
10
|
-
table.string('recordModifiedBy', 25);
|
|
11
|
-
});
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
exports.down = function(knex) {
|
|
15
|
-
return knex.schema.dropTableIfExists('apis');
|
|
16
|
-
};
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
exports.up = function(knex) {
|
|
2
|
-
return knex.schema.createTable('uiPages', function(table) {
|
|
3
|
-
table.string('id', 25).primary();
|
|
4
|
-
table.string('name', 255);
|
|
5
|
-
table.string('uiPagesGroup', 255);
|
|
6
|
-
table.boolean('isActive').defaultTo(false);
|
|
7
|
-
table.timestamp('recordCreatedDate');
|
|
8
|
-
table.timestamp('recordModifiedDate');
|
|
9
|
-
table.string('recordCreatedBy', 25);
|
|
10
|
-
table.string('recordModifiedBy', 25);
|
|
11
|
-
});
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
exports.down = function(knex) {
|
|
15
|
-
return knex.schema.dropTableIfExists('uiPages');
|
|
16
|
-
};
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
exports.up = function(knex) {
|
|
2
|
-
return knex.schema.createTable('uiElements', function(table) {
|
|
3
|
-
table.string('id', 25).primary();
|
|
4
|
-
table.string('name', 255);
|
|
5
|
-
table.string('uiElementsGroup', 255);
|
|
6
|
-
table.boolean('isActive').defaultTo(false);
|
|
7
|
-
table.timestamp('recordCreatedDate');
|
|
8
|
-
table.timestamp('recordModifiedDate');
|
|
9
|
-
table.string('recordCreatedBy', 25);
|
|
10
|
-
table.string('recordModifiedBy', 25);
|
|
11
|
-
});
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
exports.down = function(knex) {
|
|
15
|
-
return knex.schema.dropTableIfExists('uiElements');
|
|
16
|
-
};
|
package/migrations/0006_roles.js
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
exports.up = function(knex) {
|
|
2
|
-
return knex.schema.createTable('roles', function(table) {
|
|
3
|
-
table.string('id', 25).primary();
|
|
4
|
-
table.string('name', 255);
|
|
5
|
-
table.boolean('isActive').defaultTo(false);
|
|
6
|
-
table.timestamp('recordCreatedDate');
|
|
7
|
-
table.timestamp('recordModifiedDate');
|
|
8
|
-
table.string('recordCreatedBy', 25);
|
|
9
|
-
table.string('recordModifiedBy', 25);
|
|
10
|
-
});
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
exports.down = function(knex) {
|
|
14
|
-
return knex.schema.dropTableIfExists('roles');
|
|
15
|
-
};
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
exports.up = function(knex) {
|
|
2
|
-
return knex.schema.createTable('apisRolesMapping', function(table) {
|
|
3
|
-
table.string('id', 25).primary();
|
|
4
|
-
table.string('roleId', 25).references('id').inTable('roles');
|
|
5
|
-
table.string('apiId', 25).references('id').inTable('apis');
|
|
6
|
-
table.boolean('isActive').defaultTo(false);
|
|
7
|
-
table.timestamp('recordCreatedDate');
|
|
8
|
-
table.timestamp('recordModifiedDate');
|
|
9
|
-
table.string('recordCreatedBy', 25);
|
|
10
|
-
table.string('recordModifiedBy', 25);
|
|
11
|
-
});
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
exports.down = function(knex) {
|
|
15
|
-
return knex.schema.dropTableIfExists('apisRolesMapping');
|
|
16
|
-
};
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
exports.up = function(knex) {
|
|
2
|
-
return knex.schema.createTable('uiPagesRolesMapping', function(table) {
|
|
3
|
-
table.string('id', 25).primary();
|
|
4
|
-
table.string('roleId', 25).references('id').inTable('roles');
|
|
5
|
-
table.string('uiPageId', 25).references('id').inTable('uiPages');
|
|
6
|
-
table.boolean('isActive').defaultTo(false);
|
|
7
|
-
table.timestamp('recordCreatedDate');
|
|
8
|
-
table.timestamp('recordModifiedDate');
|
|
9
|
-
table.string('recordCreatedBy', 25);
|
|
10
|
-
table.string('recordModifiedBy', 25);
|
|
11
|
-
});
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
exports.down = function(knex) {
|
|
15
|
-
return knex.schema.dropTableIfExists('uiPagesRolesMapping');
|
|
16
|
-
};
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
exports.up = function(knex) {
|
|
2
|
-
return knex.schema.createTable('uiElementsRolesMapping', function(table) {
|
|
3
|
-
table.string('id', 25).primary();
|
|
4
|
-
table.string('roleId', 25).references('id').inTable('roles');
|
|
5
|
-
table.string('uiElementId', 25).references('id').inTable('uiElements');
|
|
6
|
-
table.boolean('isActive').defaultTo(false);
|
|
7
|
-
table.timestamp('recordCreatedDate');
|
|
8
|
-
table.timestamp('recordModifiedDate');
|
|
9
|
-
table.string('recordCreatedBy', 25);
|
|
10
|
-
table.string('recordModifiedBy', 25);
|
|
11
|
-
});
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
exports.down = function(knex) {
|
|
15
|
-
return knex.schema.dropTableIfExists('uiElementsRolesMapping');
|
|
16
|
-
};
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
exports.up = function(knex) {
|
|
2
|
-
return knex.schema.createTable('menuRolesMapping', function(table) {
|
|
3
|
-
table.string('id', 25).primary();
|
|
4
|
-
table.string('menuId', 25).references('id').inTable('menus');
|
|
5
|
-
table.string('roleId', 25).references('id').inTable('roles');
|
|
6
|
-
table.boolean('isActive').defaultTo(false);
|
|
7
|
-
table.timestamp('recordCreatedDate');
|
|
8
|
-
table.timestamp('recordModifiedDate');
|
|
9
|
-
table.string('recordCreatedBy', 25);
|
|
10
|
-
table.string('recordModifiedBy', 25);
|
|
11
|
-
});
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
exports.down = function(knex) {
|
|
15
|
-
return knex.schema.dropTableIfExists('menuRolesMapping');
|
|
16
|
-
};
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
exports.up = function(knex) {
|
|
2
|
-
return knex.schema.createTable('userRolesMapping', function(table) {
|
|
3
|
-
table.string('id', 25).primary();
|
|
4
|
-
table.string('userId', 25).references('id').inTable('users');
|
|
5
|
-
table.string('roleId', 25).references('id').inTable('roles');
|
|
6
|
-
table.boolean('isActive').defaultTo(false);
|
|
7
|
-
table.timestamp('recordCreatedDate');
|
|
8
|
-
table.timestamp('recordModifiedDate');
|
|
9
|
-
table.string('recordCreatedBy', 25);
|
|
10
|
-
table.string('recordModifiedBy', 25);
|
|
11
|
-
});
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
exports.down = function(knex) {
|
|
15
|
-
return knex.schema.dropTableIfExists('userRolesMapping');
|
|
16
|
-
};
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
exports.up = function(knex) {
|
|
2
|
-
return knex.schema.alterTable('users', function(table) {
|
|
3
|
-
table.string('resetToken', 255);
|
|
4
|
-
table.timestamp('resetTokenExpiry');
|
|
5
|
-
});
|
|
6
|
-
};
|
|
7
|
-
|
|
8
|
-
exports.down = function(knex) {
|
|
9
|
-
return knex.schema.alterTable('users', function(table) {
|
|
10
|
-
table.dropColumn('resetToken');
|
|
11
|
-
table.dropColumn('resetTokenExpiry');
|
|
12
|
-
});
|
|
13
|
-
};
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
exports.up = function(knex) {
|
|
2
|
-
return Promise.all([
|
|
3
|
-
knex.schema.alterTable('apis', function(table) {
|
|
4
|
-
table.boolean('isPublic').defaultTo(false);
|
|
5
|
-
}),
|
|
6
|
-
knex.schema.alterTable('uiPages', function(table) {
|
|
7
|
-
table.boolean('isPublic').defaultTo(false);
|
|
8
|
-
}),
|
|
9
|
-
knex.schema.alterTable('menus', function(table) {
|
|
10
|
-
table.boolean('isPublic').defaultTo(false);
|
|
11
|
-
})
|
|
12
|
-
]);
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
exports.down = function(knex) {
|
|
16
|
-
return Promise.all([
|
|
17
|
-
knex.schema.alterTable('apis', function(table) {
|
|
18
|
-
table.dropColumn('isPublic');
|
|
19
|
-
}),
|
|
20
|
-
knex.schema.alterTable('uiPages', function(table) {
|
|
21
|
-
table.dropColumn('isPublic');
|
|
22
|
-
}),
|
|
23
|
-
knex.schema.alterTable('menus', function(table) {
|
|
24
|
-
table.dropColumn('isPublic');
|
|
25
|
-
})
|
|
26
|
-
]);
|
|
27
|
-
};
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
exports.up = function(knex) {
|
|
2
|
-
return knex.schema.alterTable('users', function(table) {
|
|
3
|
-
table.string('activationToken', 255);
|
|
4
|
-
table.string('normalizedEmail', 255).index();
|
|
5
|
-
});
|
|
6
|
-
};
|
|
7
|
-
|
|
8
|
-
exports.down = function(knex) {
|
|
9
|
-
return knex.schema.alterTable('users', function(table) {
|
|
10
|
-
table.dropColumn('activationToken');
|
|
11
|
-
table.dropColumn('normalizedEmail');
|
|
12
|
-
});
|
|
13
|
-
};
|