Skip to content

Commit

Permalink
feat(member): changing password endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
serge1peshcoff committed Mar 23, 2020
1 parent b3e0428 commit d23ea10
Show file tree
Hide file tree
Showing 5 changed files with 197 additions and 4 deletions.
1 change: 1 addition & 0 deletions lib/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ GeneralRouter.post('/campaigns', middlewares.ensureAuthorized, campaigns.createC
MemberRouter.use(middlewares.maybeAuthorize, middlewares.ensureAuthorized, fetch.fetchUser);
MemberRouter.get('/my_permissions', myPermissions.getMyPermissions);
MemberRouter.put('/active', members.setUserActive);
MemberRouter.put('/password', members.setUserPassword);
MemberRouter.get('/', members.getUser);
MemberRouter.put('/', members.updateUser);
// MemberRouter.delete('/', members.deleteUser);
Expand Down
21 changes: 21 additions & 0 deletions middlewares/members.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,27 @@ exports.updateUser = async (req, res) => {
// });
// };

exports.setUserPassword = async (req, res) => {
if (!req.permissions.hasPermission('update:member') && req.user.id !== req.currentUser.id) {
return errors.makeForbiddenError(res, 'Permission update:member is required, but not present.');
}

const userWithPassword = await User.scope('withPassword').findByPk(req.currentUser.id);

if (!await userWithPassword.checkPassword(req.body.old_password)) {
return errors.makeForbiddenError(res, 'The old password is invalid.');
}

await userWithPassword.update({ password: req.body.password });

// TODO: add a mail that the password was changed.

return res.json({
success: true,
message: 'The password was changed.'
});
};

exports.setUserActive = async (req, res) => {
if (!req.permissions.hasPermission('update_active:member')) {
return errors.makeForbiddenError(res, 'Permission update_active:member is required, but not present.');
Expand Down
10 changes: 7 additions & 3 deletions models/User.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,11 +135,15 @@ User.beforeValidate(async (user) => {
if (typeof user.about_me === 'string') user.about_me = user.about_me.trim();
});

User.afterValidate(async (user) => {
async function encryptPassword(user) {
if (user.changed('password')) {
user.password = await bcrypt.hash(user.password, config.salt_rounds);
user.setDataValue('password', await bcrypt.hash(user.password, config.salt_rounds));
}
});
}

User.beforeCreate(encryptPassword);
User.beforeUpdate(encryptPassword);


User.prototype.checkPassword = async function checkPassword(password) {
return bcrypt.compare(password, this.password);
Expand Down
2 changes: 1 addition & 1 deletion test/api/users-activation.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ describe('User activation', () => {
const user = await generator.createUser({ superadmin: true });
const token = await generator.createAccessToken({}, user);

await generator.createPermission({ scope: 'local', action: 'update_active', object: 'member' });
await generator.createPermission({ scope: 'global', action: 'update_active', object: 'member' });

const res = await request({
uri: '/members/1337/active',
Expand Down
167 changes: 167 additions & 0 deletions test/api/users-password.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
const { startServer, stopServer } = require('../../lib/server.js');
const { User } = require('../../models');
const { request } = require('../scripts/helpers');
const generator = require('../scripts/generator');


describe('User activation', () => {
beforeAll(async () => {
await startServer();
});

afterAll(async () => {
await stopServer();
});

afterEach(async () => {
await generator.clearAll();
});

test('should return 404 if the user is not found', async () => {
const user = await generator.createUser({ password: 'testtest', superadmin: true });
const token = await generator.createAccessToken({}, user);

await generator.createPermission({ scope: 'global', action: 'update', object: 'member' });

const res = await request({
uri: '/members/1337/password',
method: 'PUT',
headers: { 'X-Auth-Token': token.value },
body: { old_password: 'testtest', password: 'testtest2' }
});

expect(res.statusCode).toEqual(404);
expect(res.body.success).toEqual(false);
expect(res.body).not.toHaveProperty('data');
expect(res.body).toHaveProperty('message');
});

test('should fail if old password is incorrect', async () => {
const user = await generator.createUser({ password: 'testtest', superadmin: true });
const token = await generator.createAccessToken({}, user);

await generator.createPermission({ scope: 'global', action: 'update', object: 'member' });

const res = await request({
uri: '/members/' + user.id + '/password',
method: 'PUT',
headers: { 'X-Auth-Token': token.value },
body: { old_password: 'not right', password: 'short' }
});

expect(res.statusCode).toEqual(403);
expect(res.body.success).toEqual(false);
expect(res.body).not.toHaveProperty('data');
expect(res.body).toHaveProperty('message');
});

test('should fail if there are validation errors', async () => {
const user = await generator.createUser({ password: 'testtest', superadmin: true });
const token = await generator.createAccessToken({}, user);

await generator.createPermission({ scope: 'global', action: 'update', object: 'member' });

const res = await request({
uri: '/members/' + user.id + '/password',
method: 'PUT',
headers: { 'X-Auth-Token': token.value },
body: { old_password: 'testtest', password: 'short' }
});

expect(res.statusCode).toEqual(422);
expect(res.body.success).toEqual(false);
expect(res.body).not.toHaveProperty('data');
expect(res.body).toHaveProperty('errors');
expect(res.body.errors).toHaveProperty('password');
});

test('should succeed if everything is okay', async () => {
const user = await generator.createUser({ password: 'testtest', superadmin: true });
const token = await generator.createAccessToken({}, user);

await generator.createPermission({ scope: 'local', action: 'update', object: 'member' });

const res = await request({
uri: '/members/' + user.id + '/password',
method: 'PUT',
headers: { 'X-Auth-Token': token.value },
body: { old_password: 'testtest', password: 'testtest2' }
});

expect(res.statusCode).toEqual(200);
expect(res.body.success).toEqual(true);
expect(res.body).not.toHaveProperty('errors');
expect(res.body).toHaveProperty('message');

const userFromDb = await User.scope('withPassword').findByPk(user.id);
expect(await userFromDb.checkPassword('testtest2')).toEqual(true);
});

test('should work for yourself without permission', async () => {
const user = await generator.createUser({ password: 'testtest', superadmin: true });
const token = await generator.createAccessToken({}, user);

const res = await request({
uri: '/members/' + user.id + '/password',
method: 'PUT',
headers: { 'X-Auth-Token': token.value },
body: { old_password: 'testtest', password: 'testtest2' }
});

expect(res.statusCode).toEqual(200);
expect(res.body.success).toEqual(true);
expect(res.body).not.toHaveProperty('errors');
expect(res.body).toHaveProperty('message');

const userFromDb = await User.scope('withPassword').findByPk(user.id);
expect(await userFromDb.checkPassword('testtest2')).toEqual(true);
});

test('should work with local permission', async () => {
const user = await generator.createUser();
const token = await generator.createAccessToken({}, user);

const otherUser = await generator.createUser({ password: 'testtest', superadmin: true });

const permission = await generator.createPermission({ scope: 'local', action: 'update', object: 'member' });
const body = await generator.createBody();
const circle = await generator.createCircle({ body_id: body.id });
await generator.createCircleMembership(circle, user);
await generator.createCirclePermission(circle, permission);
await generator.createBodyMembership(body, otherUser);

const res = await request({
uri: '/members/' + otherUser.id + '/password',
method: 'PUT',
headers: { 'X-Auth-Token': token.value },
body: { old_password: 'testtest', password: 'testtest2' }
});

expect(res.statusCode).toEqual(200);
expect(res.body.success).toEqual(true);
expect(res.body).not.toHaveProperty('errors');
expect(res.body).toHaveProperty('message');

const userFromDb = await User.scope('withPassword').findByPk(otherUser.id);
expect(await userFromDb.checkPassword('testtest2')).toEqual(true);
});

test('should fail for other person without permission', async () => {
const user = await generator.createUser();
const token = await generator.createAccessToken({}, user);

const otherUser = await generator.createUser({ password: 'testtest', superadmin: true });

const res = await request({
uri: '/members/' + otherUser.id + '/password',
method: 'PUT',
headers: { 'X-Auth-Token': token.value },
body: { old_password: 'testtest', password: 'testtest2' }
});

expect(res.statusCode).toEqual(403);
expect(res.body.success).toEqual(false);
expect(res.body).not.toHaveProperty('data');
expect(res.body).toHaveProperty('message');
});
});

0 comments on commit d23ea10

Please sign in to comment.