migrate permissions and admin flag to user.role

This commit is contained in:
Girish Ramakrishnan
2020-02-21 12:17:06 -08:00
parent a8f1b0241e
commit 0e156b9376
27 changed files with 245 additions and 254 deletions
+4 -6
View File
@@ -105,14 +105,13 @@ function tokenAuth(req, res, next) {
});
}
function authorize(requiredPermission) {
assert.strictEqual(typeof requiredPermission, 'string');
function authorize(requiredRole) {
assert.strictEqual(typeof requiredRole, 'string');
return function (req, res, next) {
assert.strictEqual(typeof req.user, 'object');
var error = accesscontrol.hasPermission(req.user, requiredPermission);
if (error) return next(new HttpError(403, error.message));
if (users.compareRoles(req.user.role, requiredRole) < 0) return next(new HttpError(403, `role '${requiredRole}' is required but user has only '${req.user.role}'`));
next();
};
@@ -129,8 +128,7 @@ function websocketAuth(requiredRole, req, res, next) {
req.user = user;
var e = accesscontrol.hasRole(req.user, requiredRole);
if (e) return next(new HttpError(403, e.message));
if (users.compareRoles(req.user.role, requiredRole) < 0) return next(new HttpError(403, `role '${requiredRole}' is required but user has only '${user.role}'`));
next();
});
+1 -2
View File
@@ -37,9 +37,8 @@ function get(req, res, next) {
fallbackEmail: req.user.fallbackEmail,
displayName: req.user.displayName,
twoFactorAuthenticationEnabled: req.user.twoFactorAuthenticationEnabled,
admin: req.user.admin,
role: req.user.role,
source: req.user.source,
permissions: req.user.permissions,
avatarUrl: fs.existsSync(path.join(paths.PROFILE_ICONS_DIR, req.user.id)) ? `${settings.adminOrigin()}/api/v1/profile/avatar/${req.user.id}` : `https://www.gravatar.com/avatar/${emailHash}.jpg`
}));
}
+2 -2
View File
@@ -58,7 +58,7 @@ function setup(done) {
function (callback) {
superagent.post(SERVER_URL + '/api/v1/users')
.query({ access_token: token })
.send({ username: 'nonadmin', email: 'notadmin@server.test', invite: false })
.send({ username: 'nonadmin', email: 'notadmin@server.test' })
.end(function (err, res) {
expect(res.statusCode).to.equal(201);
@@ -199,7 +199,7 @@ describe('Eventlog API', function () {
.query({ access_token: token, page: 1, per_page: 10, search: EMAIL })
.end(function (error, result) {
expect(result.statusCode).to.equal(200);
expect(result.body.eventlogs.length).to.equal(2);
expect(result.body.eventlogs.length).to.equal(1);
done();
});
+23 -13
View File
@@ -168,7 +168,7 @@ describe('Users API', function () {
expect(res.body.username).to.equal(USERNAME_0.toLowerCase());
expect(res.body.email).to.equal(EMAIL_0.toLowerCase());
expect(res.body.groupIds).to.eql([]);
expect(res.body.admin).to.be(true);
expect(res.body.role).to.be(users.ROLE_OWNER);
done();
});
@@ -209,7 +209,7 @@ describe('Users API', function () {
expect(res.body.username).to.equal(USERNAME_0.toLowerCase());
expect(res.body.email).to.equal(EMAIL_0.toLowerCase());
expect(res.body.groupIds).to.eql([]);
expect(res.body.admin).to.be(true);
expect(res.body.role).to.be(users.ROLE_OWNER);
done();
});
@@ -250,7 +250,7 @@ describe('Users API', function () {
expect(res.body.username).to.equal(USERNAME_0.toLowerCase());
expect(res.body.email).to.equal(EMAIL_0.toLowerCase());
expect(res.body.groupIds).to.eql([]);
expect(res.body.admin).to.be(true);
expect(res.body.role).to.be(users.ROLE_OWNER);
expect(res.body.displayName).to.be.a('string');
expect(res.body.password).to.not.be.ok();
expect(res.body.salt).to.not.be.ok();
@@ -486,7 +486,7 @@ describe('Users API', function () {
it('set second user as admin succeeds', function (done) {
superagent.post(SERVER_URL + '/api/v1/users/' + user_1.id)
.query({ access_token: token })
.send({ admin: true })
.send({ role: users.ROLE_ADMIN })
.end(function (err, res) {
expect(res.statusCode).to.equal(204);
@@ -494,17 +494,27 @@ describe('Users API', function () {
.query({ access_token: token })
.end(function (err, res) {
expect(res.statusCode).to.equal(200);
expect(res.body.admin).to.be(true);
expect(res.body.role).to.be(users.ROLE_ADMIN);
done();
});
});
});
it('remove self as admin fails', function (done) {
it('make self as admin fails', function (done) {
superagent.post(SERVER_URL + '/api/v1/users/' + user_0.id)
.query({ access_token: token })
.send({ admin: false })
.send({ role: users.ROLE_ADMIN })
.end(function (err, res) {
expect(res.statusCode).to.equal(409);
done();
});
});
it('make self as normal user fails', function (done) {
superagent.post(SERVER_URL + '/api/v1/users/' + user_0.id)
.query({ access_token: token })
.send({ role: users.ROLE_USER })
.end(function (err, res) {
expect(res.statusCode).to.equal(409);
done();
@@ -514,7 +524,7 @@ describe('Users API', function () {
it('remove second user as admin succeeds', function (done) {
superagent.post(SERVER_URL + '/api/v1/users/' + user_1.id)
.query({ access_token: token })
.send({ admin: false })
.send({ role: users.ROLE_USER })
.end(function (err, res) {
expect(res.statusCode).to.equal(204);
done();
@@ -797,11 +807,11 @@ describe('Users API', function () {
});
describe('permissions', function () {
describe('role - user manager', function () {
it('can make second user a usermanager', function (done) {
superagent.post(SERVER_URL + '/api/v1/users/' + user_1.id)
.query({ access_token: token })
.send({ permissions: [ 'manage_users' ] })
.send({ role: users.ROLE_USER_MANAGER })
.end(function (err, res) {
expect(res.statusCode).to.equal(204);
done();
@@ -850,7 +860,7 @@ describe('Users API', function () {
it('cannot change admin bit of another', function (done) {
superagent.post(SERVER_URL + '/api/v1/users/' + user_2.id)
.query({ access_token: token_1 })
.send({ admin: true })
.send({ role: users.ROLE_ADMIN })
.end(function (err, result) {
expect(result.statusCode).to.equal(403);
done();
@@ -860,9 +870,9 @@ describe('Users API', function () {
it('cannot change admin bit of self', function (done) {
superagent.post(SERVER_URL + '/api/v1/users/' + user_1.id)
.query({ access_token: token_1 })
.send({ admin: true })
.send({ role: users.ROLE_ADMIN })
.end(function (err, result) {
expect(result.statusCode).to.equal(403);
expect(result.statusCode).to.equal(409);
done();
});
});
+13 -26
View File
@@ -41,14 +41,9 @@ function create(req, res, next) {
if ('username' in req.body && typeof req.body.username !== 'string') return next(new HttpError(400, 'username must be string'));
if ('displayName' in req.body && typeof req.body.displayName !== 'string') return next(new HttpError(400, 'displayName must be string'));
if ('password' in req.body && typeof req.body.password !== 'string') return next(new HttpError(400, 'password must be string'));
if ('admin' in req.body && typeof req.body.admin !== 'boolean') return next(new HttpError(400, 'admin flag must be a boolean'));
if ('permissions' in req.body) {
if (!Array.isArray(req.body.permissions)) return next(new HttpError(400, 'permissions must be an array'));
if (req.body.permissions.some((p) => typeof p !== 'string')) return next(new HttpError(400, 'permissions array must contain strings'));
}
if (!req.user.admin) {
if ('admin' in req.body || 'permissions' in req.body) return next(new HttpError(403, 'Only admin add admins or set permissions'));
if ('role' in req.body) {
if (typeof req.body.role !== 'string') return next(new HttpError(400, 'role must be string'));
if (users.compareRoles(req.user.role, req.body.role) < 0) return next(new HttpError(403, `role '${req.body.role}' is required but user has only '${req.user.role}'`));
}
var password = req.body.password || null;
@@ -56,7 +51,7 @@ function create(req, res, next) {
var username = 'username' in req.body ? req.body.username : null;
var displayName = req.body.displayName || '';
users.create(username, password, email, displayName, { invitor: req.user, admin: req.body.admin, permissions: req.body.permissions }, auditSource.fromRequest(req), function (error, user) {
users.create(username, password, email, displayName, { invitor: req.user, role: req.body.role || users.ROLE_USER }, auditSource.fromRequest(req), function (error, user) {
if (error) return next(BoxError.toHttpError(error));
next(new HttpSuccess(201, users.removePrivateFields(user)));
@@ -73,23 +68,15 @@ function update(req, res, next) {
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 ('admin' in req.body) {
if (typeof req.body.admin !== 'boolean') return next(new HttpError(400, 'admin must be a boolean'));
// this route is only allowed for admins, so req.user has to be an admin
if (req.user.id === req.resource.id && !req.body.admin) return next(new HttpError(409, 'Cannot remove admin flag on self'));
}
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 ('permissions' in req.body) {
if (!Array.isArray(req.body.permissions)) return next(new HttpError(400, 'permissions must be an array'));
if (req.body.permissions.some((p) => typeof p !== 'string')) return next(new HttpError(400, 'permissions array must contain strings'));
if (users.compareRoles(req.user.role, req.body.role) < 0) return next(new HttpError(403, `role '${req.body.role}' is required but user has only '${req.user.role}'`));
}
if ('active' in req.body && typeof req.body.active !== 'boolean') return next(new HttpError(400, 'active must be a boolean'));
if (!req.user.admin) {
if ('admin' in req.body || 'permissions' in req.body) return next(new HttpError(403, 'Only admin add admins or set permissions'));
}
users.update(req.resource, req.body, auditSource.fromRequest(req), function (error) {
if (error) return next(BoxError.toHttpError(error));
@@ -126,7 +113,7 @@ function remove(req, res, next) {
assert.strictEqual(typeof req.resource, 'object');
if (req.user.id === req.resource.id) return next(new HttpError(409, 'Not allowed to remove yourself.'));
if (!req.user.admin && req.resource.admin) return next(new HttpError(403, 'Non-admin cannot remove admin user'));
if (users.compareRoles(req.user.role, req.resource.role) < 0) return next(new HttpError(403, `role '${req.resource.role}' is required but user has only '${req.user.role}'`));
users.remove(req.resource, auditSource.fromRequest(req), function (error) {
if (error) return next(BoxError.toHttpError(error));
@@ -152,7 +139,7 @@ function verifyPassword(req, res, next) {
function createInvite(req, res, next) {
assert.strictEqual(typeof req.resource, 'object');
if (!req.user.admin && req.resource.admin) return next(new HttpError(403, 'Non-admin cannot reset admin user'));
if (users.compareRoles(req.user.role, req.resource.role) < 0) return next(new HttpError(403, `role '${req.resource.role}' is required but user has only '${req.user.role}'`));
users.createInvite(req.resource, function (error, result) {
if (error) return next(BoxError.toHttpError(error));
@@ -164,7 +151,7 @@ function createInvite(req, res, next) {
function sendInvite(req, res, next) {
assert.strictEqual(typeof req.resource, 'object');
if (!req.user.admin && req.resource.admin) return next(new HttpError(403, 'Non-admin cannot invite admin user'));
if (users.compareRoles(req.user.role, req.resource.role) < 0) return next(new HttpError(403, `role '${req.resource.role}' is required but user has only '${req.user.role}'`));
users.sendInvite(req.resource, { invitor: req.user }, function (error) {
if (error) return next(BoxError.toHttpError(error));
@@ -178,7 +165,7 @@ function setGroups(req, res, next) {
assert.strictEqual(typeof req.resource, 'object');
if (!Array.isArray(req.body.groupIds)) return next(new HttpError(400, 'API call requires a groups array.'));
if (!req.user.admin && req.resource.admin) return next(new HttpError(403, 'Non-admin cannot modify admin user'));
if (users.compareRoles(req.user.role, req.resource.role) < 0) return next(new HttpError(403, `role '${req.resource.role}' is required but user has only '${req.user.role}'`));
users.setMembership(req.resource, req.body.groupIds, function (error) {
if (error) return next(BoxError.toHttpError(error));
@@ -192,7 +179,7 @@ function changePassword(req, res, next) {
assert.strictEqual(typeof req.resource, 'object');
if (typeof req.body.password !== 'string') return next(new HttpError(400, 'password must be a string'));
if (!req.user.admin && req.resource.admin) return next(new HttpError(403, 'Non-admin cannot modify admin user'));
if (users.compareRoles(req.user.role, req.resource.role) < 0) return next(new HttpError(403, `role '${req.resource.role}' is required but user has only '${req.user.role}'`));
users.setPassword(req.resource, req.body.password, function (error) {
if (error) return next(BoxError.toHttpError(error));