@drax/identity-back 0.0.15 → 0.0.16
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/dist/factory/TenantServiceFactory.js +24 -0
- package/dist/factory/UserServiceFactory.js +1 -1
- package/dist/graphql/resolvers/role.resolvers.js +12 -1
- package/dist/graphql/resolvers/tenant.resolvers.js +121 -0
- package/dist/graphql/resolvers/user.resolvers.js +5 -2
- package/dist/graphql/types/tenant.graphql +28 -0
- package/dist/graphql/types/user.graphql +3 -0
- package/dist/index.js +6 -3
- package/dist/interfaces/ITenant.js +1 -0
- package/dist/interfaces/ITenantRepository.js +1 -0
- package/dist/middleware/rbacMiddleware.js +1 -4
- package/dist/models/TenantModel.js +18 -0
- package/dist/models/UserModel.js +5 -0
- package/dist/permissions/IdentityPermissions.js +5 -0
- package/dist/rbac/Rbac.js +6 -0
- package/dist/repository/mongo/RoleMongoRepository.js +7 -6
- package/dist/repository/mongo/TenantMongoRepository.js +45 -0
- package/dist/repository/mongo/UserMongoRepository.js +19 -6
- package/dist/repository/sqlite/RoleSqliteRepository.js +24 -5
- package/dist/repository/sqlite/TenantSqliteRepository.js +106 -0
- package/dist/repository/sqlite/UserSqliteRepository.js +43 -37
- package/dist/routes/RoleRoutes.js +10 -1
- package/dist/routes/TenantRoutes.js +183 -0
- package/dist/routes/UserRoutes.js +6 -1
- package/dist/services/RoleService.js +0 -5
- package/dist/services/TenantService.js +59 -0
- package/dist/services/UserService.js +1 -1
- package/dist/utils/AuthUtils.js +4 -3
- package/dist/zod/TenantZod.js +8 -0
- package/package.json +2 -2
- package/src/factory/TenantServiceFactory.ts +33 -0
- package/src/factory/UserServiceFactory.ts +1 -1
- package/src/graphql/resolvers/role.resolvers.ts +13 -1
- package/src/graphql/resolvers/tenant.resolvers.ts +119 -0
- package/src/graphql/resolvers/user.resolvers.ts +5 -2
- package/src/graphql/types/tenant.graphql +28 -0
- package/src/graphql/types/user.graphql +3 -0
- package/src/index.ts +6 -0
- package/src/interfaces/ITenant.ts +9 -0
- package/src/interfaces/ITenantRepository.ts +15 -0
- package/src/interfaces/IUser.ts +4 -1
- package/src/middleware/rbacMiddleware.ts +4 -7
- package/src/models/TenantModel.ts +32 -0
- package/src/models/UserModel.ts +5 -0
- package/src/permissions/IdentityPermissions.ts +7 -0
- package/src/rbac/Rbac.ts +11 -3
- package/src/repository/mongo/RoleMongoRepository.ts +7 -6
- package/src/repository/mongo/TenantMongoRepository.ts +62 -0
- package/src/repository/mongo/UserMongoRepository.ts +20 -6
- package/src/repository/sqlite/RoleSqliteRepository.ts +27 -6
- package/src/repository/sqlite/TenantSqliteRepository.ts +139 -0
- package/src/repository/sqlite/UserSqliteRepository.ts +52 -40
- package/src/routes/RoleRoutes.ts +9 -1
- package/src/routes/TenantRoutes.ts +178 -0
- package/src/routes/UserRoutes.ts +6 -1
- package/src/services/RoleService.ts +1 -4
- package/src/services/TenantService.ts +74 -0
- package/src/services/UserService.ts +1 -1
- package/src/utils/AuthUtils.ts +4 -3
- package/src/zod/TenantZod.ts +14 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/types/factory/TenantServiceFactory.d.ts +4 -0
- package/types/factory/TenantServiceFactory.d.ts.map +1 -0
- package/types/graphql/resolvers/role.resolvers.d.ts.map +1 -1
- package/types/graphql/resolvers/tenant.resolvers.d.ts +44 -0
- package/types/graphql/resolvers/tenant.resolvers.d.ts.map +1 -0
- package/types/graphql/resolvers/user.resolvers.d.ts +2 -1
- package/types/graphql/resolvers/user.resolvers.d.ts.map +1 -1
- package/types/index.d.ts +4 -1
- package/types/index.d.ts.map +1 -1
- package/types/interfaces/ITenant.d.ts +7 -0
- package/types/interfaces/ITenant.d.ts.map +1 -0
- package/types/interfaces/ITenantRepository.d.ts +15 -0
- package/types/interfaces/ITenantRepository.d.ts.map +1 -0
- package/types/interfaces/IUser.d.ts +4 -0
- package/types/interfaces/IUser.d.ts.map +1 -1
- package/types/middleware/rbacMiddleware.d.ts.map +1 -1
- package/types/models/TenantModel.d.ts +16 -0
- package/types/models/TenantModel.d.ts.map +1 -0
- package/types/models/UserModel.d.ts.map +1 -1
- package/types/permissions/IdentityPermissions.d.ts +6 -1
- package/types/permissions/IdentityPermissions.d.ts.map +1 -1
- package/types/rbac/Rbac.d.ts +4 -2
- package/types/rbac/Rbac.d.ts.map +1 -1
- package/types/repository/mongo/RoleMongoRepository.d.ts.map +1 -1
- package/types/repository/mongo/TenantMongoRepository.d.ts +15 -0
- package/types/repository/mongo/TenantMongoRepository.d.ts.map +1 -0
- package/types/repository/mongo/UserMongoRepository.d.ts +2 -2
- package/types/repository/mongo/UserMongoRepository.d.ts.map +1 -1
- package/types/repository/sqlite/RoleSqliteRepository.d.ts +2 -0
- package/types/repository/sqlite/RoleSqliteRepository.d.ts.map +1 -1
- package/types/repository/sqlite/TenantSqliteRepository.d.ts +19 -0
- package/types/repository/sqlite/TenantSqliteRepository.d.ts.map +1 -0
- package/types/repository/sqlite/UserSqliteRepository.d.ts +4 -2
- package/types/repository/sqlite/UserSqliteRepository.d.ts.map +1 -1
- package/types/routes/RoleRoutes.d.ts.map +1 -1
- package/types/routes/TenantRoutes.d.ts +4 -0
- package/types/routes/TenantRoutes.d.ts.map +1 -0
- package/types/routes/UserRoutes.d.ts.map +1 -1
- package/types/services/RoleService.d.ts.map +1 -1
- package/types/services/TenantService.d.ts +16 -0
- package/types/services/TenantService.d.ts.map +1 -0
- package/types/utils/AuthUtils.d.ts +3 -2
- package/types/utils/AuthUtils.d.ts.map +1 -1
- package/types/zod/TenantZod.d.ts +10 -0
- package/types/zod/TenantZod.d.ts.map +1 -0
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import sqlite from "better-sqlite3";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import { SqliteErrorToValidationError } from "@drax/common-back";
|
|
4
|
+
const tenantTableSQL = `
|
|
5
|
+
CREATE TABLE IF NOT EXISTS tenants
|
|
6
|
+
(
|
|
7
|
+
id TEXT PRIMARY KEY,
|
|
8
|
+
name TEXT
|
|
9
|
+
);
|
|
10
|
+
`;
|
|
11
|
+
class TenantSqliteRepository {
|
|
12
|
+
constructor(DATABASE, verbose = false) {
|
|
13
|
+
this.db = new sqlite(DATABASE, { verbose: verbose ? console.log : null });
|
|
14
|
+
this.table();
|
|
15
|
+
}
|
|
16
|
+
table() {
|
|
17
|
+
this.db.exec(tenantTableSQL);
|
|
18
|
+
}
|
|
19
|
+
async create(tenantData) {
|
|
20
|
+
try {
|
|
21
|
+
if (!tenantData.id) {
|
|
22
|
+
tenantData.id = randomUUID();
|
|
23
|
+
}
|
|
24
|
+
const fields = Object.keys(tenantData)
|
|
25
|
+
.map(field => `${field}`)
|
|
26
|
+
.join(', ');
|
|
27
|
+
const values = Object.keys(tenantData)
|
|
28
|
+
.map(field => `@${field}`)
|
|
29
|
+
.join(', ');
|
|
30
|
+
const stmt = this.db.prepare(`INSERT INTO tenants (${fields}) VALUES (${values})`);
|
|
31
|
+
stmt.run(tenantData);
|
|
32
|
+
return this.findById(tenantData.id);
|
|
33
|
+
}
|
|
34
|
+
catch (e) {
|
|
35
|
+
console.log(e);
|
|
36
|
+
throw SqliteErrorToValidationError(e, tenantData);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
async findById(id) {
|
|
40
|
+
const tenant = this.db.prepare('SELECT * FROM tenants WHERE id = ?').get(id);
|
|
41
|
+
if (tenant) {
|
|
42
|
+
tenant.permissions = tenant.permissions ? tenant.permissions.split(",") : [];
|
|
43
|
+
return tenant;
|
|
44
|
+
}
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
async findByName(name) {
|
|
48
|
+
const tenant = this.db.prepare('SELECT * FROM tenants WHERE name = ?').get(name);
|
|
49
|
+
if (tenant) {
|
|
50
|
+
tenant.permissions = tenant.permissions ? tenant.permissions.split(",") : [];
|
|
51
|
+
return tenant;
|
|
52
|
+
}
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
async update(id, tenantData) {
|
|
56
|
+
try {
|
|
57
|
+
const setClauses = Object.keys(tenantData)
|
|
58
|
+
.map(field => `${field} = @${field}`)
|
|
59
|
+
.join(', ');
|
|
60
|
+
tenantData.id = id;
|
|
61
|
+
const stmt = this.db.prepare(`UPDATE tenants SET ${setClauses} WHERE id = @id `);
|
|
62
|
+
stmt.run(tenantData);
|
|
63
|
+
return this.findById(id);
|
|
64
|
+
}
|
|
65
|
+
catch (e) {
|
|
66
|
+
console.log(e);
|
|
67
|
+
throw SqliteErrorToValidationError(e, tenantData);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
async delete(id) {
|
|
71
|
+
const stmt = this.db.prepare('DELETE FROM tenants WHERE id = ?');
|
|
72
|
+
stmt.run(id);
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
async deleteAll() {
|
|
76
|
+
const stmt = this.db.prepare('DELETE FROM tenants');
|
|
77
|
+
stmt.run();
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
async fetchAll() {
|
|
81
|
+
const tenants = this.db.prepare('SELECT * FROM tenants').all();
|
|
82
|
+
for (const tenant of tenants) {
|
|
83
|
+
tenant.permissions = tenant.permissions ? tenant.permissions.split(",") : [];
|
|
84
|
+
}
|
|
85
|
+
return tenants;
|
|
86
|
+
}
|
|
87
|
+
async paginate(page = 1, limit = 5, search = "") {
|
|
88
|
+
const offset = page > 1 ? (page - 1) * limit : 0;
|
|
89
|
+
let where = "";
|
|
90
|
+
if (search) {
|
|
91
|
+
where = ` WHERE name LIKE '%${search}%'`;
|
|
92
|
+
}
|
|
93
|
+
const rCount = this.db.prepare('SELECT COUNT(*) as count FROM tenants' + where).get();
|
|
94
|
+
const tenants = this.db.prepare('SELECT * FROM tenants LIMIT ? OFFSET ?' + where).all([limit, offset]);
|
|
95
|
+
for (const tenant of tenants) {
|
|
96
|
+
tenant.permissions = tenant.permissions ? tenant.permissions.split(",") : [];
|
|
97
|
+
}
|
|
98
|
+
return {
|
|
99
|
+
page: page,
|
|
100
|
+
limit: limit,
|
|
101
|
+
total: rCount.count,
|
|
102
|
+
items: tenants
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
export default TenantSqliteRepository;
|
|
@@ -2,39 +2,29 @@ import sqlite from "better-sqlite3";
|
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
3
|
import { SqliteErrorToValidationError, ValidationError } from "@drax/common-back";
|
|
4
4
|
import RoleSqliteRepository from "./RoleSqliteRepository.js";
|
|
5
|
+
import TenantSqliteRepository from "./TenantSqliteRepository.js";
|
|
5
6
|
const userTableSQL = `
|
|
6
7
|
CREATE TABLE IF NOT EXISTS users
|
|
7
8
|
(
|
|
8
|
-
id
|
|
9
|
-
TEXT
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
TEXT,
|
|
14
|
-
|
|
15
|
-
TEXT
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
password
|
|
20
|
-
TEXT,
|
|
21
|
-
email
|
|
22
|
-
TEXT
|
|
23
|
-
UNIQUE,
|
|
24
|
-
phone
|
|
25
|
-
TEXT,
|
|
26
|
-
role
|
|
27
|
-
TEXT,
|
|
28
|
-
groups
|
|
29
|
-
TEXT,
|
|
30
|
-
avatar
|
|
31
|
-
TEXT
|
|
9
|
+
id TEXT PRIMARY KEY,
|
|
10
|
+
name TEXT,
|
|
11
|
+
username TEXT UNIQUE,
|
|
12
|
+
active INTEGER,
|
|
13
|
+
password TEXT,
|
|
14
|
+
email TEXT UNIQUE,
|
|
15
|
+
phone TEXT,
|
|
16
|
+
role TEXT,
|
|
17
|
+
tenant TEXT,
|
|
18
|
+
groups TEXT,
|
|
19
|
+
avatar TEXT
|
|
32
20
|
);
|
|
33
21
|
`;
|
|
34
22
|
class UserSqliteRepository {
|
|
35
23
|
constructor(DATABASE, verbose = false) {
|
|
36
24
|
this.db = new sqlite(DATABASE, { verbose: verbose ? console.log : null });
|
|
37
25
|
this.roleRepository = new RoleSqliteRepository(DATABASE, verbose);
|
|
26
|
+
this.tenantRepository = new TenantSqliteRepository(DATABASE, verbose);
|
|
27
|
+
this.table();
|
|
38
28
|
}
|
|
39
29
|
table() {
|
|
40
30
|
this.db.exec(userTableSQL);
|
|
@@ -60,9 +50,6 @@ class UserSqliteRepository {
|
|
|
60
50
|
const values = Object.keys(userData)
|
|
61
51
|
.map(field => `@${field}`)
|
|
62
52
|
.join(', ');
|
|
63
|
-
/*console.log("fields", fields)
|
|
64
|
-
console.log("values",values)
|
|
65
|
-
console.log("userData",userData)*/
|
|
66
53
|
const stmt = this.db.prepare(`INSERT INTO users (${fields})
|
|
67
54
|
VALUES (${values})`);
|
|
68
55
|
stmt.run(userData);
|
|
@@ -108,6 +95,7 @@ class UserSqliteRepository {
|
|
|
108
95
|
return null;
|
|
109
96
|
}
|
|
110
97
|
user.role = await this.findRoleById(user.role);
|
|
98
|
+
user.tenant = await this.findTenantById(user.tenant);
|
|
111
99
|
return user;
|
|
112
100
|
}
|
|
113
101
|
async findByUsername(username) {
|
|
@@ -116,24 +104,39 @@ class UserSqliteRepository {
|
|
|
116
104
|
return null;
|
|
117
105
|
}
|
|
118
106
|
user.role = await this.findRoleById(user.role);
|
|
107
|
+
user.tenant = await this.findTenantById(user.tenant);
|
|
119
108
|
return user;
|
|
120
109
|
}
|
|
121
|
-
async paginate(page = 1, limit = 5, search) {
|
|
110
|
+
async paginate(page = 1, limit = 5, search = "", filters = []) {
|
|
122
111
|
const offset = page > 1 ? (page - 1) * limit : 0;
|
|
123
|
-
let where;
|
|
112
|
+
let where = "";
|
|
124
113
|
if (search) {
|
|
125
|
-
where = ` WHERE name LIKE '%${search}%' OR username LIKE '%${search}%'`;
|
|
114
|
+
where = ` WHERE (name LIKE '%${search}%' OR username LIKE '%${search}%') `;
|
|
126
115
|
}
|
|
116
|
+
let whereFilters = [];
|
|
117
|
+
if (filters && filters.length > 0) {
|
|
118
|
+
where = where ? ` AND ` : ` WHERE `;
|
|
119
|
+
for (const filter of filters) {
|
|
120
|
+
if (filter.operator === '$eq') {
|
|
121
|
+
whereFilters.push(` ${filter.field} = '${filter.value}' `);
|
|
122
|
+
}
|
|
123
|
+
if (filter.operator === '$ne') {
|
|
124
|
+
whereFilters.push(` ${filter.field} != '${filter.value}' `);
|
|
125
|
+
}
|
|
126
|
+
if (filter.operator === '$in') {
|
|
127
|
+
whereFilters.push(` ${filter.field} LIKE '%${filter.value}%' `);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
where += whereFilters.join(" AND ");
|
|
131
|
+
}
|
|
132
|
+
// console.log("paginate where ", where, "search", search, "filters", filters, "whereFilters", whereFilters)
|
|
127
133
|
const rCount = this.db.prepare('SELECT COUNT(*) as count FROM users' + where).get();
|
|
128
|
-
const users = this.db.prepare('SELECT * FROM users LIMIT ? OFFSET ?'
|
|
134
|
+
const users = this.db.prepare('SELECT * FROM users' + where + ' LIMIT ? OFFSET ?').all([limit, offset]);
|
|
129
135
|
for (const user of users) {
|
|
130
136
|
let role = await this.findRoleById(user.role);
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
else {
|
|
135
|
-
user.role = null;
|
|
136
|
-
}
|
|
137
|
+
user.role = role ? role : null;
|
|
138
|
+
let tenant = await this.findTenantById(user.tenant);
|
|
139
|
+
user.tenant = tenant ? tenant : null;
|
|
137
140
|
user.active = user.active === 1;
|
|
138
141
|
}
|
|
139
142
|
return {
|
|
@@ -146,6 +149,9 @@ class UserSqliteRepository {
|
|
|
146
149
|
async findRoleById(id) {
|
|
147
150
|
return await this.roleRepository.findById(id);
|
|
148
151
|
}
|
|
152
|
+
async findTenantById(id) {
|
|
153
|
+
return await this.tenantRepository.findById(id);
|
|
154
|
+
}
|
|
149
155
|
async changePassword(id, password) {
|
|
150
156
|
const stmt = this.db.prepare(`UPDATE users
|
|
151
157
|
SET password = @password
|
|
@@ -75,7 +75,12 @@ async function RoleRoutes(fastify, options) {
|
|
|
75
75
|
request.rbac.assertPermission(IdentityPermissions.ViewRole);
|
|
76
76
|
const roleService = RoleServiceFactory();
|
|
77
77
|
let roles = await roleService.fetchAll();
|
|
78
|
-
|
|
78
|
+
if (request.rbac.getRole?.childRoles?.length > 0) {
|
|
79
|
+
return roles.filter(role => request.rbac.getRole.childRoles.some(childRole => childRole.id === role.id));
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
return roles;
|
|
83
|
+
}
|
|
79
84
|
}
|
|
80
85
|
catch (e) {
|
|
81
86
|
console.error(e);
|
|
@@ -177,6 +182,10 @@ async function RoleRoutes(fastify, options) {
|
|
|
177
182
|
request.rbac.assertPermission(IdentityPermissions.DeleteRole);
|
|
178
183
|
const id = request.params.id;
|
|
179
184
|
const roleService = RoleServiceFactory();
|
|
185
|
+
const currentRole = await roleService.findById(id);
|
|
186
|
+
if (currentRole.readonly) {
|
|
187
|
+
throw new UnauthorizedError();
|
|
188
|
+
}
|
|
180
189
|
let r = await roleService.delete(id);
|
|
181
190
|
return r;
|
|
182
191
|
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { ValidationError } from "@drax/common-back";
|
|
2
|
+
import TenantServiceFactory from "../factory/TenantServiceFactory.js";
|
|
3
|
+
import { IdentityPermissions } from "../permissions/IdentityPermissions.js";
|
|
4
|
+
import UnauthorizedError from "../errors/UnauthorizedError.js";
|
|
5
|
+
async function TenantRoutes(fastify, options) {
|
|
6
|
+
fastify.get('/api/tenants/:id', async (request, reply) => {
|
|
7
|
+
try {
|
|
8
|
+
request.rbac.assertPermission(IdentityPermissions.ViewTenant);
|
|
9
|
+
const id = request.params.id;
|
|
10
|
+
const tenantService = TenantServiceFactory();
|
|
11
|
+
let tenant = await tenantService.findById(id);
|
|
12
|
+
return tenant;
|
|
13
|
+
}
|
|
14
|
+
catch (e) {
|
|
15
|
+
console.error(e);
|
|
16
|
+
if (e instanceof ValidationError) {
|
|
17
|
+
reply.statusCode = e.statusCode;
|
|
18
|
+
reply.send({ error: e.message, inputErrors: e.errors });
|
|
19
|
+
}
|
|
20
|
+
else if (e instanceof UnauthorizedError) {
|
|
21
|
+
reply.statusCode = e.statusCode;
|
|
22
|
+
reply.send({ error: e.message });
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
reply.statusCode = 500;
|
|
26
|
+
reply.send({ error: 'INTERNAL_SERVER_ERROR' });
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
fastify.get('/api/tenants/name/:name', async (request, reply) => {
|
|
31
|
+
try {
|
|
32
|
+
request.rbac.assertPermission(IdentityPermissions.ViewTenant);
|
|
33
|
+
const name = request.params.name;
|
|
34
|
+
const tenantService = TenantServiceFactory();
|
|
35
|
+
let tenant = await tenantService.findByName(name);
|
|
36
|
+
return tenant;
|
|
37
|
+
}
|
|
38
|
+
catch (e) {
|
|
39
|
+
console.error(e);
|
|
40
|
+
if (e instanceof ValidationError) {
|
|
41
|
+
reply.statusCode = e.statusCode;
|
|
42
|
+
reply.send({ error: e.message, inputErrors: e.errors });
|
|
43
|
+
}
|
|
44
|
+
else if (e instanceof UnauthorizedError) {
|
|
45
|
+
reply.statusCode = e.statusCode;
|
|
46
|
+
reply.send({ error: e.message });
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
reply.statusCode = 500;
|
|
50
|
+
reply.send({ error: 'INTERNAL_SERVER_ERROR' });
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
fastify.get('/api/tenants/all', async (request, reply) => {
|
|
55
|
+
try {
|
|
56
|
+
request.rbac.assertPermission(IdentityPermissions.ViewTenant);
|
|
57
|
+
const tenantService = TenantServiceFactory();
|
|
58
|
+
let tenants = await tenantService.fetchAll();
|
|
59
|
+
if (request.rbac.getAuthUser.tenantId) {
|
|
60
|
+
return tenants.filter(t => t.id === request.rbac.getAuthUser.tenantId);
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
return tenants;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
catch (e) {
|
|
67
|
+
console.error(e);
|
|
68
|
+
if (e instanceof ValidationError) {
|
|
69
|
+
reply.statusCode = e.statusCode;
|
|
70
|
+
reply.send({ error: e.message, inputErrors: e.errors });
|
|
71
|
+
}
|
|
72
|
+
else if (e instanceof UnauthorizedError) {
|
|
73
|
+
reply.statusCode = e.statusCode;
|
|
74
|
+
reply.send({ error: e.message });
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
reply.statusCode = 500;
|
|
78
|
+
reply.send({ error: 'INTERNAL_SERVER_ERROR' });
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
fastify.get('/api/tenants', async (request, reply) => {
|
|
83
|
+
try {
|
|
84
|
+
request.rbac.assertPermission(IdentityPermissions.ViewTenant);
|
|
85
|
+
const page = request.query.page;
|
|
86
|
+
const limit = request.query.limit;
|
|
87
|
+
const search = request.query.search;
|
|
88
|
+
const tenantService = TenantServiceFactory();
|
|
89
|
+
let paginateResult = await tenantService.paginate(page, limit, search);
|
|
90
|
+
return paginateResult;
|
|
91
|
+
}
|
|
92
|
+
catch (e) {
|
|
93
|
+
console.error(e);
|
|
94
|
+
if (e instanceof ValidationError) {
|
|
95
|
+
reply.statusCode = e.statusCode;
|
|
96
|
+
reply.send({ error: e.message, inputErrors: e.errors });
|
|
97
|
+
}
|
|
98
|
+
else if (e instanceof UnauthorizedError) {
|
|
99
|
+
reply.statusCode = e.statusCode;
|
|
100
|
+
reply.send({ error: e.message });
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
reply.statusCode = 500;
|
|
104
|
+
reply.send({ error: 'INTERNAL_SERVER_ERROR' });
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
fastify.post('/api/tenants', async (request, reply) => {
|
|
109
|
+
try {
|
|
110
|
+
request.rbac.assertPermission(IdentityPermissions.CreateTenant);
|
|
111
|
+
const payload = request.body;
|
|
112
|
+
const tenantService = TenantServiceFactory();
|
|
113
|
+
let tenant = await tenantService.create(payload);
|
|
114
|
+
return tenant;
|
|
115
|
+
}
|
|
116
|
+
catch (e) {
|
|
117
|
+
console.error(e);
|
|
118
|
+
if (e instanceof ValidationError) {
|
|
119
|
+
reply.statusCode = e.statusCode;
|
|
120
|
+
reply.send({ error: e.message, inputErrors: e.errors });
|
|
121
|
+
}
|
|
122
|
+
else if (e instanceof UnauthorizedError) {
|
|
123
|
+
reply.statusCode = e.statusCode;
|
|
124
|
+
reply.send({ error: e.message });
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
reply.statusCode = 500;
|
|
128
|
+
reply.send({ error: 'INTERNAL_SERVER_ERROR' });
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
fastify.put('/api/tenants/:id', async (request, reply) => {
|
|
133
|
+
try {
|
|
134
|
+
request.rbac.assertPermission(IdentityPermissions.UpdateTenant);
|
|
135
|
+
const id = request.params.id;
|
|
136
|
+
const payload = request.body;
|
|
137
|
+
const tenantService = TenantServiceFactory();
|
|
138
|
+
let tenant = await tenantService.update(id, payload);
|
|
139
|
+
return tenant;
|
|
140
|
+
}
|
|
141
|
+
catch (e) {
|
|
142
|
+
console.error(e);
|
|
143
|
+
if (e instanceof ValidationError) {
|
|
144
|
+
reply.statusCode = e.statusCode;
|
|
145
|
+
reply.send({ error: e.message, inputErrors: e.errors });
|
|
146
|
+
}
|
|
147
|
+
else if (e instanceof UnauthorizedError) {
|
|
148
|
+
reply.statusCode = e.statusCode;
|
|
149
|
+
reply.send({ error: e.message });
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
reply.statusCode = 500;
|
|
153
|
+
reply.send({ error: 'INTERNAL_SERVER_ERROR' });
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
fastify.delete('/api/tenants/:id', async (request, reply) => {
|
|
158
|
+
try {
|
|
159
|
+
request.rbac.assertPermission(IdentityPermissions.DeleteTenant);
|
|
160
|
+
const id = request.params.id;
|
|
161
|
+
const tenantService = TenantServiceFactory();
|
|
162
|
+
let r = await tenantService.delete(id);
|
|
163
|
+
return r;
|
|
164
|
+
}
|
|
165
|
+
catch (e) {
|
|
166
|
+
console.error(e);
|
|
167
|
+
if (e instanceof ValidationError) {
|
|
168
|
+
reply.statusCode = e.statusCode;
|
|
169
|
+
reply.send({ error: e.message, inputErrors: e.errors });
|
|
170
|
+
}
|
|
171
|
+
else if (e instanceof UnauthorizedError) {
|
|
172
|
+
reply.statusCode = e.statusCode;
|
|
173
|
+
reply.send({ error: e.message });
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
reply.statusCode = 500;
|
|
177
|
+
reply.send({ error: 'INTERNAL_SERVER_ERROR' });
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
export default TenantRoutes;
|
|
183
|
+
export { TenantRoutes };
|
|
@@ -55,10 +55,15 @@ async function UserRoutes(fastify, options) {
|
|
|
55
55
|
const limit = request.query.limit;
|
|
56
56
|
const search = request.query.search;
|
|
57
57
|
const userService = UserServiceFactory();
|
|
58
|
-
|
|
58
|
+
const filters = [];
|
|
59
|
+
if (request.rbac.getAuthUser.tenantId) {
|
|
60
|
+
filters.push({ field: 'tenant', operator: '$eq', value: request.rbac.getAuthUser.tenantId });
|
|
61
|
+
}
|
|
62
|
+
let paginateResult = await userService.paginate(page, limit, search, filters);
|
|
59
63
|
return paginateResult;
|
|
60
64
|
}
|
|
61
65
|
catch (e) {
|
|
66
|
+
console.log("/api/users", e);
|
|
62
67
|
if (e instanceof ValidationError) {
|
|
63
68
|
reply.statusCode = e.statusCode;
|
|
64
69
|
reply.send({ error: e.message, inputErrors: e.errors });
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { ZodErrorToValidationError } from "@drax/common-back";
|
|
2
2
|
import { roleSchema } from "../zod/RoleZod.js";
|
|
3
3
|
import { ZodError } from "zod";
|
|
4
|
-
import UnauthorizedError from "../errors/UnauthorizedError.js";
|
|
5
4
|
class RoleService {
|
|
6
5
|
constructor(roleRepostitory) {
|
|
7
6
|
this._repository = roleRepostitory;
|
|
@@ -36,10 +35,6 @@ class RoleService {
|
|
|
36
35
|
}
|
|
37
36
|
}
|
|
38
37
|
async delete(id) {
|
|
39
|
-
const currentRole = await this.findById(id);
|
|
40
|
-
if (currentRole.readonly) {
|
|
41
|
-
throw new UnauthorizedError();
|
|
42
|
-
}
|
|
43
38
|
const deletedRole = await this._repository.delete(id);
|
|
44
39
|
return deletedRole;
|
|
45
40
|
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { ZodErrorToValidationError } from "@drax/common-back";
|
|
2
|
+
import { tenantSchema } from "../zod/TenantZod.js";
|
|
3
|
+
import { ZodError } from "zod";
|
|
4
|
+
class TenantService {
|
|
5
|
+
constructor(tenantRepostitory) {
|
|
6
|
+
this._repository = tenantRepostitory;
|
|
7
|
+
console.log("TenantService constructor");
|
|
8
|
+
}
|
|
9
|
+
async create(tenantData) {
|
|
10
|
+
try {
|
|
11
|
+
tenantData.name = tenantData?.name?.trim();
|
|
12
|
+
await tenantSchema.parseAsync(tenantData);
|
|
13
|
+
const tenant = await this._repository.create(tenantData);
|
|
14
|
+
return tenant;
|
|
15
|
+
}
|
|
16
|
+
catch (e) {
|
|
17
|
+
if (e instanceof ZodError) {
|
|
18
|
+
throw ZodErrorToValidationError(e, tenantData);
|
|
19
|
+
}
|
|
20
|
+
throw e;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
async update(id, tenantData) {
|
|
24
|
+
try {
|
|
25
|
+
tenantData.name = tenantData?.name?.trim();
|
|
26
|
+
await tenantSchema.parseAsync(tenantData);
|
|
27
|
+
const tenant = await this._repository.update(id, tenantData);
|
|
28
|
+
return tenant;
|
|
29
|
+
}
|
|
30
|
+
catch (e) {
|
|
31
|
+
if (e instanceof ZodError) {
|
|
32
|
+
throw ZodErrorToValidationError(e, tenantData);
|
|
33
|
+
}
|
|
34
|
+
throw e;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
async delete(id) {
|
|
38
|
+
const currentTenant = await this.findById(id);
|
|
39
|
+
const deletedTenant = await this._repository.delete(id);
|
|
40
|
+
return deletedTenant;
|
|
41
|
+
}
|
|
42
|
+
async findById(id) {
|
|
43
|
+
const tenant = await this._repository.findById(id);
|
|
44
|
+
return tenant;
|
|
45
|
+
}
|
|
46
|
+
async findByName(name) {
|
|
47
|
+
const tenant = await this._repository.findByName(name);
|
|
48
|
+
return tenant;
|
|
49
|
+
}
|
|
50
|
+
async fetchAll() {
|
|
51
|
+
const tenants = await this._repository.fetchAll();
|
|
52
|
+
return tenants;
|
|
53
|
+
}
|
|
54
|
+
async paginate(page = 1, limit = 5, search, filters) {
|
|
55
|
+
const pagination = await this._repository.paginate(page, limit, search, filters);
|
|
56
|
+
return pagination;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
export default TenantService;
|
|
@@ -15,7 +15,7 @@ class UserService {
|
|
|
15
15
|
if (user && user.active && AuthUtils.checkPassword(password, user.password)) {
|
|
16
16
|
//TODO: Generar Sesion
|
|
17
17
|
const session = '123';
|
|
18
|
-
const accessToken = AuthUtils.generateToken(user.id.toString(), user.username, user.role.id, session);
|
|
18
|
+
const accessToken = AuthUtils.generateToken(user.id.toString(), user.username, user.role.id, user.tenant?.id, session);
|
|
19
19
|
return { accessToken: accessToken };
|
|
20
20
|
}
|
|
21
21
|
else {
|
package/dist/utils/AuthUtils.js
CHANGED
|
@@ -24,16 +24,17 @@ class AuthUtils {
|
|
|
24
24
|
static checkPassword(password, hashPassword) {
|
|
25
25
|
return bcryptjs.compareSync(password, hashPassword);
|
|
26
26
|
}
|
|
27
|
-
static tokenSignPayload(userId, username, roleId, session) {
|
|
27
|
+
static tokenSignPayload(userId, username, roleId, tenantId, session) {
|
|
28
28
|
return {
|
|
29
29
|
id: userId,
|
|
30
30
|
username: username,
|
|
31
31
|
roleId: roleId,
|
|
32
|
+
tenantId: tenantId,
|
|
32
33
|
session: session
|
|
33
34
|
};
|
|
34
35
|
}
|
|
35
|
-
static generateToken(userId, username, roleId, session) {
|
|
36
|
-
const payload = AuthUtils.tokenSignPayload(userId, username, roleId, session);
|
|
36
|
+
static generateToken(userId, username, roleId, tenantId, session) {
|
|
37
|
+
const payload = AuthUtils.tokenSignPayload(userId, username, roleId, tenantId, session);
|
|
37
38
|
const JWT_SECRET = DraxConfig.getOrLoad(IdentityConfig.JwtSecret);
|
|
38
39
|
if (!JWT_SECRET) {
|
|
39
40
|
throw new Error("JWT_SECRET ENV must be provided");
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { object, string } from "zod";
|
|
2
|
+
const tenantSchema = object({
|
|
3
|
+
name: string({ required_error: "validation.required" })
|
|
4
|
+
.min(1, "validation.required")
|
|
5
|
+
.regex(/^[A-Z]/, "validation.startWithUpperCase"),
|
|
6
|
+
});
|
|
7
|
+
export default tenantSchema;
|
|
8
|
+
export { tenantSchema };
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "0.0.
|
|
6
|
+
"version": "0.0.16",
|
|
7
7
|
"description": "Identity module for user management, authentication and authorization.",
|
|
8
8
|
"main": "dist/index.js",
|
|
9
9
|
"types": "types/index.d.ts",
|
|
@@ -56,5 +56,5 @@
|
|
|
56
56
|
"debug": "0"
|
|
57
57
|
}
|
|
58
58
|
},
|
|
59
|
-
"gitHead": "
|
|
59
|
+
"gitHead": "edddb7a152dab85eb7bca07bd79c497bfd12c51f"
|
|
60
60
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import {DraxConfig} from "@drax/common-back"
|
|
2
|
+
import TenantService from "../services/TenantService.js";
|
|
3
|
+
import TenantMongoRepository from "../repository/mongo/TenantMongoRepository.js";
|
|
4
|
+
import TenantSqliteRepository from "../repository/sqlite/TenantSqliteRepository.js";
|
|
5
|
+
import {DbSetupUtils, DbEngine} from "../utils/DbSetupUtils.js";
|
|
6
|
+
import type {ITenantRepository} from "../interfaces/ITenantRepository";
|
|
7
|
+
|
|
8
|
+
let tenantService: TenantService
|
|
9
|
+
|
|
10
|
+
const TenantServiceFactory = () : TenantService => {
|
|
11
|
+
|
|
12
|
+
if(!tenantService){
|
|
13
|
+
let tenantRepository: ITenantRepository
|
|
14
|
+
|
|
15
|
+
switch (DbSetupUtils.getDbEngine()) {
|
|
16
|
+
case DbEngine.Mongo:
|
|
17
|
+
console.log("TenantServiceFactory DB ENGINE MONGODB")
|
|
18
|
+
tenantRepository = new TenantMongoRepository()
|
|
19
|
+
break;
|
|
20
|
+
case DbEngine.Sqlite:
|
|
21
|
+
console.log("TenantServiceFactory DB ENGINE SQLITE")
|
|
22
|
+
tenantRepository = new TenantSqliteRepository(DbSetupUtils.getDbConfig(), false)
|
|
23
|
+
tenantRepository.table()
|
|
24
|
+
break;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
tenantService = new TenantService(tenantRepository)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return tenantService
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export default TenantServiceFactory
|
|
@@ -17,7 +17,7 @@ const UserServiceFactory = () : UserService => {
|
|
|
17
17
|
break;
|
|
18
18
|
case DbEngine.Sqlite:
|
|
19
19
|
console.log("UserServiceFactory DB ENGINE SQLITE")
|
|
20
|
-
userRepository = new UserSqliteRepository(DbSetupUtils.getDbConfig(),
|
|
20
|
+
userRepository = new UserSqliteRepository(DbSetupUtils.getDbConfig(),true)
|
|
21
21
|
userRepository.table()
|
|
22
22
|
break;
|
|
23
23
|
}
|
|
@@ -36,8 +36,15 @@ export default {
|
|
|
36
36
|
try {
|
|
37
37
|
rbac.assertPermission(IdentityPermissions.ViewRole)
|
|
38
38
|
const roleService = RoleServiceFactory()
|
|
39
|
-
|
|
39
|
+
const roles = await roleService.fetchAll()
|
|
40
|
+
if(rbac.getRole?.childRoles?.length > 0) {
|
|
41
|
+
return roles.filter(role => rbac.getRole.childRoles.some(childRole => childRole.id === role.id));
|
|
42
|
+
}else{
|
|
43
|
+
return roles
|
|
44
|
+
}
|
|
45
|
+
|
|
40
46
|
} catch (e) {
|
|
47
|
+
console.error("fetchRole",e)
|
|
41
48
|
if (e instanceof UnauthorizedError) {
|
|
42
49
|
throw new GraphQLError(e.message)
|
|
43
50
|
}
|
|
@@ -112,6 +119,11 @@ export default {
|
|
|
112
119
|
try {
|
|
113
120
|
rbac.assertPermission(IdentityPermissions.DeleteRole)
|
|
114
121
|
const roleService = RoleServiceFactory()
|
|
122
|
+
const currentRole = await roleService.findById(id)
|
|
123
|
+
if(currentRole.readonly){
|
|
124
|
+
throw new UnauthorizedError()
|
|
125
|
+
}
|
|
126
|
+
|
|
115
127
|
return await roleService.delete(id)
|
|
116
128
|
} catch (e) {
|
|
117
129
|
console.error("deleteRole",e)
|