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