diff --git a/back/models/vn-user.json b/back/models/vn-user.json index f5eb3ae0f..cf4912495 100644 --- a/back/models/vn-user.json +++ b/back/models/vn-user.json @@ -38,6 +38,12 @@ "active": { "type": "boolean" }, + "email": { + "type": "string" + }, + "emailVerified": { + "type": "boolean" + }, "created": { "type": "date" }, @@ -137,7 +143,8 @@ "image", "hasGrant", "realm", - "email" + "email", + "emailVerified" ] } } diff --git a/db/changes/234201/00-aclSetPassword.sql b/db/changes/234201/00-aclSetPassword.sql new file mode 100644 index 000000000..44b3e9de0 --- /dev/null +++ b/db/changes/234201/00-aclSetPassword.sql @@ -0,0 +1,4 @@ +INSERT INTO `salix`.`ACL` (model,property,accessType,permission,principalType,principalId) + VALUES ('Worker','setPassword','*','ALLOW','ROLE','employee'); + + diff --git a/modules/worker/back/methods/worker/setPassword.js b/modules/worker/back/methods/worker/setPassword.js new file mode 100644 index 000000000..43d3d946f --- /dev/null +++ b/modules/worker/back/methods/worker/setPassword.js @@ -0,0 +1,48 @@ +const UserError = require('vn-loopback/util/user-error'); +module.exports = Self => { + Self.remoteMethodCtx('setPassword', { + description: 'Set a new password', + accepts: [ + { + arg: 'workerFk', + type: 'number', + required: true, + description: 'The worker id', + }, + { + arg: 'newPass', + type: 'String', + required: true, + description: 'The new worker password' + } + ], + http: { + path: `/:id/setPassword`, + verb: 'PATCH' + } + }); + Self.setPassword = async(ctx, options) => { + const models = Self.app.models; + const myOptions = {}; + const {args} = ctx; + let tx; + if (typeof options == 'object') + Object.assign(myOptions, options); + if (!myOptions.transaction) { + tx = await Self.beginTransaction({}); + myOptions.transaction = tx; + } + try { + const isSubordinate = await models.Worker.isSubordinate(ctx, args.workerFk, myOptions); + if (!isSubordinate) throw new UserError('You don\'t have enough privileges.'); + + await models.VnUser.setPassword(args.workerFk, args.newPass, myOptions); + await models.VnUser.updateAll({id: args.workerFk}, {emailVerified: true}, myOptions); + + if (tx) await tx.commit(); + } catch (e) { + if (tx) await tx.rollback(); + throw e; + } + }; +}; diff --git a/modules/worker/back/methods/worker/specs/setPassword.spec.js b/modules/worker/back/methods/worker/specs/setPassword.spec.js new file mode 100644 index 000000000..fbb403b24 --- /dev/null +++ b/modules/worker/back/methods/worker/specs/setPassword.spec.js @@ -0,0 +1,61 @@ +const UserError = require('vn-loopback/util/user-error'); + +const models = require('vn-loopback/server/server').models; + +describe('worker setPassword()', () => { + let ctx; + beforeAll(() => { + ctx = { + req: { + accessToken: {}, + headers: {origin: 'http://localhost'} + }, + args: {workerFk: 9} + }; + }); + + beforeEach(() => { + ctx.req.accessToken.userId = 20; + ctx.args.newPass = 'H3rn4d3z#'; + }); + + it('should change the password', async() => { + const tx = await models.Worker.beginTransaction({}); + + try { + const options = {transaction: tx}; + await models.Worker.setPassword(ctx, options); + + await tx.rollback(); + } catch (e) { + await tx.rollback(); + throw e; + } + }); + + it('should throw an error: Password does not meet requirements', async() => { + const tx = await models.Collection.beginTransaction({}); + ctx.args.newPass = 'Hi'; + try { + const options = {transaction: tx}; + await models.Worker.setPassword(ctx, options); + await tx.rollback(); + } catch (e) { + expect(e.sqlMessage).toEqual('Password does not meet requirements'); + await tx.rollback(); + } + }); + + it('should throw an error: You don\'t have enough privileges.', async() => { + ctx.req.accessToken.userId = 5; + const tx = await models.Collection.beginTransaction({}); + try { + const options = {transaction: tx}; + await models.Worker.setPassword(ctx, options); + await tx.rollback(); + } catch (e) { + expect(e).toEqual(new UserError(`You don't have enough privileges.`)); + await tx.rollback(); + } + }); +}); diff --git a/modules/worker/back/models/worker.js b/modules/worker/back/models/worker.js index ccae3a6e6..985d83e9f 100644 --- a/modules/worker/back/models/worker.js +++ b/modules/worker/back/models/worker.js @@ -18,6 +18,7 @@ module.exports = Self => { require('../methods/worker/allocatePDA')(Self); require('../methods/worker/search')(Self); require('../methods/worker/isAuthorized')(Self); + require('../methods/worker/setPassword')(Self); Self.validatesUniquenessOf('locker', { message: 'This locker has already been assigned' diff --git a/modules/worker/front/card/index.js b/modules/worker/front/card/index.js index b8b533c5d..9a40e31c2 100644 --- a/modules/worker/front/card/index.js +++ b/modules/worker/front/card/index.js @@ -8,7 +8,7 @@ class Controller extends ModuleCard { { relation: 'user', scope: { - fields: ['name'], + fields: ['name', 'emailVerified'], include: { relation: 'emailUser', scope: { diff --git a/modules/worker/front/descriptor/index.html b/modules/worker/front/descriptor/index.html index 758f639ff..aa6b80300 100644 --- a/modules/worker/front/descriptor/index.html +++ b/modules/worker/front/descriptor/index.html @@ -11,6 +11,9 @@ ? 'Click to allow the user to be disabled' : 'Click to exclude the user from getting disabled'}} + + Change password +
@@ -72,4 +75,29 @@ - + + + + + + + + + + + + diff --git a/modules/worker/front/descriptor/index.js b/modules/worker/front/descriptor/index.js index 07e16c0d6..13ffa6f2f 100644 --- a/modules/worker/front/descriptor/index.js +++ b/modules/worker/front/descriptor/index.js @@ -1,5 +1,6 @@ import ngModule from '../module'; import Descriptor from 'salix/components/descriptor'; +const UserError = require('vn-loopback/util/user-error'); class Controller extends Descriptor { constructor($element, $, $rootScope) { super($element, $); @@ -12,9 +13,11 @@ class Controller extends Descriptor { set worker(value) { this.entity = value; - if (value) this.getIsExcluded(); + + if (this.entity && !this.entity.user.emailVerified) + this.getPassRequirements(); } getIsExcluded() { @@ -38,7 +41,7 @@ class Controller extends Descriptor { { relation: 'user', scope: { - fields: ['name'], + fields: ['name', 'emailVerified'], include: { relation: 'emailUser', scope: { @@ -66,10 +69,29 @@ class Controller extends Descriptor { } ] }; - return this.getData(`Workers/${this.id}`, {filter}) .then(res => this.entity = res.data); } + + getPassRequirements() { + this.$http.get('UserPasswords/findOne') + .then(res => { + this.passRequirements = res.data; + }); + } + + setPassword() { + if (!this.newPassword) + throw new UserError(`You must enter a new password`); + if (this.newPassword != this.repeatPassword) + throw new UserError(`Passwords don't match`); + this.$http.patch( + `Workers/${this.entity.id}/setPassword`, + {workerFk: this.entity.id, newPass: this.newPassword} + ) .then(() => { + this.vnApp.showSuccess(this.$translate.instant('Password changed!')); + }); + } } Controller.$inject = ['$element', '$scope', '$rootScope']; diff --git a/modules/worker/front/descriptor/index.spec.js b/modules/worker/front/descriptor/index.spec.js index dfb800415..d158a9e8e 100644 --- a/modules/worker/front/descriptor/index.spec.js +++ b/modules/worker/front/descriptor/index.spec.js @@ -23,4 +23,24 @@ describe('vnWorkerDescriptor', () => { expect(controller.worker).toEqual(response); }); }); + + describe('setPassword()', () => { + it('should throw an error: You must enter a new password', () => { + try { + controller.setPassword(); + } catch (error) { + expect(error.message).toEqual('You must enter a new password'); + } + }); + + it('should throw an error: Passwords don\'t match', () => { + controller.newPassword = 'aaa'; + controller.repeatPassword = 'bbb'; + try { + controller.setPassword(); + } catch (error) { + expect(error.message).toEqual('Passwords don\'t match'); + } + }); + }); });