From cce55cf3d83edf1e7b380c6b564bfb1594c8ffaa Mon Sep 17 00:00:00 2001 From: Andrew Rumble Date: Wed, 6 Aug 2025 17:00:30 +0100 Subject: [PATCH] Add authorization helper for admin capabilities GitOrigin-RevId: fbf28c89500481e379db6c49512876d867478eb7 --- .../UserMembershipAuthorization.js | 13 ++++ .../UserMembershipAuthorizationTests.js | 67 +++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 services/web/test/unit/src/UserMembership/UserMembershipAuthorizationTests.js diff --git a/services/web/app/src/Features/UserMembership/UserMembershipAuthorization.js b/services/web/app/src/Features/UserMembership/UserMembershipAuthorization.js index a7f25c10a5..866e74763e 100644 --- a/services/web/app/src/Features/UserMembership/UserMembershipAuthorization.js +++ b/services/web/app/src/Features/UserMembership/UserMembershipAuthorization.js @@ -1,3 +1,4 @@ +const { hasAdminAccess } = require('../Helpers/AdminAuthorizationHelper') const UserMembershipAuthorization = { hasStaffAccess(requiredStaffAccess) { return req => { @@ -12,6 +13,18 @@ const UserMembershipAuthorization = { } }, + hasAdminCapability(capability) { + return req => { + if (!hasAdminAccess(req.user)) { + return false + } + if (!req.adminCapabilitiesAvailable) { + return true + } + return req.adminCapabilities?.includes(capability) + } + }, + hasEntityAccess() { return req => { if (!req.entity) { diff --git a/services/web/test/unit/src/UserMembership/UserMembershipAuthorizationTests.js b/services/web/test/unit/src/UserMembership/UserMembershipAuthorizationTests.js new file mode 100644 index 0000000000..f95ec183c6 --- /dev/null +++ b/services/web/test/unit/src/UserMembership/UserMembershipAuthorizationTests.js @@ -0,0 +1,67 @@ +const { expect } = require('chai') +const sinon = require('sinon') +const SandboxedModule = require('sandboxed-module') + +const modulePath = + '../../../../app/src/Features/UserMembership/UserMembershipAuthorization' + +describe('UserMembershipAuthorization', function () { + let hasAdminAccess, UserMembershipAuthorization + beforeEach(function () { + hasAdminAccess = sinon.stub().returns(true) + UserMembershipAuthorization = SandboxedModule.require(modulePath, { + requires: { + '../Helpers/AdminAuthorizationHelper': { + hasAdminAccess, + }, + }, + }) + }) + describe('hasAdminCapability', function () { + describe('when user is not an admin', function () { + it('returns false', function () { + hasAdminAccess.returns(false) + const req = { user: {} } + expect( + UserMembershipAuthorization.hasAdminCapability('capability')(req) + ).to.be.false + }) + }) + describe('when user is an admin', function () { + describe('when adminCapabilitiesAvailable is falsey', function () { + it('returns true', function () { + const req = { user: {}, adminCapabilitiesAvailable: false } + expect( + UserMembershipAuthorization.hasAdminCapability('capability')(req) + ).to.be.true + }) + }) + describe('when adminCapabilitiesAvailable is true', function () { + describe('when user has the requested capability', function () { + it('returns true', function () { + const req = { + user: {}, + adminCapabilitiesAvailable: true, + adminCapabilities: ['capability'], + } + expect( + UserMembershipAuthorization.hasAdminCapability('capability')(req) + ).to.be.true + }) + }) + describe('when user does not have the requested capability', function () { + it('returns false', function () { + const req = { + user: {}, + adminCapabilitiesAvailable: true, + adminCapabilities: ['other-capability'], + } + expect( + UserMembershipAuthorization.hasAdminCapability('capability')(req) + ).to.be.false + }) + }) + }) + }) + }) +})