refs #5475 feat(account_changePassword): accessScope and backTest
gitea/salix/pipeline/head There was a failure building this commit
Details
gitea/salix/pipeline/head There was a failure building this commit
Details
This commit is contained in:
parent
ee1b901dea
commit
092471f74e
|
@ -32,7 +32,7 @@ module.exports = Self => {
|
|||
});
|
||||
|
||||
Self.validateAuth = async function(username, password, code) {
|
||||
await Self.validateCode(code);
|
||||
await Self.validateCode(username, code);
|
||||
return Self.validateLogin(username, password);
|
||||
};
|
||||
|
||||
|
@ -52,7 +52,7 @@ module.exports = Self => {
|
|||
const user = await Self.findById(authCode.userFk, {
|
||||
fields: ['name', 'twoFactor']
|
||||
});
|
||||
console.log(username, code);
|
||||
|
||||
if (user.name !== username)
|
||||
throw new UserError('Authentication failed');
|
||||
|
||||
|
|
|
@ -153,41 +153,10 @@ module.exports = function(Self) {
|
|||
}
|
||||
};
|
||||
|
||||
// Self.sharedClass._methods.find(method => method.name == 'changePassword')
|
||||
// .accessScopes = ['change-password'];
|
||||
Self.sharedClass._methods.find(method => method.name == 'changePassword').ctor.settings.acls =
|
||||
Self.sharedClass._methods.find(method => method.name == 'changePassword').ctor.settings.acls
|
||||
.filter(acl => acl.property != 'changePassword');
|
||||
|
||||
const _changePassword = Self.prototype.ChangePassword;
|
||||
Self.prototype.changePassword = async function(oldPassword, newPassword, options, cb) {
|
||||
if (cb === undefined && typeof options === 'function') {
|
||||
cb = options;
|
||||
options = undefined;
|
||||
}
|
||||
|
||||
const myOptions = {};
|
||||
let tx;
|
||||
|
||||
if (typeof options == 'object')
|
||||
Object.assign(myOptions, options);
|
||||
|
||||
if (!myOptions.transaction) {
|
||||
tx = await Self.beginTransaction({});
|
||||
myOptions.transaction = tx;
|
||||
}
|
||||
options = myOptions;
|
||||
|
||||
try {
|
||||
await _changePassword.call(this, oldPassword, newPassword, options);
|
||||
tx && await tx.commit();
|
||||
cb && cb();
|
||||
} catch (err) {
|
||||
tx && await tx.rollback();
|
||||
if (cb) cb(err); else throw err;
|
||||
}
|
||||
};
|
||||
|
||||
// FIXME: https://redmine.verdnatura.es/issues/5761
|
||||
// Self.afterRemote('prototype.patchAttributes', async(ctx, instance) => {
|
||||
// if (!ctx.args || !ctx.args.data.email) return;
|
||||
|
|
|
@ -24,8 +24,8 @@
|
|||
<vn-textfield
|
||||
ng-if="$ctrl.$state.params.twoFactor"
|
||||
label="Verification code"
|
||||
ng-model="$ctrl.verificationCode"
|
||||
vn-name="verificationCode"
|
||||
ng-model="$ctrl.code"
|
||||
vn-name="code"
|
||||
autocomplete="false"
|
||||
class="vn-mt-md">
|
||||
</vn-textfield>
|
||||
|
|
|
@ -15,11 +15,6 @@ export default class Controller {
|
|||
}
|
||||
|
||||
$onInit() {
|
||||
this.oldPassword = 'nightmare';
|
||||
this.repeatPassword = 'test.1234';
|
||||
this.newPassword = 'test.1234';
|
||||
this.verificationCode = '1234';
|
||||
console.log(this.$state.params);
|
||||
if (!this.$state.params.id)
|
||||
this.$state.go('login');
|
||||
|
||||
|
@ -34,7 +29,7 @@ export default class Controller {
|
|||
const oldPassword = this.oldPassword;
|
||||
const newPassword = this.newPassword;
|
||||
const repeatPassword = this.repeatPassword;
|
||||
const verificationCode = this.verificationCode;
|
||||
const code = this.code;
|
||||
|
||||
if (!oldPassword || !newPassword || !repeatPassword)
|
||||
throw new UserError(`You must fill all the fields`);
|
||||
|
@ -46,18 +41,13 @@ export default class Controller {
|
|||
const headers = {
|
||||
Authorization: this.$state.params.id
|
||||
};
|
||||
console.log({
|
||||
id: userId,
|
||||
oldPassword,
|
||||
newPassword,
|
||||
verificationCode
|
||||
});
|
||||
|
||||
this.$http.patch('Accounts/change-password',
|
||||
{
|
||||
id: userId,
|
||||
oldPassword,
|
||||
newPassword,
|
||||
verificationCode
|
||||
code
|
||||
},
|
||||
{headers}
|
||||
).then(() => {
|
||||
|
|
|
@ -5,6 +5,7 @@ Repeat password: Repetir contraseña
|
|||
Passwords don't match: Las contraseñas no coinciden
|
||||
You must fill all the fields: Debes rellenar todos los campos
|
||||
You can't use the same password: No puedes usar la misma contraseña
|
||||
Verification code: Código de verificación
|
||||
Password updated!: ¡Contraseña actualizada!
|
||||
Password requirements: >
|
||||
La contraseña debe tener al menos {{ length }} caracteres de longitud,
|
||||
|
|
|
@ -16,27 +16,30 @@ module.exports = Self => {
|
|||
description: 'The new password',
|
||||
required: true
|
||||
}, {
|
||||
arg: 'verificationCode',
|
||||
arg: 'code',
|
||||
type: 'string',
|
||||
description: 'The 2FA code'
|
||||
}
|
||||
],
|
||||
http: {
|
||||
path: `/changePassword`,
|
||||
path: `/change-password`,
|
||||
verb: 'PATCH'
|
||||
}
|
||||
});
|
||||
|
||||
Self.changePassword = async function(ctx, oldPassword, newPassword, verificationCode) {
|
||||
Self.changePassword = async function(ctx, oldPassword, newPassword, code, options) {
|
||||
const userId = ctx.req.accessToken.userId;
|
||||
const {vnUser} = Self.app.models;
|
||||
|
||||
const myOptions = {...(options || {})};
|
||||
|
||||
const {VnUser} = Self.app.models;
|
||||
if (oldPassword == newPassword)
|
||||
throw new UserError(`You can't use the same password`);
|
||||
|
||||
const user = await vnUser.findById(userId, {fields: ['name', 'twoFactor']});
|
||||
const user = await VnUser.findById(userId, {fields: ['name', 'twoFactor']}, myOptions);
|
||||
if (user.twoFactor)
|
||||
await vnUser.validateCode(user.name, verificationCode);
|
||||
await VnUser.validateCode(user.name, code, myOptions);
|
||||
|
||||
await vnUser.changePassword(userId, oldPassword, newPassword);
|
||||
await VnUser.changePassword(userId, oldPassword, newPassword, myOptions);
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,11 +1,20 @@
|
|||
const {models} = require('vn-loopback/server/server');
|
||||
|
||||
describe('account changePassword()', () => {
|
||||
fdescribe('account changePassword()', () => {
|
||||
let ctx = {req: {accessToken: {userId: 70}}};
|
||||
|
||||
describe('Without 2FA', () => {
|
||||
it('should throw an error when old password is wrong', async() => {
|
||||
const tx = await models.Account.beginTransaction({});
|
||||
|
||||
let error;
|
||||
try {
|
||||
await models.Account.changePassword(1, 'wrongPassword', 'nightmare.9999');
|
||||
const options = {transaction: tx};
|
||||
|
||||
await models.Account.changePassword(ctx, 'wrongPassword', 'nightmare.9999', null, options);
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
error = e.message;
|
||||
}
|
||||
|
||||
|
@ -13,10 +22,60 @@ describe('account changePassword()', () => {
|
|||
});
|
||||
|
||||
it('should change password', async() => {
|
||||
const tx = await models.Account.beginTransaction({});
|
||||
|
||||
try {
|
||||
await models.Account.changePassword(70, 'nightmare', 'nightmare.9999');
|
||||
const options = {transaction: tx};
|
||||
|
||||
await models.Account.changePassword(ctx, 'nightmare', 'nightmare.9999', null, options);
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
|
||||
expect(e).toBeUndefined();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('With 2FA', () => {
|
||||
it('should throw an error when code is incorrect', async() => {
|
||||
const tx = await models.Account.beginTransaction({});
|
||||
|
||||
let error;
|
||||
try {
|
||||
const options = {transaction: tx};
|
||||
await models.VnUser.updateAll(
|
||||
{id: 70},
|
||||
{twoFactor: 'email'}
|
||||
, options);
|
||||
await models.Account.changePassword(ctx, 'wrongPassword', 'nightmare.9999', null, options);
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
error = e.message;
|
||||
}
|
||||
|
||||
expect(error).toContain('Invalid current password');
|
||||
});
|
||||
|
||||
it('should change password when code is correct', async() => {
|
||||
const tx = await models.Account.beginTransaction({});
|
||||
|
||||
try {
|
||||
const options = {transaction: tx};
|
||||
await models.VnUser.updateAll(
|
||||
{id: 70},
|
||||
{twoFactor: 'email'}
|
||||
, options);
|
||||
await models.VnUser.signin('trainee', 'nightmare', options);
|
||||
const authCode = await models.AuthCode.findOne({where: {userFk: 70}}, options);
|
||||
await models.Account.changePassword(ctx, 'nightmare', 'nightmare.9999', authCode.code, options);
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
|
||||
expect(e).toBeUndefined();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -37,13 +37,6 @@
|
|||
"principalType": "ROLE",
|
||||
"principalId": "$authenticated",
|
||||
"permission": "ALLOW"
|
||||
},
|
||||
{
|
||||
"property": "changePassword",
|
||||
"accessType": "EXECUTE",
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$everyone",
|
||||
"permission": "ALLOW"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ module.exports = Self => {
|
|||
const isAccount = await models.Account.findById(id);
|
||||
|
||||
if (isClient && !isAccount)
|
||||
await models.Account.setPassword(id, newPassword);
|
||||
await models.VnUser.setPassword(id, newPassword);
|
||||
else
|
||||
throw new UserError(`Modifiable password only via recovery or by an administrator`);
|
||||
};
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
"packages": {
|
||||
"": {
|
||||
"name": "salix-back",
|
||||
"version": "23.24.01",
|
||||
"version": "23.26.01",
|
||||
"license": "GPL-3.0",
|
||||
"dependencies": {
|
||||
"axios": "^1.2.2",
|
||||
|
|
|
@ -15,7 +15,7 @@ module.exports = {
|
|||
type: String
|
||||
},
|
||||
ip: {
|
||||
type: Number
|
||||
type: String
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue