diff --git a/src/controllers/admin/groups.js b/src/controllers/admin/groups.js index a1a7c6fd1c..a43afa9c17 100644 --- a/src/controllers/admin/groups.js +++ b/src/controllers/admin/groups.js @@ -34,6 +34,9 @@ groupsController.list = async function (req, res) { groupsController.get = async function (req, res, next) { const slug = slugify(req.params.name); const groupName = await groups.getGroupNameByGroupSlug(slug); + if (!groupName) { + return next(); + } const [groupNames, group] = await Promise.all([ getGroupNames(), groups.get(groupName, { uid: req.uid, truncateUserList: true, userListCount: 20 }), diff --git a/src/groups/create.js b/src/groups/create.js index 74ef56a41f..5172038052 100644 --- a/src/groups/create.js +++ b/src/groups/create.js @@ -18,8 +18,11 @@ module.exports = function (Groups) { Groups.validateGroupName(data.name); - const exists = await meta.userOrGroupExists(data.name); - if (exists) { + const [exists, privGroupExists] = await Promise.all([ + meta.userOrGroupExists(data.name), + privilegeGroupExists(data.name), + ]); + if (exists || privGroupExists) { throw new Error('[[error:group-already-exists]]'); } @@ -58,7 +61,9 @@ module.exports = function (Groups) { ]); } - await db.setObjectField('groupslug:groupname', groupData.slug, groupData.name); + if (!Groups.isPrivilegeGroup(groupData.name)) { + await db.setObjectField('groupslug:groupname', groupData.slug, groupData.name); + } groupData = await Groups.getGroupData(groupData.name); plugins.hooks.fire('action:group.create', { group: groupData }); @@ -71,6 +76,10 @@ module.exports = function (Groups) { Groups.isPrivilegeGroup(data.name); } + async function privilegeGroupExists(name) { + return Groups.isPrivilegeGroup(name) && await db.isSortedSetMember('groups:createtime', name); + } + Groups.validateGroupName = function (name) { if (!name) { throw new Error('[[error:group-name-too-short]]'); diff --git a/src/groups/delete.js b/src/groups/delete.js index 1fc8beef02..449640c190 100644 --- a/src/groups/delete.js +++ b/src/groups/delete.js @@ -28,7 +28,9 @@ module.exports = function (Groups) { ); }); const sets = groupNames.map(groupName => `${groupName.toLowerCase()}:${groupName}`); - const fields = groupNames.map(groupName => slugify(groupName)); + const groupSlugs = groupNames + .filter(groupName => !Groups.isPrivilegeGroup(groupName)) + .map(groupName => slugify(groupName)); await Promise.all([ db.deleteAll(keys), @@ -38,7 +40,7 @@ module.exports = function (Groups) { 'groups:visible:memberCount', ], groupNames), db.sortedSetRemove('groups:visible:name', sets), - db.deleteObjectFields('groupslug:groupname', fields), + db.deleteObjectFields('groupslug:groupname', groupSlugs), removeGroupsFromPrivilegeGroups(groupNames), ]); Groups.cache.reset(); diff --git a/src/groups/update.js b/src/groups/update.js index 9da76c9180..804268c587 100644 --- a/src/groups/update.js +++ b/src/groups/update.js @@ -191,8 +191,10 @@ module.exports = function (Groups) { await updateConfig(oldName, newName); await updateChatRooms(oldName, newName); await db.setObject(`group:${oldName}`, { name: newName, slug: slugify(newName) }); - await db.deleteObjectField('groupslug:groupname', group.slug); - await db.setObjectField('groupslug:groupname', slugify(newName), newName); + if (!Groups.isPrivilegeGroup(oldName) && !Groups.isPrivilegeGroup(newName)) { + await db.deleteObjectField('groupslug:groupname', group.slug); + await db.setObjectField('groupslug:groupname', slugify(newName), newName); + } const allGroups = await db.getSortedSetRange('groups:createtime', 0, -1); const keys = allGroups.map(group => `group:${group}:members`); diff --git a/src/upgrades/3.8.0/remove-privilege-slugs.js b/src/upgrades/3.8.0/remove-privilege-slugs.js new file mode 100644 index 0000000000..bfd589f6fc --- /dev/null +++ b/src/upgrades/3.8.0/remove-privilege-slugs.js @@ -0,0 +1,31 @@ +/* eslint-disable no-await-in-loop */ + +'use strict'; + +const db = require('../../database'); +const groups = require('../../groups'); +const batch = require('../../batch'); + +module.exports = { + name: 'Remove privilege groups from groupslug:groupname object', + timestamp: Date.UTC(2024, 3, 8), + method: async function () { + const { progress } = this; + + const slugsToNames = await db.getObject(`groupslug:groupname`); + const privilegeGroups = []; + for (const [slug, name] of Object.entries(slugsToNames)) { + if (groups.isPrivilegeGroup(name)) { + privilegeGroups.push(slug); + } + } + + progress.total = privilegeGroups.length; + await batch.processArray(privilegeGroups, async (slugs) => { + progress.incr(slugs.length); + await db.deleteObjectFields(`groupslug:groupname`, slugs); + }, { + batch: 500, + }); + }, +};