Merge pull request #14106 from overleaf/ab-ab-group-settings-admin-only

[web] Restrict group settings page and managed users activation to group admin

GitOrigin-RevId: 97235d3e78d97d9c367ce7de70072607f15d98f0
This commit is contained in:
Alexandre Bourdin 2023-08-03 12:06:04 +02:00 committed by Copybot
parent d981ac2279
commit d2d2386441
4 changed files with 46 additions and 62 deletions

View file

@ -138,8 +138,8 @@ async function plansPage(req, res) {
} }
/** /**
* @param {import("express").Request} req * @param {import('express').Request} req
* @param {import("express").Response} res * @param {import('express').Response} res
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
async function paymentPage(req, res) { async function paymentPage(req, res) {
@ -208,8 +208,8 @@ function formatGroupPlansDataForDash() {
} }
/** /**
* @param {import("express").Request} req * @param {import('express').Request} req
* @param {import("express").Response} res * @param {import('express').Response} res
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
async function userSubscriptionPage(req, res) { async function userSubscriptionPage(req, res) {
@ -246,9 +246,13 @@ async function userSubscriptionPage(req, res) {
const groupPlansDataForDash = formatGroupPlansDataForDash() const groupPlansDataForDash = formatGroupPlansDataForDash()
// display the Group Settings button only to admins of group subscriptions with the Managed Users feature available
const groupSettingsEnabledFor = (managedGroupSubscriptions || []) const groupSettingsEnabledFor = (managedGroupSubscriptions || [])
.filter(subscription => .filter(
ManagedUsersManager.hasManagedUsersFeature(subscription) subscription =>
ManagedUsersManager.hasManagedUsersFeature(subscription) &&
(subscription.admin_id._id || subscription.admin_id).toString() ===
user._id.toString()
) )
.map(subscription => subscription._id.toString()) .map(subscription => subscription._id.toString())
@ -399,8 +403,8 @@ async function createSubscription(req, res) {
} }
/** /**
* @param {import("express").Request} req * @param {import('express').Request} req
* @param {import("express").Response} res * @param {import('express').Response} res
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
async function successfulSubscription(req, res) { async function successfulSubscription(req, res) {
@ -440,9 +444,9 @@ function cancelSubscription(req, res, next) {
} }
/** /**
* @param {import("express").Request} req * @param {import('express').Request} req
* @param {import("express").Response} res * @param {import('express').Response} res
* @param {import("express").NextFunction} next * @param {import('express').NextFunction} next
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
function canceledSubscription(req, res, next) { function canceledSubscription(req, res, next) {

View file

@ -17,19 +17,12 @@ const UserMembershipAuthorization = {
if (!req.entity) { if (!req.entity) {
return false return false
} }
return req.entity[req.entityConfig.fields.access].some(accessUserId => const fieldAccess = req.entity[req.entityConfig.fields.access]
accessUserId.equals(req.user._id) const fieldAccessArray = Array.isArray(fieldAccess)
) ? fieldAccess
} : [fieldAccess.toString()]
}, return fieldAccessArray.some(
accessUserId => accessUserId.toString() === req.user._id.toString()
isEntityMember() {
return req => {
if (!req.entity) {
return false
}
return req.entity[req.entityConfig.fields.membership].some(accessUserId =>
accessUserId.equals(req.user._id)
) )
} }
}, },

View file

@ -14,19 +14,6 @@ module.exports = {
baseQuery: { baseQuery: {
groupPlan: true, groupPlan: true,
}, },
translations: {
title: 'group_subscription',
subtitle: 'members_management',
remove: 'remove_from_group',
},
pathsFor(id) {
return {
addMember: `/manage/groups/${id}/invites`,
removeMember: `/manage/groups/${id}/user`,
removeInvite: `/manage/groups/${id}/invites`,
exportMembers: `/manage/groups/${id}/members/export`,
}
},
}, },
team: { team: {
@ -54,16 +41,20 @@ module.exports = {
baseQuery: { baseQuery: {
groupPlan: true, groupPlan: true,
}, },
translations: { },
title: 'group_subscription',
subtitle: 'managers_management', groupAdmin: {
remove: 'remove_manager', modelName: 'Subscription',
fields: {
primaryKey: '_id',
read: ['admin_id'],
write: 'admin_id',
access: 'admin_id',
membership: 'admin_id',
name: 'teamName',
}, },
pathsFor(id) { baseQuery: {
return { groupPlan: true,
addMember: `/manage/groups/${id}/managers`,
removeMember: `/manage/groups/${id}/managers`,
}
}, },
}, },
@ -77,16 +68,9 @@ module.exports = {
membership: 'member_ids', membership: 'member_ids',
name: 'name', name: 'name',
}, },
translations: {
title: 'institution_account',
subtitle: 'managers_management',
remove: 'remove_manager',
},
pathsFor(id) { pathsFor(id) {
return { return {
index: `/manage/institutions/${id}/managers`, index: `/manage/institutions/${id}/managers`,
addMember: `/manage/institutions/${id}/managers`,
removeMember: `/manage/institutions/${id}/managers`,
} }
}, },
}, },
@ -101,16 +85,9 @@ module.exports = {
membership: 'member_ids', membership: 'member_ids',
name: 'name', name: 'name',
}, },
translations: {
title: 'publisher_account',
subtitle: 'managers_management',
remove: 'remove_manager',
},
pathsFor(id) { pathsFor(id) {
return { return {
index: `/manage/publishers/${id}/managers`, index: `/manage/publishers/${id}/managers`,
addMember: `/manage/publishers/${id}/managers`,
removeMember: `/manage/publishers/${id}/managers`,
} }
}, },
}, },

View file

@ -64,6 +64,17 @@ const UserMembershipMiddleware = {
]), ]),
], ],
requireGroupAdminAccess: [
AuthenticationController.requireLogin(),
fetchEntityConfig('groupAdmin'),
fetchEntity(),
requireEntity(),
allowAccessIfAny([
UserMembershipAuthorization.hasEntityAccess(),
UserMembershipAuthorization.hasStaffAccess('groupManagement'),
]),
],
requireInstitutionMetricsAccess: [ requireInstitutionMetricsAccess: [
AuthenticationController.requireLogin(), AuthenticationController.requireLogin(),
fetchEntityConfig('institution'), fetchEntityConfig('institution'),
@ -222,12 +233,11 @@ function fetchEntityConfig(entityName) {
// fetch the entity with id and config, and set it in the request // fetch the entity with id and config, and set it in the request
function fetchEntity() { function fetchEntity() {
return expressify(async (req, res, next) => { return expressify(async (req, res, next) => {
const entity = req.entity =
await UserMembershipHandler.promises.getEntityWithoutAuthorizationCheck( await UserMembershipHandler.promises.getEntityWithoutAuthorizationCheck(
req.params.id, req.params.id,
req.entityConfig req.entityConfig
) )
req.entity = entity
next() next()
}) })
} }