users: cannot update profile fields of external user

This commit is contained in:
Girish Ramakrishnan
2024-01-20 10:41:24 +01:00
parent bd1ab000f3
commit c99c24b3bd
7 changed files with 174 additions and 93 deletions
+48 -18
View File
@@ -213,7 +213,7 @@ describe('Users API', function () {
describe('admin status', function () {
it('set second user as admin succeeds', async function () {
const response = await superagent.post(`${serverUrl}/api/v1/users/${user.id}`)
const response = await superagent.put(`${serverUrl}/api/v1/users/${user.id}/role`)
.query({ access_token: owner.token })
.send({ role: users.ROLE_ADMIN });
@@ -229,7 +229,7 @@ describe('Users API', function () {
});
it('make self as admin fails', async function () {
const response = await superagent.post(`${serverUrl}/api/v1/users/${owner.id}`)
const response = await superagent.put(`${serverUrl}/api/v1/users/${owner.id}/role`)
.query({ access_token: owner.token })
.send({ role: users.ROLE_ADMIN })
.ok(() => true);
@@ -238,7 +238,7 @@ describe('Users API', function () {
});
it('make self as normal user fails', async function () {
const response = await superagent.post(`${serverUrl}/api/v1/users/${owner.id}`)
const response = await superagent.put(`${serverUrl}/api/v1/users/${owner.id}/role`)
.query({ access_token: owner.token })
.send({ role: users.ROLE_USER })
.ok(() => true);
@@ -247,7 +247,7 @@ describe('Users API', function () {
});
it('remove second user as admin succeeds', async function () {
const response = await superagent.post(`${serverUrl}/api/v1/users/${user.id}`)
const response = await superagent.put(`${serverUrl}/api/v1/users/${user.id}/role`)
.query({ access_token: owner.token })
.send({ role: users.ROLE_USER });
@@ -255,7 +255,7 @@ describe('Users API', function () {
});
it('normal user cannot change role of admin', async function () {
const response = await superagent.post(`${serverUrl}/api/v1/users/${owner.id}`)
const response = await superagent.put(`${serverUrl}/api/v1/users/${owner.id}/role`)
.query({ access_token: user.token })
.send({ role: users.ROLE_USER })
.ok(() => true);
@@ -307,9 +307,9 @@ describe('Users API', function () {
});
});
describe('update', function () {
describe('profile update', function () {
it('change email fails due to missing token', async function () {
const response = await superagent.post(`${serverUrl}/api/v1/users/${user2.id}`)
const response = await superagent.post(`${serverUrl}/api/v1/users/${user2.id}/profile`)
.send({ email: 'newemail@cloudron.local' })
.ok(() => true);
@@ -317,7 +317,7 @@ describe('Users API', function () {
});
it('change email fails due to invalid email', async function () {
const response = await superagent.post(`${serverUrl}/api/v1/users/${user2.id}`)
const response = await superagent.post(`${serverUrl}/api/v1/users/${user2.id}/profile`)
.query({ access_token: owner.token })
.send({ email: 'newemail@cloudron' })
.ok(() => true);
@@ -326,7 +326,7 @@ describe('Users API', function () {
});
it('change fallbackEmail fails due to invalid email', async function () {
const response = await superagent.post(`${serverUrl}/api/v1/users/${user2.id}`)
const response = await superagent.post(`${serverUrl}/api/v1/users/${user2.id}/profile`)
.query({ access_token: owner.token })
.send({ fallbackEmail: 'newemail@cloudron' })
.ok(() => true);
@@ -335,7 +335,7 @@ describe('Users API', function () {
});
it('change user succeeds without email nor displayName', async function () {
const response = await superagent.post(`${serverUrl}/api/v1/users/${user2.id}`)
const response = await superagent.post(`${serverUrl}/api/v1/users/${user2.id}/profile`)
.query({ access_token: owner.token })
.send({});
@@ -344,7 +344,7 @@ describe('Users API', function () {
it('change email succeeds', async function () {
user2.email = 'NewEmail@cloudron.local';
const response = await superagent.post(`${serverUrl}/api/v1/users/${user2.id}`)
const response = await superagent.post(`${serverUrl}/api/v1/users/${user2.id}/profile`)
.query({ access_token: owner.token })
.send({ email: user2.email });
@@ -360,7 +360,7 @@ describe('Users API', function () {
});
it('cannot change email to existing one', async function () {
const response = await superagent.post(`${serverUrl}/api/v1/users/${user2.id}`)
const response = await superagent.post(`${serverUrl}/api/v1/users/${user2.id}/profile`)
.query({ access_token: owner.token })
.send({ email: owner.email })
.ok(() => true);
@@ -371,7 +371,7 @@ describe('Users API', function () {
it('can change display name', async function () {
const displayName = 'New name';
const response = await superagent.post(`${serverUrl}/api/v1/users/${user2.id}`)
const response = await superagent.post(`${serverUrl}/api/v1/users/${user2.id}/profile`)
.query({ access_token: owner.token })
.send({ displayName: displayName });
@@ -416,9 +416,39 @@ describe('Users API', function () {
});
});
describe('active', function () {
it('can make user inactive', async function () {
const response = await superagent.put(`${serverUrl}/api/v1/users/${user.id}/active`)
.query({ access_token: owner.token })
.send({ active: false });
expect(response.statusCode).to.equal(204);
const response2 = await superagent.get(`${serverUrl}/api/v1/users/${user.id}`)
.query({ access_token: owner.token });
expect(response2.statusCode).to.equal(200);
expect(response2.body.active).to.equal(false);
});
it('can make user active', async function () {
const response = await superagent.put(`${serverUrl}/api/v1/users/${user.id}/active`)
.query({ access_token: owner.token })
.send({ active: true });
expect(response.statusCode).to.equal(204);
const response2 = await superagent.get(`${serverUrl}/api/v1/users/${user.id}`)
.query({ access_token: owner.token });
expect(response2.statusCode).to.equal(200);
expect(response2.body.active).to.equal(true);
});
});
describe('role - user manager', function () {
it('can make normal user a usermanager', async function () {
const response = await superagent.post(`${serverUrl}/api/v1/users/${user.id}`)
const response = await superagent.put(`${serverUrl}/api/v1/users/${user.id}/role`)
.query({ access_token: owner.token })
.send({ role: users.ROLE_USER_MANAGER });
@@ -452,7 +482,7 @@ describe('Users API', function () {
});
it('cannot change admin bit of another', async function () {
const response = await superagent.post(`${serverUrl}/api/v1/users/${owner.id}`)
const response = await superagent.put(`${serverUrl}/api/v1/users/${owner.id}/role`)
.query({ access_token: user.token })
.send({ role: users.ROLE_ADMIN })
.ok(() => true);
@@ -461,7 +491,7 @@ describe('Users API', function () {
});
it('cannot change admin bit of self', async function () {
const response = await superagent.post(`${serverUrl}/api/v1/users/${user.id}`)
const response = await superagent.put(`${serverUrl}/api/v1/users/${user.id}/role`)
.query({ access_token: user.token })
.send({ role: users.ROLE_ADMIN })
.ok(() => true);
@@ -506,7 +536,7 @@ describe('Users API', function () {
describe('role - mail manager', function () {
it('can make normal user a usermanager', async function () {
const response = await superagent.post(`${serverUrl}/api/v1/users/${user.id}`)
const response = await superagent.put(`${serverUrl}/api/v1/users/${user.id}/role`)
.query({ access_token: owner.token })
.send({ role: users.ROLE_MAIL_MANAGER });
@@ -523,7 +553,7 @@ describe('Users API', function () {
});
it('cannot change admin bit of self', async function () {
const response = await superagent.post(`${serverUrl}/api/v1/users/${user.id}`)
const response = await superagent.put(`${serverUrl}/api/v1/users/${user.id}/role`)
.query({ access_token: user.token })
.send({ role: users.ROLE_ADMIN })
.ok(() => true);
+43 -17
View File
@@ -2,10 +2,14 @@
exports = module.exports = {
get,
update,
list,
add,
del,
setRole,
setActive,
updateProfile,
setPassword,
verifyPassword,
setGroups,
@@ -32,7 +36,8 @@ const assert = require('assert'),
HttpError = require('connect-lastmile').HttpError,
HttpSuccess = require('connect-lastmile').HttpSuccess,
safe = require('safetydance'),
users = require('../users.js');
users = require('../users.js'),
_ = require('underscore');
async function load(req, res, next) {
assert.strictEqual(typeof req.params.userId, 'string');
@@ -70,7 +75,40 @@ async function add(req, res, next) {
next(new HttpSuccess(201, { id }));
}
async function update(req, res, next) {
async function setRole(req, res, next) {
assert.strictEqual(typeof req.resource, 'object');
assert.strictEqual(typeof req.user, 'object');
assert.strictEqual(typeof req.body, 'object');
if (typeof req.body.role !== 'string') return next(new HttpError(400, 'role must be a string'));
if (req.user.id === req.resource.id) return next(new HttpError(409, 'Cannot set role flag on self'));
if (users.compareRoles(req.user.role, req.body.role) < 0) return next(new HttpError(403, `role '${req.body.role}' is required but you are only '${req.user.role}'`));
if (users.compareRoles(req.user.role, req.resource.role) < 0) return next(new HttpError(403, `role '${req.resource.role}' is required but you are only '${req.user.role}'`));
const [error] = await safe(users.update(req.resource, { role: req.body.role }, AuditSource.fromRequest(req)));
if (error) return next(BoxError.toHttpError(error));
next(new HttpSuccess(204));
}
async function setActive(req, res, next) {
assert.strictEqual(typeof req.resource, 'object');
assert.strictEqual(typeof req.user, 'object');
assert.strictEqual(typeof req.body, 'object');
if (typeof req.body.active !== 'boolean') return next(new HttpError(400, 'active must be a boolean'));
if (req.user.id === req.resource.id) return next(new HttpError(409, 'Cannot set active flag on self'));
if (users.compareRoles(req.user.role, req.resource.role) < 0) return next(new HttpError(403, `role '${req.resource.role}' is required but you are only '${req.user.role}'`));
const [error] = await safe(users.update(req.resource, { active: req.body.active }, AuditSource.fromRequest(req)));
if (error) return next(BoxError.toHttpError(error));
next(new HttpSuccess(204));
}
async function updateProfile(req, res, next) {
assert.strictEqual(typeof req.resource, 'object');
assert.strictEqual(typeof req.user, 'object');
assert.strictEqual(typeof req.body, 'object');
@@ -79,23 +117,11 @@ async function update(req, res, next) {
if ('email' in req.body && typeof req.body.email !== 'string') return next(new HttpError(400, 'email must be string'));
if ('fallbackEmail' in req.body && typeof req.body.fallbackEmail !== 'string') return next(new HttpError(400, 'fallbackEmail must be string'));
if ('displayName' in req.body && typeof req.body.displayName !== 'string') return next(new HttpError(400, 'displayName must be string'));
if ('username' in req.body && typeof req.body.username !== 'string') return next(new HttpError(400, 'username must be a string'));
if ('role' in req.body) {
if (typeof req.body.role !== 'string') return next(new HttpError(400, 'role must be a string'));
if (req.user.id === req.resource.id) return next(new HttpError(409, 'Cannot set role flag on self'));
if (users.compareRoles(req.user.role, req.body.role) < 0) return next(new HttpError(403, `role '${req.body.role}' is required but you are only '${req.user.role}'`));
}
if ('active' in req.body) {
if (typeof req.body.active !== 'boolean') return next(new HttpError(400, 'active must be a boolean'));
if (req.user.id === req.resource.id) return next(new HttpError(409, 'Cannot set active flag on self'));
}
if (users.compareRoles(req.user.role, req.resource.role) < 0) return next(new HttpError(403, `role '${req.resource.role}' is required but you are only '${req.user.role}'`));
const [error] = await safe(users.update(req.resource, req.body, AuditSource.fromRequest(req)));
const data = _.pick(req.body, 'username', 'email', 'fallbackEmail', 'displayName');
const [error] = await safe(users.updateProfile(req.resource, data, AuditSource.fromRequest(req)));
if (error) return next(BoxError.toHttpError(error));
next(new HttpSuccess(204));