@drax/identity-back 0.0.31 → 0.1.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/dist/config/IdentityConfig.js +2 -3
- package/dist/factory/RoleServiceFactory.js +8 -7
- package/dist/factory/TenantServiceFactory.js +8 -7
- package/dist/factory/UserApiKeyServiceFactory.js +24 -0
- package/dist/factory/UserServiceFactory.js +8 -7
- package/dist/graphql/resolvers/user-api-key.resolvers.js +89 -0
- package/dist/graphql/resolvers/user.resolvers.js +7 -2
- package/dist/graphql/types/userApiKey.graphql +33 -0
- package/dist/index.js +4 -2
- package/dist/interfaces/IUserApiKeyRepository.js +1 -0
- package/dist/middleware/apiKeyMiddleware.js +30 -0
- package/dist/middleware/rbacMiddleware.js +0 -1
- package/dist/models/UserApiKeyModel.js +44 -0
- package/dist/permissions/IdentityPermissions.js +6 -0
- package/dist/rbac/Rbac.js +8 -0
- package/dist/repository/mongo/UserApiKeyMongoRepository.js +82 -0
- package/dist/repository/mongo/UserMongoRepository.js +3 -3
- package/dist/repository/sqlite/RoleSqliteRepository.js +16 -18
- package/dist/repository/sqlite/TenantSqliteRepository.js +13 -11
- package/dist/repository/sqlite/UserApiKeySqliteRepository.js +147 -0
- package/dist/repository/sqlite/UserSqliteRepository.js +29 -26
- package/dist/routes/UserApiKeyRoutes.js +119 -0
- package/dist/routes/UserRoutes.js +5 -1
- package/dist/services/TenantService.js +42 -11
- package/dist/services/UserApiKeyService.js +90 -0
- package/dist/services/UserService.js +34 -8
- package/dist/setup/LoadIdentityConfigFromEnv.js +3 -3
- package/dist/utils/AuthUtils.js +10 -0
- package/dist/zod/UserApiKeyZod.js +9 -0
- package/package.json +7 -6
- package/src/config/IdentityConfig.ts +4 -3
- package/src/factory/RoleServiceFactory.ts +11 -11
- package/src/factory/TenantServiceFactory.ts +11 -11
- package/src/factory/UserApiKeyServiceFactory.ts +30 -0
- package/src/factory/UserServiceFactory.ts +8 -7
- package/src/graphql/resolvers/tenant.resolvers.ts +0 -1
- package/src/graphql/resolvers/user-api-key.resolvers.ts +94 -0
- package/src/graphql/resolvers/user.resolvers.ts +9 -2
- package/src/graphql/types/userApiKey.graphql +33 -0
- package/src/index.ts +10 -0
- package/src/interfaces/IUserApiKeyRepository.ts +8 -0
- package/src/middleware/apiKeyMiddleware.ts +35 -0
- package/src/middleware/rbacMiddleware.ts +1 -2
- package/src/models/UserApiKeyModel.ts +59 -0
- package/src/permissions/IdentityPermissions.ts +7 -0
- package/src/rbac/Rbac.ts +13 -2
- package/src/repository/mongo/UserApiKeyMongoRepository.ts +114 -0
- package/src/repository/mongo/UserMongoRepository.ts +3 -3
- package/src/repository/sqlite/RoleSqliteRepository.ts +28 -20
- package/src/repository/sqlite/TenantSqliteRepository.ts +25 -11
- package/src/repository/sqlite/UserApiKeySqliteRepository.ts +197 -0
- package/src/repository/sqlite/UserSqliteRepository.ts +37 -27
- package/src/routes/UserApiKeyRoutes.ts +128 -0
- package/src/routes/UserRoutes.ts +5 -1
- package/src/services/TenantService.ts +49 -17
- package/src/services/UserApiKeyService.ts +111 -0
- package/src/services/UserService.ts +74 -48
- package/src/setup/LoadIdentityConfigFromEnv.ts +5 -3
- package/src/utils/AuthUtils.ts +11 -0
- package/src/zod/UserApiKeyZod.ts +15 -0
- package/test/data-obj/apikey/root-mongo-user-apikey.ts +10 -0
- package/test/data-obj/roles/admin-mongo-role.ts +0 -3
- package/test/initializers/RoleMongoInitializer.ts +1 -0
- package/test/initializers/RoleSqliteInitializer.ts +1 -0
- package/test/initializers/UserMongoInitializer.ts +18 -0
- package/test/repository/mongo/user-apikey-mongo-repository.test.ts +73 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/types/config/IdentityConfig.d.ts +2 -3
- package/types/config/IdentityConfig.d.ts.map +1 -1
- package/types/errors/BadCredentialsError.d.ts +6 -0
- package/types/errors/BadCredentialsError.d.ts.map +1 -0
- package/types/errors/UnauthorizedError.d.ts +6 -0
- package/types/errors/UnauthorizedError.d.ts.map +1 -0
- package/types/factory/RoleServiceFactory.d.ts +4 -0
- package/types/factory/RoleServiceFactory.d.ts.map +1 -1
- package/types/factory/TenantServiceFactory.d.ts +4 -0
- package/types/factory/TenantServiceFactory.d.ts.map +1 -1
- package/types/factory/UserApiKeyServiceFactory.d.ts +4 -0
- package/types/factory/UserApiKeyServiceFactory.d.ts.map +1 -0
- package/types/factory/UserServiceFactory.d.ts +4 -0
- package/types/factory/UserServiceFactory.d.ts.map +1 -1
- package/types/graphql/index.d.ts +6 -0
- package/types/graphql/resolvers/tenant.resolvers.d.ts.map +1 -1
- package/types/graphql/resolvers/user-api-key.resolvers.d.ts +37 -0
- package/types/graphql/resolvers/user-api-key.resolvers.d.ts.map +1 -0
- package/types/graphql/resolvers/user.resolvers.d.ts.map +1 -1
- package/types/index.d.ts +35 -0
- package/types/index.d.ts.map +1 -1
- package/types/interfaces/IRoleRepository.d.ts +9 -0
- package/types/interfaces/ITenantRepository.d.ts +9 -0
- package/types/interfaces/IUserApiKeyRepository.d.ts +7 -0
- package/types/interfaces/IUserApiKeyRepository.d.ts.map +1 -0
- package/types/interfaces/IUserRepository.d.ts +10 -0
- package/types/middleware/apiKeyMiddleware.d.ts +4 -0
- package/types/middleware/apiKeyMiddleware.d.ts.map +1 -0
- package/types/middleware/jwtMiddleware.d.ts +4 -0
- package/types/middleware/rbacMiddleware.d.ts +4 -0
- package/types/middleware/rbacMiddleware.d.ts.map +1 -1
- package/types/models/RoleModel.d.ts +16 -0
- package/types/models/TenantModel.d.ts +16 -0
- package/types/models/UserApiKeyModel.d.ts +16 -0
- package/types/models/UserApiKeyModel.d.ts.map +1 -0
- package/types/models/UserGroupModel.d.ts +16 -0
- package/types/models/UserModel.d.ts +16 -0
- package/types/permissions/IdentityPermissions.d.ts +27 -0
- package/types/permissions/IdentityPermissions.d.ts.map +1 -0
- package/types/rbac/Rbac.d.ts +15 -0
- package/types/rbac/Rbac.d.ts.map +1 -1
- package/types/repository/mongo/RoleMongoRepository.d.ts +14 -0
- package/types/repository/mongo/TenantMongoRepository.d.ts +14 -0
- package/types/repository/mongo/UserApiKeyMongoRepository.d.ts +14 -0
- package/types/repository/mongo/UserApiKeyMongoRepository.d.ts.map +1 -0
- package/types/repository/mongo/UserMongoRepository.d.ts +17 -0
- package/types/repository/sqlite/RoleSqliteRepository.d.ts +22 -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 -1
- package/types/repository/sqlite/UserApiKeySqliteRepository.d.ts +19 -0
- package/types/repository/sqlite/UserApiKeySqliteRepository.d.ts.map +1 -0
- package/types/repository/sqlite/UserSqliteRepository.d.ts +25 -0
- package/types/repository/sqlite/UserSqliteRepository.d.ts.map +1 -1
- package/types/routes/RoleRoutes.d.ts +4 -0
- package/types/routes/TenantRoutes.d.ts +4 -0
- package/types/routes/UserApiKeyRoutes.d.ts +4 -0
- package/types/routes/UserApiKeyRoutes.d.ts.map +1 -0
- package/types/routes/UserAvatarRoutes.d.ts +4 -0
- package/types/routes/UserRoutes.d.ts +4 -0
- package/types/routes/UserRoutes.d.ts.map +1 -1
- package/types/services/PermissionService.d.ts +9 -0
- package/types/services/PermissionService.d.ts.map +1 -0
- package/types/services/RoleService.d.ts +16 -0
- package/types/services/TenantService.d.ts +16 -0
- package/types/services/TenantService.d.ts.map +1 -1
- package/types/services/UserApiKeyService.d.ts +15 -0
- package/types/services/UserApiKeyService.d.ts.map +1 -0
- package/types/services/UserService.d.ts +21 -0
- package/types/services/UserService.d.ts.map +1 -1
- package/types/setup/CreateOrUpdateRole.d.ts +5 -0
- package/types/setup/CreateUserIfNotExist.d.ts +5 -0
- package/types/setup/LoadIdentityConfigFromEnv.d.ts +4 -0
- package/types/setup/LoadIdentityConfigFromEnv.d.ts.map +1 -1
- package/types/setup/LoadPermissions.d.ts +4 -0
- package/types/setup/LoadPermissions.d.ts.map +1 -0
- package/types/setup/RecoveryUserPassword.d.ts +4 -0
- package/types/utils/AuthUtils.d.ts +18 -0
- package/types/utils/AuthUtils.d.ts.map +1 -1
- package/types/zod/RoleZod.d.ts +10 -0
- package/types/zod/RoleZod.d.ts.map +1 -0
- package/types/zod/TenantZod.d.ts +10 -0
- package/types/zod/TenantZod.d.ts.map +1 -0
- package/types/zod/UserApiKeyZod.d.ts +16 -0
- package/types/zod/UserApiKeyZod.d.ts.map +1 -0
- package/types/zod/UserZod.d.ts +53 -0
- package/types/zod/UserZod.d.ts.map +1 -0
- package/src/utils/DbSetupUtils.ts +0 -41
- package/types/utils/DbSetupUtils.d.ts.map +0 -1
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import sqlite from "better-sqlite3";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import { SqliteErrorToValidationError, SqliteTableBuilder } from "@drax/common-back";
|
|
4
|
+
import UserSqliteRepository from "./UserSqliteRepository.js";
|
|
5
|
+
const tableFields = [
|
|
6
|
+
{ name: "secret", type: "TEXT", unique: true, primary: false },
|
|
7
|
+
{ name: "name", type: "TEXT", unique: false, primary: false },
|
|
8
|
+
{ name: "user", type: "TEXT", unique: false, primary: false },
|
|
9
|
+
{ name: "ipv4", type: "TEXT", unique: false, primary: false },
|
|
10
|
+
{ name: "ipv6", type: "TEXT", unique: false, primary: false },
|
|
11
|
+
{ name: "createdAt", type: "TEXT", unique: false, primary: false }
|
|
12
|
+
];
|
|
13
|
+
class UserApiKeySqliteRepository {
|
|
14
|
+
constructor(dataBaseFile, verbose = false) {
|
|
15
|
+
this.dataBaseFile = dataBaseFile;
|
|
16
|
+
this.userRepository = new UserSqliteRepository(dataBaseFile, verbose);
|
|
17
|
+
this.db = new sqlite(dataBaseFile, { verbose: verbose ? console.log : null });
|
|
18
|
+
this.table();
|
|
19
|
+
}
|
|
20
|
+
async findUserById(id) {
|
|
21
|
+
return await this.userRepository.findById(id);
|
|
22
|
+
}
|
|
23
|
+
table() {
|
|
24
|
+
const builder = new SqliteTableBuilder(this.dataBaseFile, 'user_api_keys', tableFields, false);
|
|
25
|
+
builder.build('id');
|
|
26
|
+
}
|
|
27
|
+
async create(userApiKeyData) {
|
|
28
|
+
try {
|
|
29
|
+
if (!userApiKeyData.id) {
|
|
30
|
+
userApiKeyData.id = randomUUID();
|
|
31
|
+
}
|
|
32
|
+
if (userApiKeyData.ipv4 && Array.isArray(userApiKeyData.ipv4) && userApiKeyData.ipv4.length > 0) {
|
|
33
|
+
userApiKeyData.ipv4 = userApiKeyData.ipv4.join(',');
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
userApiKeyData.ipv4 = "";
|
|
37
|
+
}
|
|
38
|
+
if (userApiKeyData.ipv6 && Array.isArray(userApiKeyData.ipv6) && userApiKeyData.ipv6.length > 0) {
|
|
39
|
+
userApiKeyData.ipv6 = userApiKeyData.ipv6.join(',');
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
userApiKeyData.ipv6 = "";
|
|
43
|
+
}
|
|
44
|
+
userApiKeyData.createdAt = (new Date().toISOString());
|
|
45
|
+
const fields = Object.keys(userApiKeyData)
|
|
46
|
+
.map(field => `${field}`)
|
|
47
|
+
.join(', ');
|
|
48
|
+
const values = Object.keys(userApiKeyData)
|
|
49
|
+
.map(field => `@${field}`)
|
|
50
|
+
.join(', ');
|
|
51
|
+
const stmt = this.db.prepare(`INSERT INTO user_api_keys (${fields})
|
|
52
|
+
VALUES (${values})`);
|
|
53
|
+
stmt.run(userApiKeyData);
|
|
54
|
+
return this.findById(userApiKeyData.id);
|
|
55
|
+
}
|
|
56
|
+
catch (e) {
|
|
57
|
+
console.log(e);
|
|
58
|
+
throw SqliteErrorToValidationError(e, userApiKeyData);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
async findById(id) {
|
|
62
|
+
const userApiKey = this.db.prepare('SELECT * FROM user_api_keys WHERE id = ?').get(id);
|
|
63
|
+
userApiKey.ipv4 = userApiKey.ipv4 != "" ? userApiKey.ipv4.split(',') : [];
|
|
64
|
+
userApiKey.ipv6 = userApiKey.ipv6 != "" ? userApiKey.ipv6.split(',') : [];
|
|
65
|
+
userApiKey.user = await this.findUserById(userApiKey.user);
|
|
66
|
+
return userApiKey;
|
|
67
|
+
}
|
|
68
|
+
async findBySecret(secret) {
|
|
69
|
+
const userApiKey = this.db.prepare('SELECT * FROM user_api_keys WHERE secret = ?').get(secret);
|
|
70
|
+
userApiKey.ipv4 = userApiKey.ipv4 != "" ? userApiKey.ipv4.split(',') : [];
|
|
71
|
+
userApiKey.ipv6 = userApiKey.ipv6 != "" ? userApiKey.ipv6.split(',') : [];
|
|
72
|
+
userApiKey.user = await this.findUserById(userApiKey.user);
|
|
73
|
+
return userApiKey;
|
|
74
|
+
}
|
|
75
|
+
async update(id, userApiKeyData) {
|
|
76
|
+
try {
|
|
77
|
+
if (userApiKeyData.ipv4 && Array.isArray(userApiKeyData.ipv4) && userApiKeyData.ipv4.length > 0) {
|
|
78
|
+
userApiKeyData.ipv4 = userApiKeyData.ipv4.join(',');
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
userApiKeyData.ipv4 = "";
|
|
82
|
+
}
|
|
83
|
+
if (userApiKeyData.ipv6 && Array.isArray(userApiKeyData.ipv6) && userApiKeyData.ipv6.length > 0) {
|
|
84
|
+
userApiKeyData.ipv6 = userApiKeyData.ipv6.join(',');
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
userApiKeyData.ipv6 = "";
|
|
88
|
+
}
|
|
89
|
+
delete userApiKeyData.secret;
|
|
90
|
+
const setClauses = Object.keys(userApiKeyData)
|
|
91
|
+
.map(field => `${field} = @${field}`)
|
|
92
|
+
.join(', ');
|
|
93
|
+
userApiKeyData.id = id;
|
|
94
|
+
const stmt = this.db.prepare(`UPDATE user_api_keys
|
|
95
|
+
SET ${setClauses}
|
|
96
|
+
WHERE id = @id `);
|
|
97
|
+
stmt.run(userApiKeyData);
|
|
98
|
+
return this.findById(id);
|
|
99
|
+
}
|
|
100
|
+
catch (e) {
|
|
101
|
+
console.log(e);
|
|
102
|
+
throw SqliteErrorToValidationError(e, userApiKeyData);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
async delete(id) {
|
|
106
|
+
const stmt = this.db.prepare('DELETE FROM user_api_keys WHERE id = ?');
|
|
107
|
+
stmt.run(id);
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
async paginate({ page = 1, limit = 5, orderBy = '', orderDesc = false, search = '', filters = [] }) {
|
|
111
|
+
const offset = page > 1 ? (page - 1) * limit : 0;
|
|
112
|
+
let where = "";
|
|
113
|
+
if (search) {
|
|
114
|
+
where = ` WHERE name LIKE '%${search}%'`;
|
|
115
|
+
}
|
|
116
|
+
let whereFilters = [];
|
|
117
|
+
if (filters && filters.length > 0) {
|
|
118
|
+
where = where ? ` AND ` : ` WHERE `;
|
|
119
|
+
for (const filter of filters) {
|
|
120
|
+
if (['eq', '$eq'].includes(filter.operator)) {
|
|
121
|
+
whereFilters.push(` ${filter.field} = '${filter.value}' `);
|
|
122
|
+
}
|
|
123
|
+
if (['ne', '$ne'].includes(filter.operator)) {
|
|
124
|
+
whereFilters.push(` ${filter.field} != '${filter.value}' `);
|
|
125
|
+
}
|
|
126
|
+
if (['in', '$in'].includes(filter.operator)) {
|
|
127
|
+
whereFilters.push(` ${filter.field} LIKE '%${filter.value}%' `);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
where += whereFilters.join(" AND ");
|
|
131
|
+
}
|
|
132
|
+
const rCount = this.db.prepare('SELECT COUNT(*) as count FROM user_api_keys' + where).get();
|
|
133
|
+
const userApiKeys = this.db.prepare('SELECT * FROM user_api_keys ' + where + ' LIMIT ? OFFSET ?').all([limit, offset]);
|
|
134
|
+
for (const userApiKey of userApiKeys) {
|
|
135
|
+
userApiKey.ipv4 = userApiKey.ipv4 != "" ? userApiKey.ipv4.split(',') : [];
|
|
136
|
+
userApiKey.ipv6 = userApiKey.ipv6 != "" ? userApiKey.ipv6.split(',') : [];
|
|
137
|
+
userApiKey.user = await this.findUserById(userApiKey.user);
|
|
138
|
+
}
|
|
139
|
+
return {
|
|
140
|
+
page: page,
|
|
141
|
+
limit: limit,
|
|
142
|
+
total: rCount.count,
|
|
143
|
+
items: userApiKeys
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
export default UserApiKeySqliteRepository;
|
|
@@ -1,33 +1,34 @@
|
|
|
1
1
|
import sqlite from "better-sqlite3";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
|
-
import { SqliteErrorToValidationError, ValidationError } from "@drax/common-back";
|
|
3
|
+
import { SqliteErrorToValidationError, SqliteTableBuilder, ValidationError } from "@drax/common-back";
|
|
4
4
|
import RoleSqliteRepository from "./RoleSqliteRepository.js";
|
|
5
5
|
import TenantSqliteRepository from "./TenantSqliteRepository.js";
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
`;
|
|
6
|
+
const tableFields = [
|
|
7
|
+
{ name: "name", type: "TEXT", unique: false, primary: false },
|
|
8
|
+
{ name: "username", type: "TEXT", unique: true, primary: false },
|
|
9
|
+
{ name: "active", type: "INTEGER", unique: false, primary: false },
|
|
10
|
+
{ name: "active", type: "INTEGER", unique: false, primary: false },
|
|
11
|
+
{ name: "password", type: "TEXT", unique: false, primary: false },
|
|
12
|
+
{ name: "email", type: "TEXT", unique: true, primary: false },
|
|
13
|
+
{ name: "phone", type: "TEXT", unique: false, primary: false },
|
|
14
|
+
{ name: "role", type: "TEXT", unique: false, primary: false },
|
|
15
|
+
{ name: "tenant", type: "TEXT", unique: false, primary: false },
|
|
16
|
+
{ name: "groups", type: "TEXT", unique: false, primary: false },
|
|
17
|
+
{ name: "avatar", type: "TEXT", unique: false, primary: false },
|
|
18
|
+
{ name: "createdAt", type: "TEXT", unique: false, primary: false },
|
|
19
|
+
{ name: "updatedAt", type: "TEXT", unique: false, primary: false }
|
|
20
|
+
];
|
|
22
21
|
class UserSqliteRepository {
|
|
23
|
-
constructor(
|
|
24
|
-
this.
|
|
25
|
-
this.
|
|
26
|
-
this.
|
|
22
|
+
constructor(dataBaseFile, verbose = false) {
|
|
23
|
+
this.dataBaseFile = dataBaseFile;
|
|
24
|
+
this.db = new sqlite(dataBaseFile, { verbose: verbose ? console.log : null });
|
|
25
|
+
this.roleRepository = new RoleSqliteRepository(dataBaseFile, verbose);
|
|
26
|
+
this.tenantRepository = new TenantSqliteRepository(dataBaseFile, verbose);
|
|
27
27
|
this.table();
|
|
28
28
|
}
|
|
29
29
|
table() {
|
|
30
|
-
this.
|
|
30
|
+
const builder = new SqliteTableBuilder(this.dataBaseFile, 'users', tableFields, false);
|
|
31
|
+
builder.build('id');
|
|
31
32
|
}
|
|
32
33
|
normalizeData(userData) {
|
|
33
34
|
if (userData.groups && Array.isArray(userData.groups)) {
|
|
@@ -42,6 +43,7 @@ class UserSqliteRepository {
|
|
|
42
43
|
if (!await this.findRoleById(userData.role)) {
|
|
43
44
|
throw new ValidationError([{ field: 'role', reason: 'validation.notfound', value: userData.role }]);
|
|
44
45
|
}
|
|
46
|
+
userData.createdAt = (new Date().toISOString());
|
|
45
47
|
this.normalizeData(userData);
|
|
46
48
|
try {
|
|
47
49
|
const fields = Object.keys(userData)
|
|
@@ -64,6 +66,7 @@ class UserSqliteRepository {
|
|
|
64
66
|
if (!await this.findRoleById(userData.role)) {
|
|
65
67
|
throw new ValidationError([{ field: 'role', reason: 'validation.notfound', value: userData.role }]);
|
|
66
68
|
}
|
|
69
|
+
userData.updatedAt = (new Date().toISOString());
|
|
67
70
|
this.normalizeData(userData);
|
|
68
71
|
const setClauses = Object.keys(userData)
|
|
69
72
|
.map(field => `${field} = @${field}`)
|
|
@@ -117,19 +120,19 @@ class UserSqliteRepository {
|
|
|
117
120
|
if (filters && filters.length > 0) {
|
|
118
121
|
where = where ? ` AND ` : ` WHERE `;
|
|
119
122
|
for (const filter of filters) {
|
|
120
|
-
if (
|
|
123
|
+
if (['eq', '$eq'].includes(filter.operator)) {
|
|
121
124
|
whereFilters.push(` ${filter.field} = '${filter.value}' `);
|
|
122
125
|
}
|
|
123
|
-
if (
|
|
126
|
+
if (['ne', '$ne'].includes(filter.operator)) {
|
|
124
127
|
whereFilters.push(` ${filter.field} != '${filter.value}' `);
|
|
125
128
|
}
|
|
126
|
-
if (
|
|
129
|
+
if (['in', '$in'].includes(filter.operator)) {
|
|
127
130
|
whereFilters.push(` ${filter.field} LIKE '%${filter.value}%' `);
|
|
128
131
|
}
|
|
129
132
|
}
|
|
130
133
|
where += whereFilters.join(" AND ");
|
|
131
134
|
}
|
|
132
|
-
|
|
135
|
+
console.log("paginate where ", where, "search", search, "filters", filters, "whereFilters", whereFilters);
|
|
133
136
|
const rCount = this.db.prepare('SELECT COUNT(*) as count FROM users' + where).get();
|
|
134
137
|
const users = this.db.prepare('SELECT * FROM users' + where + ' LIMIT ? OFFSET ?').all([limit, offset]);
|
|
135
138
|
for (const user of users) {
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import UserApiKeyServiceFactory from "../factory/UserApiKeyServiceFactory.js";
|
|
2
|
+
import { ValidationError } from "@drax/common-back";
|
|
3
|
+
import { IdentityPermissions } from "../permissions/IdentityPermissions.js";
|
|
4
|
+
import UnauthorizedError from "../errors/UnauthorizedError.js";
|
|
5
|
+
async function UserApiKeyRoutes(fastify, options) {
|
|
6
|
+
fastify.get('/api/user-api-keys', async (request, reply) => {
|
|
7
|
+
try {
|
|
8
|
+
request.rbac.assertAuthenticated();
|
|
9
|
+
request.rbac.assertOrPermissions([
|
|
10
|
+
IdentityPermissions.ViewUserApiKey,
|
|
11
|
+
IdentityPermissions.ViewMyUserApiKey
|
|
12
|
+
]);
|
|
13
|
+
const filters = [];
|
|
14
|
+
if (!request.rbac.hasPermission(IdentityPermissions.ViewUserApiKey)) {
|
|
15
|
+
filters.push({ field: "user", operator: "eq", value: request.rbac.authUser.id });
|
|
16
|
+
}
|
|
17
|
+
const page = request.query.page;
|
|
18
|
+
const limit = request.query.limit;
|
|
19
|
+
const orderBy = request.query.orderBy;
|
|
20
|
+
const orderDesc = request.query.orderDesc;
|
|
21
|
+
const search = request.query.search;
|
|
22
|
+
const userApiKeyService = UserApiKeyServiceFactory();
|
|
23
|
+
let paginateResult = await userApiKeyService.paginate({ page, limit, orderBy, orderDesc, search, filters });
|
|
24
|
+
return paginateResult;
|
|
25
|
+
}
|
|
26
|
+
catch (e) {
|
|
27
|
+
console.log("/api/user-api-keys", e);
|
|
28
|
+
if (e instanceof ValidationError) {
|
|
29
|
+
reply.statusCode = e.statusCode;
|
|
30
|
+
reply.send({ error: e.message, inputErrors: e.errors });
|
|
31
|
+
}
|
|
32
|
+
else if (e instanceof UnauthorizedError) {
|
|
33
|
+
reply.statusCode = e.statusCode;
|
|
34
|
+
reply.send({ error: e.message });
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
reply.statusCode = 500;
|
|
38
|
+
reply.send({ error: 'error.server' });
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
fastify.post('/api/user-api-keys', async (request, reply) => {
|
|
43
|
+
try {
|
|
44
|
+
request.rbac.assertPermission(IdentityPermissions.CreateUser);
|
|
45
|
+
const payload = request.body;
|
|
46
|
+
payload.user = request.rbac.authUser.id;
|
|
47
|
+
const userApiKeyService = UserApiKeyServiceFactory();
|
|
48
|
+
let userApiKey = await userApiKeyService.create(payload);
|
|
49
|
+
return userApiKey;
|
|
50
|
+
}
|
|
51
|
+
catch (e) {
|
|
52
|
+
if (e instanceof ValidationError) {
|
|
53
|
+
reply.statusCode = e.statusCode;
|
|
54
|
+
reply.send({ error: e.message, inputErrors: e.errors });
|
|
55
|
+
}
|
|
56
|
+
else if (e instanceof UnauthorizedError) {
|
|
57
|
+
reply.statusCode = e.statusCode;
|
|
58
|
+
reply.send({ error: e.message });
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
reply.statusCode = 500;
|
|
62
|
+
reply.send({ error: 'error.server' });
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
fastify.put('/api/user-api-keys/:id', async (request, reply) => {
|
|
67
|
+
try {
|
|
68
|
+
request.rbac.assertPermission(IdentityPermissions.UpdateUser);
|
|
69
|
+
const id = request.params.id;
|
|
70
|
+
const payload = request.body;
|
|
71
|
+
const userApiKeyService = UserApiKeyServiceFactory();
|
|
72
|
+
let userApiKey = await userApiKeyService.update(id, payload);
|
|
73
|
+
return userApiKey;
|
|
74
|
+
}
|
|
75
|
+
catch (e) {
|
|
76
|
+
if (e instanceof ValidationError) {
|
|
77
|
+
reply.statusCode = e.statusCode;
|
|
78
|
+
reply.send({ error: e.message, inputErrors: e.errors });
|
|
79
|
+
}
|
|
80
|
+
if (e instanceof UnauthorizedError) {
|
|
81
|
+
reply.statusCode = e.statusCode;
|
|
82
|
+
reply.send({ error: e.message });
|
|
83
|
+
}
|
|
84
|
+
else if (e instanceof UnauthorizedError) {
|
|
85
|
+
reply.statusCode = e.statusCode;
|
|
86
|
+
reply.send({ error: e.message });
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
reply.statusCode = 500;
|
|
90
|
+
reply.send({ error: 'error.server' });
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
fastify.delete('/api/user-api-keys/:id', async (request, reply) => {
|
|
95
|
+
try {
|
|
96
|
+
request.rbac.assertPermission(IdentityPermissions.DeleteUser);
|
|
97
|
+
const id = request.params.id;
|
|
98
|
+
const userApiKeyService = UserApiKeyServiceFactory();
|
|
99
|
+
let r = await userApiKeyService.delete(id);
|
|
100
|
+
return r;
|
|
101
|
+
}
|
|
102
|
+
catch (e) {
|
|
103
|
+
if (e instanceof ValidationError) {
|
|
104
|
+
reply.statusCode = e.statusCode;
|
|
105
|
+
reply.send({ error: e.message, inputErrors: e.errors });
|
|
106
|
+
}
|
|
107
|
+
else if (e instanceof UnauthorizedError) {
|
|
108
|
+
reply.statusCode = e.statusCode;
|
|
109
|
+
reply.send({ error: e.message });
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
reply.statusCode = 500;
|
|
113
|
+
reply.send({ error: 'error.server' });
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
export default UserApiKeyRoutes;
|
|
119
|
+
export { UserApiKeyRoutes };
|
|
@@ -26,6 +26,7 @@ async function UserRoutes(fastify, options) {
|
|
|
26
26
|
if (request.authUser) {
|
|
27
27
|
const userService = UserServiceFactory();
|
|
28
28
|
let user = await userService.findById(request.authUser.id);
|
|
29
|
+
user.password = undefined;
|
|
29
30
|
delete user.password;
|
|
30
31
|
return user;
|
|
31
32
|
}
|
|
@@ -62,10 +63,13 @@ async function UserRoutes(fastify, options) {
|
|
|
62
63
|
filters.push({ field: 'tenant', operator: '$eq', value: request.rbac.getAuthUser.tenantId });
|
|
63
64
|
}
|
|
64
65
|
let paginateResult = await userService.paginate({ page, limit, orderBy, orderDesc, search, filters });
|
|
66
|
+
for (let item of paginateResult.items) {
|
|
67
|
+
item.password = undefined;
|
|
68
|
+
delete item.password;
|
|
69
|
+
}
|
|
65
70
|
return paginateResult;
|
|
66
71
|
}
|
|
67
72
|
catch (e) {
|
|
68
|
-
console.log("/api/users", e);
|
|
69
73
|
if (e instanceof ValidationError) {
|
|
70
74
|
reply.statusCode = e.statusCode;
|
|
71
75
|
reply.send({ error: e.message, inputErrors: e.errors });
|
|
@@ -14,6 +14,7 @@ class TenantService {
|
|
|
14
14
|
return tenant;
|
|
15
15
|
}
|
|
16
16
|
catch (e) {
|
|
17
|
+
console.error("Error creating tenant", e);
|
|
17
18
|
if (e instanceof ZodError) {
|
|
18
19
|
throw ZodErrorToValidationError(e, tenantData);
|
|
19
20
|
}
|
|
@@ -28,6 +29,7 @@ class TenantService {
|
|
|
28
29
|
return tenant;
|
|
29
30
|
}
|
|
30
31
|
catch (e) {
|
|
32
|
+
console.error("Error updating tenant", e);
|
|
31
33
|
if (e instanceof ZodError) {
|
|
32
34
|
throw ZodErrorToValidationError(e, tenantData);
|
|
33
35
|
}
|
|
@@ -35,25 +37,54 @@ class TenantService {
|
|
|
35
37
|
}
|
|
36
38
|
}
|
|
37
39
|
async delete(id) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
40
|
+
try {
|
|
41
|
+
const deletedTenant = await this._repository.delete(id);
|
|
42
|
+
return deletedTenant;
|
|
43
|
+
}
|
|
44
|
+
catch (e) {
|
|
45
|
+
console.error("Error deleting tenant", e);
|
|
46
|
+
throw e;
|
|
47
|
+
}
|
|
41
48
|
}
|
|
42
49
|
async findById(id) {
|
|
43
|
-
|
|
44
|
-
|
|
50
|
+
try {
|
|
51
|
+
const tenant = await this._repository.findById(id);
|
|
52
|
+
return tenant;
|
|
53
|
+
}
|
|
54
|
+
catch (e) {
|
|
55
|
+
console.error("Error finding tenant by id", e);
|
|
56
|
+
throw e;
|
|
57
|
+
}
|
|
45
58
|
}
|
|
46
59
|
async findByName(name) {
|
|
47
|
-
|
|
48
|
-
|
|
60
|
+
try {
|
|
61
|
+
const tenant = await this._repository.findByName(name);
|
|
62
|
+
return tenant;
|
|
63
|
+
}
|
|
64
|
+
catch (e) {
|
|
65
|
+
console.error("Error finding tenant by name", e);
|
|
66
|
+
throw e;
|
|
67
|
+
}
|
|
49
68
|
}
|
|
50
69
|
async fetchAll() {
|
|
51
|
-
|
|
52
|
-
|
|
70
|
+
try {
|
|
71
|
+
const tenants = await this._repository.fetchAll();
|
|
72
|
+
return tenants;
|
|
73
|
+
}
|
|
74
|
+
catch (e) {
|
|
75
|
+
console.error("Error fetching all tenants", e);
|
|
76
|
+
throw e;
|
|
77
|
+
}
|
|
53
78
|
}
|
|
54
79
|
async paginate({ page = 1, limit = 5, orderBy = '', orderDesc = false, search = '', filters = [] }) {
|
|
55
|
-
|
|
56
|
-
|
|
80
|
+
try {
|
|
81
|
+
const pagination = await this._repository.paginate({ page, limit, orderBy, orderDesc, search, filters });
|
|
82
|
+
return pagination;
|
|
83
|
+
}
|
|
84
|
+
catch (e) {
|
|
85
|
+
console.error("Error paginating tenants", e);
|
|
86
|
+
throw e;
|
|
87
|
+
}
|
|
57
88
|
}
|
|
58
89
|
}
|
|
59
90
|
export default TenantService;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { DraxConfig, ZodErrorToValidationError } from "@drax/common-back";
|
|
2
|
+
import { userApiKeySchema } from "../zod/UserApiKeyZod.js";
|
|
3
|
+
import { ZodError } from "zod";
|
|
4
|
+
import crypto from "node:crypto";
|
|
5
|
+
import AuthUtils from "../utils/AuthUtils.js";
|
|
6
|
+
import IdentityConfig from "../config/IdentityConfig.js";
|
|
7
|
+
class UserApiKeyService {
|
|
8
|
+
constructor(userApiKeyRepostitory) {
|
|
9
|
+
this._repository = userApiKeyRepostitory;
|
|
10
|
+
console.log("UserApiKeyService constructor");
|
|
11
|
+
}
|
|
12
|
+
async create(userApiKeyData) {
|
|
13
|
+
try {
|
|
14
|
+
userApiKeyData.name = userApiKeyData?.name?.trim();
|
|
15
|
+
const secret = crypto.randomUUID();
|
|
16
|
+
const APIKEY_SECRET = DraxConfig.getOrLoad(IdentityConfig.ApiKeySecret);
|
|
17
|
+
userApiKeyData.secret = AuthUtils.generateHMAC(APIKEY_SECRET, secret);
|
|
18
|
+
await userApiKeySchema.parseAsync(userApiKeyData);
|
|
19
|
+
const userApiKey = await this._repository.create(userApiKeyData);
|
|
20
|
+
userApiKey.secret = secret;
|
|
21
|
+
return userApiKey;
|
|
22
|
+
}
|
|
23
|
+
catch (e) {
|
|
24
|
+
console.error("Error creating userApiKey", e);
|
|
25
|
+
if (e instanceof ZodError) {
|
|
26
|
+
throw ZodErrorToValidationError(e, userApiKeyData);
|
|
27
|
+
}
|
|
28
|
+
throw e;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
async update(id, userApiKeyData) {
|
|
32
|
+
try {
|
|
33
|
+
userApiKeyData.name = userApiKeyData?.name?.trim();
|
|
34
|
+
delete userApiKeyData.secret;
|
|
35
|
+
await userApiKeySchema.parseAsync(userApiKeyData);
|
|
36
|
+
const userApiKey = await this._repository.update(id, userApiKeyData);
|
|
37
|
+
return userApiKey;
|
|
38
|
+
}
|
|
39
|
+
catch (e) {
|
|
40
|
+
console.error("Error updating userApiKey", e);
|
|
41
|
+
if (e instanceof ZodError) {
|
|
42
|
+
throw ZodErrorToValidationError(e, userApiKeyData);
|
|
43
|
+
}
|
|
44
|
+
throw e;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
async delete(id) {
|
|
48
|
+
try {
|
|
49
|
+
const deletedUserApiKey = await this._repository.delete(id);
|
|
50
|
+
return deletedUserApiKey;
|
|
51
|
+
}
|
|
52
|
+
catch (e) {
|
|
53
|
+
console.error("Error deleting userApiKey", e);
|
|
54
|
+
throw e;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
async findById(id) {
|
|
58
|
+
try {
|
|
59
|
+
const userApiKey = await this._repository.findById(id);
|
|
60
|
+
return userApiKey;
|
|
61
|
+
}
|
|
62
|
+
catch (e) {
|
|
63
|
+
console.error("Error finding userApiKey by id", e);
|
|
64
|
+
throw e;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
async findBySecret(secret) {
|
|
68
|
+
try {
|
|
69
|
+
const APIKEY_SECRET = DraxConfig.getOrLoad(IdentityConfig.ApiKeySecret);
|
|
70
|
+
const hashedSecret = AuthUtils.generateHMAC(APIKEY_SECRET, secret);
|
|
71
|
+
const userApiKey = await this._repository.findBySecret(hashedSecret);
|
|
72
|
+
return userApiKey;
|
|
73
|
+
}
|
|
74
|
+
catch (e) {
|
|
75
|
+
console.error("Error finding userApiKey by secret", e);
|
|
76
|
+
throw e;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
async paginate({ page = 1, limit = 5, orderBy = '', orderDesc = false, search = '', filters = [] }) {
|
|
80
|
+
try {
|
|
81
|
+
const pagination = await this._repository.paginate({ page, limit, orderBy, orderDesc, search, filters });
|
|
82
|
+
return pagination;
|
|
83
|
+
}
|
|
84
|
+
catch (e) {
|
|
85
|
+
console.error("Error paginating userApiKeys", e);
|
|
86
|
+
throw e;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
export default UserApiKeyService;
|
|
@@ -74,6 +74,7 @@ class UserService {
|
|
|
74
74
|
return user;
|
|
75
75
|
}
|
|
76
76
|
catch (e) {
|
|
77
|
+
console.error("Error creating user", e);
|
|
77
78
|
if (e instanceof ZodError) {
|
|
78
79
|
throw ZodErrorToValidationError(e, userData);
|
|
79
80
|
}
|
|
@@ -91,6 +92,7 @@ class UserService {
|
|
|
91
92
|
return user;
|
|
92
93
|
}
|
|
93
94
|
catch (e) {
|
|
95
|
+
console.error("Error updating user", e);
|
|
94
96
|
if (e instanceof ZodError) {
|
|
95
97
|
throw ZodErrorToValidationError(e, userData);
|
|
96
98
|
}
|
|
@@ -98,20 +100,44 @@ class UserService {
|
|
|
98
100
|
}
|
|
99
101
|
}
|
|
100
102
|
async delete(id) {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
+
try {
|
|
104
|
+
const deletedRole = await this._repository.delete(id);
|
|
105
|
+
return deletedRole;
|
|
106
|
+
}
|
|
107
|
+
catch (e) {
|
|
108
|
+
console.error("Error deleting user", e);
|
|
109
|
+
throw e;
|
|
110
|
+
}
|
|
103
111
|
}
|
|
104
112
|
async findById(id) {
|
|
105
|
-
|
|
106
|
-
|
|
113
|
+
try {
|
|
114
|
+
const user = await this._repository.findById(id);
|
|
115
|
+
return user;
|
|
116
|
+
}
|
|
117
|
+
catch (e) {
|
|
118
|
+
console.error("Error finding user by id", e);
|
|
119
|
+
throw e;
|
|
120
|
+
}
|
|
107
121
|
}
|
|
108
122
|
async findByUsername(username) {
|
|
109
|
-
|
|
110
|
-
|
|
123
|
+
try {
|
|
124
|
+
const user = await this._repository.findByUsername(username);
|
|
125
|
+
return user;
|
|
126
|
+
}
|
|
127
|
+
catch (e) {
|
|
128
|
+
console.error("Error finding user by username", e);
|
|
129
|
+
throw e;
|
|
130
|
+
}
|
|
111
131
|
}
|
|
112
132
|
async paginate({ page = 1, limit = 5, orderBy = '', orderDesc = false, search = '', filters = [] }) {
|
|
113
|
-
|
|
114
|
-
|
|
133
|
+
try {
|
|
134
|
+
const pagination = await this._repository.paginate({ page, limit, orderBy, orderDesc, search, filters });
|
|
135
|
+
return pagination;
|
|
136
|
+
}
|
|
137
|
+
catch (e) {
|
|
138
|
+
console.error("Error paginating users", e);
|
|
139
|
+
throw e;
|
|
140
|
+
}
|
|
115
141
|
}
|
|
116
142
|
}
|
|
117
143
|
export default UserService;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { DraxConfig } from "@drax/common-back";
|
|
2
2
|
import IdentityConfig from "../config/IdentityConfig.js";
|
|
3
3
|
function LoadIdentityConfigFromEnv() {
|
|
4
|
-
DraxConfig.set(IdentityConfig.DbEngine, process.env[IdentityConfig.DbEngine]);
|
|
5
|
-
DraxConfig.set(IdentityConfig.SqliteDbFile, process.env[IdentityConfig.SqliteDbFile]);
|
|
6
|
-
DraxConfig.set(IdentityConfig.MongoDbUri, process.env[IdentityConfig.MongoDbUri]);
|
|
7
4
|
DraxConfig.set(IdentityConfig.JwtSecret, process.env[IdentityConfig.JwtSecret]);
|
|
8
5
|
DraxConfig.set(IdentityConfig.JwtExpiration, process.env[IdentityConfig.JwtExpiration]);
|
|
9
6
|
DraxConfig.set(IdentityConfig.JwtIssuer, process.env[IdentityConfig.JwtIssuer]);
|
|
7
|
+
DraxConfig.set(IdentityConfig.ApiKeySecret, process.env[IdentityConfig.ApiKeySecret]);
|
|
8
|
+
DraxConfig.set(IdentityConfig.RbacCacheTTL, process.env[IdentityConfig.RbacCacheTTL]);
|
|
9
|
+
DraxConfig.set(IdentityConfig.AvatarDir, process.env[IdentityConfig.AvatarDir]);
|
|
10
10
|
}
|
|
11
11
|
export default LoadIdentityConfigFromEnv;
|
|
12
12
|
export { LoadIdentityConfigFromEnv };
|
package/dist/utils/AuthUtils.js
CHANGED
|
@@ -2,6 +2,7 @@ import bcryptjs from "bcryptjs";
|
|
|
2
2
|
import jsonwebtoken from "jsonwebtoken";
|
|
3
3
|
import { DraxConfig } from "@drax/common-back";
|
|
4
4
|
import IdentityConfig from "../config/IdentityConfig.js";
|
|
5
|
+
import crypto from "crypto";
|
|
5
6
|
class AuthUtils {
|
|
6
7
|
static verifyToken(token) {
|
|
7
8
|
const JWT_SECRET = DraxConfig.getOrLoad(IdentityConfig.JwtSecret);
|
|
@@ -51,5 +52,14 @@ class AuthUtils {
|
|
|
51
52
|
let token = jsonwebtoken.sign(payload, JWT_SECRET, options);
|
|
52
53
|
return token;
|
|
53
54
|
}
|
|
55
|
+
static generateHMAC(secret, apikey) {
|
|
56
|
+
// Crear un objeto HMAC utilizando el algoritmo SHA-256 y el secreto
|
|
57
|
+
const hmac = crypto.createHmac('sha256', secret);
|
|
58
|
+
// Actualizar el HMAC con la apikey
|
|
59
|
+
hmac.update(apikey);
|
|
60
|
+
// Generar el hash en formato hexadecimal
|
|
61
|
+
return hmac.digest('hex');
|
|
62
|
+
}
|
|
54
63
|
}
|
|
55
64
|
export default AuthUtils;
|
|
65
|
+
export { AuthUtils };
|