From dc4cc74fbd28026d63410990f265e41b2072aa18 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 8 Nov 2023 15:10:44 -0500 Subject: [PATCH] refator(socket.io): deprecate socketGroups.getChatGroups in favour of api.admin.listGroups --- public/openapi/write.yaml | 6 ++++-- public/openapi/write/admin/groups.yaml | 22 ++++++++++++++++++++++ public/src/client/chats/create.js | 8 ++++++-- public/src/client/chats/manage.js | 7 ++++++- src/api/admin.js | 9 +++++++++ src/controllers/write/admin.js | 4 ++++ src/groups/index.js | 14 ++++++++++++-- src/routes/write/admin.js | 2 ++ src/socket.io/groups.js | 12 ++++++++---- 9 files changed, 73 insertions(+), 11 deletions(-) create mode 100644 public/openapi/write/admin/groups.yaml diff --git a/public/openapi/write.yaml b/public/openapi/write.yaml index 70462881aa..91a736cd0f 100644 --- a/public/openapi/write.yaml +++ b/public/openapi/write.yaml @@ -218,14 +218,16 @@ paths: $ref: 'write/admin/analytics.yaml' /admin/analytics/{set}: $ref: 'write/admin/analytics/set.yaml' - /admin/chats/{roomId}: - $ref: 'write/admin/chats/roomId.yaml' /admin/tokens: $ref: 'write/admin/tokens.yaml' /admin/tokens/{token}: $ref: 'write/admin/tokens/token.yaml' /admin/tokens/{token}/roll: $ref: 'write/admin/tokens/token/roll.yaml' + /admin/chats/{roomId}: + $ref: 'write/admin/chats/roomId.yaml' + /admin/groups: + $ref: 'write/admin/groups.yaml' /files/: $ref: 'write/files.yaml' /files/folder: diff --git a/public/openapi/write/admin/groups.yaml b/public/openapi/write/admin/groups.yaml new file mode 100644 index 0000000000..fa2e3a2474 --- /dev/null +++ b/public/openapi/write/admin/groups.yaml @@ -0,0 +1,22 @@ +get: + tags: + - admin + summary: list all groups + description: This operation returns a full list of user groups, including hidden groups. + responses: + '200': + description: user groups successfully listed + content: + application/json: + schema: + type: object + properties: + status: + $ref: ../../components/schemas/Status.yaml#/Status + response: + type: object + properties: + groups: + type: array + items: + $ref: ../../components/schemas/GroupObject.yaml#/GroupDataObject \ No newline at end of file diff --git a/public/src/client/chats/create.js b/public/src/client/chats/create.js index f9e48c2f13..ce70fa8bab 100644 --- a/public/src/client/chats/create.js +++ b/public/src/client/chats/create.js @@ -12,11 +12,15 @@ define('forum/chats/create', [ async function handleCreate() { let groups = []; if (app.user.isAdmin) { - groups = await socket.emit('groups.getChatGroups', {}); + ({ groups } = await api.get('/admin/groups')); + groups.sort((a, b) => b.system - a.system).map((g) => { + const { name, displayName } = g; + return { name, displayName }; + }); } const html = await app.parseAndTranslate('modals/create-room', { user: app.user, - groups: groups, + groups, }); const modal = bootbox.dialog({ diff --git a/public/src/client/chats/manage.js b/public/src/client/chats/manage.js index 73f33c53d3..a69cafc4f4 100644 --- a/public/src/client/chats/manage.js +++ b/public/src/client/chats/manage.js @@ -12,7 +12,12 @@ define('forum/chats/manage', [ buttonEl.on('click', async function () { let groups = []; if (app.user.isAdmin) { - groups = await socket.emit('groups.getChatGroups', {}); + ({ groups } = await api.get('/admin/groups')); + groups.sort((a, b) => b.system - a.system).map((g) => { + const { name, displayName } = g; + return { name, displayName }; + }); + if (Array.isArray(ajaxify.data.groups)) { groups.forEach((g) => { g.selected = ajaxify.data.groups.includes(g.name); diff --git a/src/api/admin.js b/src/api/admin.js index e6b6994597..1b52ccd1ba 100644 --- a/src/api/admin.js +++ b/src/api/admin.js @@ -3,6 +3,7 @@ const meta = require('../meta'); const analytics = require('../analytics'); const privileges = require('../privileges'); +const groups = require('../groups'); const adminApi = module.exports; @@ -34,3 +35,11 @@ adminApi.getAnalyticsData = async (caller, { set, until, amount, units }) => { const getStats = units === 'days' ? analytics.getDailyStatsForSet : analytics.getHourlyStatsForSet; return await getStats(`analytics:${set}`, parseInt(until, 10) || Date.now(), amount); }; + +adminApi.listGroups = async () => { + // N.B. Returns all groups, even hidden. Beware of leakage. + // Access control handled at controller level + + const payload = await groups.getNonPrivilegeGroups('groups:createtime', 0, -1, { ephemeral: false }); + return { groups: payload }; +}; diff --git a/src/controllers/write/admin.js b/src/controllers/write/admin.js index 0e5669c2e6..c4c8e29c8c 100644 --- a/src/controllers/write/admin.js +++ b/src/controllers/write/admin.js @@ -78,3 +78,7 @@ Admin.chats.deleteRoom = async (req, res) => { }); helpers.formatApiResponse(200, res); }; + +Admin.listGroups = async (req, res) => { + helpers.formatApiResponse(200, res, await api.admin.listGroups()); +}; diff --git a/src/groups/index.js b/src/groups/index.js index d9549ba805..ec92f05fb1 100644 --- a/src/groups/index.js +++ b/src/groups/index.js @@ -84,9 +84,19 @@ Groups.getGroupsBySort = async function (sort, start, stop) { return await Groups.getGroupsFromSet(set, start, stop); }; -Groups.getNonPrivilegeGroups = async function (set, start, stop) { +Groups.getNonPrivilegeGroups = async function (set, start, stop, flags) { + if (!flags) { + flags = { + ephemeral: true, + }; + } + let groupNames = await db.getSortedSetRevRange(set, start, stop); - groupNames = groupNames.concat(Groups.ephemeralGroups).filter(groupName => !Groups.isPrivilegeGroup(groupName)); + groupNames = groupNames.filter(groupName => !Groups.isPrivilegeGroup(groupName)); + if (flags.ephemeral) { + groupNames = groupNames.concat(Groups.ephemeralGroups); + } + const groupsData = await Groups.getGroupsData(groupNames); return groupsData.filter(Boolean); }; diff --git a/src/routes/write/admin.js b/src/routes/write/admin.js index 8aaf27fc5d..4a70e48022 100644 --- a/src/routes/write/admin.js +++ b/src/routes/write/admin.js @@ -23,5 +23,7 @@ module.exports = function () { setupApiRoute(router, 'delete', '/chats/:roomId', [...middlewares, middleware.assert.room], controllers.write.admin.chats.deleteRoom); + setupApiRoute(router, 'get', '/groups', [...middlewares], controllers.write.admin.listGroups); + return router; }; diff --git a/src/socket.io/groups.js b/src/socket.io/groups.js index 760c6e7e14..bf07173e0b 100644 --- a/src/socket.io/groups.js +++ b/src/socket.io/groups.js @@ -65,14 +65,18 @@ SocketGroups.loadMoreMembers = async (socket, data) => { }; SocketGroups.getChatGroups = async (socket) => { + sockets.warnDeprecated(socket, 'GET /api/v3/admin/groups'); + const isAdmin = await user.isAdministrator(socket.uid); if (!isAdmin) { throw new Error('[[error:no-privileges]]'); } - const allGroups = await groups.getNonPrivilegeGroups('groups:createtime', 0, -1); - const groupsList = allGroups.filter(g => !groups.ephemeralGroups.includes(g.name)); - groupsList.sort((a, b) => b.system - a.system); - return groupsList.map(g => ({ name: g.name, displayName: g.displayName })); + + const { groups } = await api.admin.listGroups(socket); + + // Float system groups to top and return only name/displayName + groups.sort((a, b) => b.system - a.system); + return groups.map(g => ({ name: g.name, displayName: g.displayName })); }; SocketGroups.cover = {};