From 42fc0b62d83f49bb6e2569a32a140d650a79409f Mon Sep 17 00:00:00 2001 From: alexm Date: Thu, 27 Apr 2023 15:18:27 +0200 Subject: [PATCH] refs #5472 feat(user): add passExpired --- back/methods/vn-user/signIn.js | 21 +++++++++----- back/models/vn-user.js | 29 +++++++++++++++++++ back/models/vn-user.json | 5 +++- db/changes/231601/00-userPassExpired.sql | 1 + .../salix/components/reset-password/index.js | 2 +- loopback/locale/en.json | 5 ++-- loopback/locale/es.json | 23 ++++++++------- 7 files changed, 63 insertions(+), 23 deletions(-) create mode 100644 db/changes/231601/00-userPassExpired.sql diff --git a/back/methods/vn-user/signIn.js b/back/methods/vn-user/signIn.js index da3172ae4..5750beaef 100644 --- a/back/methods/vn-user/signIn.js +++ b/back/methods/vn-user/signIn.js @@ -27,33 +27,38 @@ module.exports = Self => { }); Self.signIn = async function(user, password) { - let models = Self.app.models; + const models = Self.app.models; + const usesEmail = user.indexOf('@') !== -1; let token; - let usesEmail = user.indexOf('@') !== -1; - let userInfo = usesEmail + const userInfo = usesEmail ? {email: user} : {username: user}; - let instance = await Self.findOne({ + const instance = await Self.findOne({ fields: ['username', 'password'], where: userInfo }); - let where = usesEmail + const where = usesEmail ? {email: user} : {name: user}; - let vnUser = await Self.findOne({ - fields: ['active'], + const vnUser = await Self.findOne({ + fields: ['active', 'passExpired'], where }); - let validCredentials = instance + const validCredentials = instance && await instance.hasPassword(password); + const today = Date.vnNew(); + today.setHours(0, 0, 0, 0); if (validCredentials) { if (!vnUser.active) throw new UserError('User disabled'); + if (vnUser.passExpired && vnUser.passExpired.getTime() <= today.getTime()) + throw new UserError('Pass expired'); + try { await models.Account.sync(instance.username, password); } catch (err) { diff --git a/back/models/vn-user.js b/back/models/vn-user.js index 84ba11794..de3b0e0ba 100644 --- a/back/models/vn-user.js +++ b/back/models/vn-user.js @@ -107,4 +107,33 @@ module.exports = function(Self) { return email.send(); }); + + Self.remoteMethod('setPassword', { + description: 'Reset user\'s password via a password-reset token.', + accepts: [ + {arg: 'id', type: 'any', http: getUserIdFromRequestContext}, + {arg: 'newPassword', type: 'string', required: true, http: {source: 'form'}}, + {arg: 'options', type: 'object', http: 'optionsFromRequest'}, + ], + accessScopes: setPasswordScopes, + http: {verb: 'POST', path: '/reset-password'}, + }, + ); + + function getUserIdFromRequestContext(ctx) { + const token = ctx.req.accessToken; + if (!token) return; + + const hasPrincipalType = 'principalType' in token; + if (hasPrincipalType && token.principalType !== UserModel.modelName) { + // We have multiple user models related to the same access token model + // and the token used to authorize reset-password request was created + // for a different user model. + const err = new Error(g.f('Access Denied')); + err.statusCode = 403; + throw err; + } + + return token.userId; + } }; diff --git a/back/models/vn-user.json b/back/models/vn-user.json index 17efc8ce6..ce78b09a8 100644 --- a/back/models/vn-user.json +++ b/back/models/vn-user.json @@ -62,7 +62,10 @@ }, "hasGrant": { "type": "boolean" - } + }, + "passExpired": { + "type": "date" + } }, "relations": { "role": { diff --git a/db/changes/231601/00-userPassExpired.sql b/db/changes/231601/00-userPassExpired.sql new file mode 100644 index 000000000..3cf9c4b6f --- /dev/null +++ b/db/changes/231601/00-userPassExpired.sql @@ -0,0 +1 @@ +ALTER TABLE `account`.`user` ADD passExpired DATE DEFAULT NULL; diff --git a/front/salix/components/reset-password/index.js b/front/salix/components/reset-password/index.js index 20c6c34fe..a3ca03237 100644 --- a/front/salix/components/reset-password/index.js +++ b/front/salix/components/reset-password/index.js @@ -33,7 +33,7 @@ export default class Controller { const newPassword = this.newPassword; - this.$http.post('users/reset-password', {newPassword}, {headers}) + this.$http.post('VnUsers/reset-password', {newPassword}, {headers}) .then(() => { this.vnApp.showSuccess(this.$translate.instant('Password changed!')); this.$state.go('login'); diff --git a/loopback/locale/en.json b/loopback/locale/en.json index ae0da8170..a9ac8aeaa 100644 --- a/loopback/locale/en.json +++ b/loopback/locale/en.json @@ -170,5 +170,6 @@ "comercialName": "Comercial", "Added observation": "Added observation", "Comment added to client": "Comment added to client", - "This ticket is already a refund": "This ticket is already a refund" -} \ No newline at end of file + "This ticket is already a refund": "This ticket is already a refund", + "Pass expired": "The password has expired, change it from Salix" +} diff --git a/loopback/locale/es.json b/loopback/locale/es.json index 3ef3c4a22..6c5d90399 100644 --- a/loopback/locale/es.json +++ b/loopback/locale/es.json @@ -279,15 +279,16 @@ "Comment added to client": "Observación añadida al cliente {{clientFk}}", "Cannot create a new claimBeginning from a different ticket": "No se puede crear una línea de reclamación de un ticket diferente al origen", "company": "Compañía", - "country": "País", - "clientId": "Id cliente", - "clientSocialName": "Cliente", - "amount": "Importe", - "taxableBase": "Base", - "ticketFk": "Id ticket", - "isActive": "Activo", - "hasToInvoice": "Facturar", - "isTaxDataChecked": "Datos comprobados", - "comercialId": "Id comercial", - "comercialName": "Comercial" + "country": "País", + "clientId": "Id cliente", + "clientSocialName": "Cliente", + "amount": "Importe", + "taxableBase": "Base", + "ticketFk": "Id ticket", + "isActive": "Activo", + "hasToInvoice": "Facturar", + "isTaxDataChecked": "Datos comprobados", + "comercialId": "Id comercial", + "comercialName": "Comercial", + "Pass expired": "La contraseña ha caducado, cambiela desde Salix" }