@drax/identity-back 0.0.28 → 0.0.30
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 +1 -0
- package/dist/graphql/resolvers/role.resolvers.js +2 -2
- package/dist/graphql/resolvers/tenant.resolvers.js +2 -2
- package/dist/graphql/resolvers/user.resolvers.js +40 -4
- package/dist/graphql/types/user.graphql +2 -0
- package/dist/index.js +2 -2
- package/dist/rbac/Rbac.js +5 -0
- package/dist/repository/mongo/UserMongoRepository.js +10 -0
- package/dist/repository/sqlite/RoleSqliteRepository.js +1 -1
- package/dist/repository/sqlite/TenantSqliteRepository.js +1 -1
- package/dist/repository/sqlite/UserSqliteRepository.js +7 -0
- package/dist/routes/UserAvatarRoutes.js +70 -0
- package/dist/services/UserService.js +13 -0
- package/package.json +5 -5
- package/src/config/IdentityConfig.ts +2 -0
- package/src/graphql/resolvers/role.resolvers.ts +2 -2
- package/src/graphql/resolvers/tenant.resolvers.ts +2 -2
- package/src/graphql/resolvers/user.resolvers.ts +63 -16
- package/src/graphql/types/user.graphql +2 -0
- package/src/index.ts +2 -1
- package/src/interfaces/IUserRepository.ts +1 -0
- package/src/rbac/Rbac.ts +6 -0
- package/src/repository/mongo/UserMongoRepository.ts +10 -0
- package/src/repository/sqlite/RoleSqliteRepository.ts +1 -1
- package/src/repository/sqlite/TenantSqliteRepository.ts +1 -1
- package/src/repository/sqlite/UserSqliteRepository.ts +8 -0
- package/src/routes/UserAvatarRoutes.ts +82 -0
- package/src/services/UserService.ts +17 -0
- package/test/data-obj/roles/admin-mongo-role.ts +1 -1
- package/test/repository/mongo/role-mongo-repository.test.ts +2 -3
- package/test/repository/mongo/user-mongo-repository.test.ts +2 -2
- package/test/repository/sqlite/role-sqlite-repository.test.ts +2 -2
- package/test/repository/sqlite/user-sqlite-repository.test.ts +1 -1
- package/tsconfig.json +1 -11
- package/tsconfig.tsbuildinfo +1 -1
- package/types/config/IdentityConfig.d.ts +2 -1
- package/types/config/IdentityConfig.d.ts.map +1 -1
- package/types/graphql/resolvers/role.resolvers.d.ts +9 -6
- package/types/graphql/resolvers/role.resolvers.d.ts.map +1 -1
- package/types/graphql/resolvers/tenant.resolvers.d.ts +9 -6
- package/types/graphql/resolvers/tenant.resolvers.d.ts.map +1 -1
- package/types/graphql/resolvers/user.resolvers.d.ts +15 -7
- package/types/graphql/resolvers/user.resolvers.d.ts.map +1 -1
- package/types/index.d.ts.map +1 -1
- package/types/repository/mongo/UserMongoRepository.d.ts.map +1 -1
- package/types/repository/sqlite/UserSqliteRepository.d.ts.map +1 -1
- package/types/routes/UserAvatarRoutes.d.ts.map +1 -0
- package/types/services/UserService.d.ts.map +1 -1
- package/types/errors/BadCredentialsError.d.ts +0 -6
- package/types/errors/BadCredentialsError.d.ts.map +0 -1
- package/types/errors/UnauthorizedError.d.ts +0 -6
- package/types/errors/UnauthorizedError.d.ts.map +0 -1
- package/types/factory/RoleServiceFactory.d.ts +0 -4
- package/types/factory/TenantServiceFactory.d.ts +0 -4
- package/types/factory/UserServiceFactory.d.ts +0 -4
- package/types/graphql/index.d.ts +0 -6
- package/types/index.d.ts +0 -31
- package/types/interfaces/IID.d.ts +0 -6
- package/types/interfaces/IID.d.ts.map +0 -1
- package/types/interfaces/IJwtUser.d.ts +0 -7
- package/types/interfaces/IJwtUser.d.ts.map +0 -1
- package/types/interfaces/IRole.d.ts +0 -18
- package/types/interfaces/IRole.d.ts.map +0 -1
- package/types/interfaces/IRoleRepository.d.ts +0 -9
- package/types/interfaces/IRoleRepository.d.ts.map +0 -1
- package/types/interfaces/ITenant.d.ts +0 -7
- package/types/interfaces/ITenant.d.ts.map +0 -1
- package/types/interfaces/ITenantRepository.d.ts +0 -9
- package/types/interfaces/ITenantRepository.d.ts.map +0 -1
- package/types/interfaces/IUser.d.ts +0 -45
- package/types/interfaces/IUser.d.ts.map +0 -1
- package/types/interfaces/IUserGroup.d.ts +0 -11
- package/types/interfaces/IUserGroup.d.ts.map +0 -1
- package/types/interfaces/IUserRepository.d.ts +0 -9
- package/types/interfaces/IUserRepository.d.ts.map +0 -1
- package/types/middleware/jwtMiddleware.d.ts +0 -4
- package/types/middleware/rbacMiddleware.d.ts +0 -4
- package/types/models/RoleModel.d.ts +0 -16
- package/types/models/TenantModel.d.ts +0 -16
- package/types/models/UserGroupModel.d.ts +0 -16
- package/types/models/UserModel.d.ts +0 -16
- package/types/permissions/IdentityPermissions.d.ts +0 -21
- package/types/permissions/IdentityPermissions.d.ts.map +0 -1
- package/types/rbac/Rbac.d.ts +0 -13
- package/types/rbac/Rbac.d.ts.map +0 -1
- package/types/repository/mongo/RoleMongoRepository.d.ts +0 -14
- package/types/repository/mongo/TenantMongoRepository.d.ts +0 -14
- package/types/repository/mongo/UserMongoRepository.d.ts +0 -16
- package/types/repository/sqlite/RoleSqliteRepository.d.ts +0 -21
- package/types/repository/sqlite/TenantSqliteRepository.d.ts +0 -18
- package/types/repository/sqlite/UserSqliteRepository.d.ts +0 -23
- package/types/routes/RoleRoutes.d.ts +0 -4
- package/types/routes/TenantRoutes.d.ts +0 -4
- package/types/routes/UserRoutes.d.ts +0 -4
- package/types/services/PermissionService.d.ts +0 -9
- package/types/services/PermissionService.d.ts.map +0 -1
- package/types/services/RoleService.d.ts +0 -16
- package/types/services/TenantService.d.ts +0 -16
- package/types/services/UserService.d.ts +0 -20
- package/types/setup/CreateOrUpdateRole.d.ts +0 -5
- package/types/setup/CreateUserIfNotExist.d.ts +0 -5
- package/types/setup/LoadConfigFromEnv.d.ts +0 -4
- package/types/setup/LoadConfigFromEnv.d.ts.map +0 -1
- package/types/setup/LoadIdentityConfigFromEnv.d.ts +0 -4
- package/types/setup/LoadPermissions.d.ts +0 -4
- package/types/setup/LoadPermissions.d.ts.map +0 -1
- package/types/setup/RecoveryUserPassword.d.ts +0 -4
- package/types/utils/AuthUtils.d.ts +0 -16
- package/types/utils/DbSetupUtils.d.ts +0 -10
- package/types/zod/RoleZod.d.ts +0 -10
- package/types/zod/RoleZod.d.ts.map +0 -1
- package/types/zod/TenantZod.d.ts +0 -10
- package/types/zod/TenantZod.d.ts.map +0 -1
- package/types/zod/UserZod.d.ts +0 -53
- package/types/zod/UserZod.d.ts.map +0 -1
|
@@ -7,6 +7,7 @@ var IdentityConfig;
|
|
|
7
7
|
IdentityConfig["JwtExpiration"] = "DRAX_JWT_EXPIRATION";
|
|
8
8
|
IdentityConfig["JwtIssuer"] = "DRAX_JWT_ISSUER";
|
|
9
9
|
IdentityConfig["RbacCacheTTL"] = "DRAX_RBAC_CACHE_TTL";
|
|
10
|
+
IdentityConfig["AvatarDir"] = "DRAX_AVATAR_DIR";
|
|
10
11
|
})(IdentityConfig || (IdentityConfig = {}));
|
|
11
12
|
export default IdentityConfig;
|
|
12
13
|
export { IdentityConfig };
|
|
@@ -64,11 +64,11 @@ export default {
|
|
|
64
64
|
throw new GraphQLError('error.server');
|
|
65
65
|
}
|
|
66
66
|
},
|
|
67
|
-
paginateRole: async (_, { page, limit, orderBy, orderDesc, search }, { rbac }) => {
|
|
67
|
+
paginateRole: async (_, { options = { page: 1, limit: 5, orderBy: "", orderDesc: false, search: "", filters: [] } }, { rbac }) => {
|
|
68
68
|
try {
|
|
69
69
|
rbac.assertPermission(IdentityPermissions.ViewRole);
|
|
70
70
|
const roleService = RoleServiceFactory();
|
|
71
|
-
return await roleService.paginate(
|
|
71
|
+
return await roleService.paginate(options);
|
|
72
72
|
}
|
|
73
73
|
catch (e) {
|
|
74
74
|
console.error("paginateRole", e);
|
|
@@ -50,11 +50,11 @@ export default {
|
|
|
50
50
|
throw new GraphQLError('error.server');
|
|
51
51
|
}
|
|
52
52
|
},
|
|
53
|
-
paginateTenant: async (_, { page, limit, orderBy, orderDesc, search }, { rbac }) => {
|
|
53
|
+
paginateTenant: async (_, { options = { page: 1, limit: 5, orderBy: "", orderDesc: false, search: "", filters: [] } }, { rbac }) => {
|
|
54
54
|
try {
|
|
55
55
|
rbac.assertPermission(IdentityPermissions.ViewTenant);
|
|
56
56
|
const tenantService = TenantServiceFactory();
|
|
57
|
-
return await tenantService.paginate(
|
|
57
|
+
return await tenantService.paginate(options);
|
|
58
58
|
}
|
|
59
59
|
catch (e) {
|
|
60
60
|
console.error("paginateTenant", e);
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import UserServiceFactory from "../../factory/UserServiceFactory.js";
|
|
2
2
|
import { GraphQLError } from "graphql";
|
|
3
|
-
import { ValidationErrorToGraphQLError, ValidationError } from "@drax/common-back";
|
|
3
|
+
import { ValidationErrorToGraphQLError, ValidationError, StoreManager, DraxConfig, CommonConfig } from "@drax/common-back";
|
|
4
4
|
import { IdentityPermissions } from "../../permissions/IdentityPermissions.js";
|
|
5
5
|
import UnauthorizedError from "../../errors/UnauthorizedError.js";
|
|
6
6
|
import BadCredentialsError from "../../errors/BadCredentialsError.js";
|
|
7
|
+
import { join } from "path";
|
|
8
|
+
import IdentityConfig from "../../config/IdentityConfig.js";
|
|
7
9
|
export default {
|
|
8
10
|
Query: {
|
|
9
11
|
me: async (_, {}, { authUser }) => {
|
|
@@ -34,14 +36,14 @@ export default {
|
|
|
34
36
|
throw new GraphQLError('error.server');
|
|
35
37
|
}
|
|
36
38
|
},
|
|
37
|
-
paginateUser: async (_, { page, limit, orderBy, orderDesc, search, filters
|
|
39
|
+
paginateUser: async (_, { options = { page: 1, limit: 5, orderBy: "", orderDesc: false, search: "", filters: [] } }, { rbac }) => {
|
|
38
40
|
try {
|
|
39
41
|
rbac.assertPermission(IdentityPermissions.ViewUser);
|
|
40
42
|
let userService = UserServiceFactory();
|
|
41
43
|
if (rbac.getAuthUser.tenantId) {
|
|
42
|
-
filters.push({ field: 'tenant', operator: '$eq', value: rbac.getAuthUser.tenantId });
|
|
44
|
+
options.filters.push({ field: 'tenant', operator: '$eq', value: rbac.getAuthUser.tenantId });
|
|
43
45
|
}
|
|
44
|
-
return await userService.paginate(
|
|
46
|
+
return await userService.paginate(options);
|
|
45
47
|
}
|
|
46
48
|
catch (e) {
|
|
47
49
|
if (e instanceof UnauthorizedError) {
|
|
@@ -158,5 +160,39 @@ export default {
|
|
|
158
160
|
throw new GraphQLError('error.server');
|
|
159
161
|
}
|
|
160
162
|
},
|
|
163
|
+
changeAvatar: async (_, { file }, { rbac, authUser }) => {
|
|
164
|
+
try {
|
|
165
|
+
rbac.assertAuthenticated();
|
|
166
|
+
const userId = authUser.id;
|
|
167
|
+
const FILE_DIR = DraxConfig.getOrLoad(IdentityConfig.AvatarDir) || 'avatars';
|
|
168
|
+
const BASE_URL = DraxConfig.getOrLoad(CommonConfig.BaseUrl) ? DraxConfig.get(CommonConfig.BaseUrl).replace(/\/$/, '') : '';
|
|
169
|
+
//console.log("FILE:", file)
|
|
170
|
+
let preFile;
|
|
171
|
+
//Yoga PonyfillFile
|
|
172
|
+
if (file.blobParts) {
|
|
173
|
+
preFile = {
|
|
174
|
+
filename: file.name,
|
|
175
|
+
fileStream: file.blobParts,
|
|
176
|
+
mimetype: file.type,
|
|
177
|
+
encoding: file.encoding,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
const destinationPath = join(FILE_DIR);
|
|
181
|
+
const storedFile = await StoreManager.saveFile(preFile, destinationPath);
|
|
182
|
+
const urlFile = BASE_URL + '/api/user/avatar/' + storedFile.filename;
|
|
183
|
+
let userService = UserServiceFactory();
|
|
184
|
+
return await userService.changeAvatar(userId, urlFile);
|
|
185
|
+
}
|
|
186
|
+
catch (e) {
|
|
187
|
+
console.error("changeAvatar", e);
|
|
188
|
+
if (e instanceof ValidationError) {
|
|
189
|
+
throw ValidationErrorToGraphQLError(e);
|
|
190
|
+
}
|
|
191
|
+
else if (e instanceof UnauthorizedError) {
|
|
192
|
+
throw new GraphQLError(e.message);
|
|
193
|
+
}
|
|
194
|
+
throw new GraphQLError('error.server');
|
|
195
|
+
}
|
|
196
|
+
},
|
|
161
197
|
}
|
|
162
198
|
};
|
|
@@ -54,6 +54,7 @@ input AuthInput{
|
|
|
54
54
|
password: String!
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
|
|
57
58
|
type Mutation{
|
|
58
59
|
auth(input: AuthInput): Auth
|
|
59
60
|
createUser(input: UserCreateInput): User
|
|
@@ -61,4 +62,5 @@ type Mutation{
|
|
|
61
62
|
deleteUser(id: ID!): Boolean
|
|
62
63
|
changeOwnPassword(currentPassword:String!, newPassword: String!): Boolean
|
|
63
64
|
changeUserPassword(userId:ID!, newPassword:String!): Boolean
|
|
65
|
+
changeAvatar(file: File!): Boolean
|
|
64
66
|
}
|
package/dist/index.js
CHANGED
|
@@ -8,6 +8,7 @@ import TenantService from "./services/TenantService.js";
|
|
|
8
8
|
import PermissionService from "./services/PermissionService.js";
|
|
9
9
|
import Rbac from "./rbac/Rbac.js";
|
|
10
10
|
import { UserRoutes } from "./routes/UserRoutes.js";
|
|
11
|
+
import { UserAvatarRoutes } from "./routes/UserAvatarRoutes.js";
|
|
11
12
|
import { RoleRoutes } from "./routes/RoleRoutes.js";
|
|
12
13
|
import { TenantRoutes } from "./routes/TenantRoutes.js";
|
|
13
14
|
import AuthUtils from "./utils/AuthUtils.js";
|
|
@@ -33,7 +34,7 @@ UserServiceFactory, RoleServiceFactory, TenantServiceFactory,
|
|
|
33
34
|
//GQL
|
|
34
35
|
identityTypeDefs, identityResolvers,
|
|
35
36
|
//API REST
|
|
36
|
-
UserRoutes, RoleRoutes, TenantRoutes, AuthUtils,
|
|
37
|
+
UserRoutes, RoleRoutes, TenantRoutes, UserAvatarRoutes, AuthUtils,
|
|
37
38
|
//API MIDDLEWARE
|
|
38
39
|
jwtMiddleware, rbacMiddleware,
|
|
39
40
|
//Permissions
|
|
@@ -44,4 +45,3 @@ IdentityConfig,
|
|
|
44
45
|
UnauthorizedError, BadCredentialsError,
|
|
45
46
|
//Setup
|
|
46
47
|
LoadIdentityConfigFromEnv, LoadPermissions, CreateOrUpdateRole, CreateUserIfNotExist, RecoveryUserPassword };
|
|
47
|
-
/// <reference types="index.d.ts" />
|
package/dist/rbac/Rbac.js
CHANGED
|
@@ -91,5 +91,15 @@ class UserMongoRepository {
|
|
|
91
91
|
return false;
|
|
92
92
|
}
|
|
93
93
|
}
|
|
94
|
+
async changeAvatar(id, avatar) {
|
|
95
|
+
try {
|
|
96
|
+
await UserModel.findOneAndUpdate({ _id: id }, { avatar }).exec();
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
catch (e) {
|
|
100
|
+
console.error(e);
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
94
104
|
}
|
|
95
105
|
export default UserMongoRepository;
|
|
@@ -76,7 +76,7 @@ class RoleSqliteRepository {
|
|
|
76
76
|
where = ` WHERE name LIKE '%${search}%'`;
|
|
77
77
|
}
|
|
78
78
|
const rCount = this.db.prepare('SELECT COUNT(*) as count FROM roles' + where).get();
|
|
79
|
-
const roles = this.db.prepare('SELECT * FROM roles LIMIT ? OFFSET ?'
|
|
79
|
+
const roles = this.db.prepare('SELECT * FROM roles ' + where + ' LIMIT ? OFFSET ?').all([limit, offset]);
|
|
80
80
|
for (const role of roles) {
|
|
81
81
|
await this.populateRole(role);
|
|
82
82
|
}
|
|
@@ -83,7 +83,7 @@ class TenantSqliteRepository {
|
|
|
83
83
|
where = ` WHERE name LIKE '%${search}%'`;
|
|
84
84
|
}
|
|
85
85
|
const rCount = this.db.prepare('SELECT COUNT(*) as count FROM tenants' + where).get();
|
|
86
|
-
const tenants = this.db.prepare('SELECT * FROM tenants LIMIT ? OFFSET ?'
|
|
86
|
+
const tenants = this.db.prepare('SELECT * FROM tenants ' + where + ' LIMIT ? OFFSET ?').all([limit, offset]);
|
|
87
87
|
return {
|
|
88
88
|
page: page,
|
|
89
89
|
limit: limit,
|
|
@@ -159,5 +159,12 @@ class UserSqliteRepository {
|
|
|
159
159
|
stmt.run({ id: id, password: password });
|
|
160
160
|
return true;
|
|
161
161
|
}
|
|
162
|
+
async changeAvatar(id, avatar) {
|
|
163
|
+
const stmt = this.db.prepare(`UPDATE users
|
|
164
|
+
SET avatar = @avatar
|
|
165
|
+
WHERE id = @id `);
|
|
166
|
+
stmt.run({ id: id, avatar: avatar });
|
|
167
|
+
return true;
|
|
168
|
+
}
|
|
162
169
|
}
|
|
163
170
|
export default UserSqliteRepository;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { join } from "path";
|
|
2
|
+
import { UnauthorizedError } from "@drax/identity-back";
|
|
3
|
+
import { StoreManager, UploadFileError, DraxConfig, CommonConfig } from "@drax/common-back";
|
|
4
|
+
import UserServiceFactory from "../factory/UserServiceFactory.js";
|
|
5
|
+
import IdentityConfig from "../config/IdentityConfig.js";
|
|
6
|
+
const FILE_DIR = DraxConfig.getOrLoad(IdentityConfig.AvatarDir) || 'avatars';
|
|
7
|
+
const BASE_URL = DraxConfig.getOrLoad(CommonConfig.BaseUrl) ? DraxConfig.get(CommonConfig.BaseUrl).replace(/\/$/, '') : '';
|
|
8
|
+
async function UserAvatarRoutes(fastify, options) {
|
|
9
|
+
fastify.post('/api/user/avatar', async (request, reply) => {
|
|
10
|
+
try {
|
|
11
|
+
request.rbac.assertAuthenticated();
|
|
12
|
+
const userId = request.rbac.getAuthUser.id;
|
|
13
|
+
const data = await request.file();
|
|
14
|
+
const file = {
|
|
15
|
+
filename: data.filename,
|
|
16
|
+
fileStream: data.file,
|
|
17
|
+
mimetype: data.mimetype
|
|
18
|
+
};
|
|
19
|
+
const destinationPath = join(FILE_DIR);
|
|
20
|
+
const storedFile = await StoreManager.saveFile(file, destinationPath);
|
|
21
|
+
const urlFile = BASE_URL + '/api/user/avatar/' + storedFile.filename;
|
|
22
|
+
//Save into DB
|
|
23
|
+
const userService = UserServiceFactory();
|
|
24
|
+
return await userService.changeAvatar(userId, urlFile);
|
|
25
|
+
return {
|
|
26
|
+
filename: storedFile.filename,
|
|
27
|
+
size: storedFile.size,
|
|
28
|
+
mimetype: storedFile.mimetype,
|
|
29
|
+
url: urlFile,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
catch (e) {
|
|
33
|
+
console.error(e);
|
|
34
|
+
if (e instanceof UploadFileError) {
|
|
35
|
+
reply.statusCode = e.statusCode;
|
|
36
|
+
reply.send({ error: e.message });
|
|
37
|
+
}
|
|
38
|
+
else if (e instanceof UnauthorizedError) {
|
|
39
|
+
reply.statusCode = e.statusCode;
|
|
40
|
+
reply.send({ error: e.message });
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
reply.statusCode = 500;
|
|
44
|
+
reply.send({ error: 'INTERNAL_SERVER_ERROR' });
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
fastify.get('/api/user/avatar/:filename', async (request, reply) => {
|
|
49
|
+
try {
|
|
50
|
+
const filename = request.params.filename;
|
|
51
|
+
const [year, month] = filename.split('-');
|
|
52
|
+
const fileDir = join(FILE_DIR, year, month);
|
|
53
|
+
//console.log("FILE_DIR: ",fileDir, " FILENAME:", filename)
|
|
54
|
+
return reply.sendFile(filename, fileDir);
|
|
55
|
+
}
|
|
56
|
+
catch (e) {
|
|
57
|
+
console.error(e);
|
|
58
|
+
if (e instanceof UnauthorizedError) {
|
|
59
|
+
reply.statusCode = e.statusCode;
|
|
60
|
+
reply.send({ error: e.message });
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
reply.statusCode = 500;
|
|
64
|
+
reply.send({ error: 'INTERNAL_SERVER_ERROR' });
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
export default UserAvatarRoutes;
|
|
70
|
+
export { UserAvatarRoutes };
|
|
@@ -36,6 +36,9 @@ class UserService {
|
|
|
36
36
|
async changeOwnPassword(userId, currentPassword, newPassword) {
|
|
37
37
|
const user = await this.findById(userId);
|
|
38
38
|
if (user && user.active) {
|
|
39
|
+
if (currentPassword === newPassword) {
|
|
40
|
+
throw new ValidationError([{ field: 'newPassword', reason: 'validation.password.currentDifferent' }]);
|
|
41
|
+
}
|
|
39
42
|
if (AuthUtils.checkPassword(currentPassword, user.password)) {
|
|
40
43
|
newPassword = AuthUtils.hashPassword(newPassword);
|
|
41
44
|
await this._repository.changePassword(userId, newPassword);
|
|
@@ -49,6 +52,16 @@ class UserService {
|
|
|
49
52
|
throw new BadCredentialsError();
|
|
50
53
|
}
|
|
51
54
|
}
|
|
55
|
+
async changeAvatar(userId, avatar) {
|
|
56
|
+
const user = await this.findById(userId);
|
|
57
|
+
if (user && user.active) {
|
|
58
|
+
await this._repository.changeAvatar(userId, avatar);
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
throw new BadCredentialsError();
|
|
63
|
+
}
|
|
64
|
+
}
|
|
52
65
|
async create(userData) {
|
|
53
66
|
try {
|
|
54
67
|
userData.name = userData?.name?.trim();
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "0.0.
|
|
6
|
+
"version": "0.0.30",
|
|
7
7
|
"description": "Identity module for user management, authentication and authorization.",
|
|
8
8
|
"main": "dist/index.js",
|
|
9
9
|
"types": "types/index.d.ts",
|
|
@@ -26,9 +26,9 @@
|
|
|
26
26
|
"author": "Cristian Incarnato & Drax Team",
|
|
27
27
|
"license": "ISC",
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@drax/common-back": "^0.0.
|
|
30
|
-
"@drax/common-share": "^0.0.
|
|
31
|
-
"@drax/identity-share": "^0.0.
|
|
29
|
+
"@drax/common-back": "^0.0.30",
|
|
30
|
+
"@drax/common-share": "^0.0.30",
|
|
31
|
+
"@drax/identity-share": "^0.0.30",
|
|
32
32
|
"bcryptjs": "^2.4.3",
|
|
33
33
|
"express-jwt": "^8.4.1",
|
|
34
34
|
"graphql": "^16.8.2",
|
|
@@ -59,5 +59,5 @@
|
|
|
59
59
|
"debug": "0"
|
|
60
60
|
}
|
|
61
61
|
},
|
|
62
|
-
"gitHead": "
|
|
62
|
+
"gitHead": "b0882d3ace0d0004cbc850e8d86381b9d6432535"
|
|
63
63
|
}
|
|
@@ -62,11 +62,11 @@ export default {
|
|
|
62
62
|
throw new GraphQLError('error.server')
|
|
63
63
|
}
|
|
64
64
|
},
|
|
65
|
-
paginateRole: async (_, {page, limit, orderBy, orderDesc, search}, {rbac}) => {
|
|
65
|
+
paginateRole: async (_, {options= {page:1, limit:5, orderBy:"", orderDesc:false, search:"", filters: []} }, {rbac}) => {
|
|
66
66
|
try {
|
|
67
67
|
rbac.assertPermission(IdentityPermissions.ViewRole)
|
|
68
68
|
const roleService = RoleServiceFactory()
|
|
69
|
-
return await roleService.paginate(
|
|
69
|
+
return await roleService.paginate(options)
|
|
70
70
|
} catch (e) {
|
|
71
71
|
console.error("paginateRole",e)
|
|
72
72
|
if (e instanceof UnauthorizedError) {
|
|
@@ -50,11 +50,11 @@ export default {
|
|
|
50
50
|
throw new GraphQLError('error.server')
|
|
51
51
|
}
|
|
52
52
|
},
|
|
53
|
-
paginateTenant: async (_, {page, limit, orderBy, orderDesc, search}, {rbac}) => {
|
|
53
|
+
paginateTenant: async (_, {options= {page:1, limit:5, orderBy:"", orderDesc:false, search:"", filters: []} }, {rbac}) => {
|
|
54
54
|
try {
|
|
55
55
|
rbac.assertPermission(IdentityPermissions.ViewTenant)
|
|
56
56
|
const tenantService = TenantServiceFactory()
|
|
57
|
-
return await tenantService.paginate(
|
|
57
|
+
return await tenantService.paginate(options)
|
|
58
58
|
} catch (e) {
|
|
59
59
|
console.error("paginateTenant",e)
|
|
60
60
|
if (e instanceof UnauthorizedError) {
|
|
@@ -1,16 +1,24 @@
|
|
|
1
1
|
import UserServiceFactory from "../../factory/UserServiceFactory.js";
|
|
2
2
|
import {GraphQLError} from "graphql";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
ValidationErrorToGraphQLError,
|
|
5
|
+
ValidationError,
|
|
6
|
+
StoreManager,
|
|
7
|
+
DraxConfig,
|
|
8
|
+
CommonConfig
|
|
9
|
+
} from "@drax/common-back";
|
|
4
10
|
import {IdentityPermissions} from "../../permissions/IdentityPermissions.js";
|
|
5
11
|
import UnauthorizedError from "../../errors/UnauthorizedError.js";
|
|
6
12
|
import BadCredentialsError from "../../errors/BadCredentialsError.js";
|
|
13
|
+
import {join} from "path";
|
|
14
|
+
import IdentityConfig from "../../config/IdentityConfig.js";
|
|
7
15
|
|
|
8
16
|
export default {
|
|
9
17
|
Query: {
|
|
10
18
|
me: async (_, {}, {authUser}) => {
|
|
11
19
|
try {
|
|
12
20
|
if (authUser) {
|
|
13
|
-
let userService= UserServiceFactory()
|
|
21
|
+
let userService = UserServiceFactory()
|
|
14
22
|
let user = await userService.findById(authUser.id)
|
|
15
23
|
delete user.password
|
|
16
24
|
return user
|
|
@@ -25,7 +33,7 @@ export default {
|
|
|
25
33
|
findUserById: async (_, {id}, {rbac}) => {
|
|
26
34
|
try {
|
|
27
35
|
rbac.assertPermission(IdentityPermissions.ViewUser)
|
|
28
|
-
let userService= UserServiceFactory()
|
|
36
|
+
let userService = UserServiceFactory()
|
|
29
37
|
return await userService.findById(id)
|
|
30
38
|
} catch (e) {
|
|
31
39
|
if (e instanceof UnauthorizedError) {
|
|
@@ -35,14 +43,14 @@ export default {
|
|
|
35
43
|
}
|
|
36
44
|
|
|
37
45
|
},
|
|
38
|
-
paginateUser: async (_, {page, limit, orderBy, orderDesc, search, filters
|
|
46
|
+
paginateUser: async (_, { options= {page:1, limit:5, orderBy:"", orderDesc:false, search:"", filters: []} }, {rbac}) => {
|
|
39
47
|
try {
|
|
40
48
|
rbac.assertPermission(IdentityPermissions.ViewUser)
|
|
41
|
-
let userService= UserServiceFactory()
|
|
42
|
-
if(rbac.getAuthUser.tenantId){
|
|
43
|
-
filters.push({field: 'tenant', operator: '$eq', value: rbac.getAuthUser.tenantId})
|
|
49
|
+
let userService = UserServiceFactory()
|
|
50
|
+
if (rbac.getAuthUser.tenantId) {
|
|
51
|
+
options.filters.push({field: 'tenant', operator: '$eq', value: rbac.getAuthUser.tenantId})
|
|
44
52
|
}
|
|
45
|
-
return await userService.paginate(
|
|
53
|
+
return await userService.paginate(options)
|
|
46
54
|
} catch (e) {
|
|
47
55
|
if (e instanceof UnauthorizedError) {
|
|
48
56
|
throw new GraphQLError(e.message)
|
|
@@ -54,7 +62,7 @@ export default {
|
|
|
54
62
|
Mutation: {
|
|
55
63
|
auth: async (_, {input}) => {
|
|
56
64
|
try {
|
|
57
|
-
let userService= UserServiceFactory()
|
|
65
|
+
let userService = UserServiceFactory()
|
|
58
66
|
return await userService.auth(input.username, input.password)
|
|
59
67
|
} catch (e) {
|
|
60
68
|
console.error("auth", e)
|
|
@@ -68,8 +76,8 @@ export default {
|
|
|
68
76
|
createUser: async (_, {input}, {rbac}) => {
|
|
69
77
|
try {
|
|
70
78
|
rbac.assertPermission(IdentityPermissions.CreateUser)
|
|
71
|
-
let userService= UserServiceFactory()
|
|
72
|
-
if(rbac.getAuthUser.tenantId){
|
|
79
|
+
let userService = UserServiceFactory()
|
|
80
|
+
if (rbac.getAuthUser.tenantId) {
|
|
73
81
|
input.tenant = rbac.getAuthUser.tenantId
|
|
74
82
|
}
|
|
75
83
|
const user = await userService.create(input)
|
|
@@ -88,8 +96,8 @@ export default {
|
|
|
88
96
|
updateUser: async (_, {id, input}, {rbac}) => {
|
|
89
97
|
try {
|
|
90
98
|
rbac.assertPermission(IdentityPermissions.UpdateUser)
|
|
91
|
-
let userService= UserServiceFactory()
|
|
92
|
-
if(rbac.getAuthUser.tenantId){
|
|
99
|
+
let userService = UserServiceFactory()
|
|
100
|
+
if (rbac.getAuthUser.tenantId) {
|
|
93
101
|
input.tenant = rbac.getAuthUser.tenantId
|
|
94
102
|
}
|
|
95
103
|
const user = await userService.update(id, input)
|
|
@@ -106,7 +114,7 @@ export default {
|
|
|
106
114
|
deleteUser: async (_, {id}, {rbac}) => {
|
|
107
115
|
try {
|
|
108
116
|
rbac.assertPermission(IdentityPermissions.DeleteUser)
|
|
109
|
-
let userService= UserServiceFactory()
|
|
117
|
+
let userService = UserServiceFactory()
|
|
110
118
|
return await userService.delete(id)
|
|
111
119
|
} catch (e) {
|
|
112
120
|
console.error("deleteUser", e)
|
|
@@ -124,7 +132,7 @@ export default {
|
|
|
124
132
|
throw new UnauthorizedError()
|
|
125
133
|
}
|
|
126
134
|
let userId = authUser.id
|
|
127
|
-
let userService= UserServiceFactory()
|
|
135
|
+
let userService = UserServiceFactory()
|
|
128
136
|
return await userService.changeOwnPassword(userId, currentPassword, newPassword)
|
|
129
137
|
} catch (e) {
|
|
130
138
|
if (e instanceof ValidationError) {
|
|
@@ -138,7 +146,7 @@ export default {
|
|
|
138
146
|
changeUserPassword: async (_, {userId, newPassword}, {rbac}) => {
|
|
139
147
|
try {
|
|
140
148
|
rbac.assertPermission(IdentityPermissions.UpdateUser)
|
|
141
|
-
let userService= UserServiceFactory()
|
|
149
|
+
let userService = UserServiceFactory()
|
|
142
150
|
return await userService.changeUserPassword(userId, newPassword)
|
|
143
151
|
} catch (e) {
|
|
144
152
|
if (e instanceof ValidationError) {
|
|
@@ -149,6 +157,45 @@ export default {
|
|
|
149
157
|
throw new GraphQLError('error.server')
|
|
150
158
|
}
|
|
151
159
|
},
|
|
160
|
+
changeAvatar: async (_, {file}, {rbac, authUser}) => {
|
|
161
|
+
try {
|
|
162
|
+
rbac.assertAuthenticated()
|
|
163
|
+
const userId = authUser.id
|
|
164
|
+
|
|
165
|
+
const FILE_DIR = DraxConfig.getOrLoad(IdentityConfig.AvatarDir) || 'avatars';
|
|
166
|
+
const BASE_URL = DraxConfig.getOrLoad(CommonConfig.BaseUrl) ? DraxConfig.get(CommonConfig.BaseUrl).replace(/\/$/, '') : ''
|
|
167
|
+
|
|
168
|
+
//console.log("FILE:", file)
|
|
169
|
+
|
|
170
|
+
let preFile
|
|
171
|
+
|
|
172
|
+
//Yoga PonyfillFile
|
|
173
|
+
if (file.blobParts) {
|
|
174
|
+
preFile = {
|
|
175
|
+
filename: file.name,
|
|
176
|
+
fileStream: file.blobParts,
|
|
177
|
+
mimetype: file.type,
|
|
178
|
+
encoding: file.encoding,
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
const destinationPath = join(FILE_DIR)
|
|
184
|
+
const storedFile = await StoreManager.saveFile(preFile, destinationPath)
|
|
185
|
+
const urlFile = BASE_URL + '/api/user/avatar/' + storedFile.filename
|
|
186
|
+
|
|
187
|
+
let userService = UserServiceFactory()
|
|
188
|
+
return await userService.changeAvatar(userId, urlFile)
|
|
189
|
+
} catch (e) {
|
|
190
|
+
console.error("changeAvatar", e)
|
|
191
|
+
if (e instanceof ValidationError) {
|
|
192
|
+
throw ValidationErrorToGraphQLError(e)
|
|
193
|
+
} else if (e instanceof UnauthorizedError) {
|
|
194
|
+
throw new GraphQLError(e.message)
|
|
195
|
+
}
|
|
196
|
+
throw new GraphQLError('error.server')
|
|
197
|
+
}
|
|
198
|
+
},
|
|
152
199
|
|
|
153
200
|
}
|
|
154
201
|
}
|
|
@@ -54,6 +54,7 @@ input AuthInput{
|
|
|
54
54
|
password: String!
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
|
|
57
58
|
type Mutation{
|
|
58
59
|
auth(input: AuthInput): Auth
|
|
59
60
|
createUser(input: UserCreateInput): User
|
|
@@ -61,4 +62,5 @@ type Mutation{
|
|
|
61
62
|
deleteUser(id: ID!): Boolean
|
|
62
63
|
changeOwnPassword(currentPassword:String!, newPassword: String!): Boolean
|
|
63
64
|
changeUserPassword(userId:ID!, newPassword:String!): Boolean
|
|
65
|
+
changeAvatar(file: File!): Boolean
|
|
64
66
|
}
|
package/src/index.ts
CHANGED
|
@@ -8,6 +8,7 @@ import TenantService from "./services/TenantService.js";
|
|
|
8
8
|
import PermissionService from "./services/PermissionService.js";
|
|
9
9
|
import Rbac from "./rbac/Rbac.js";
|
|
10
10
|
import {UserRoutes} from "./routes/UserRoutes.js";
|
|
11
|
+
import {UserAvatarRoutes} from "./routes/UserAvatarRoutes.js";
|
|
11
12
|
import {RoleRoutes} from "./routes/RoleRoutes.js";
|
|
12
13
|
import {TenantRoutes} from "./routes/TenantRoutes.js";
|
|
13
14
|
import AuthUtils from "./utils/AuthUtils.js";
|
|
@@ -62,6 +63,7 @@ export {
|
|
|
62
63
|
UserRoutes,
|
|
63
64
|
RoleRoutes,
|
|
64
65
|
TenantRoutes,
|
|
66
|
+
UserAvatarRoutes,
|
|
65
67
|
|
|
66
68
|
AuthUtils,
|
|
67
69
|
|
|
@@ -88,4 +90,3 @@ export {
|
|
|
88
90
|
}
|
|
89
91
|
|
|
90
92
|
|
|
91
|
-
/// <reference types="index.d.ts" />
|
|
@@ -5,6 +5,7 @@ interface IUserRepository extends IDraxCrud<IUser, IUserCreate, IUserUpdate>{
|
|
|
5
5
|
findById(id: string): Promise<IUser | null>;
|
|
6
6
|
findByUsername(username: string): Promise<IUser | null>;
|
|
7
7
|
changePassword(id: string, password:string):Promise<Boolean>;
|
|
8
|
+
changeAvatar(id: string, avatarUrl: string): Promise<Boolean>;
|
|
8
9
|
}
|
|
9
10
|
|
|
10
11
|
export {IUserRepository}
|
package/src/rbac/Rbac.ts
CHANGED
|
@@ -118,6 +118,16 @@ class UserMongoRepository implements IUserRepository {
|
|
|
118
118
|
return false
|
|
119
119
|
}
|
|
120
120
|
}
|
|
121
|
+
|
|
122
|
+
async changeAvatar(id: string, avatar: string):Promise<boolean> {
|
|
123
|
+
try{
|
|
124
|
+
await UserModel.findOneAndUpdate({_id: id}, {avatar}).exec()
|
|
125
|
+
return true
|
|
126
|
+
}catch (e){
|
|
127
|
+
console.error(e)
|
|
128
|
+
return false
|
|
129
|
+
}
|
|
130
|
+
}
|
|
121
131
|
}
|
|
122
132
|
|
|
123
133
|
export default UserMongoRepository
|
|
@@ -109,7 +109,7 @@ class RoleSqliteRepository implements IRoleRepository{
|
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
const rCount = this.db.prepare('SELECT COUNT(*) as count FROM roles'+where).get();
|
|
112
|
-
const roles = this.db.prepare('SELECT * FROM roles LIMIT ? OFFSET ?'
|
|
112
|
+
const roles = this.db.prepare('SELECT * FROM roles ' + where + ' LIMIT ? OFFSET ?').all([limit, offset]);
|
|
113
113
|
|
|
114
114
|
for (const role of roles) {
|
|
115
115
|
await this.populateRole(role)
|
|
@@ -115,7 +115,7 @@ class TenantSqliteRepository implements ITenantRepository{
|
|
|
115
115
|
}
|
|
116
116
|
|
|
117
117
|
const rCount = this.db.prepare('SELECT COUNT(*) as count FROM tenants'+where).get();
|
|
118
|
-
const tenants = this.db.prepare('SELECT * FROM tenants LIMIT ? OFFSET ?'
|
|
118
|
+
const tenants = this.db.prepare('SELECT * FROM tenants ' + where + ' LIMIT ? OFFSET ?').all([limit, offset]);
|
|
119
119
|
|
|
120
120
|
return {
|
|
121
121
|
page: page,
|
|
@@ -206,6 +206,14 @@ class UserSqliteRepository implements IUserRepository {
|
|
|
206
206
|
stmt.run({id: id, password: password});
|
|
207
207
|
return true
|
|
208
208
|
}
|
|
209
|
+
|
|
210
|
+
async changeAvatar(id: string, avatar: string): Promise<boolean> {
|
|
211
|
+
const stmt = this.db.prepare(`UPDATE users
|
|
212
|
+
SET avatar = @avatar
|
|
213
|
+
WHERE id = @id `);
|
|
214
|
+
stmt.run({id: id, avatar: avatar});
|
|
215
|
+
return true
|
|
216
|
+
}
|
|
209
217
|
}
|
|
210
218
|
|
|
211
219
|
export default UserSqliteRepository
|