@drax/identity-back 0.0.30 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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/tenant.graphql +2 -0
- package/dist/graphql/types/user.graphql +2 -0
- 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/TenantModel.js +2 -4
- 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/UserApiKeyService.js +65 -0
- 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/tenant.graphql +2 -0
- package/src/graphql/types/user.graphql +2 -0
- 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/TenantModel.ts +2 -4
- 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/UserApiKeyService.ts +86 -0
- 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/factory/RoleServiceFactory.d.ts.map +1 -1
- package/types/factory/TenantServiceFactory.d.ts.map +1 -1
- package/types/factory/UserApiKeyServiceFactory.d.ts.map +1 -0
- package/types/factory/UserServiceFactory.d.ts.map +1 -1
- package/types/graphql/resolvers/tenant.resolvers.d.ts.map +1 -1
- 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.map +1 -1
- package/types/interfaces/IRoleRepository.d.ts.map +1 -0
- package/types/interfaces/ITenantRepository.d.ts.map +1 -0
- package/types/interfaces/IUserApiKeyRepository.d.ts.map +1 -0
- package/types/interfaces/IUserRepository.d.ts.map +1 -0
- package/types/middleware/apiKeyMiddleware.d.ts.map +1 -0
- package/types/middleware/rbacMiddleware.d.ts.map +1 -1
- package/types/models/TenantModel.d.ts.map +1 -1
- package/types/models/UserApiKeyModel.d.ts.map +1 -0
- package/types/rbac/Rbac.d.ts +15 -0
- package/types/rbac/Rbac.d.ts.map +1 -0
- package/types/repository/mongo/UserApiKeyMongoRepository.d.ts.map +1 -0
- package/types/repository/sqlite/RoleSqliteRepository.d.ts.map +1 -1
- 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.map +1 -1
- package/types/routes/UserApiKeyRoutes.d.ts.map +1 -0
- package/types/routes/UserRoutes.d.ts.map +1 -1
- package/types/services/UserApiKeyService.d.ts.map +1 -0
- package/types/setup/LoadIdentityConfigFromEnv.d.ts.map +1 -1
- package/types/utils/AuthUtils.d.ts.map +1 -1
- package/types/zod/UserApiKeyZod.d.ts +16 -0
- package/types/zod/UserApiKeyZod.d.ts.map +1 -0
- package/src/utils/DbSetupUtils.ts +0 -41
- package/types/config/IdentityConfig.d.ts +0 -13
- package/types/config/IdentityConfig.d.ts.map +0 -1
- package/types/graphql/resolvers/role.resolvers.d.ts +0 -52
- package/types/graphql/resolvers/tenant.resolvers.d.ts +0 -49
- package/types/graphql/resolvers/user.resolvers.d.ts +0 -67
- package/types/utils/DbSetupUtils.d.ts.map +0 -1
|
@@ -4,31 +4,36 @@ import sqlite from "better-sqlite3";
|
|
|
4
4
|
import {randomUUID} from "node:crypto";
|
|
5
5
|
import {IDraxPaginateResult, IDraxPaginateOptions} from "@drax/common-share";
|
|
6
6
|
import {IRole, IRoleBase} from "@drax/identity-share";
|
|
7
|
-
import {SqliteErrorToValidationError} from "@drax/common-back";
|
|
8
|
-
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
);
|
|
19
|
-
`;
|
|
7
|
+
import {SqliteErrorToValidationError, SqliteTableBuilder, SqliteTableField} from "@drax/common-back";
|
|
8
|
+
|
|
9
|
+
const tableFields: SqliteTableField[] = [
|
|
10
|
+
{name: "name", type: "TEXT", unique: true, primary: false},
|
|
11
|
+
{name: "permissions", type: "TEXT", unique: false, primary: false},
|
|
12
|
+
{name: "childRoles", type: "TEXT", unique: false, primary: false},
|
|
13
|
+
{name: "readonly", type: "INTEGER", unique: false, primary: false},
|
|
14
|
+
{name: "createdAt", type: "TEXT", unique: false, primary: false},
|
|
15
|
+
{name: "updatedAt", type: "TEXT", unique: false, primary: false},
|
|
16
|
+
]
|
|
17
|
+
|
|
20
18
|
|
|
21
19
|
class RoleSqliteRepository implements IRoleRepository{
|
|
22
20
|
|
|
23
21
|
private db: any;
|
|
22
|
+
private dataBaseFile: string;
|
|
24
23
|
|
|
25
|
-
constructor(
|
|
26
|
-
this.
|
|
24
|
+
constructor(dataBaseFile:string, verbose:boolean = false) {
|
|
25
|
+
this.dataBaseFile = dataBaseFile;
|
|
26
|
+
this.db = new sqlite(dataBaseFile, {verbose: verbose ? console.log : null});
|
|
27
27
|
this.table()
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
table() {
|
|
31
|
-
|
|
31
|
+
const builder = new SqliteTableBuilder(
|
|
32
|
+
this.dataBaseFile,
|
|
33
|
+
'roles',
|
|
34
|
+
tableFields,
|
|
35
|
+
false);
|
|
36
|
+
builder.build('id')
|
|
32
37
|
}
|
|
33
38
|
|
|
34
39
|
normalizeData(roleData: IRoleBase){
|
|
@@ -52,7 +57,7 @@ class RoleSqliteRepository implements IRoleRepository{
|
|
|
52
57
|
}
|
|
53
58
|
|
|
54
59
|
this.normalizeData(roleData)
|
|
55
|
-
|
|
60
|
+
roleData.createdAt = (new Date().toISOString())
|
|
56
61
|
|
|
57
62
|
const fields = Object.keys(roleData)
|
|
58
63
|
.map(field => `${field}`)
|
|
@@ -62,9 +67,6 @@ class RoleSqliteRepository implements IRoleRepository{
|
|
|
62
67
|
.map(field => `@${field}`)
|
|
63
68
|
.join(', ');
|
|
64
69
|
|
|
65
|
-
/* console.log("fields", fields)
|
|
66
|
-
console.log("values",values)
|
|
67
|
-
console.log("userData",roleData)*/
|
|
68
70
|
|
|
69
71
|
const stmt = this.db.prepare(`INSERT INTO roles (${fields}) VALUES (${values})`);
|
|
70
72
|
stmt.run(roleData)
|
|
@@ -80,12 +82,18 @@ class RoleSqliteRepository implements IRoleRepository{
|
|
|
80
82
|
async update(id: string, roleData: IRoleBase): Promise<IRole> {
|
|
81
83
|
try{
|
|
82
84
|
this.normalizeData(roleData)
|
|
85
|
+
roleData.updatedAt = (new Date().toISOString())
|
|
86
|
+
|
|
83
87
|
const setClauses = Object.keys(roleData)
|
|
84
88
|
.map(field => `${field} = @${field}`)
|
|
85
89
|
.join(', ');
|
|
90
|
+
|
|
86
91
|
roleData.id = id
|
|
92
|
+
|
|
87
93
|
const stmt = this.db.prepare( `UPDATE roles SET ${setClauses} WHERE id = @id `);
|
|
94
|
+
|
|
88
95
|
stmt.run(roleData);
|
|
96
|
+
|
|
89
97
|
return this.findById(id)
|
|
90
98
|
}catch (e){
|
|
91
99
|
console.log(e)
|
|
@@ -4,27 +4,35 @@ import {UUID} from "crypto";
|
|
|
4
4
|
import sqlite from "better-sqlite3";
|
|
5
5
|
import {randomUUID} from "node:crypto";
|
|
6
6
|
import {IDraxPaginateResult, IDraxPaginateOptions} from "@drax/common-share";
|
|
7
|
-
import {SqliteErrorToValidationError} from "@drax/common-back";
|
|
7
|
+
import {SqliteErrorToValidationError, SqliteTableBuilder} from "@drax/common-back";
|
|
8
|
+
import type {SqliteTableField} from "@drax/common-back";
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
const tableFields: SqliteTableField[] = [
|
|
12
|
+
{name: "name", type: "TEXT", unique: false, primary: false},
|
|
13
|
+
{name: "createdAt", type: "TEXT", unique: false, primary: false},
|
|
14
|
+
{name: "updatedAt", type: "TEXT", unique: false, primary: false},
|
|
15
|
+
]
|
|
8
16
|
|
|
9
|
-
const tenantTableSQL: string = `
|
|
10
|
-
CREATE TABLE IF NOT EXISTS tenants
|
|
11
|
-
(
|
|
12
|
-
id TEXT PRIMARY KEY,
|
|
13
|
-
name TEXT
|
|
14
|
-
);
|
|
15
|
-
`;
|
|
16
17
|
|
|
17
18
|
class TenantSqliteRepository implements ITenantRepository{
|
|
18
19
|
|
|
19
20
|
private db: any;
|
|
21
|
+
private dataBaseFile: string;
|
|
20
22
|
|
|
21
|
-
constructor(
|
|
22
|
-
this.
|
|
23
|
+
constructor(dataBaseFile:string, verbose:boolean = false) {
|
|
24
|
+
this.dataBaseFile = dataBaseFile;
|
|
25
|
+
this.db = new sqlite(this.dataBaseFile, {verbose: verbose ? console.log : null});
|
|
23
26
|
this.table()
|
|
24
27
|
}
|
|
25
28
|
|
|
26
29
|
table() {
|
|
27
|
-
|
|
30
|
+
const builder = new SqliteTableBuilder(
|
|
31
|
+
this.dataBaseFile,
|
|
32
|
+
'tenants',
|
|
33
|
+
tableFields,
|
|
34
|
+
false);
|
|
35
|
+
builder.build('id')
|
|
28
36
|
}
|
|
29
37
|
|
|
30
38
|
|
|
@@ -36,6 +44,7 @@ class TenantSqliteRepository implements ITenantRepository{
|
|
|
36
44
|
tenantData.id = randomUUID()
|
|
37
45
|
}
|
|
38
46
|
|
|
47
|
+
tenantData.createdAt = (new Date().toISOString())
|
|
39
48
|
|
|
40
49
|
const fields = Object.keys(tenantData)
|
|
41
50
|
.map(field => `${field}`)
|
|
@@ -66,10 +75,15 @@ class TenantSqliteRepository implements ITenantRepository{
|
|
|
66
75
|
|
|
67
76
|
async update(id: string, tenantData: ITenantBase): Promise<ITenant> {
|
|
68
77
|
try{
|
|
78
|
+
|
|
79
|
+
tenantData.updatedAt = (new Date().toISOString())
|
|
80
|
+
|
|
69
81
|
const setClauses = Object.keys(tenantData)
|
|
70
82
|
.map(field => `${field} = @${field}`)
|
|
71
83
|
.join(', ');
|
|
84
|
+
|
|
72
85
|
tenantData.id = id
|
|
86
|
+
|
|
73
87
|
const stmt = this.db.prepare( `UPDATE tenants SET ${setClauses} WHERE id = @id `);
|
|
74
88
|
stmt.run(tenantData);
|
|
75
89
|
return this.findById(id)
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import {IUserApiKey, IUserApiKeyBase} from '@drax/identity-share'
|
|
2
|
+
import {IUserApiKeyRepository} from '../../interfaces/IUserApiKeyRepository'
|
|
3
|
+
import {UUID} from "crypto";
|
|
4
|
+
import sqlite from "better-sqlite3";
|
|
5
|
+
import {randomUUID} from "node:crypto";
|
|
6
|
+
import {IDraxPaginateResult, IDraxPaginateOptions} from "@drax/common-share";
|
|
7
|
+
import {SqliteErrorToValidationError, SqliteTableBuilder} from "@drax/common-back";
|
|
8
|
+
import type {SqliteTableField} from "@drax/common-back";
|
|
9
|
+
import UserSqliteRepository from "./UserSqliteRepository.js";
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
const tableFields: SqliteTableField[] = [
|
|
13
|
+
{name: "secret", type: "TEXT", unique: true, primary: false},
|
|
14
|
+
{name: "name", type: "TEXT", unique: false, primary: false},
|
|
15
|
+
{name: "user", type: "TEXT", unique: false, primary: false},
|
|
16
|
+
{name: "ipv4", type: "TEXT", unique: false, primary: false},
|
|
17
|
+
{name: "ipv6", type: "TEXT", unique: false, primary: false},
|
|
18
|
+
{name: "createdAt", type: "TEXT", unique: false, primary: false}
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
class UserApiKeySqliteRepository implements IUserApiKeyRepository {
|
|
22
|
+
|
|
23
|
+
private db: any;
|
|
24
|
+
private dataBaseFile: string;
|
|
25
|
+
private userRepository: UserSqliteRepository;
|
|
26
|
+
|
|
27
|
+
constructor(dataBaseFile: string, verbose: boolean = false) {
|
|
28
|
+
this.dataBaseFile = dataBaseFile
|
|
29
|
+
this.userRepository = new UserSqliteRepository(dataBaseFile, verbose)
|
|
30
|
+
this.db = new sqlite(dataBaseFile, {verbose: verbose ? console.log : null});
|
|
31
|
+
this.table()
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async findUserById(id: string) {
|
|
35
|
+
return await this.userRepository.findById(id)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
table() {
|
|
39
|
+
const builder = new SqliteTableBuilder(
|
|
40
|
+
this.dataBaseFile,
|
|
41
|
+
'user_api_keys',
|
|
42
|
+
tableFields,
|
|
43
|
+
false);
|
|
44
|
+
builder.build('id')
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
async create(userApiKeyData: IUserApiKeyBase): Promise<IUserApiKey> {
|
|
49
|
+
try {
|
|
50
|
+
|
|
51
|
+
if (!userApiKeyData.id) {
|
|
52
|
+
userApiKeyData.id = randomUUID()
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (userApiKeyData.ipv4 && Array.isArray(userApiKeyData.ipv4) && userApiKeyData.ipv4.length > 0) {
|
|
56
|
+
userApiKeyData.ipv4 = userApiKeyData.ipv4.join(',')
|
|
57
|
+
}else{
|
|
58
|
+
userApiKeyData.ipv4 = ""
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (userApiKeyData.ipv6 && Array.isArray(userApiKeyData.ipv6) && userApiKeyData.ipv6.length > 0) {
|
|
62
|
+
userApiKeyData.ipv6 = userApiKeyData.ipv6.join(',')
|
|
63
|
+
}else{
|
|
64
|
+
userApiKeyData.ipv6 = ""
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
userApiKeyData.createdAt = (new Date().toISOString())
|
|
68
|
+
|
|
69
|
+
const fields = Object.keys(userApiKeyData)
|
|
70
|
+
.map(field => `${field}`)
|
|
71
|
+
.join(', ');
|
|
72
|
+
|
|
73
|
+
const values = Object.keys(userApiKeyData)
|
|
74
|
+
.map(field => `@${field}`)
|
|
75
|
+
.join(', ');
|
|
76
|
+
|
|
77
|
+
const stmt = this.db.prepare(`INSERT INTO user_api_keys (${fields})
|
|
78
|
+
VALUES (${values})`);
|
|
79
|
+
stmt.run(userApiKeyData)
|
|
80
|
+
return this.findById(userApiKeyData.id as UUID)
|
|
81
|
+
} catch (e) {
|
|
82
|
+
console.log(e)
|
|
83
|
+
throw SqliteErrorToValidationError(e, userApiKeyData)
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async findById(id: string): Promise<IUserApiKey | null> {
|
|
88
|
+
const userApiKey = this.db.prepare('SELECT * FROM user_api_keys WHERE id = ?').get(id);
|
|
89
|
+
userApiKey.ipv4 = userApiKey.ipv4 != "" ? userApiKey.ipv4.split(',') : []
|
|
90
|
+
userApiKey.ipv6 = userApiKey.ipv6 != "" ? userApiKey.ipv6.split(',') : []
|
|
91
|
+
userApiKey.user = await this.findUserById(userApiKey.user)
|
|
92
|
+
return userApiKey
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async findBySecret(secret: string): Promise<IUserApiKey | null> {
|
|
96
|
+
const userApiKey = this.db.prepare('SELECT * FROM user_api_keys WHERE secret = ?').get(secret);
|
|
97
|
+
userApiKey.ipv4 = userApiKey.ipv4 != "" ? userApiKey.ipv4.split(',') : []
|
|
98
|
+
userApiKey.ipv6 = userApiKey.ipv6 != "" ? userApiKey.ipv6.split(',') : []
|
|
99
|
+
userApiKey.user = await this.findUserById(userApiKey.user)
|
|
100
|
+
return userApiKey
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async update(id: string, userApiKeyData: IUserApiKeyBase): Promise<IUserApiKey> {
|
|
104
|
+
try {
|
|
105
|
+
|
|
106
|
+
if (userApiKeyData.ipv4 && Array.isArray(userApiKeyData.ipv4) && userApiKeyData.ipv4.length > 0) {
|
|
107
|
+
userApiKeyData.ipv4 = userApiKeyData.ipv4.join(',')
|
|
108
|
+
}else{
|
|
109
|
+
userApiKeyData.ipv4 = ""
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (userApiKeyData.ipv6 && Array.isArray(userApiKeyData.ipv6) && userApiKeyData.ipv6.length > 0) {
|
|
113
|
+
userApiKeyData.ipv6 = userApiKeyData.ipv6.join(',')
|
|
114
|
+
}else{
|
|
115
|
+
userApiKeyData.ipv6 = ""
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
delete userApiKeyData.secret
|
|
119
|
+
|
|
120
|
+
const setClauses = Object.keys(userApiKeyData)
|
|
121
|
+
.map(field => `${field} = @${field}`)
|
|
122
|
+
.join(', ');
|
|
123
|
+
|
|
124
|
+
userApiKeyData.id = id
|
|
125
|
+
|
|
126
|
+
const stmt = this.db.prepare(`UPDATE user_api_keys
|
|
127
|
+
SET ${setClauses}
|
|
128
|
+
WHERE id = @id `);
|
|
129
|
+
stmt.run(userApiKeyData);
|
|
130
|
+
return this.findById(id)
|
|
131
|
+
} catch (e) {
|
|
132
|
+
console.log(e)
|
|
133
|
+
throw SqliteErrorToValidationError(e, userApiKeyData)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async delete(id: string): Promise<boolean> {
|
|
139
|
+
const stmt = this.db.prepare('DELETE FROM user_api_keys WHERE id = ?');
|
|
140
|
+
stmt.run(id);
|
|
141
|
+
return true
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
async paginate({
|
|
146
|
+
page = 1,
|
|
147
|
+
limit = 5,
|
|
148
|
+
orderBy = '',
|
|
149
|
+
orderDesc = false,
|
|
150
|
+
search = '',
|
|
151
|
+
filters = []
|
|
152
|
+
}: IDraxPaginateOptions): Promise<IDraxPaginateResult<IUserApiKey>> {
|
|
153
|
+
const offset = page > 1 ? (page - 1) * limit : 0
|
|
154
|
+
|
|
155
|
+
let where = ""
|
|
156
|
+
if (search) {
|
|
157
|
+
where = ` WHERE name LIKE '%${search}%'`
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
let whereFilters= []
|
|
161
|
+
if(filters && filters.length > 0 ){
|
|
162
|
+
where = where ? ` AND ` : ` WHERE `
|
|
163
|
+
for(const filter of filters){
|
|
164
|
+
if(['eq','$eq'].includes(filter.operator)){
|
|
165
|
+
whereFilters.push(` ${filter.field} = '${filter.value}' `)
|
|
166
|
+
}
|
|
167
|
+
if(['ne','$ne'].includes(filter.operator)){
|
|
168
|
+
whereFilters.push(` ${filter.field} != '${filter.value}' `)
|
|
169
|
+
}
|
|
170
|
+
if(['in','$in'].includes(filter.operator)){
|
|
171
|
+
whereFilters.push(` ${filter.field} LIKE '%${filter.value}%' `)
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
where += whereFilters.join(" AND ")
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const rCount = this.db.prepare('SELECT COUNT(*) as count FROM user_api_keys' + where).get();
|
|
178
|
+
const userApiKeys = this.db.prepare('SELECT * FROM user_api_keys ' + where + ' LIMIT ? OFFSET ?').all([limit, offset]);
|
|
179
|
+
|
|
180
|
+
for (const userApiKey of userApiKeys) {
|
|
181
|
+
userApiKey.ipv4 = userApiKey.ipv4 != "" ? userApiKey.ipv4.split(',') : []
|
|
182
|
+
userApiKey.ipv6 = userApiKey.ipv6 != "" ? userApiKey.ipv6.split(',') : []
|
|
183
|
+
userApiKey.user = await this.findUserById(userApiKey.user)
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return {
|
|
187
|
+
page: page,
|
|
188
|
+
limit: limit,
|
|
189
|
+
total: rCount.count,
|
|
190
|
+
items: userApiKeys
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export default UserApiKeySqliteRepository
|
|
@@ -4,42 +4,48 @@ import {randomUUID} from "node:crypto";
|
|
|
4
4
|
import {UUID} from "crypto";
|
|
5
5
|
import {IUserRepository} from "../../interfaces/IUserRepository";
|
|
6
6
|
import {IDraxPaginateResult, IDraxPaginateOptions} from "@drax/common-share";
|
|
7
|
-
import {
|
|
7
|
+
import {SqliteErrorToValidationError, SqliteTableBuilder, ValidationError} from "@drax/common-back";
|
|
8
|
+
import type {SqliteTableField} from "@drax/common-back";
|
|
8
9
|
import RoleSqliteRepository from "./RoleSqliteRepository.js";
|
|
9
10
|
import TenantSqliteRepository from "./TenantSqliteRepository.js";
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
);
|
|
27
|
-
`;
|
|
12
|
+
const tableFields: SqliteTableField[] = [
|
|
13
|
+
{name: "name", type: "TEXT", unique: false, primary: false},
|
|
14
|
+
{name: "username", type: "TEXT", unique: true, primary: false},
|
|
15
|
+
{name: "active", type: "INTEGER", unique: false, primary: false},
|
|
16
|
+
{name: "active", type: "INTEGER", unique: false, primary: false},
|
|
17
|
+
{name: "password", type: "TEXT", unique: false, primary: false},
|
|
18
|
+
{name: "email", type: "TEXT", unique: true, primary: false},
|
|
19
|
+
{name: "phone", type: "TEXT", unique: false, primary: false},
|
|
20
|
+
{name: "role", type: "TEXT", unique: false, primary: false},
|
|
21
|
+
{name: "tenant", type: "TEXT", unique: false, primary: false},
|
|
22
|
+
{name: "groups", type: "TEXT", unique: false, primary: false},
|
|
23
|
+
{name: "avatar", type: "TEXT", unique: false, primary: false},
|
|
24
|
+
{name: "createdAt", type: "TEXT", unique: false, primary: false},
|
|
25
|
+
{name: "updatedAt", type: "TEXT", unique: false, primary: false}
|
|
26
|
+
]
|
|
28
27
|
|
|
29
28
|
class UserSqliteRepository implements IUserRepository {
|
|
30
29
|
private db: any;
|
|
31
30
|
private roleRepository: RoleSqliteRepository;
|
|
32
31
|
private tenantRepository: TenantSqliteRepository;
|
|
32
|
+
private dataBaseFile: string;
|
|
33
33
|
|
|
34
|
-
constructor(
|
|
35
|
-
this.
|
|
36
|
-
this.
|
|
37
|
-
this.
|
|
34
|
+
constructor(dataBaseFile: string, verbose: boolean = false) {
|
|
35
|
+
this.dataBaseFile = dataBaseFile
|
|
36
|
+
this.db = new sqlite(dataBaseFile, {verbose: verbose ? console.log : null});
|
|
37
|
+
this.roleRepository = new RoleSqliteRepository(dataBaseFile, verbose)
|
|
38
|
+
this.tenantRepository = new TenantSqliteRepository(dataBaseFile, verbose)
|
|
38
39
|
this.table()
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
table() {
|
|
42
|
-
|
|
43
|
+
const builder = new SqliteTableBuilder(
|
|
44
|
+
this.dataBaseFile,
|
|
45
|
+
'users',
|
|
46
|
+
tableFields,
|
|
47
|
+
false);
|
|
48
|
+
builder.build('id')
|
|
43
49
|
}
|
|
44
50
|
|
|
45
51
|
normalizeData(userData: IUserCreate | IUserUpdate): void {
|
|
@@ -58,6 +64,8 @@ class UserSqliteRepository implements IUserRepository {
|
|
|
58
64
|
throw new ValidationError([{field: 'role', reason: 'validation.notfound', value: userData.role}])
|
|
59
65
|
}
|
|
60
66
|
|
|
67
|
+
userData.createdAt = (new Date().toISOString())
|
|
68
|
+
|
|
61
69
|
this.normalizeData(userData)
|
|
62
70
|
|
|
63
71
|
try {
|
|
@@ -86,6 +94,8 @@ class UserSqliteRepository implements IUserRepository {
|
|
|
86
94
|
throw new ValidationError([{field: 'role', reason: 'validation.notfound', value: userData.role}])
|
|
87
95
|
}
|
|
88
96
|
|
|
97
|
+
userData.updatedAt = (new Date().toISOString())
|
|
98
|
+
|
|
89
99
|
this.normalizeData(userData)
|
|
90
100
|
|
|
91
101
|
const setClauses = Object.keys(userData)
|
|
@@ -153,20 +163,20 @@ class UserSqliteRepository implements IUserRepository {
|
|
|
153
163
|
if(filters && filters.length > 0 ){
|
|
154
164
|
where = where ? ` AND ` : ` WHERE `
|
|
155
165
|
for(const filter of filters){
|
|
156
|
-
if(
|
|
166
|
+
if(['eq','$eq'].includes(filter.operator)){
|
|
157
167
|
whereFilters.push(` ${filter.field} = '${filter.value}' `)
|
|
158
168
|
}
|
|
159
|
-
if(
|
|
169
|
+
if(['ne','$ne'].includes(filter.operator)){
|
|
160
170
|
whereFilters.push(` ${filter.field} != '${filter.value}' `)
|
|
161
171
|
}
|
|
162
|
-
if(
|
|
172
|
+
if(['in','$in'].includes(filter.operator)){
|
|
163
173
|
whereFilters.push(` ${filter.field} LIKE '%${filter.value}%' `)
|
|
164
174
|
}
|
|
165
175
|
}
|
|
166
176
|
where += whereFilters.join(" AND ")
|
|
167
177
|
}
|
|
168
178
|
|
|
169
|
-
|
|
179
|
+
console.log("paginate where ", where, "search", search, "filters", filters, "whereFilters", whereFilters)
|
|
170
180
|
|
|
171
181
|
const rCount = this.db.prepare('SELECT COUNT(*) as count FROM users' + where).get();
|
|
172
182
|
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import UserApiKeyServiceFactory from "../factory/UserApiKeyServiceFactory.js";
|
|
2
|
+
import type {IUserApiKey} from "@drax/identity-share";
|
|
3
|
+
import {ValidationError} from "@drax/common-back";
|
|
4
|
+
import {IdentityPermissions} from "../permissions/IdentityPermissions.js";
|
|
5
|
+
import UnauthorizedError from "../errors/UnauthorizedError.js";
|
|
6
|
+
import {IDraxPaginateResult} from "@drax/common-share";
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
async function UserApiKeyRoutes(fastify, options) {
|
|
10
|
+
|
|
11
|
+
fastify.get('/api/user-api-keys', async (request, reply): Promise<IDraxPaginateResult<IUserApiKey>> => {
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
request.rbac.assertAuthenticated()
|
|
15
|
+
|
|
16
|
+
request.rbac.assertOrPermissions([
|
|
17
|
+
IdentityPermissions.ViewUserApiKey,
|
|
18
|
+
IdentityPermissions.ViewMyUserApiKey
|
|
19
|
+
])
|
|
20
|
+
|
|
21
|
+
const filters = []
|
|
22
|
+
|
|
23
|
+
if(!request.rbac.hasPermission(IdentityPermissions.ViewUserApiKey)){
|
|
24
|
+
filters.push({field: "user", operator: "eq", value: request.rbac.authUser.id})
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const page = request.query.page
|
|
28
|
+
const limit = request.query.limit
|
|
29
|
+
const orderBy = request.query.orderBy
|
|
30
|
+
const orderDesc = request.query.orderDesc
|
|
31
|
+
const search = request.query.search
|
|
32
|
+
const userApiKeyService = UserApiKeyServiceFactory()
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
let paginateResult = await userApiKeyService.paginate({page, limit, orderBy, orderDesc, search, filters})
|
|
36
|
+
return paginateResult
|
|
37
|
+
} catch (e) {
|
|
38
|
+
console.log("/api/user-api-keys",e)
|
|
39
|
+
if (e instanceof ValidationError) {
|
|
40
|
+
reply.statusCode = e.statusCode
|
|
41
|
+
reply.send({error: e.message, inputErrors: e.errors})
|
|
42
|
+
} else if (e instanceof UnauthorizedError) {
|
|
43
|
+
reply.statusCode = e.statusCode
|
|
44
|
+
reply.send({error: e.message})
|
|
45
|
+
} else {
|
|
46
|
+
reply.statusCode = 500
|
|
47
|
+
reply.send({error: 'error.server'})
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
fastify.post('/api/user-api-keys', async (request, reply): Promise<IUserApiKey> => {
|
|
53
|
+
try {
|
|
54
|
+
request.rbac.assertPermission(IdentityPermissions.CreateUser)
|
|
55
|
+
const payload = request.body
|
|
56
|
+
payload.user = request.rbac.authUser.id
|
|
57
|
+
|
|
58
|
+
const userApiKeyService = UserApiKeyServiceFactory()
|
|
59
|
+
|
|
60
|
+
let userApiKey = await userApiKeyService.create(payload)
|
|
61
|
+
return userApiKey
|
|
62
|
+
} catch (e) {
|
|
63
|
+
if (e instanceof ValidationError) {
|
|
64
|
+
reply.statusCode = e.statusCode
|
|
65
|
+
reply.send({error: e.message, inputErrors: e.errors})
|
|
66
|
+
} else if (e instanceof UnauthorizedError) {
|
|
67
|
+
reply.statusCode = e.statusCode
|
|
68
|
+
reply.send({error: e.message})
|
|
69
|
+
} else {
|
|
70
|
+
reply.statusCode = 500
|
|
71
|
+
reply.send({error: 'error.server'})
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
fastify.put('/api/user-api-keys/:id', async (request, reply): Promise<IUserApiKey> => {
|
|
78
|
+
try {
|
|
79
|
+
request.rbac.assertPermission(IdentityPermissions.UpdateUser)
|
|
80
|
+
const id = request.params.id
|
|
81
|
+
const payload = request.body
|
|
82
|
+
const userApiKeyService = UserApiKeyServiceFactory()
|
|
83
|
+
let userApiKey = await userApiKeyService.update(id, payload)
|
|
84
|
+
return userApiKey
|
|
85
|
+
} catch (e) {
|
|
86
|
+
if (e instanceof ValidationError) {
|
|
87
|
+
reply.statusCode = e.statusCode
|
|
88
|
+
reply.send({error: e.message, inputErrors: e.errors})
|
|
89
|
+
}
|
|
90
|
+
if (e instanceof UnauthorizedError) {
|
|
91
|
+
reply.statusCode = e.statusCode
|
|
92
|
+
reply.send({error: e.message})
|
|
93
|
+
} else if (e instanceof UnauthorizedError) {
|
|
94
|
+
reply.statusCode = e.statusCode
|
|
95
|
+
reply.send({error: e.message})
|
|
96
|
+
} else {
|
|
97
|
+
reply.statusCode = 500
|
|
98
|
+
reply.send({error: 'error.server'})
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
fastify.delete('/api/user-api-keys/:id', async (request, reply): Promise<any> => {
|
|
104
|
+
try {
|
|
105
|
+
request.rbac.assertPermission(IdentityPermissions.DeleteUser)
|
|
106
|
+
const id = request.params.id
|
|
107
|
+
const userApiKeyService = UserApiKeyServiceFactory()
|
|
108
|
+
let r = await userApiKeyService.delete(id)
|
|
109
|
+
return r
|
|
110
|
+
} catch (e) {
|
|
111
|
+
if (e instanceof ValidationError) {
|
|
112
|
+
reply.statusCode = e.statusCode
|
|
113
|
+
reply.send({error: e.message, inputErrors: e.errors})
|
|
114
|
+
} else if (e instanceof UnauthorizedError) {
|
|
115
|
+
reply.statusCode = e.statusCode
|
|
116
|
+
reply.send({error: e.message})
|
|
117
|
+
} else {
|
|
118
|
+
reply.statusCode = 500
|
|
119
|
+
reply.send({error: 'error.server'})
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export default UserApiKeyRoutes;
|
|
128
|
+
export {UserApiKeyRoutes}
|
package/src/routes/UserRoutes.ts
CHANGED
|
@@ -32,6 +32,7 @@ async function UserRoutes(fastify, options) {
|
|
|
32
32
|
if (request.authUser) {
|
|
33
33
|
const userService = UserServiceFactory()
|
|
34
34
|
let user = await userService.findById(request.authUser.id)
|
|
35
|
+
user.password = undefined
|
|
35
36
|
delete user.password
|
|
36
37
|
return user
|
|
37
38
|
} else {
|
|
@@ -69,9 +70,12 @@ async function UserRoutes(fastify, options) {
|
|
|
69
70
|
filters.push({field: 'tenant', operator: '$eq', value: request.rbac.getAuthUser.tenantId})
|
|
70
71
|
}
|
|
71
72
|
let paginateResult = await userService.paginate({page, limit, orderBy, orderDesc, search, filters})
|
|
73
|
+
for(let item of paginateResult.items){
|
|
74
|
+
item.password = undefined
|
|
75
|
+
delete item.password
|
|
76
|
+
}
|
|
72
77
|
return paginateResult
|
|
73
78
|
} catch (e) {
|
|
74
|
-
console.log("/api/users",e)
|
|
75
79
|
if (e instanceof ValidationError) {
|
|
76
80
|
reply.statusCode = e.statusCode
|
|
77
81
|
reply.send({error: e.message, inputErrors: e.errors})
|