From 493552d00292ddf79d9cc26772b6c0f7597432ea Mon Sep 17 00:00:00 2001 From: Rik Smale <13023439+WikiRik@users.noreply.github.com> Date: Thu, 9 Dec 2021 15:49:01 +0100 Subject: [PATCH] fix(superadmin): add endpoint for superadmin (#400) * fix(superadmin): add endpoint for superadmin * fix(superadmin): remove local and add tests * fix(tests): fix tests Co-authored-by: WikiRik --- lib/server.js | 1 + middlewares/members.js | 12 +++ models/User.js | 5 +- scripts/seed.js | 6 ++ test/api/users-superadmin.test.js | 137 ++++++++++++++++++++++++++++++ 5 files changed, 160 insertions(+), 1 deletion(-) create mode 100644 test/api/users-superadmin.test.js diff --git a/lib/server.js b/lib/server.js index 85fef85c..3238d7a8 100644 --- a/lib/server.js +++ b/lib/server.js @@ -93,6 +93,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('/superadmin', members.setUserSuperadmin); MemberRouter.post('/confirm', members.confirmUser); MemberRouter.put('/primary-body', members.setPrimaryBody); MemberRouter.put('/email', members.triggerEmailChange); diff --git a/middlewares/members.js b/middlewares/members.js index aac6c26b..59378500 100644 --- a/middlewares/members.js +++ b/middlewares/members.js @@ -173,6 +173,18 @@ exports.setUserActive = async (req, res) => { }); }; +exports.setUserSuperadmin = async (req, res) => { + if (!req.permissions.hasPermission('global:update_superadmin:member')) { + return errors.makeForbiddenError(res, 'Permission global:update_superadmin:member is required, but not present.'); + } + + await req.currentUser.update({ superadmin: req.body.superadmin }); + return res.json({ + success: true, + data: req.currentUser + }); +}; + exports.confirmUser = async (req, res) => { if (!req.permissions.hasPermission('global:confirm:member')) { return errors.makeForbiddenError(res, 'Permission global:confirm:member is required, but not present.'); diff --git a/models/User.js b/models/User.js index b1d5713f..803c00ac 100644 --- a/models/User.js +++ b/models/User.js @@ -65,7 +65,10 @@ const User = sequelize.define('user', { superadmin: { type: Sequelize.BOOLEAN, allowNull: false, - defaultValue: false + defaultValue: false, + validate: { + isBoolean: { msg: 'Superadmin should be valid.' } + } }, privacy_consent: { type: Sequelize.DATE, diff --git a/scripts/seed.js b/scripts/seed.js index 221f9885..5a5a60f1 100644 --- a/scripts/seed.js +++ b/scripts/seed.js @@ -801,6 +801,12 @@ async function createPermissions() { object: 'member', scope: 'global', description: 'Allows to confirm unconfirmed users manually' + }, + { + action: 'update_superadmin', + object: 'member', + scope: 'global', + description: 'Allows to add or remove superadmin powers to any user in the system' } ], { individualHooks: true, validate: true }); diff --git a/test/api/users-superadmin.test.js b/test/api/users-superadmin.test.js new file mode 100644 index 00000000..cb60dcd6 --- /dev/null +++ b/test/api/users-superadmin.test.js @@ -0,0 +1,137 @@ +const { startServer, stopServer } = require('../../lib/server'); +const { request } = require('../scripts/helpers'); +const generator = require('../scripts/generator'); + +describe('User superadmin', () => { + 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({ superadmin: true }); + const token = await generator.createAccessToken({}, user); + + await generator.createPermission({ scope: 'global', action: 'update_superadmin', object: 'member' }); + + const res = await request({ + uri: '/members/1337/superadmin', + method: 'PUT', + headers: { 'X-Auth-Token': token.value }, + body: { superadmin: false } + }); + + 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 there are validation errors', async () => { + const user = await generator.createUser({ superadmin: true }); + const token = await generator.createAccessToken({}, user); + + await generator.createPermission({ scope: 'global', action: 'update_superadmin', object: 'member' }); + + const res = await request({ + uri: '/members/' + user.id + '/superadmin', + method: 'PUT', + headers: { 'X-Auth-Token': token.value }, + body: { superadmin: 'aaa' } + }); + + 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('superadmin'); + }); + + test('should succeed if everything is okay', async () => { + const user = await generator.createUser({ superadmin: true }); + const token = await generator.createAccessToken({}, user); + + await generator.createPermission({ scope: 'global', action: 'update_superadmin', object: 'member' }); + + const res = await request({ + uri: '/members/' + user.id + '/superadmin', + method: 'PUT', + headers: { 'X-Auth-Token': token.value }, + body: { superadmin: false } + }); + + expect(res.statusCode).toEqual(200); + expect(res.body.success).toEqual(true); + expect(res.body).not.toHaveProperty('errors'); + expect(res.body).toHaveProperty('data'); + expect(res.body.data.superadmin).toEqual(false); + }); + + test('should fail for yourself without permission', async () => { + const user = await generator.createUser(); + const token = await generator.createAccessToken({}, user); + + const res = await request({ + uri: '/members/' + user.id + '/superadmin', + method: 'PUT', + headers: { 'X-Auth-Token': token.value }, + body: { superadmin: true } + }); + + expect(res.statusCode).toEqual(403); + expect(res.body.success).toEqual(false); + expect(res.body).not.toHaveProperty('data'); + expect(res.body).toHaveProperty('message'); + }); + + test('should not work with local permission', async () => { + const user = await generator.createUser(); + const token = await generator.createAccessToken({}, user); + + const otherUser = await generator.createUser(); + const permission = await generator.createPermission({ scope: 'local', action: 'update_superadmin', 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 + '/superadmin', + method: 'PUT', + headers: { 'X-Auth-Token': token.value }, + body: { superadmin: true } + }); + + 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 for other person without permission', async () => { + const user = await generator.createUser(); + const token = await generator.createAccessToken({}, user); + + const otherUser = await generator.createUser(); + + const res = await request({ + uri: '/members/' + otherUser.id + '/superadmin', + method: 'PUT', + headers: { 'X-Auth-Token': token.value }, + body: { superadmin: true } + }); + + expect(res.statusCode).toEqual(403); + expect(res.body.success).toEqual(false); + expect(res.body).not.toHaveProperty('data'); + expect(res.body).toHaveProperty('message'); + }); +});