-
Notifications
You must be signed in to change notification settings - Fork 5.5k
Refactoring Microsoft Teams code to reliably check for external users. #14755
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 4 commits
5089915
fb04371
8a1fd96
be50bf2
c190826
344b2fa
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -34,7 +34,8 @@ export default { | |
| label: "Channel", | ||
| description: "Team Channel", | ||
| async options({ | ||
| teamId, prevContext, | ||
| teamId, | ||
| prevContext, | ||
| }) { | ||
| const response = prevContext.nextLink | ||
| ? await this.makeRequest({ | ||
|
|
@@ -58,76 +59,77 @@ export default { | |
| chat: { | ||
| type: "string", | ||
| label: "Chat", | ||
| description: "Team Chat (internal and external contacts)", | ||
| async options({ prevContext }) { | ||
| const response = prevContext.nextLink | ||
| description: "Select a chat (type to search by participant names)", | ||
| async options({ | ||
| prevContext, searchTerm, | ||
| }) { | ||
| let path = "/chats?$expand=members"; | ||
| path += "&$top=20"; | ||
|
|
||
| if (searchTerm) { | ||
| path += `&$search="${searchTerm}"`; | ||
| } | ||
|
|
||
| const response = prevContext?.nextLink | ||
| ? await this.makeRequest({ | ||
| path: prevContext.nextLink, | ||
| }) | ||
| : await this.listChats(); | ||
|
|
||
| const myTenantId = await this.getAuthenticatedUserTenant(); | ||
| const options = []; | ||
| : await this.makeRequest({ | ||
| path, | ||
| }); | ||
|
|
||
| this._userCache = this._userCache || new Map(); | ||
| const options = []; | ||
|
|
||
| for (const chat of response.value) { | ||
| const messages = await this.makeRequest({ | ||
| path: `/chats/${chat.id}/messages?$top=50`, | ||
| }); | ||
|
|
||
| const members = await Promise.all(chat.members.map(async (member) => { | ||
| const cacheKey = `user_${member.userId}`; | ||
| let displayName = member.displayName || this._userCache.get(cacheKey); | ||
| let members = chat.members.map((member) => ({ | ||
| displayName: member.displayName, | ||
| wasNull: !member.displayName, | ||
| userId: member.userId, | ||
| email: member.email, | ||
| })); | ||
|
|
||
| if (!displayName) { | ||
| try { | ||
| if (messages?.value?.length > 0) { | ||
| const userMessage = messages.value.find((msg) => | ||
| msg.from?.user?.id === member.userId); | ||
| if (userMessage?.from?.user?.displayName) { | ||
| displayName = userMessage.from.user.displayName; | ||
| } | ||
| } | ||
| if (members.some((member) => !member.displayName)) { | ||
| try { | ||
| const messages = await this.makeRequest({ | ||
| path: `/chats/${chat.id}/messages?$top=10&$orderby=createdDateTime desc`, | ||
| }); | ||
|
|
||
| if (!displayName) { | ||
| const userDetails = await this.makeRequest({ | ||
| path: `/users/${member.userId}`, | ||
| }); | ||
| displayName = userDetails.displayName; | ||
| const nameMap = new Map(); | ||
| messages.value.forEach((msg) => { | ||
| if (msg.from?.user?.id && msg.from?.user?.displayName) { | ||
| nameMap.set(msg.from.user.id, msg.from.user.displayName); | ||
| } | ||
| }); | ||
|
|
||
| this._userCache.set(cacheKey, displayName); | ||
| } catch (err) { | ||
| if (err.statusCode === 404) { | ||
| displayName = "User Not Found"; | ||
| } else if (err.statusCode === 403) { | ||
| displayName = "Access Denied"; | ||
| } else { | ||
| displayName = "Unknown User"; | ||
| } | ||
| console.error(`Failed to fetch user details for ${member.userId}:`, err); | ||
| } | ||
| members = members.map((member) => ({ | ||
| ...member, | ||
| displayName: member.displayName || nameMap.get(member.userId) || member.email || "Unknown User", | ||
| })); | ||
| } catch (err) { | ||
| console.error(`Failed to fetch messages for chat ${chat.id}:`, err); | ||
| } | ||
| } | ||
|
|
||
| const isExternal = member.tenantId !== myTenantId || !member.tenantId; | ||
| return isExternal | ||
| ? `${displayName} (External)` | ||
| : displayName; | ||
| })); | ||
| const memberNames = members.map((member) => | ||
| member.wasNull | ||
| ? `${member.displayName} (External)` | ||
| : member.displayName); | ||
|
Comment on lines
+114
to
+117
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Codebase verification External user detection logic needs revision The current implementation assumes a user is external when their
🔗 Analysis chainVerify external user detection logic. The external user detection logic assumes that missing display names indicate external users. Let's verify this assumption against Microsoft's documentation. 🏁 Scripts executedThe following scripts were executed for the analysis: Script: #!/bin/bash
# Search for Microsoft documentation about external user identification in Teams
gh api search/code -f q='filename:README.md path:microsoft-graph-docs "external user" Teams displayName'
Length of output: 303 Script: #!/bin/bash
# Let's examine the actual implementation to understand the external user detection logic
rg -B 5 -A 5 "wasNull" --type js --type ts
# Also search for any related tests or documentation
fd -e md -e js -e ts -e mjs | xargs rg -l "external.*user|guest.*user"
Length of output: 1709 Script: #!/bin/bash
# Let's examine the context around this Teams integration
rg -B 10 -A 10 "wasNull.*displayName" components/microsoft_teams/
# Look for any tests or documentation specific to the Teams component
fd . components/microsoft_teams/ -t f -e md -e test.mjs -e spec.mjs | xargs cat
# Check if there's any documentation about the member object structure
ast-grep --pattern 'const members = $_' components/microsoft_teams/
Length of output: 3081 |
||
|
|
||
| options.push({ | ||
| label: members.join(", "), | ||
| label: memberNames.join(", "), | ||
| value: chat.id, | ||
| }); | ||
| } | ||
|
|
||
| return { | ||
| options, | ||
| context: { | ||
| nextLink: response["@odata.nextLink"], | ||
| }, | ||
| }; | ||
| }, | ||
| useQuery: true, | ||
| }, | ||
| channelDisplayName: { | ||
| type: "string", | ||
|
|
@@ -168,7 +170,10 @@ export default { | |
| }); | ||
| }, | ||
| async makeRequest({ | ||
| method, path, params = {}, content, | ||
| method, | ||
| path, | ||
| params = {}, | ||
| content, | ||
| }) { | ||
| const api = this.client().api(path); | ||
|
|
||
|
|
@@ -192,22 +197,6 @@ export default { | |
| : reduction; | ||
| }, api); | ||
| }, | ||
| async getAuthenticatedUserTenant() { | ||
| try { | ||
| const { value } = await this.client() | ||
| .api("/organization") | ||
| .get(); | ||
|
|
||
| if (!value || value.length === 0) { | ||
| throw new Error("No organization found"); | ||
| } | ||
|
|
||
| return value[0].id; | ||
| } catch (error) { | ||
| console.error("Failed to fetch tenant ID:", error); | ||
| throw new Error("Unable to determine tenant ID"); | ||
| } | ||
| }, | ||
| async authenticatedUserId() { | ||
| const { id } = await this.client() | ||
| .api("/me") | ||
|
|
@@ -231,7 +220,8 @@ export default { | |
| }); | ||
| }, | ||
| async createChannel({ | ||
| teamId, content, | ||
| teamId, | ||
| content, | ||
| }) { | ||
| return this.makeRequest({ | ||
| method: "post", | ||
|
|
@@ -240,7 +230,9 @@ export default { | |
| }); | ||
| }, | ||
| async sendChannelMessage({ | ||
| teamId, channelId, content, | ||
| teamId, | ||
| channelId, | ||
| content, | ||
| }) { | ||
| return this.makeRequest({ | ||
| method: "post", | ||
|
|
@@ -249,7 +241,8 @@ export default { | |
| }); | ||
| }, | ||
| async sendChatMessage({ | ||
| chatId, content, | ||
| chatId, | ||
| content, | ||
| }) { | ||
| return this.makeRequest({ | ||
| method: "post", | ||
|
|
@@ -279,7 +272,8 @@ export default { | |
| .get(); | ||
| }, | ||
| async listChannelMessages({ | ||
| teamId, channelId, | ||
| teamId, | ||
| channelId, | ||
| }) { | ||
| return this.makeRequest({ | ||
| path: `/teams/${teamId}/channels/${channelId}/messages/delta`, | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.