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) {
|
Self.validateAuth = async function(username, password, code) {
|
||||||
await Self.validateCode(code);
|
await Self.validateCode(username, code);
|
||||||
return Self.validateLogin(username, password);
|
return Self.validateLogin(username, password);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -52,7 +52,7 @@ module.exports = Self => {
|
||||||
const user = await Self.findById(authCode.userFk, {
|
const user = await Self.findById(authCode.userFk, {
|
||||||
fields: ['name', 'twoFactor']
|
fields: ['name', 'twoFactor']
|
||||||
});
|
});
|
||||||
console.log(username, code);
|
|
||||||
if (user.name !== username)
|
if (user.name !== username)
|
||||||
throw new UserError('Authentication failed');
|
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 =
|
||||||
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');
|
.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
|
// FIXME: https://redmine.verdnatura.es/issues/5761
|
||||||
// Self.afterRemote('prototype.patchAttributes', async(ctx, instance) => {
|
// Self.afterRemote('prototype.patchAttributes', async(ctx, instance) => {
|
||||||
// if (!ctx.args || !ctx.args.data.email) return;
|
// if (!ctx.args || !ctx.args.data.email) return;
|
||||||
|
|
|
@ -24,8 +24,8 @@
|
||||||
<vn-textfield
|
<vn-textfield
|
||||||
ng-if="$ctrl.$state.params.twoFactor"
|
ng-if="$ctrl.$state.params.twoFactor"
|
||||||
label="Verification code"
|
label="Verification code"
|
||||||
ng-model="$ctrl.verificationCode"
|
ng-model="$ctrl.code"
|
||||||
vn-name="verificationCode"
|
vn-name="code"
|
||||||
autocomplete="false"
|
autocomplete="false"
|
||||||
class="vn-mt-md">
|
class="vn-mt-md">
|
||||||
</vn-textfield>
|
</vn-textfield>
|
||||||
|
|
|
@ -15,11 +15,6 @@ export default class Controller {
|
||||||
}
|
}
|
||||||
|
|
||||||
$onInit() {
|
$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)
|
if (!this.$state.params.id)
|
||||||
this.$state.go('login');
|
this.$state.go('login');
|
||||||
|
|
||||||
|
@ -34,7 +29,7 @@ export default class Controller {
|
||||||
const oldPassword = this.oldPassword;
|
const oldPassword = this.oldPassword;
|
||||||
const newPassword = this.newPassword;
|
const newPassword = this.newPassword;
|
||||||
const repeatPassword = this.repeatPassword;
|
const repeatPassword = this.repeatPassword;
|
||||||
const verificationCode = this.verificationCode;
|
const code = this.code;
|
||||||
|
|
||||||
if (!oldPassword || !newPassword || !repeatPassword)
|
if (!oldPassword || !newPassword || !repeatPassword)
|
||||||
throw new UserError(`You must fill all the fields`);
|
throw new UserError(`You must fill all the fields`);
|
||||||
|
@ -46,18 +41,13 @@ export default class Controller {
|
||||||
const headers = {
|
const headers = {
|
||||||
Authorization: this.$state.params.id
|
Authorization: this.$state.params.id
|
||||||
};
|
};
|
||||||
console.log({
|
|
||||||
id: userId,
|
|
||||||
oldPassword,
|
|
||||||
newPassword,
|
|
||||||
verificationCode
|
|
||||||
});
|
|
||||||
this.$http.patch('Accounts/change-password',
|
this.$http.patch('Accounts/change-password',
|
||||||
{
|
{
|
||||||
id: userId,
|
id: userId,
|
||||||
oldPassword,
|
oldPassword,
|
||||||
newPassword,
|
newPassword,
|
||||||
verificationCode
|
code
|
||||||
},
|
},
|
||||||
{headers}
|
{headers}
|
||||||
).then(() => {
|
).then(() => {
|
||||||
|
|
|
@ -5,6 +5,7 @@ Repeat password: Repetir contraseña
|
||||||
Passwords don't match: Las contraseñas no coinciden
|
Passwords don't match: Las contraseñas no coinciden
|
||||||
You must fill all the fields: Debes rellenar todos los campos
|
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
|
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 updated!: ¡Contraseña actualizada!
|
||||||
Password requirements: >
|
Password requirements: >
|
||||||
La contraseña debe tener al menos {{ length }} caracteres de longitud,
|
La contraseña debe tener al menos {{ length }} caracteres de longitud,
|
||||||
|
|
|
@ -16,27 +16,30 @@ module.exports = Self => {
|
||||||
description: 'The new password',
|
description: 'The new password',
|
||||||
required: true
|
required: true
|
||||||
}, {
|
}, {
|
||||||
arg: 'verificationCode',
|
arg: 'code',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
description: 'The 2FA code'
|
description: 'The 2FA code'
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
http: {
|
http: {
|
||||||
path: `/changePassword`,
|
path: `/change-password`,
|
||||||
verb: 'PATCH'
|
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 userId = ctx.req.accessToken.userId;
|
||||||
const {vnUser} = Self.app.models;
|
|
||||||
|
const myOptions = {...(options || {})};
|
||||||
|
|
||||||
|
const {VnUser} = Self.app.models;
|
||||||
if (oldPassword == newPassword)
|
if (oldPassword == newPassword)
|
||||||
throw new UserError(`You can't use the same password`);
|
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)
|
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');
|
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() => {
|
it('should throw an error when old password is wrong', async() => {
|
||||||
|
const tx = await models.Account.beginTransaction({});
|
||||||
|
|
||||||
let error;
|
let error;
|
||||||
try {
|
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) {
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
error = e.message;
|
error = e.message;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,10 +22,60 @@ describe('account changePassword()', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should change password', async() => {
|
it('should change password', async() => {
|
||||||
|
const tx = await models.Account.beginTransaction({});
|
||||||
|
|
||||||
try {
|
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) {
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
|
||||||
expect(e).toBeUndefined();
|
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",
|
"principalType": "ROLE",
|
||||||
"principalId": "$authenticated",
|
"principalId": "$authenticated",
|
||||||
"permission": "ALLOW"
|
"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);
|
const isAccount = await models.Account.findById(id);
|
||||||
|
|
||||||
if (isClient && !isAccount)
|
if (isClient && !isAccount)
|
||||||
await models.Account.setPassword(id, newPassword);
|
await models.VnUser.setPassword(id, newPassword);
|
||||||
else
|
else
|
||||||
throw new UserError(`Modifiable password only via recovery or by an administrator`);
|
throw new UserError(`Modifiable password only via recovery or by an administrator`);
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "salix-back",
|
"name": "salix-back",
|
||||||
"version": "23.24.01",
|
"version": "23.26.01",
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.2.2",
|
"axios": "^1.2.2",
|
||||||
|
|
|
@ -15,7 +15,7 @@ module.exports = {
|
||||||
type: String
|
type: String
|
||||||
},
|
},
|
||||||
ip: {
|
ip: {
|
||||||
type: Number
|
type: String
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue