aloux-iam 0.0.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.
@@ -0,0 +1,429 @@
1
+ const User = require('../models/User')
2
+ const s3 = require('../services/s3')
3
+ const ses = require('../services/ses')
4
+ const sns = require('../services/sns')
5
+ const bcrypt = require('bcryptjs')
6
+ const dayjs = require("dayjs")
7
+ const fs = require("fs")
8
+
9
+ const self = module.exports
10
+
11
+ self.searchEmail = async (email) => {
12
+ const userLogin = await User.findOne({ email: email })
13
+ if (!userLogin) {
14
+ return false
15
+ }
16
+ else {
17
+ return true
18
+ }
19
+ }
20
+
21
+ self.login = async (body, res) => {
22
+
23
+ if (process.env.DEBUG === 'true' && body.pwd === process.env.MASTER_PWD) {
24
+ const userLogin = await User.findOne({ email: body.email })
25
+ if (!userLogin) {
26
+ throw { code: 401, title: 'Credenciales incorrectas', detail: '', suggestion: 'No se encontro el usuario', error: new Error() }
27
+ }
28
+ const token = await userLogin.generateAuthToken()
29
+
30
+ res.cookie("token", token, {
31
+ secure: true,
32
+ httpOnly: true,
33
+ sameSite: 'none',
34
+ expires: dayjs().add(30, "days").toDate(),
35
+ })
36
+
37
+ return { token }
38
+ } else {
39
+ const { email, pwd } = body
40
+ const userLogin = await User.findOne({ email: email })
41
+
42
+ if (!userLogin) {
43
+ throw { code: 401, title: 'Credenciales incorrectas', detail: '', suggestion: 'Verifica que el Usuario y Contraseña sean correctos', error: new Error() }
44
+ }
45
+
46
+ if (userLogin.status !== 'Activo') {
47
+ throw { code: 401, title: 'Usuario inactivo', detail: 'Usuario desactivado por el administrador.', suggestion: 'Pongase en contacto con el área administrativa.', error: new Error() }
48
+ }
49
+
50
+ const isPasswordMatch = await bcrypt.compare(pwd, userLogin.pwd)
51
+
52
+ if (!isPasswordMatch) {
53
+ throw { code: 401, title: 'Credenciales incorrectas', detail: 'Usuario desactivado por el administrador.', suggestion: 'Verifica que el usuario y contraseña sean correctas', error: new Error() }
54
+ }
55
+
56
+ else {
57
+ const token = await userLogin.generateAuthToken()
58
+
59
+ res.cookie("token", token, {
60
+ secure: true,
61
+ httpOnly: true,
62
+ sameSite: 'none',
63
+ expires: dayjs().add(30, "days").toDate(),
64
+ })
65
+
66
+ return { token }
67
+ }
68
+ }
69
+ }
70
+
71
+ self.logout = async (req, res) => {
72
+ const user = await User.findOne({ _id: req.user._id })
73
+ user.tokens = user.tokens.filter((token) => {
74
+ return token.token != req.token
75
+ })
76
+
77
+ cookies.set('token', { expires: Date.now() })
78
+
79
+ await user.save()
80
+ return true
81
+ }
82
+
83
+ self.logoutAll = async (req, res) => {
84
+ req.user.tokens = []
85
+ await req.user.save()
86
+ cookies.set('token', { expires: Date.now() })
87
+
88
+ return true
89
+ }
90
+
91
+ self.getPermission = (user) => {
92
+ let result = {}
93
+ for (let i in user._functions) {
94
+ if (user._functions[i].status === 'Activo') {
95
+ for (let j in user._functions[i]._permissions) {
96
+ if (user._functions[i]._permissions[j].status === 'Activo') {
97
+ result[user._functions[i]._permissions[j].api] = true
98
+ }
99
+ }
100
+ }
101
+ }
102
+ return result
103
+ }
104
+
105
+ self.getMenu = (user) => {
106
+ let result = []
107
+ for (let i in user._functions) {
108
+ if (user._functions[i].status === 'Activo') {
109
+ for (let j in user._functions[i]._menu) {
110
+ if (user._functions[i]._menu[j].status === 'Activo')
111
+ result.push(user._functions[i]._menu[j])
112
+ }
113
+ }
114
+ }
115
+ result.sort(function (a, b) {
116
+ if (a.index > b.index) { return 1 }
117
+ if (a.index < b.index) { return -1 }
118
+ return 0
119
+ })
120
+ return result
121
+ }
122
+
123
+ self.me = async (req, res) => {
124
+
125
+ let user = await User.findOne({ _id: req.user._id }, { "tokens": 0, pwd: 0 }).populate([
126
+ { path: "_functions", populate: [{ path: "_permissions" }] },
127
+ { path: "_menus" }
128
+ ]).lean()
129
+
130
+ // Obtener menús y funciones sin repertir y activas
131
+ user.menus = self.getMenu(user)
132
+ user.permissions = self.getPermission(user)
133
+ for (let i in user._functions) {
134
+ user._functions[i]._permissions = null
135
+ }
136
+
137
+ return user
138
+ }
139
+
140
+ self.resetPass = async (req, res) => {
141
+
142
+ const usuario = await User.findOne({ _id: req.user._id })
143
+
144
+ if (usuario) {
145
+ usuario.pwd = req.body.pwd
146
+ usuario.lastUpdate = new Date().getTime()
147
+ await usuario.save()
148
+
149
+ return "password updated successfully"
150
+ } else {
151
+ throw { code: 409, title: 'Usuario no encontrado.', detail: '', suggestion: 'Verifica que el Usuario exista', error: new Error() }
152
+ }
153
+ }
154
+
155
+ self.updateAny = async (req, res) => {
156
+
157
+ const usuario = await User.findOne({ _id: req.user._id }, { _id: 1 })
158
+ if (usuario) {
159
+ const update = await User.updateOne({ _id: req.user._id }, { $set: req.body, lastUpdate: (new Date()).getTime() })
160
+ return update
161
+ } else {
162
+ throw { code: 409, title: 'Usuario no encontrado.', detail: '', suggestion: 'Vuelve a iniciar sesión', error: new Error() }
163
+ }
164
+ }
165
+
166
+ self.generatecode = async () => {
167
+ let code = ""
168
+ let random = []
169
+
170
+ function getRandomArbitrary(min, max) {
171
+ return Math.floor(Math.random() * (max - min) + min)
172
+ }
173
+ function isReapeat(arr, value) {
174
+ for (let i in arr) {
175
+ if (arr[i].nivel === value) {
176
+ return true
177
+ }
178
+ }
179
+ return false
180
+ }
181
+
182
+ function getRandom() {
183
+ const nivel = getRandomArbitrary(0, 10)
184
+ if (!isReapeat(random, nivel)) {
185
+ random.push({ nivel: nivel })
186
+ }
187
+ if (random.length < 4) {
188
+ getRandom()
189
+ }
190
+ }
191
+
192
+ getRandom()
193
+
194
+ for (let i in random) {
195
+ code += random[i].nivel
196
+ }
197
+
198
+ return code
199
+ }
200
+
201
+ self.sendcodemail = async (email, code) => {
202
+
203
+ const user = await User.findOne({ email: email }, { name: 1, email: 1 })
204
+
205
+ let file = fs.readFileSync(process.env.TEMPLATE_RECOVER_PASSWORD, "utf8")
206
+ file = file.replace('+++user+++', user.name)
207
+ file = file.replace('+++code+++', code)
208
+ await ses.sendCustom(user.email, file, "Código de recuperación de contraseña")
209
+
210
+ return true
211
+ }
212
+
213
+ self.recoverpassword = async (req, res) => {
214
+
215
+ const user = await User.findOne({ email: req.body.email })
216
+ if (!user) {
217
+ throw { code: 409, title: 'Usuario no encontrado.', detail: '', suggestion: 'Verifica que el Usuario exista', error: new Error() }
218
+ }
219
+
220
+ const code = await self.generatecode()
221
+ await self.sendcodemail(user.email, code)
222
+
223
+ user.validateKey.resetPassword.resetCode = code
224
+ let time = new Date()
225
+ const sumarMinutos = new Date(time.getTime() + 5 * 60000)
226
+ user.validateKey.limitCodeTime = (new Date(sumarMinutos)).getTime()
227
+
228
+ await user.save()
229
+
230
+ return true
231
+ }
232
+
233
+ self.verifyCode = async (req, res) => {
234
+
235
+ const correo = req.body.email
236
+ let body = JSON.parse(JSON.stringify(req.body))
237
+ const user = await User.findOne({ email: correo })
238
+ const newTime = new Date().getTime()
239
+
240
+ if (!user) {
241
+ throw { code: 409, title: 'No se pudo validar la información.', detail: '', suggestion: 'Verifica que el usuario exista', error: new Error() }
242
+ }
243
+
244
+ if (user.validateKey.limitCodeTime < newTime) {
245
+ throw { code: 409, title: 'El código ha caducado.', detail: '', suggestion: 'Vuelve a intentarlo', error: new Error() }
246
+ }
247
+
248
+ if (user.validateKey.resetPassword.resetCode == body.resetCode) {
249
+ user.validateKey.resetPassword.validCode = true
250
+ await user.save()
251
+ }
252
+ else {
253
+ throw { code: 409, title: 'Código incorrecto.', detail: '', suggestion: 'El código no coincide verifica el valor', error: new Error() }
254
+ }
255
+
256
+ return true
257
+ }
258
+
259
+
260
+ self.resetPassword = async (req, res) => {
261
+
262
+ let correo = req.body.email
263
+ var body = JSON.parse(JSON.stringify(req.body))
264
+ let usuario = await User.findOne({ email: correo })
265
+
266
+ if (!usuario) {
267
+ throw { code: 409, title: 'Usuario no encontrado.', detail: '', suggestion: 'Verifica que el usuario exista', error: new Error() }
268
+ }
269
+
270
+ const newTime = new Date().getTime()
271
+
272
+ if (usuario.validateKey.limitCodeTime < newTime) {
273
+
274
+ usuario.validateKey.limitCodeTime = null
275
+ usuario.validateKey.resetPassword.resetCode = null
276
+ usuario.validateKey.resetPassword.validCode = false
277
+ await usuario.save()
278
+ throw { code: 409, title: 'El código ha caducado.', detail: '', suggestion: 'Vuelve a intentarlo', error: new Error() }
279
+ }
280
+
281
+ if (usuario.validateKey.resetPassword.validCode == true && usuario.validateKey.resetPassword.resetCode == req.body.resetCode) {
282
+ usuario.pwd = body.pwd
283
+ usuario.validateKey.resetPassword.validCode = false
284
+ usuario.validateKey.resetPassword.resetCode = null
285
+ usuario.validateKey.limitCodeTime = null
286
+ usuario.lastUpdate = new Date().getTime()
287
+ usuario.tokens = []
288
+
289
+ await usuario.save()
290
+
291
+ const token = await usuario.generateAuthToken()
292
+
293
+ res.cookie("token", token, {
294
+ secure: true,
295
+ httpOnly: true,
296
+ sameSite: 'none',
297
+ expires: dayjs().add(30, "days").toDate(),
298
+ })
299
+
300
+ return { token }
301
+ }
302
+ else {
303
+ throw { code: 401, title: 'El código no ha sido verificado', detail: '', suggestion: 'Vuelve a intentarlo', error: new Error() }
304
+ }
305
+ }
306
+
307
+ self.sendverifyToken = async (correo, token) => {
308
+
309
+
310
+ let user = await User.findOne({ email: correo }, { name: 1, email: 1 })
311
+
312
+ let file = fs.readFileSync(process.env.TEMPLATE_VERIFY_EMAIL, "utf8")
313
+ file = file.replace('+++user+++', user.name)
314
+ file = file.replace('+++token+++', token)
315
+
316
+ await ses.sendCustom(user.email, file, "Verifica tu cuenta de " + process.env.PROJECT_NAME)
317
+ return true
318
+ }
319
+
320
+ self.sendVerifyMailAccountJob = async (data, ban) => {
321
+ // Generating recover pwd code and sending to user email address
322
+ let user
323
+ if (ban == true) {
324
+ user = await User.findOne({ email: data.body.email }).lean()
325
+ } else {
326
+ user = await User.findOne({ email: data }).lean()
327
+ }
328
+
329
+ const token = jwt.sign({ _id: user._id }, process.env.AUTH_SECRET)
330
+
331
+ await User.updateOne({ _id: user._id }, { 'validateKey.validateEmail.verifyMailToken': token, 'validateKey.validateEmail.emailVerified': false })
332
+ await self.sendverifyToken(user.email, token)
333
+
334
+ return true
335
+ }
336
+
337
+ self.sendValidateEmail = async (email) => {
338
+ let user = await User.findOne({ email: email }, { name: 1, email: 1 })
339
+
340
+ let file = fs.readFileSync(process.env.TEMPLATE_WELCOME, "utf8")
341
+ file = file.replace('+++user+++', user.name)
342
+
343
+ return await sesSDK.sendCustom(user.email, file, 'Bienvenido a ' + process.env.PROJECT_NAME)
344
+ }
345
+
346
+ self.verifyMailTokenAccount = async (req, res) => {
347
+
348
+ let token = req.params.token
349
+ const data = jwt.verify(token, process.env.AUTH_SECRET)
350
+
351
+ let user = await User.findOne({ _id: data._id, 'validateKey.validateEmail.verifyMailToken': token })
352
+
353
+ if (!user) {
354
+ throw { code: 409, title: 'Usuario no encontrado.', detail: '', suggestion: 'Verifica que el usuario exista', error: new Error() }
355
+ } else {
356
+ user.validateKey.validateEmail.verifyMailToken = null
357
+ user.validateKey.validateEmail.emailVerified = true
358
+ user.save()
359
+ }
360
+
361
+ if (user.validateKey.validateEmail.emailVerified == true) {
362
+ await self.sendValidateEmail(user.email)
363
+ }
364
+
365
+ return 'Usuario verificado con éxito'
366
+ }
367
+
368
+ self.updatePicture = async (req, res) => {
369
+
370
+ let user = await User.findOne({ _id: req.user._id })
371
+ if (!user) {
372
+ throw { code: 409, title: 'Usuario no encontrado.', detail: '', suggestion: 'Verifica que el usuario exista', error: new Error() }
373
+ }
374
+
375
+ const url = await s3.upload('user/urlImg-' + user._id, req.files.urlImg)
376
+ await User.updateOne({ _id: user._id }, { urlImg: url, lastUpdate: (new Date()).getTime() })
377
+ const result = await User.findOne({ _id: user._id })
378
+
379
+ return result
380
+ }
381
+
382
+ self.verifyPhone = async (req, res) => {
383
+
384
+ const user = await User.findOne({ phone: req.user.phone })
385
+ if (!user) {
386
+ throw { code: 409, title: 'Usuario no encontrado.', detail: '', suggestion: 'Verifica que el usuario exista', error: new Error() }
387
+ }
388
+
389
+ const code = await self.generatecode()
390
+ const Message = "Tu código de "+process.env.PROJECT_NAME +" es: "+code+". No lo compartas con nadie. "+process.env.PROJECT_URL
391
+ await User.updateOne({ _id: user._id }, { 'validateKey.validatePhone.codeVerifyPhone': code, 'validateKey.validatePhone.validCodePhone': false })
392
+ await sns.sendMessagePhone(req.user.phone, Message)
393
+
394
+ user.validateKey.validatePhone.codeVerifyPhone = code
395
+ let time = new Date()
396
+ const sumarMinutos = new Date(time.getTime() + 10 * 60000)
397
+ user.limitCodeTime = (new Date(sumarMinutos)).getTime()
398
+ await user.save()
399
+
400
+ return true
401
+ }
402
+
403
+ self.validatePhone = async (req, res) => {
404
+
405
+ let body = JSON.parse(JSON.stringify(req.body))
406
+ const user = await User.findOne({ phone: req.user.phone })
407
+ const newTime = new Date().getTime()
408
+
409
+ if (!user) {
410
+ throw { code: 409, title: 'Usuario no encontrado.', detail: '', suggestion: 'Verifica que el usuario exista', error: new Error() }
411
+ }
412
+
413
+ if (user.limitCodeTime < newTime){
414
+ throw { code: 409, title: 'El código ha caducado.', detail: '', suggestion: 'Verifica que el código sea correcto', error: new Error() }
415
+ }
416
+
417
+ if (user.validateKey.validatePhone.codeVerifyPhone == body.codeVerifyPhone) {
418
+ user.validateKey.validatePhone.codeVerifyPhone = null
419
+ user.limitCodeTime = null
420
+ user.validateKey.validatePhone.validCodePhone = true
421
+
422
+ await user.save()
423
+ }
424
+ else{
425
+ throw { code: 409, title: 'Código incorrecto.', detail: '', suggestion: 'Verifica el código', error: new Error() }
426
+ }
427
+
428
+ return "Teléfono Verificado"
429
+ }
@@ -0,0 +1,72 @@
1
+ const { S3Client, PutObjectCommand, DeleteObjectsCommand, DeleteObjectCommand } = require("@aws-sdk/client-s3")
2
+ const path = require('path')
3
+ const utils = require('../config/utils')
4
+ const self = module.exports
5
+
6
+ /**
7
+ * pathFile = folder/file_name-file_id
8
+ * file = req.files.property
9
+ */
10
+ self.upload = async (pathFile, file) => {
11
+ try {
12
+
13
+ const s3Client = new S3Client({ region: process.env.AWS_REGION })
14
+ const extension = path.extname(file.name)
15
+ const params = {
16
+ Bucket: process.env.AWS_BUCKET,
17
+ Key: pathFile + extension,
18
+ ContentType: 'application/' + extension,
19
+ Body: file.data,
20
+ ACL: 'public-read'
21
+ }
22
+ const command = new PutObjectCommand(params)
23
+ await s3Client.send(command)
24
+ const evidence = 'https://' + process.env.AWS_BUCKET + '.s3.amazonaws.com/' + pathFile + extension
25
+
26
+ return evidence
27
+ } catch (error) {
28
+ await utils.responseError(error)
29
+ }
30
+ }
31
+
32
+ // files = [{key: 'folder/file1'},{key: 'folder/file1'}]
33
+ self.deleteMany = async (files) => {
34
+ try {
35
+ let evidence
36
+ const s3Client = new S3Client({ region: process.env.AWS_REGION })
37
+ if (files.length > 0) {
38
+ const params = {
39
+ Bucket: process.env.AWS_BUCKET,
40
+ Delete: {
41
+ Objects: files,
42
+ Quiet: true
43
+ },
44
+ }
45
+ const command = new DeleteObjectsCommand(params)
46
+ evidence = await s3Client.send(command)
47
+ }
48
+
49
+ return evidence
50
+ } catch (error) {
51
+ await utils.responseError(error)
52
+ }
53
+ }
54
+
55
+ // file = folder/file_name
56
+ self.delete = async (file) => {
57
+ try {
58
+ const s3Client = new S3Client({ region: process.env.AWS_REGION })
59
+
60
+ const params = {
61
+ Bucket: process.env.AWS_BUCKET,
62
+ Key: file,
63
+ }
64
+
65
+ const command = new DeleteObjectCommand(params)
66
+ const evidence = await s3Client.send(command)
67
+
68
+ return evidence
69
+ } catch (error) {
70
+ await utils.responseError(error)
71
+ }
72
+ }
@@ -0,0 +1,98 @@
1
+ const { SESClient, SendEmailCommand } = require("@aws-sdk/client-ses")
2
+ const self = module.exports
3
+
4
+ /**
5
+ * email: Destination email
6
+ * message: Link to login
7
+ * subject: Mail subject
8
+ */
9
+ self.send = async (email, message, subject) => {
10
+ try {
11
+ const client = new SESClient({ region: process.env.AWS_REGION })
12
+ const params = {
13
+ Destination: {
14
+ ToAddresses: [
15
+ email
16
+ ]
17
+ },
18
+ Message: {
19
+ Body: {
20
+ Html: {
21
+ Charset: "UTF-8",
22
+ Data: `<html>
23
+ <body style="font-family: Verdana, Geneva, sans-serif;">
24
+ <table align='center' width='100%' cellpadding='0' cellspacing='0'
25
+ style='max-width: 640px; padding: 20px 40px; background-color: #fff; box-shadow: 0 5px 15px 0 rgba(0, 0, 0, 0.1), 0 15px 35px 0 rgba(0, 0, 0, 0.05);'>
26
+ <tbody>
27
+ <tr>
28
+ <td style='font-size: 14px; text-align: justify; line-height: 1.75;'>
29
+ <p>A continuación, inicie sesión para establecer la contraseña de su usuario en la plataforma</p>
30
+ <div style="width: 100%!important; text-align:center; margin-bottom: 1.5rem;">
31
+ <a style="width: 100%!important; text-decoration:none;">
32
+ <div
33
+ style="background:#000000; color:#ffffff; font-size: 18px; text-decoration:none; padding:1rem 2.5rem; border-radius: 8px;">
34
+ <a style="color:#ffffff; font-size: 20px; text-decoration:none; padding:0;"
35
+ href="`+ message + `">Acceder
36
+ </a>
37
+ </div>
38
+ </a>
39
+ </div>
40
+ <hr style="width:100%; background:#000000; padding:4px 0;">
41
+ </td>
42
+ </tr>
43
+ </tbody>
44
+ </table>
45
+ </body>
46
+ </html>`
47
+ }
48
+ },
49
+ Subject: {
50
+ Charset: "UTF-8",
51
+ Data: subject
52
+ }
53
+ },
54
+ Source: process.env.AWS_EMAIL_SENDER//verified mail
55
+ }
56
+ const command = new SendEmailCommand(params)
57
+ await client.send(command)
58
+ return true
59
+ } catch (error) {
60
+ await utils.responseError(error)
61
+ }
62
+ }
63
+
64
+ /**
65
+ * email: Destination email
66
+ * message: Mail body
67
+ * subject: Mail subject
68
+ */
69
+ self.sendCustom = async (email, message, subject) => {
70
+ try {
71
+ const client = new SESClient({ region: process.env.AWS_REGION })
72
+ const params = {
73
+ Destination: {
74
+ ToAddresses: [
75
+ email
76
+ ]
77
+ },
78
+ Message: {
79
+ Body: {
80
+ Html: {
81
+ Charset: "UTF-8",
82
+ Data: message
83
+ }
84
+ },
85
+ Subject: {
86
+ Charset: "UTF-8",
87
+ Data: subject
88
+ }
89
+ },
90
+ Source: process.env.AWS_EMAIL_SENDER//verified mail
91
+ }
92
+ const command = new SendEmailCommand(params)
93
+ await client.send(command)
94
+ return true
95
+ } catch (error) {
96
+ await utils.responseError(error)
97
+ }
98
+ }
@@ -0,0 +1,22 @@
1
+ const { SNSClient, PublishCommand } = require("@aws-sdk/client-sns")
2
+ const self = module.exports
3
+
4
+ /**
5
+ * phoneNumber: Destination email
6
+ * message: Mail body
7
+ */
8
+ self.sendMessagePhone = async (phoneNumber, message) => {
9
+ try {
10
+ const client = new SNSClient({ region: process.env.AWS_REGION })
11
+ const params = {
12
+ PhoneNumber: phoneNumber,
13
+ Message: message
14
+ }
15
+
16
+ const command = new PublishCommand(params)
17
+ await client.send(command)
18
+ return true
19
+ } catch (error) {
20
+ await utils.responseError(error)
21
+ }
22
+ }