diff --git a/src/routes/index.js b/src/routes/index.js index 361a4efaa..6c3118e81 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -9,6 +9,7 @@ exports = module.exports = { graphs: require('./graphs.js'), groups: require('./groups.js'), oauth2: require('./oauth2.js'), + profile: require('./profile.js'), settings: require('./settings.js'), sysadmin: require('./sysadmin.js'), user: require('./user.js') diff --git a/src/routes/profile.js b/src/routes/profile.js new file mode 100644 index 000000000..6a4229287 --- /dev/null +++ b/src/routes/profile.js @@ -0,0 +1,79 @@ +/* jslint node:true */ + +'use strict'; + +exports = module.exports = { + get: get, + update: update, + changePassword: changePassword +}; + +var assert = require('assert'), + groups = require('../groups.js'), + HttpError = require('connect-lastmile').HttpError, + HttpSuccess = require('connect-lastmile').HttpSuccess, + user = require('../user.js'), + tokendb = require('../tokendb.js'), + UserError = user.UserError; + +function get(req, res, next) { + assert.strictEqual(typeof req.user, 'object'); + + var result = {}; + result.id = req.user.id; + result.tokenType = req.user.tokenType; + + if (req.user.tokenType === tokendb.TYPE_USER || req.user.tokenType === tokendb.TYPE_DEV) { + result.username = req.user.username; + result.email = req.user.email; + result.displayName = req.user.displayName; + + groups.isMember(groups.ADMIN_GROUP_ID, req.user.id, function (error, isAdmin) { + if (error) return next(new HttpError(500, error)); + + result.admin = isAdmin; + + next(new HttpSuccess(200, result)); + }); + } else { + next(new HttpSuccess(200, result)); + } +} + +function update(req, res, next) { + assert.strictEqual(typeof req.user, 'object'); + assert.strictEqual(typeof req.body, 'object'); + + if ('email' in req.body && typeof req.body.email !== 'string') return next(new HttpError(400, 'email must be string')); + if ('displayName' in req.body && typeof req.body.displayName !== 'string') return next(new HttpError(400, 'displayName must be string')); + + if (req.user.tokenType !== tokendb.TYPE_USER) return next(new HttpError(403, 'Token type not allowed')); + + user.update(req.user.id, req.user.username, req.body.email || req.user.email, req.body.displayName || req.user.displayName, function (error) { + if (error && error.reason === UserError.BAD_USERNAME) return next(new HttpError(400, error.message)); + if (error && error.reason === UserError.BAD_EMAIL) return next(new HttpError(400, error.message)); + if (error && error.reason === UserError.ALREADY_EXISTS) return next(new HttpError(409, 'Already exists')); + if (error && error.reason === UserError.NOT_FOUND) return next(new HttpError(404, 'User not found')); + if (error) return next(new HttpError(500, error)); + + next(new HttpSuccess(204)); + }); +} + +function changePassword(req, res, next) { + assert.strictEqual(typeof req.body, 'object'); + assert.strictEqual(typeof req.user, 'object'); + + if (typeof req.body.password !== 'string') return next(new HttpError(400, 'API call requires the users old password.')); + if (typeof req.body.newPassword !== 'string') return next(new HttpError(400, 'API call requires the users new password.')); + + if (req.user.tokenType !== tokendb.TYPE_USER) return next(new HttpError(403, 'Token type not allowed')); + + user.setPassword(req.user.id, req.body.newPassword, function (error) { + if (error && error.reason === UserError.BAD_PASSWORD) return next(new HttpError(400, error.message)); + if (error && error.reason === UserError.NOT_FOUND) return next(new HttpError(403, 'Wrong password')); + if (error) return next(new HttpError(500, error)); + + next(new HttpSuccess(204)); + }); +} diff --git a/src/routes/test/profile-test.js b/src/routes/test/profile-test.js new file mode 100644 index 000000000..645dd596d --- /dev/null +++ b/src/routes/test/profile-test.js @@ -0,0 +1,293 @@ +/* jslint node:true */ +/* global it:false */ +/* global describe:false */ +/* global before:false */ +/* global after:false */ + +'use strict'; + +var config = require('../../config.js'), + database = require('../../database.js'), + tokendb = require('../../tokendb.js'), + expect = require('expect.js'), + groups = require('../../groups.js'), + mailer = require('../../mailer.js'), + superagent = require('superagent'), + nock = require('nock'), + server = require('../../server.js'), + userdb = require('../../userdb.js'); + +var SERVER_URL = 'http://localhost:' + config.get('port'); + +var USERNAME_0 = 'superaDmIn', PASSWORD = 'Foobar?1337', EMAIL_0 = 'silLY@me.com', EMAIL_0_NEW = 'stupID@me.com', DISPLAY_NAME_0_NEW = 'New Name'; +var USERNAME_1 = 'userTheFirst', EMAIL_1 = 'taO@zen.mac'; +var USERNAME_2 = 'userTheSecond', EMAIL_2 = 'USER@foo.bar', EMAIL_2_NEW = 'happy@ME.com'; +var USERNAME_3 = 'userTheThird', EMAIL_3 = 'user3@FOO.bar'; + +describe('Profile API', function () { + this.timeout(5000); + + var user_0, user_1, user_2, user_3 = null; + var token_0; + var token_1 = tokendb.generateToken(); + var token_2 = tokendb.generateToken(); + var token_3; + + function setup(done) { + server.start(function (error) { + expect(!error).to.be.ok(); + + mailer._clearMailQueue(); + + database._clear(function (error) { + expect(error).to.eql(null); + + var scope1 = nock(config.apiServerOrigin()).get('/api/v1/boxes/' + config.fqdn() + '/setup/verify?setupToken=somesetuptoken').reply(200, {}); + var scope2 = nock(config.apiServerOrigin()).post('/api/v1/boxes/' + config.fqdn() + '/setup/done?setupToken=somesetuptoken').reply(201, {}); + + superagent.post(SERVER_URL + '/api/v1/cloudron/activate') + .query({ setupToken: 'somesetuptoken' }) + .send({ username: USERNAME_0, password: PASSWORD, email: EMAIL_0 }) + .end(function (err, res) { + expect(err).to.eql(null); + expect(res.statusCode).to.equal(201); + + // stash for later use + token_0 = res.body.token; + + expect(scope1.isDone()).to.be.ok(); + expect(scope2.isDone()).to.be.ok(); + + done(); + }); + }); + }); + } + + function cleanup(done) { + database._clear(function (error) { + expect(!error).to.be.ok(); + + mailer._clearMailQueue(); + + server.stop(done); + }); + } + + function checkMails(number, done) { + // mails are enqueued async + setTimeout(function () { + expect(mailer._getMailQueue().length).to.equal(number); + mailer._clearMailQueue(); + done(); + }, 500); + } + + describe('get profile', function () { + before(setup); + after(cleanup); + + it('fails without token', function (done) { + superagent.get(SERVER_URL + '/api/v1/profile/').end(function (error, result) { + expect(result.statusCode).to.equal(401); + + done(); + }); + }); + + it('fails with empty token', function (done) { + superagent.get(SERVER_URL + '/api/v1/profile/').query({ access_token: '' }).end(function (error, result) { + expect(result.statusCode).to.equal(401); + + done(); + }); + }); + + it('fails with invalid token', function (done) { + superagent.get(SERVER_URL + '/api/v1/profile/').query({ access_token: 'some token' }).end(function (error, result) { + expect(result.statusCode).to.equal(401); + + done(); + }); + }); + + it('succeeds', function (done) { + superagent.get(SERVER_URL + '/api/v1/profile/').query({ access_token: token_0 }).end(function (error, result) { + expect(result.statusCode).to.equal(200); + expect(result.body.username).to.equal(USERNAME_0.toLowerCase()); + expect(result.body.email).to.equal(EMAIL_0.toLowerCase()); + expect(result.body.admin).to.be.ok(); + + user_0 = result.body; + + done(); + }); + }); + + it('fails with expired token', function (done) { + var token = tokendb.generateToken(); + var expires = Date.now() - 2000; // 1 sec + + tokendb.add(token, tokendb.PREFIX_USER + user_0.id, null, expires, '*', function (error) { + expect(error).to.not.be.ok(); + + superagent.get(SERVER_URL + '/api/v1/profile').query({ access_token: token }).end(function (error, result) { + expect(result.statusCode).to.equal(401); + + done(); + }); + }); + }); + + it('fails with invalid token in auth header', function (done) { + superagent.get(SERVER_URL + '/api/v1/profile').set('Authorization', 'Bearer ' + 'x' + token_0).end(function (error, result) { + expect(result.statusCode).to.equal(401); + done(); + }); + }); + + it('succeeds with token in auth header', function (done) { + superagent.get(SERVER_URL + '/api/v1/profile').set('Authorization', 'Bearer ' + token_0).end(function (error, result) { + expect(result.statusCode).to.equal(200); + expect(result.body.username).to.equal(USERNAME_0.toLowerCase()); + expect(result.body.email).to.equal(EMAIL_0.toLowerCase()); + expect(result.body.admin).to.be.ok(); + expect(result.body.displayName).to.be.a('string'); + expect(result.body.password).to.not.be.ok(); + expect(result.body.salt).to.not.be.ok(); + done(); + }); + }); + }); + + describe('update', function () { + before(setup); + after(cleanup); + + it('change email fails due to missing token', function (done) { + superagent.put(SERVER_URL + '/api/v1/profile') + .send({ email: EMAIL_0_NEW }) + .end(function (error, result) { + expect(result.statusCode).to.equal(401); + done(); + }); + }); + + it('change email fails due to invalid email', function (done) { + superagent.put(SERVER_URL + '/api/v1/profile') + .query({ access_token: token_0 }) + .send({ email: 'foo@bar' }) + .end(function (error, result) { + expect(result.statusCode).to.equal(400); + done(); + }); + }); + + it('change user succeeds without email nor displayName', function (done) { + superagent.put(SERVER_URL + '/api/v1/profile') + .query({ access_token: token_0 }) + .send({}) + .end(function (error, result) { + expect(result.statusCode).to.equal(204); + done(); + }); + }); + + it('change email succeeds', function (done) { + superagent.put(SERVER_URL + '/api/v1/profile') + .query({ access_token: token_0 }) + .send({ email: EMAIL_0_NEW }) + .end(function (error, result) { + expect(result.statusCode).to.equal(204); + + superagent.get(SERVER_URL + '/api/v1/profile') + .query({ access_token: token_0 }) + .end(function (err, res) { + expect(res.statusCode).to.equal(200); + expect(res.body.username).to.equal(USERNAME_0.toLowerCase()); + expect(res.body.email).to.equal(EMAIL_0_NEW.toLowerCase()); + expect(res.body.admin).to.equal(true); + expect(res.body.displayName).to.equal(''); + + done(); + }); + }); + }); + + it('change displayName succeeds', function (done) { + superagent.put(SERVER_URL + '/api/v1/profile') + .query({ access_token: token_0 }) + .send({ displayName: DISPLAY_NAME_0_NEW }) + .end(function (error, result) { + expect(result.statusCode).to.equal(204); + + superagent.get(SERVER_URL + '/api/v1/profile') + .query({ access_token: token_0 }) + .end(function (err, res) { + expect(res.statusCode).to.equal(200); + expect(res.body.username).to.equal(USERNAME_0.toLowerCase()); + expect(res.body.email).to.equal(EMAIL_0_NEW.toLowerCase()); + expect(res.body.admin).to.be.ok(); + expect(res.body.displayName).to.equal(DISPLAY_NAME_0_NEW); + + done(); + }); + }); + }); + }); + + describe('password change', function () { + before(setup); + after(cleanup); + + it('change password fails due to missing current password', function (done) { + superagent.put(SERVER_URL + '/api/v1/profile/password') + .query({ access_token: token_0 }) + .send({ newPassword: 'some wrong password' }) + .end(function (err, res) { + expect(res.statusCode).to.equal(400); + done(); + }); + }); + + it('change password fails due to missing new password', function (done) { + superagent.put(SERVER_URL + '/api/v1/profile/password') + .query({ access_token: token_0 }) + .send({ password: PASSWORD }) + .end(function (err, res) { + expect(res.statusCode).to.equal(400); + done(); + }); + }); + + it('change password fails due to wrong password', function (done) { + superagent.put(SERVER_URL + '/api/v1/profile/password') + .query({ access_token: token_0 }) + .send({ password: 'some wrong password', newPassword: 'MOre#$%34' }) + .end(function (err, res) { + expect(res.statusCode).to.equal(403); + done(); + }); + }); + + it('change password fails due to invalid password', function (done) { + superagent.put(SERVER_URL + '/api/v1/profile/password') + .query({ access_token: token_0 }) + .send({ password: PASSWORD, newPassword: 'five' }) + .end(function (err, res) { + expect(res.statusCode).to.equal(400); + done(); + }); + }); + + it('change password succeeds', function (done) { + superagent.put(SERVER_URL + '/api/v1/profile/password') + .query({ access_token: token_0 }) + .send({ password: PASSWORD, newPassword: 'MOre#$%34' }) + .end(function (err, res) { + expect(res.statusCode).to.equal(204); + done(); + }); + }); + }); +}); diff --git a/src/routes/test/user-test.js b/src/routes/test/user-test.js index 97ad5b8ea..b33f699f3 100644 --- a/src/routes/test/user-test.js +++ b/src/routes/test/user-test.js @@ -14,8 +14,7 @@ var config = require('../../config.js'), mailer = require('../../mailer.js'), superagent = require('superagent'), nock = require('nock'), - server = require('../../server.js'), - userdb = require('../../userdb.js'); + server = require('../../server.js'); var SERVER_URL = 'http://localhost:' + config.get('port'); @@ -24,14 +23,13 @@ var USERNAME_1 = 'userTheFirst', EMAIL_1 = 'taO@zen.mac'; var USERNAME_2 = 'userTheSecond', EMAIL_2 = 'USER@foo.bar', EMAIL_2_NEW = 'happy@ME.com'; var USERNAME_3 = 'userTheThird', EMAIL_3 = 'user3@FOO.bar'; -var server; function setup(done) { server.start(function (error) { expect(!error).to.be.ok(); mailer._clearMailQueue(); - userdb._clear(function (error) { + database._clear(function (error) { expect(error).to.eql(null); groups.create('somegroupid', done); @@ -64,7 +62,6 @@ describe('User API', function () { var user_0, user_1, user_2, user_3 = null; var token = null; var token_1 = tokendb.generateToken(); - var token_2 = tokendb.generateToken(); before(setup); after(cleanup); @@ -391,26 +388,14 @@ describe('User API', function () { user_3 = result.body; // one mail for first user creation, two mails for second user creation (see 'invite' flag) - checkMails(3, function () { - // HACK to get a token for second user (passwords are generated and the user should have gotten a password setup link...) - tokendb.add(token_2, tokendb.PREFIX_USER + user_2.id, 'test-client-id', Date.now() + 10000, '*', done); - }); + checkMails(3, done); }); }); }); - it('second user userInfo fails for first user', function (done) { + it('get userInfo succeeds for second user', function (done) { superagent.get(SERVER_URL + '/api/v1/users/' + user_2.id) - .query({ access_token: token_1 }) - .end(function (error, result) { - expect(result.statusCode).to.equal(403); - done(); - }); - }); - - it('second user userInfo succeeds for second user', function (done) { - superagent.get(SERVER_URL + '/api/v1/users/' + user_2.id) - .query({ access_token: token_2 }) + .query({ access_token: token }) .end(function (error, result) { expect(result.statusCode).to.equal(200); expect(result.body.username).to.equal(USERNAME_2.toLowerCase()); @@ -433,7 +418,7 @@ describe('User API', function () { it('list users fails for normal user', function (done) { superagent.get(SERVER_URL + '/api/v1/users') - .query({ access_token: token_2 }) + .query({ access_token: token_1 }) .end(function (error, res) { expect(res.statusCode).to.equal(403); done(); @@ -541,16 +526,6 @@ describe('User API', function () { }); }); - it('change email for other user fails', function (done) { - superagent.put(SERVER_URL + '/api/v1/users/' + user_0.id) - .query({ access_token: token_2 }) - .send({ email: 'foobar@bar.baz' }) - .end(function (error, result) { - expect(result.statusCode).to.equal(403); - done(); - }); - }); - it('change user succeeds without email nor displayName', function (done) { superagent.put(SERVER_URL + '/api/v1/users/' + user_0.id) .query({ access_token: token }) @@ -561,15 +536,15 @@ describe('User API', function () { }); }); - it('change email for own user succeeds', function (done) { + it('change email succeeds', function (done) { superagent.put(SERVER_URL + '/api/v1/users/' + user_2.id) - .query({ access_token: token_2 }) + .query({ access_token: token }) .send({ email: EMAIL_2_NEW }) .end(function (error, result) { expect(result.statusCode).to.equal(204); superagent.get(SERVER_URL + '/api/v1/users/' + user_2.id) - .query({ access_token: token_2 }) + .query({ access_token: token }) .end(function (err, res) { expect(res.statusCode).to.equal(200); expect(res.body.username).to.equal(USERNAME_2.toLowerCase()); @@ -623,55 +598,4 @@ describe('User API', function () { }); }); }); - - // Change password - it('change password fails due to missing current password', function (done) { - superagent.post(SERVER_URL + '/api/v1/users/' + USERNAME_0 + '/password') - .query({ access_token: token }) - .send({ newPassword: 'some wrong password' }) - .end(function (err, res) { - expect(res.statusCode).to.equal(400); - done(); - }); - }); - - it('change password fails due to missing new password', function (done) { - superagent.post(SERVER_URL + '/api/v1/users/' + USERNAME_0 + '/password') - .query({ access_token: token }) - .send({ password: PASSWORD }) - .end(function (err, res) { - expect(res.statusCode).to.equal(400); - done(); - }); - }); - - it('change password fails due to wrong password', function (done) { - superagent.post(SERVER_URL + '/api/v1/users/' + USERNAME_0 + '/password') - .query({ access_token: token }) - .send({ password: 'some wrong password', newPassword: 'MOre#$%34' }) - .end(function (err, res) { - expect(res.statusCode).to.equal(403); - done(); - }); - }); - - it('change password fails due to invalid password', function (done) { - superagent.post(SERVER_URL + '/api/v1/users/' + USERNAME_0 + '/password') - .query({ access_token: token }) - .send({ password: PASSWORD, newPassword: 'five' }) - .end(function (err, res) { - expect(res.statusCode).to.equal(400); - done(); - }); - }); - - it('change password succeeds', function (done) { - superagent.post(SERVER_URL + '/api/v1/users/' + USERNAME_0 + '/password') - .query({ access_token: token }) - .send({ password: PASSWORD, newPassword: 'MOre#$%34' }) - .end(function (err, res) { - expect(res.statusCode).to.equal(204); - done(); - }); - }); }); diff --git a/src/routes/user.js b/src/routes/user.js index 5ff758a64..8bd5858f9 100644 --- a/src/routes/user.js +++ b/src/routes/user.js @@ -3,13 +3,11 @@ 'use strict'; exports = module.exports = { - profile: profile, - info: info, + get: get, update: update, - list: listUser, - create: createUser, - changePassword: changePassword, - remove: removeUser, + list: list, + create: create, + remove: remove, verifyPassword: verifyPassword, requireAdmin: requireAdmin, sendInvite: sendInvite, @@ -25,31 +23,7 @@ var assert = require('assert'), tokendb = require('../tokendb.js'), UserError = user.UserError; -function profile(req, res, next) { - assert.strictEqual(typeof req.user, 'object'); - - var result = {}; - result.id = req.user.id; - result.tokenType = req.user.tokenType; - - if (req.user.tokenType === tokendb.TYPE_USER || req.user.tokenType === tokendb.TYPE_DEV) { - result.username = req.user.username; - result.email = req.user.email; - result.displayName = req.user.displayName; - - groups.isMember(groups.ADMIN_GROUP_ID, req.user.id, function (error, isAdmin) { - if (error) return next(new HttpError(500, error)); - - result.admin = isAdmin; - - next(new HttpSuccess(200, result)); - }); - } else { - next(new HttpSuccess(200, result)); - } -} - -function createUser(req, res, next) { +function create(req, res, next) { assert.strictEqual(typeof req.body, 'object'); if (typeof req.body.email !== 'string') return next(new HttpError(400, 'email must be string')); @@ -111,33 +85,14 @@ function update(req, res, next) { }); } -function changePassword(req, res, next) { - assert.strictEqual(typeof req.body, 'object'); - assert.strictEqual(typeof req.user, 'object'); - - if (typeof req.body.password !== 'string') return next(new HttpError(400, 'API call requires the users old password.')); - if (typeof req.body.newPassword !== 'string') return next(new HttpError(400, 'API call requires the users new password.')); - - if (req.user.tokenType !== tokendb.TYPE_USER) return next(new HttpError(403, 'Token type not allowed')); - - user.changePassword(req.user.username, req.body.password, req.body.newPassword, function (error) { - if (error && error.reason === UserError.BAD_PASSWORD) return next(new HttpError(400, error.message)); - if (error && error.reason === UserError.WRONG_PASSWORD) return next(new HttpError(403, 'Wrong password')); - if (error && error.reason === UserError.NOT_FOUND) return next(new HttpError(403, 'Wrong password')); - if (error) return next(new HttpError(500, error)); - - next(new HttpSuccess(204)); - }); -} - -function listUser(req, res, next) { +function list(req, res, next) { user.list(function (error, result) { if (error) return next(new HttpError(500, error)); next(new HttpSuccess(200, { users: result })); }); } -function info(req, res, next) { +function get(req, res, next) { assert.strictEqual(typeof req.params.userId, 'string'); assert.strictEqual(typeof req.user, 'object'); @@ -161,7 +116,7 @@ function info(req, res, next) { }); } -function removeUser(req, res, next) { +function remove(req, res, next) { assert.strictEqual(typeof req.params.userId, 'string'); // rules: @@ -187,24 +142,17 @@ function removeUser(req, res, next) { function verifyPassword(req, res, next) { assert.strictEqual(typeof req.body, 'object'); - // developers are allowed to through without password + // developers are allowed through without password if (req.user.tokenType === tokendb.TYPE_DEV) return next(); if (typeof req.body.password !== 'string') return next(new HttpError(400, 'API call requires user password')); - groups.isMember(groups.ADMIN_GROUP_ID, req.user.id, function (error, isAdmin) { + user.verifyWithUsername(req.user.username, req.body.password, function (error) { + if (error && error.reason === UserError.WRONG_PASSWORD) return next(new HttpError(403, 'Password incorrect')); + if (error && error.reason === UserError.NOT_FOUND) return next(new HttpError(403, 'Password incorrect')); if (error) return next(new HttpError(500, error)); - // Only allow admins or users, operating on themselves - if (req.params.userId && !(req.user.id === req.params.userId || isAdmin)) return next(new HttpError(403, 'Not allowed')); - - user.verifyWithUsername(req.user.username, req.body.password, function (error) { - if (error && error.reason === UserError.WRONG_PASSWORD) return next(new HttpError(403, 'Password incorrect')); - if (error && error.reason === UserError.NOT_FOUND) return next(new HttpError(403, 'Password incorrect')); - if (error) return next(new HttpError(500, error)); - - next(); - }); + next(); }); } diff --git a/src/server.js b/src/server.js index 1d8fc483d..413ffe838 100644 --- a/src/server.js +++ b/src/server.js @@ -96,20 +96,20 @@ function initializeExpressSync() { // feedback router.post('/api/v1/cloudron/feedback', usersScope, routes.cloudron.feedback); - router.get ('/api/v1/profile', profileScope, routes.user.profile); + // profile api, working off the user behind the provided token + router.get ('/api/v1/profile', profileScope, routes.profile.get); + router.put ('/api/v1/profile', profileScope, routes.profile.update); + router.put ('/api/v1/profile/password', profileScope, routes.user.verifyPassword, routes.profile.changePassword); // user routes only for admins router.get ('/api/v1/users', usersScope, routes.user.requireAdmin, routes.user.list); router.post('/api/v1/users', usersScope, routes.user.requireAdmin, routes.user.create); + router.get ('/api/v1/users/:userId', usersScope, routes.user.requireAdmin, routes.user.get); router.del ('/api/v1/users/:userId', usersScope, routes.user.requireAdmin, routes.user.verifyPassword, routes.user.remove); + router.put ('/api/v1/users/:userId', usersScope, routes.user.requireAdmin, routes.user.update); router.put ('/api/v1/users/:userId/set_groups', usersScope, routes.user.requireAdmin, routes.user.setGroups); router.post('/api/v1/users/:userId/invite', usersScope, routes.user.requireAdmin, routes.user.sendInvite); - // user routes for admins and users operating on their own account - router.get ('/api/v1/users/:userId', usersScope, routes.user.info); - router.put ('/api/v1/users/:userId', usersScope, routes.user.update); - router.post('/api/v1/users/:userId/password', usersScope, routes.user.changePassword); // changePassword verifies password - // Group management router.get ('/api/v1/groups', usersScope, routes.user.requireAdmin, routes.groups.list); router.post('/api/v1/groups', usersScope, routes.user.requireAdmin, routes.groups.create); diff --git a/src/test/user-test.js b/src/test/user-test.js index 87272e959..b23a49081 100644 --- a/src/test/user-test.js +++ b/src/test/user-test.js @@ -529,53 +529,40 @@ describe('User', function () { }); }); - describe('password change', function () { + describe('set password', function () { before(createOwner); after(cleanupUsers); - it('fails due to wrong arumgent count', function () { - expect(function () { user.changePassword(); }).to.throwError(); - expect(function () { user.changePassword(USERNAME); }).to.throwError(); - expect(function () { user.changePassword(USERNAME, PASSWORD, NEW_PASSWORD); }).to.throwError(); - }); - - it('fails due to wrong arumgents', function () { - expect(function () { user.changePassword(USERNAME, {}, NEW_PASSWORD, function () {}); }).to.throwError(); - expect(function () { user.changePassword(1337, PASSWORD, NEW_PASSWORD, function () {}); }).to.throwError(); - expect(function () { user.changePassword(USERNAME, PASSWORD, 1337, function () {}); }).to.throwError(); - expect(function () { user.changePassword(USERNAME, PASSWORD, NEW_PASSWORD, 'some string'); }).to.throwError(); - }); - - it('fails due to wrong password', function (done) { - user.changePassword(USERNAME, 'wrongpassword', NEW_PASSWORD, function (error) { - expect(error).to.be.ok(); - done(); - }); - }); - - it('fails due to empty new password', function (done) { - user.changePassword(USERNAME, PASSWORD, '', function (error) { - expect(error).to.be.ok(); - done(); - }); - }); - it('fails due to unknown user', function (done) { - user.changePassword('somerandomuser', PASSWORD, NEW_PASSWORD, function (error) { + user.setPassword('doesnotexist', NEW_PASSWORD, function (error) { + expect(error).to.be.ok(); + done(); + }); + }); + + it('fails due to empty password', function (done) { + user.setPassword(userObject.id, '', function (error) { + expect(error).to.be.ok(); + done(); + }); + }); + + it('fails due to invalid password', function (done) { + user.setPassword(userObject.id, 'foobar', function (error) { expect(error).to.be.ok(); done(); }); }); it('succeeds', function (done) { - user.changePassword(USERNAME, PASSWORD, NEW_PASSWORD, function (error) { + user.setPassword(userObject.id, NEW_PASSWORD, function (error) { expect(error).to.not.be.ok(); done(); }); }); it('actually changed the password (unable to login with old pasword)', function (done) { - user.verifyWithUsername(USERNAME, PASSWORD, function (error, result) { + user.verify(userObject.id, PASSWORD, function (error, result) { expect(error).to.be.ok(); expect(result).to.not.be.ok(); expect(error.reason).to.equal(UserError.WRONG_PASSWORD); @@ -584,7 +571,7 @@ describe('User', function () { }); it('actually changed the password (login with new password)', function (done) { - user.verifyWithUsername(USERNAME, NEW_PASSWORD, function (error, result) { + user.verify(userObject.id, NEW_PASSWORD, function (error, result) { expect(error).to.not.be.ok(); expect(result).to.be.ok(); done(); diff --git a/src/user.js b/src/user.js index 857f125dc..171ca23c8 100644 --- a/src/user.js +++ b/src/user.js @@ -14,7 +14,6 @@ exports = module.exports = { getAllAdmins: getAllAdmins, resetPasswordByIdentifier: resetPasswordByIdentifier, setPassword: setPassword, - changePassword: changePassword, update: updateUser, createOwner: createOwner, getOwner: getOwner, @@ -421,22 +420,6 @@ function setPassword(userId, newPassword, callback) { }); } -function changePassword(username, oldPassword, newPassword, callback) { - assert.strictEqual(typeof username, 'string'); - assert.strictEqual(typeof oldPassword, 'string'); - assert.strictEqual(typeof newPassword, 'string'); - assert.strictEqual(typeof callback, 'function'); - - var error = validatePassword(newPassword); - if (error) return callback(new UserError(UserError.BAD_PASSWORD, error.message)); - - verifyWithUsername(username.toLowerCase(), oldPassword, function (error, user) { - if (error) return callback(error); - - setPassword(user.id, newPassword, callback); - }); -} - function createOwner(username, password, email, displayName, callback) { assert.strictEqual(typeof username, 'string'); assert.strictEqual(typeof password, 'string'); diff --git a/webadmin/src/js/client.js b/webadmin/src/js/client.js index eb301ee7c..88b9d2b4b 100644 --- a/webadmin/src/js/client.js +++ b/webadmin/src/js/client.js @@ -498,13 +498,6 @@ angular.module('Application').service('Client', ['$http', 'md5', 'Notification', }).error(defaultErrorHandler(callback)); }; - Client.prototype.listUsers = function (callback) { - $http.get(client.apiOrigin + '/api/v1/users').success(function(data, status) { - if (status !== 200 || typeof data !== 'object') return callback(new ClientError(status, data)); - callback(null, data); - }).error(defaultErrorHandler(callback)); - }; - Client.prototype.getOAuthClients = function (callback) { $http.get(client.apiOrigin + '/api/v1/oauth/clients').success(function(data, status) { if (status !== 200 || typeof data !== 'object') return callback(new ClientError(status, data)); @@ -625,7 +618,7 @@ angular.module('Application').service('Client', ['$http', 'md5', 'Notification', newPassword: newPassword }; - $http.post(client.apiOrigin + '/api/v1/users/' + this._userInfo.id + '/password', data).success(function(data, status) { + $http.post(client.apiOrigin + '/api/v1/profile/' + this._userInfo.id + '/password', data).success(function(data, status) { if (status !== 204) return callback(new ClientError(status, data)); callback(null, data); }).error(defaultErrorHandler(callback)); diff --git a/webadmin/src/views/users.js b/webadmin/src/views/users.js index fcacf7c79..6cbcce3a3 100644 --- a/webadmin/src/views/users.js +++ b/webadmin/src/views/users.js @@ -331,10 +331,10 @@ angular.module('Application').controller('UsersController', ['$scope', '$locatio $scope.groups = result; - Client.listUsers(function (error, result) { + Client.getUsers(function (error, result) { if (error) return console.error('Unable to get user listing.', error); - $scope.users = result.users; + $scope.users = result; $scope.ready = true; }); });