From e157911aa1f762de5ee3218453822a8e188070ff Mon Sep 17 00:00:00 2001 From: Aubin <60398825+aubin-tchoi@users.noreply.github.com> Date: Fri, 10 Jan 2025 12:30:17 +0100 Subject: [PATCH] enh(Zendesk) - add a check for user being an admin upon update (#9876) * add a check for user being an admin upon update * rename the message code * update the error type * fix the error type caught and add a comment * make the error type connector_oauth_user_missing_rights reusable * update the error message --- connectors/src/api/update_connector.ts | 8 +++++ connectors/src/connectors/interface.ts | 5 +-- connectors/src/connectors/zendesk/index.ts | 22 +++++++++++-- .../src/connectors/zendesk/lib/zendesk_api.ts | 4 +++ .../components/ConnectorPermissionsModal.tsx | 32 +++++++++++-------- types/src/connectors/api.ts | 1 + 6 files changed, 54 insertions(+), 18 deletions(-) diff --git a/connectors/src/api/update_connector.ts b/connectors/src/api/update_connector.ts index b8a0c0b08d31..ad4ed7018f82 100644 --- a/connectors/src/api/update_connector.ts +++ b/connectors/src/api/update_connector.ts @@ -72,6 +72,14 @@ const _postConnectorUpdateAPIHandler = async ( message: updateRes.error.message, }, }); + case "CONNECTOR_OAUTH_USER_MISSING_RIGHTS": + return apiError(req, res, { + status_code: 401, + api_error: { + type: "connector_oauth_user_missing_rights", + message: updateRes.error.message, + }, + }); case "INVALID_CONFIGURATION": return apiError(req, res, { status_code: 400, diff --git a/connectors/src/connectors/interface.ts b/connectors/src/connectors/interface.ts index eed0181d24e1..2f07818a3c8c 100644 --- a/connectors/src/connectors/interface.ts +++ b/connectors/src/connectors/interface.ts @@ -1,11 +1,11 @@ import type { + ConnectorConfiguration, ConnectorPermission, ContentNode, ContentNodesViewType, ModelId, Result, } from "@dust-tt/types"; -import type { ConnectorConfiguration } from "@dust-tt/types"; import type { DataSourceConfig } from "@connectors/types/data_source_config"; @@ -13,7 +13,8 @@ export type CreateConnectorErrorCode = "INVALID_CONFIGURATION"; export type UpdateConnectorErrorCode = | "INVALID_CONFIGURATION" - | "CONNECTOR_OAUTH_TARGET_MISMATCH"; + | "CONNECTOR_OAUTH_TARGET_MISMATCH" + | "CONNECTOR_OAUTH_USER_MISSING_RIGHTS"; export type RetrievePermissionsErrorCode = | "INVALID_PARENT_INTERNAL_ID" diff --git a/connectors/src/connectors/zendesk/index.ts b/connectors/src/connectors/zendesk/index.ts index 9b84e7f421e6..18944c354871 100644 --- a/connectors/src/connectors/zendesk/index.ts +++ b/connectors/src/connectors/zendesk/index.ts @@ -38,7 +38,10 @@ import { forbidSyncZendeskTickets, } from "@connectors/connectors/zendesk/lib/ticket_permissions"; import { getZendeskSubdomainAndAccessToken } from "@connectors/connectors/zendesk/lib/zendesk_access_token"; -import { fetchZendeskCurrentUser } from "@connectors/connectors/zendesk/lib/zendesk_api"; +import { + fetchZendeskCurrentUser, + isUserAdmin, +} from "@connectors/connectors/zendesk/lib/zendesk_api"; import { launchZendeskFullSyncWorkflow, launchZendeskGarbageCollectionWorkflow, @@ -74,7 +77,7 @@ export class ZendeskConnectorManager extends BaseConnectorManager { subdomain, accessToken, }); - if (!zendeskUser.active || zendeskUser.role !== "admin") { + if (!isUserAdmin(zendeskUser)) { throw new ExternalOAuthTokenError( new Error(`Zendesk user is not an admin: connectionId=${connectionId}`) ); @@ -145,7 +148,7 @@ export class ZendeskConnectorManager extends BaseConnectorManager { if (connectionId) { const newConnectionId = connectionId; - const { subdomain: newSubdomain } = + const { accessToken, subdomain: newSubdomain } = await getZendeskSubdomainAndAccessToken(newConnectionId); if (configuration.subdomain !== newSubdomain) { @@ -157,6 +160,19 @@ export class ZendeskConnectorManager extends BaseConnectorManager { ); } + const zendeskUser = await fetchZendeskCurrentUser({ + subdomain: newSubdomain, + accessToken, + }); + if (!isUserAdmin(zendeskUser)) { + return new Err( + new ConnectorManagerError( + "CONNECTOR_OAUTH_USER_MISSING_RIGHTS", + "New authenticated user is not an admin" + ) + ); + } + await connector.update({ connectionId: newConnectionId }); // if the connector was previously paused, unpause it. diff --git a/connectors/src/connectors/zendesk/lib/zendesk_api.ts b/connectors/src/connectors/zendesk/lib/zendesk_api.ts index a5fec80c8233..070067a30499 100644 --- a/connectors/src/connectors/zendesk/lib/zendesk_api.ts +++ b/connectors/src/connectors/zendesk/lib/zendesk_api.ts @@ -459,6 +459,10 @@ export async function fetchZendeskCurrentUser({ return response.user; } +export function isUserAdmin(user: ZendeskFetchedUser): boolean { + return user.active && user.role === "admin"; +} + /** * Fetches a multiple users at once from the Zendesk API. * May run multiple queries, more precisely we need userCount // 100 + 1 API calls. diff --git a/front/components/ConnectorPermissionsModal.tsx b/front/components/ConnectorPermissionsModal.tsx index 7e81c352508f..e75e19edc248 100644 --- a/front/components/ConnectorPermissionsModal.tsx +++ b/front/components/ConnectorPermissionsModal.tsx @@ -1,16 +1,4 @@ import type { NotificationType } from "@dust-tt/sparkle"; -import { - NewDialog, - NewDialogContainer, - NewDialogContent, - NewDialogFooter, - NewDialogHeader, - NewDialogTitle, - NewDialogTrigger, - Spinner, -} from "@dust-tt/sparkle"; -import { SheetContainer, SheetTitle } from "@dust-tt/sparkle"; -import { SheetHeader } from "@dust-tt/sparkle"; import { Avatar, Button, @@ -21,12 +9,23 @@ import { Input, LockIcon, Modal, + NewDialog, + NewDialogContainer, + NewDialogContent, + NewDialogFooter, + NewDialogHeader, + NewDialogTitle, + NewDialogTrigger, Page, Sheet, + SheetContainer, SheetContent, + SheetHeader, + SheetTitle, + Spinner, TrashIcon, + useSendNotification, } from "@dust-tt/sparkle"; -import { useSendNotification } from "@dust-tt/sparkle"; import type { APIError, BaseContentNode, @@ -205,6 +204,13 @@ async function updateConnectorConnectionId( error: CONNECTOR_TYPE_TO_MISMATCH_ERROR[provider as ConnectorProvider], }; } + if (error.type === "connector_oauth_user_missing_rights") { + return { + success: false, + error: + "The authenticated user needs higher permissions from your service provider.", + }; + } return { success: false, error: `Failed to update the permissions of the Data Source: (contact support@dust.tt for assistance)`, diff --git a/types/src/connectors/api.ts b/types/src/connectors/api.ts index a3abd7ab4656..13ef84810ef2 100644 --- a/types/src/connectors/api.ts +++ b/types/src/connectors/api.ts @@ -12,6 +12,7 @@ export type ConnectorsAPIErrorType = | "connector_update_error" | "connector_update_unauthorized" | "connector_oauth_target_mismatch" + | "connector_oauth_user_missing_rights" | "connector_oauth_error" | "connector_authorization_error" | "slack_channel_not_found"