From 9c849553d855083dd076a5b1cf7a81b17043a4be Mon Sep 17 00:00:00 2001 From: Gavin Barron Date: Mon, 13 Nov 2023 11:51:49 -0800 Subject: [PATCH] fix: ensure that allChatScopes provides all scopes needed (#2844) allChatScopes provides all scopes that may be need when using chat component. uses the current config for the person card to determine some of the scopes --- packages/mgt-chat/src/index.ts | 2 +- .../chatOperationScopes.tests.ts | 22 ++++++++ .../src/statefulClient/chatOperationScopes.ts | 55 +++++++++++++++++-- .../mgt-chat/src/statefulClient/graph.chat.ts | 14 +---- samples/react-chat/src/App.tsx | 2 +- 5 files changed, 77 insertions(+), 18 deletions(-) create mode 100644 packages/mgt-chat/src/statefulClient/chatOperationScopes.tests.ts diff --git a/packages/mgt-chat/src/index.ts b/packages/mgt-chat/src/index.ts index 4de580118b..b1ab4e0f4c 100644 --- a/packages/mgt-chat/src/index.ts +++ b/packages/mgt-chat/src/index.ts @@ -5,7 +5,7 @@ * ------------------------------------------------------------------------------------------- */ -import { allChatScopes } from './statefulClient/graph.chat'; +import { allChatScopes } from './statefulClient/chatOperationScopes'; import { appSettings } from './statefulClient/GraphNotificationClient'; diff --git a/packages/mgt-chat/src/statefulClient/chatOperationScopes.tests.ts b/packages/mgt-chat/src/statefulClient/chatOperationScopes.tests.ts new file mode 100644 index 0000000000..b4b87302dd --- /dev/null +++ b/packages/mgt-chat/src/statefulClient/chatOperationScopes.tests.ts @@ -0,0 +1,22 @@ +import { expect } from '@open-wc/testing'; +import { allChatScopes } from './chatOperationScopes'; + +describe('chatOperationScopes tests', () => { + it('should have a minimal permission set', () => { + const expectedScopes = [ + 'User.Read', + 'User.ReadBasic.All', + 'User.Read.All', + 'People.Read', + 'People.Read.All', + 'Presence.Read.All', + 'Sites.Read.All', + 'Mail.Read', + 'Mail.ReadBasic', + 'Contacts.Read', + 'Chat.ReadWrite', + 'ChatMember.ReadWrite' + ]; + expect(allChatScopes).to.have.members(expectedScopes); + }); +}); diff --git a/packages/mgt-chat/src/statefulClient/chatOperationScopes.ts b/packages/mgt-chat/src/statefulClient/chatOperationScopes.ts index 865320c75d..d34e234e1d 100644 --- a/packages/mgt-chat/src/statefulClient/chatOperationScopes.ts +++ b/packages/mgt-chat/src/statefulClient/chatOperationScopes.ts @@ -4,18 +4,65 @@ * See License in the project root for license information. * ------------------------------------------------------------------------------------------- */ +import { getMgtPersonCardScopes } from '@microsoft/mgt-components/dist/es6/exports'; + +/** + * The lowest count of scopes required to perform all chat operations + */ +export const minimalChatScopesList = ['ChatMember.ReadWrite', 'Chat.ReadWrite']; /** * Object mapping chat operations to the scopes required to perform them */ export const chatOperationScopes: Record = { - loadChat: ['Chat.ReadBasic', 'Chat.Read', 'Chat.ReadWrite'], - loadChatMessages: ['Chat.Read', 'Chat.ReadWrite'], - loadChatImage: ['Chat.Read', 'Chat.ReadWrite'], - sendChatMessage: ['ChatMessage.Send', 'Chat.ReadWrite'], + loadChat: ['Chat.ReadWrite'], + loadChatMessages: ['Chat.ReadWrite'], + loadChatImage: ['Chat.ReadWrite'], + sendChatMessage: ['Chat.ReadWrite'], updateChatMessage: ['Chat.ReadWrite'], deleteChatMessage: ['Chat.ReadWrite'], removeChatMember: ['ChatMember.ReadWrite'], + addChatMember: ['ChatMember.ReadWrite'], + createChat: ['Chat.ReadWrite'] +}; + +/** + * Object mapping chat operations to the scopes required to perform them + * This should be used when we migrate this code to use scope aware requests + */ +export const chatOperationScopesFullListing: Record = { + loadChat: ['Chat.ReadBasic', 'Chat.Read', 'Chat.ReadWrite'], + loadChatMessages: ['Chat.Read', 'Chat.ReadWrite'], + loadChatImage: ['ChannelMessage.Read.All', 'Chat.Read', 'Chat.ReadWrite', 'Group.Read.All', 'Group.ReadWrite.All'], + sendChatMessage: ['ChatMessage.Send', 'ChatMessage.Send', 'Chat.ReadWrite', 'Group.ReadWrite.All'], + updateChatMessage: ['ChannelMessage.ReadWrite', 'Chat.ReadWrite', 'Group.ReadWrite.All'], + deleteChatMessage: ['ChannelMessage.ReadWrite', 'Chat.ReadWrite'], + removeChatMember: ['ChatMember.ReadWrite'], addChatMember: ['ChatMember.ReadWrite', 'Chat.ReadWrite'], createChat: ['Chat.Create', 'Chat.ReadWrite'] }; + +/** + * The permission set required to use the people picker to search for users + * as defined in docs: https://learn.microsoft.com/en-us/graph/toolkit/components/people-picker#microsoft-graph-permissions + * N.B. presence permissions not currently documented + */ +export const peoplePickerScopes: Record = { + loadPresence: ['Presence.Read.All'], + searchUsers: ['User.ReadBasic.All'], + searchPeople: ['People.Read'] +}; + +/** + * Provides an array of the distinct scopes required for all chat operations + */ +export const allChatScopes = Array.from( + [['User.Read']] + .concat([minimalChatScopesList]) + .concat(Object.values(peoplePickerScopes)) + .concat([getMgtPersonCardScopes()]) + .reduce((acc, scopes) => { + scopes.forEach(s => acc.add(s)); + return acc; + }, new Set()) +); diff --git a/packages/mgt-chat/src/statefulClient/graph.chat.ts b/packages/mgt-chat/src/statefulClient/graph.chat.ts index 645caaec4e..cf28e96334 100644 --- a/packages/mgt-chat/src/statefulClient/graph.chat.ts +++ b/packages/mgt-chat/src/statefulClient/graph.chat.ts @@ -31,16 +31,6 @@ export interface GraphCollection { */ export type MessageCollection = GraphCollection; -/** - * Provides an array of the distinct scopes required for all chat operations - */ -export const allChatScopes = Array.from( - Object.values(chatOperationScopes).reduce((acc, scopes) => { - scopes.forEach(s => acc.add(s)); - return acc; - }, new Set()) -); - /** * Load the specified chat from graph with the members expanded * @@ -188,7 +178,7 @@ export const deleteChatMessage = async (graph: IGraph, chatId: string, messageId export const removeChatMember = async (graph: IGraph, chatId: string, membershipId: string): Promise => { await graph .api(`/chats/${chatId}/members/${membershipId}`) - .middlewareOptions(prepScopes(...chatOperationScopes.deleteChatMessage)) + .middlewareOptions(prepScopes(...chatOperationScopes.removeChatMember)) .delete(); }; @@ -299,7 +289,7 @@ export const createChatThread = async ( const chat = (await graph .api('/chats') - .middlewareOptions(prepScopes(...chatOperationScopes.loadChatImage)) + .middlewareOptions(prepScopes(...chatOperationScopes.createChat)) .post(body)) as Chat; if (!chat?.id) throw new Error('Chat id not returned from create chat thread'); if (chatMessage) { diff --git a/samples/react-chat/src/App.tsx b/samples/react-chat/src/App.tsx index 247e2f790b..f390153e18 100644 --- a/samples/react-chat/src/App.tsx +++ b/samples/react-chat/src/App.tsx @@ -7,7 +7,7 @@ import ChatListTemplate from './components/ChatListTemplate/ChatListTemplate'; const ChatList = memo(({ chatSelected }: { chatSelected: (e: GraphChat) => void }) => { return ( - + );