From 1f2554a52941786ba02d6e50e3b687b263d16b36 Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Mon, 11 May 2026 15:31:01 -0400 Subject: [PATCH] Remove MDM and organization membership enforcement --- packages/types/src/vscode-extension-host.ts | 2 - src/__tests__/extension.spec.ts | 6 - src/activate/registerCommands.ts | 12 +- src/core/webview/ClineProvider.ts | 41 -- src/core/webview/webviewMessageHandler.ts | 6 - src/extension.ts | 6 +- src/i18n/locales/ca/common.json | 15 - src/i18n/locales/de/common.json | 10 - src/i18n/locales/en/common.json | 10 - src/i18n/locales/es/common.json | 10 - src/i18n/locales/fr/common.json | 15 - src/i18n/locales/hi/common.json | 15 - src/i18n/locales/id/common.json | 15 - src/i18n/locales/it/common.json | 15 - src/i18n/locales/ja/common.json | 15 - src/i18n/locales/ko/common.json | 15 - src/i18n/locales/nl/common.json | 15 - src/i18n/locales/pl/common.json | 15 - src/i18n/locales/pt-BR/common.json | 15 - src/i18n/locales/ru/common.json | 15 - src/i18n/locales/tr/common.json | 15 - src/i18n/locales/vi/common.json | 15 - src/i18n/locales/zh-CN/common.json | 15 - src/i18n/locales/zh-TW/common.json | 15 - src/services/mdm/MdmService.ts | 202 ---------- src/services/mdm/__tests__/MdmService.spec.ts | 364 ------------------ webview-ui/src/App.tsx | 30 +- .../src/context/ExtensionStateContext.tsx | 1 - 28 files changed, 11 insertions(+), 914 deletions(-) delete mode 100644 src/services/mdm/MdmService.ts delete mode 100644 src/services/mdm/__tests__/MdmService.spec.ts diff --git a/packages/types/src/vscode-extension-host.ts b/packages/types/src/vscode-extension-host.ts index b20539afe49..f006cfba1df 100644 --- a/packages/types/src/vscode-extension-host.ts +++ b/packages/types/src/vscode-extension-host.ts @@ -370,7 +370,6 @@ export type ExtensionState = Pick< lastShownAnnouncementId?: string apiModelId?: string mcpServers?: McpServer[] - mdmCompliant?: boolean taskSyncEnabled: boolean openAiCodexIsAuthenticated?: boolean debug?: boolean @@ -542,7 +541,6 @@ export interface WebviewMessage { | "deleteCommand" | "createCommand" | "insertTextIntoTextarea" - | "showMdmAuthRequiredNotification" | "imageGenerationSettings" | "queueMessage" | "removeQueuedMessage" diff --git a/src/__tests__/extension.spec.ts b/src/__tests__/extension.spec.ts index a13e78c4bde..c6c44b13fc4 100644 --- a/src/__tests__/extension.spec.ts +++ b/src/__tests__/extension.spec.ts @@ -143,12 +143,6 @@ vi.mock("../services/code-index/manager", () => ({ }, })) -vi.mock("../services/mdm/MdmService", () => ({ - MdmService: { - createInstance: vi.fn().mockResolvedValue(null), - }, -})) - vi.mock("../utils/migrateSettings", () => ({ migrateSettings: vi.fn().mockResolvedValue(undefined), })) diff --git a/src/activate/registerCommands.ts b/src/activate/registerCommands.ts index f02ee8309a3..e3056a8206a 100644 --- a/src/activate/registerCommands.ts +++ b/src/activate/registerCommands.ts @@ -12,7 +12,6 @@ import { focusPanel } from "../utils/focusPanel" import { handleNewTask } from "./handleTask" import { CodeIndexManager } from "../services/code-index/manager" import { importSettingsWithFeedback } from "../core/config/importExport" -import { MdmService } from "../services/mdm/MdmService" import { t } from "../i18n" /** @@ -205,16 +204,7 @@ export const openClineInNewTab = async ({ context, outputChannel }: Omit editor.viewColumn || 0)) // Check if there are any visible text editors, otherwise open a new group diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 1106d340050..cc768d595d1 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -74,7 +74,6 @@ import { MarketplaceManager } from "../../services/marketplace" import { ShadowCheckpointService } from "../../services/checkpoints/ShadowCheckpointService" import { CodeIndexManager } from "../../services/code-index/manager" import type { IndexProgressUpdate } from "../../services/code-index/interfaces/manager" -import { MdmService } from "../../services/mdm/MdmService" import { SkillsManager } from "../../services/skills/SkillsManager" import { fileExistsAtPath } from "../../utils/fs" @@ -143,7 +142,6 @@ export class ClineProvider protected mcpHub?: McpHub // Change from private to protected protected skillsManager?: SkillsManager private marketplaceManager: MarketplaceManager - private mdmService?: MdmService private taskCreationCallback: (task: Task) => void private taskEventListeners: WeakMap void>> = new WeakMap() private currentWorkspacePath: string | undefined @@ -178,14 +176,12 @@ export class ClineProvider private readonly outputChannel: vscode.OutputChannel, private readonly renderContext: "sidebar" | "editor" = "sidebar", public readonly contextProxy: ContextProxy, - mdmService?: MdmService, ) { super() this.currentWorkspacePath = getWorkspacePath() ClineProvider.activeInstances.add(this) - this.mdmService = mdmService this.updateGlobalState("codebaseIndexModels", EMBEDDING_MODEL_PROFILES) // Initialize the per-task file-based history store. @@ -1971,12 +1967,6 @@ export class ClineProvider this.clineMessagesSeq++ state.clineMessagesSeq = this.clineMessagesSeq this.postMessageToWebview({ type: "state", state }) - - // Check MDM compliance and send user to account tab if not compliant - // Only redirect if there's an actual MDM policy requiring authentication - if (this.mdmService?.requiresCloudAuth() && !this.checkMdmCompliance()) { - await this.postMessageToWebview({ type: "action", action: "cloudButtonClicked" }) - } } /** @@ -1993,11 +1983,6 @@ export class ClineProvider state.clineMessagesSeq = this.clineMessagesSeq const { taskHistory: _omit, ...rest } = state this.postMessageToWebview({ type: "state", state: rest }) - - // Preserve existing MDM redirect behavior - if (this.mdmService?.requiresCloudAuth() && !this.checkMdmCompliance()) { - await this.postMessageToWebview({ type: "action", action: "cloudButtonClicked" }) - } } /** @@ -2015,11 +2000,6 @@ export class ClineProvider const state = await this.getStateToPostToWebview() const { clineMessages: _omitMessages, taskHistory: _omitHistory, ...rest } = state this.postMessageToWebview({ type: "state", state: rest }) - - // Preserve existing MDM redirect behavior - if (this.mdmService?.requiresCloudAuth() && !this.checkMdmCompliance()) { - await this.postMessageToWebview({ type: "action", action: "cloudButtonClicked" }) - } } /** @@ -2335,9 +2315,6 @@ export class ClineProvider codebaseIndexBedrockProfile: codebaseIndexConfig?.codebaseIndexBedrockProfile, codebaseIndexOpenRouterSpecificProvider: codebaseIndexConfig?.codebaseIndexOpenRouterSpecificProvider, }, - // Only set mdmCompliant if there's an actual MDM policy - // undefined means no MDM policy, true means compliant, false means non-compliant - mdmCompliant: this.mdmService?.requiresCloudAuth() ? this.checkMdmCompliance() : undefined, profileThresholds: profileThresholds ?? {}, cloudApiUrl: getRooCodeApiUrl(), hasOpenedModeSelector: this.getGlobalState("hasOpenedModeSelector") ?? false, @@ -2751,24 +2728,6 @@ export class ClineProvider return this.skillsManager } - /** - * Check if the current state is compliant with MDM policy - * @returns true if compliant or no MDM policy exists, false if MDM policy exists and user is non-compliant - */ - public checkMdmCompliance(): boolean { - if (!this.mdmService) { - return true // No MDM service, allow operation - } - - const compliance = this.mdmService.isCompliant() - - if (!compliance.compliant) { - return false - } - - return true - } - /** * Gets the CodeIndexManager for the current active workspace * @returns CodeIndexManager instance for the current workspace or the default one diff --git a/src/core/webview/webviewMessageHandler.ts b/src/core/webview/webviewMessageHandler.ts index e3b8c1bea88..5de75fc19d2 100644 --- a/src/core/webview/webviewMessageHandler.ts +++ b/src/core/webview/webviewMessageHandler.ts @@ -3229,12 +3229,6 @@ export const webviewMessageHandler = async ( } break } - case "showMdmAuthRequiredNotification": { - // Show notification that organization requires authentication - vscode.window.showWarningMessage(t("common:mdm.info.organization_requires_auth")) - break - } - /** * Chat Message Queue */ diff --git a/src/extension.ts b/src/extension.ts index 19c0d70585a..665fff3ff98 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -35,7 +35,6 @@ import { TerminalRegistry } from "./integrations/terminal/TerminalRegistry" import { openAiCodexOAuthManager } from "./integrations/openai-codex/oauth" import { McpServerManager } from "./services/mcp/McpServerManager" import { CodeIndexManager } from "./services/code-index/manager" -import { MdmService } from "./services/mdm/MdmService" import { migrateSettings } from "./utils/migrateSettings" import { autoImportSettings } from "./utils/autoImportSettings" import { API } from "./extension/api" @@ -146,9 +145,6 @@ export async function activate(context: vscode.ExtensionContext) { // Create logger for cloud services. const cloudLogger = createDualLogger(createOutputChannelLogger(outputChannel)) - // Initialize MDM service - const mdmService = await MdmService.createInstance(cloudLogger) - // Initialize i18n for internationalization support. initializeI18n(context.globalState.get("language") ?? formatLanguage(vscode.env.language)) @@ -192,7 +188,7 @@ export async function activate(context: vscode.ExtensionContext) { } // Initialize the provider *before* the Roo Code Cloud service. - const provider = new ClineProvider(context, outputChannel, "sidebar", contextProxy, mdmService) + const provider = new ClineProvider(context, outputChannel, "sidebar", contextProxy) // Initialize Roo Code Cloud service. const postStateListener = () => ClineProvider.getVisibleInstance()?.postStateToWebviewWithoutClineMessages() diff --git a/src/i18n/locales/ca/common.json b/src/i18n/locales/ca/common.json index 33188fce193..bef4dbea8ef 100644 --- a/src/i18n/locales/ca/common.json +++ b/src/i18n/locales/ca/common.json @@ -220,21 +220,6 @@ "rulesCleanupFailed": "El mode s'ha eliminat correctament, però no s'ha pogut eliminar la carpeta de regles a {{rulesFolderPath}}. És possible que l'hagis d'eliminar manualment." } }, - "mdm": { - "errors": { - "cloud_auth_required": "La teva organització requereix autenticació de Roo Code Cloud. Si us plau, inicia sessió per continuar.", - "organization_mismatch": "Has d'estar autenticat amb el compte de Roo Code Cloud de la teva organització.", - "manual_url_empty": "Si us plau, introdueix una URL de callback vàlida", - "manual_url_no_query": "URL de callback no vàlida: falten paràmetres de consulta", - "manual_url_missing_params": "URL de callback no vàlida: falten paràmetres requerits (code i state)", - "manual_url_auth_failed": "Autenticació manual per URL ha fallat", - "manual_url_auth_error": "Autenticació fallida", - "verification_failed": "No s'ha pogut verificar l'autenticació de l'organització." - }, - "info": { - "organization_requires_auth": "La teva organització requereix autenticació." - } - }, "prompts": { "deleteMode": { "title": "Suprimeix el mode personalitzat", diff --git a/src/i18n/locales/de/common.json b/src/i18n/locales/de/common.json index 861d9da5768..4344fb7d849 100644 --- a/src/i18n/locales/de/common.json +++ b/src/i18n/locales/de/common.json @@ -220,16 +220,6 @@ "rulesCleanupFailed": "Der Modus wurde erfolgreich entfernt, aber der Regelordner unter {{rulesFolderPath}} konnte nicht gelöscht werden. Möglicherweise musst du ihn manuell löschen." } }, - "mdm": { - "errors": { - "cloud_auth_required": "Deine Organisation erfordert eine Roo Code Cloud-Authentifizierung. Bitte melde dich an, um fortzufahren.", - "organization_mismatch": "Du musst mit dem Roo Code Cloud-Konto deiner Organisation authentifiziert sein.", - "verification_failed": "Die Organisationsauthentifizierung konnte nicht verifiziert werden." - }, - "info": { - "organization_requires_auth": "Deine Organisation erfordert eine Authentifizierung." - } - }, "prompts": { "deleteMode": { "title": "Benutzerdefinierten Modus löschen", diff --git a/src/i18n/locales/en/common.json b/src/i18n/locales/en/common.json index d65fe183679..43003afc96f 100644 --- a/src/i18n/locales/en/common.json +++ b/src/i18n/locales/en/common.json @@ -217,16 +217,6 @@ "rulesCleanupFailed": "Mode removed successfully, but failed to delete rules folder at {{rulesFolderPath}}. You may need to delete it manually." } }, - "mdm": { - "errors": { - "cloud_auth_required": "Your organization requires Roo Code Cloud authentication. Please sign in to continue.", - "organization_mismatch": "You must be authenticated with your organization's Roo Code Cloud account.", - "verification_failed": "Unable to verify organization authentication." - }, - "info": { - "organization_requires_auth": "Your organization requires authentication." - } - }, "prompts": { "deleteMode": { "title": "Delete Custom Mode", diff --git a/src/i18n/locales/es/common.json b/src/i18n/locales/es/common.json index 82be83956b0..3f039940bc6 100644 --- a/src/i18n/locales/es/common.json +++ b/src/i18n/locales/es/common.json @@ -220,16 +220,6 @@ "rulesCleanupFailed": "El modo se eliminó correctamente, pero no se pudo eliminar la carpeta de reglas en {{rulesFolderPath}}. Es posible que debas eliminarla manually." } }, - "mdm": { - "errors": { - "cloud_auth_required": "Tu organización requiere autenticación de Roo Code Cloud. Por favor, inicia sesión para continuar.", - "organization_mismatch": "Debes estar autenticado con la cuenta de Roo Code Cloud de tu organización.", - "verification_failed": "No se pudo verificar la autenticación de la organización." - }, - "info": { - "organization_requires_auth": "Tu organización requiere autenticación." - } - }, "prompts": { "deleteMode": { "title": "Eliminar modo personalizado", diff --git a/src/i18n/locales/fr/common.json b/src/i18n/locales/fr/common.json index 6fc05ff94a3..b4e3aae0bc9 100644 --- a/src/i18n/locales/fr/common.json +++ b/src/i18n/locales/fr/common.json @@ -220,21 +220,6 @@ "rulesCleanupFailed": "Le mode a été supprimé avec succès, mais la suppression du dossier de règles à l'adresse {{rulesFolderPath}} a échoué. Vous devrez peut-être le supprimer manuellement." } }, - "mdm": { - "errors": { - "cloud_auth_required": "Votre organisation nécessite une authentification Roo Code Cloud. Veuillez vous connecter pour continuer.", - "organization_mismatch": "Vous devez être authentifié avec le compte Roo Code Cloud de votre organisation.", - "manual_url_empty": "Veuillez entrer une URL de callback valide", - "manual_url_no_query": "URL de callback invalide : paramètres de requête manquants", - "manual_url_missing_params": "URL de callback invalide : paramètres requis manquants (code et state)", - "manual_url_auth_failed": "Authentification par URL manuelle échouée", - "manual_url_auth_error": "Échec de l'authentification", - "verification_failed": "Impossible de vérifier l'authentification de l'organisation." - }, - "info": { - "organization_requires_auth": "Votre organisation nécessite une authentification." - } - }, "prompts": { "deleteMode": { "title": "Supprimer le mode personnalisé", diff --git a/src/i18n/locales/hi/common.json b/src/i18n/locales/hi/common.json index 528ed6d45f5..13c96ef3639 100644 --- a/src/i18n/locales/hi/common.json +++ b/src/i18n/locales/hi/common.json @@ -220,21 +220,6 @@ "rulesCleanupFailed": "मोड सफलतापूर्वक हटा दिया गया, लेकिन {{rulesFolderPath}} पर नियम फ़ोल्डर को हटाने में विफल रहा। आपको इसे मैन्युअल रूप से हटाना पड़ सकता है।" } }, - "mdm": { - "errors": { - "cloud_auth_required": "आपके संगठन को Roo Code Cloud प्रमाणीकरण की आवश्यकता है। कृपया जारी रखने के लिए साइन इन करें।", - "organization_mismatch": "आपको अपने संगठन के Roo Code Cloud खाते से प्रमाणित होना होगा।", - "manual_url_empty": "कृपया एक वैध callback URL दर्ज करें", - "manual_url_no_query": "अवैध callback URL: क्वेरी पैरामीटर गुम हैं", - "manual_url_missing_params": "अवैध callback URL: आवश्यक पैरामीटर गुम हैं (code और state)", - "manual_url_auth_failed": "मैनुअल URL प्रमाणीकरण असफल", - "manual_url_auth_error": "प्रमाणीकरण असफल", - "verification_failed": "संगठन प्रमाणीकरण सत्यापित करने में असमर्थ।" - }, - "info": { - "organization_requires_auth": "आपके संगठन को प्रमाणीकरण की आवश्यकता है।" - } - }, "prompts": { "deleteMode": { "title": "कस्टम मोड हटाएं", diff --git a/src/i18n/locales/id/common.json b/src/i18n/locales/id/common.json index cb1c3231fb8..a7e9f2360d1 100644 --- a/src/i18n/locales/id/common.json +++ b/src/i18n/locales/id/common.json @@ -220,21 +220,6 @@ "rulesCleanupFailed": "Mode berhasil dihapus, tetapi gagal menghapus folder aturan di {{rulesFolderPath}}. Kamu mungkin perlu menghapusnya secara manual." } }, - "mdm": { - "errors": { - "cloud_auth_required": "Organisasi kamu memerlukan autentikasi Roo Code Cloud. Silakan masuk untuk melanjutkan.", - "organization_mismatch": "Kamu harus diautentikasi dengan akun Roo Code Cloud organisasi kamu.", - "manual_url_empty": "Silakan masukkan URL callback yang valid", - "manual_url_no_query": "URL callback tidak valid: parameter query hilang", - "manual_url_missing_params": "URL callback tidak valid: parameter yang diperlukan hilang (code dan state)", - "manual_url_auth_failed": "Autentikasi URL manual gagal", - "manual_url_auth_error": "Autentikasi gagal", - "verification_failed": "Tidak dapat memverifikasi autentikasi organisasi." - }, - "info": { - "organization_requires_auth": "Organisasi kamu memerlukan autentikasi." - } - }, "prompts": { "deleteMode": { "title": "Hapus Mode Kustom", diff --git a/src/i18n/locales/it/common.json b/src/i18n/locales/it/common.json index b4e522cb732..664bfb1857d 100644 --- a/src/i18n/locales/it/common.json +++ b/src/i18n/locales/it/common.json @@ -220,21 +220,6 @@ "rulesCleanupFailed": "La modalità è stata rimossa con successo, ma non è stato possibile eliminare la cartella delle regole in {{rulesFolderPath}}. Potrebbe essere necessario eliminarla manualmente." } }, - "mdm": { - "errors": { - "cloud_auth_required": "La tua organizzazione richiede l'autenticazione Roo Code Cloud. Accedi per continuare.", - "organization_mismatch": "Devi essere autenticato con l'account Roo Code Cloud della tua organizzazione.", - "manual_url_empty": "Inserisci un URL di callback valido", - "manual_url_no_query": "URL di callback non valido: parametri di query mancanti", - "manual_url_missing_params": "URL di callback non valido: parametri richiesti mancanti (code e state)", - "manual_url_auth_failed": "Autenticazione manuale tramite URL fallita", - "manual_url_auth_error": "Autenticazione fallita", - "verification_failed": "Impossibile verificare l'autenticazione dell'organizzazione." - }, - "info": { - "organization_requires_auth": "La tua organizzazione richiede l'autenticazione." - } - }, "prompts": { "deleteMode": { "title": "Elimina Modalità Personalizzata", diff --git a/src/i18n/locales/ja/common.json b/src/i18n/locales/ja/common.json index 7b63b6f7298..056dbad3c89 100644 --- a/src/i18n/locales/ja/common.json +++ b/src/i18n/locales/ja/common.json @@ -220,21 +220,6 @@ "rulesCleanupFailed": "モードは正常に削除されましたが、{{rulesFolderPath}} にあるルールフォルダの削除に失敗しました。手動で削除する必要がある場合があります。" } }, - "mdm": { - "errors": { - "cloud_auth_required": "あなたの組織では Roo Code Cloud 認証が必要です。続行するにはサインインしてください。", - "organization_mismatch": "組織の Roo Code Cloud アカウントで認証する必要があります。", - "manual_url_empty": "有効なコールバック URL を入力してください", - "manual_url_no_query": "無効なコールバック URL:クエリパラメータがありません", - "manual_url_missing_params": "無効なコールバック URL:必要なパラメータ(code と state)がありません", - "manual_url_auth_failed": "手動 URL 認証が失敗しました", - "manual_url_auth_error": "認証に失敗しました", - "verification_failed": "組織認証の確認ができませんでした。" - }, - "info": { - "organization_requires_auth": "あなたの組織では認証が必要です。" - } - }, "prompts": { "deleteMode": { "title": "カスタムモードの削除", diff --git a/src/i18n/locales/ko/common.json b/src/i18n/locales/ko/common.json index fbde3225bb1..aaef6bb7bd1 100644 --- a/src/i18n/locales/ko/common.json +++ b/src/i18n/locales/ko/common.json @@ -220,21 +220,6 @@ "rulesCleanupFailed": "모드가 성공적으로 제거되었지만 {{rulesFolderPath}}의 규칙 폴더를 삭제하지 못했습니다. 수동으로 삭제해야 할 수도 있습니다." } }, - "mdm": { - "errors": { - "cloud_auth_required": "조직에서 Roo Code Cloud 인증이 필요합니다. 계속하려면 로그인하세요.", - "organization_mismatch": "조직의 Roo Code Cloud 계정으로 인증해야 합니다.", - "manual_url_empty": "유효한 콜백 URL을 입력하세요", - "manual_url_no_query": "유효하지 않은 콜백 URL: 쿼리 매개변수 누락", - "manual_url_missing_params": "유효하지 않은 콜백 URL: 필요한 매개변수 누락 (code와 state)", - "manual_url_auth_failed": "수동 URL 인증 실패", - "manual_url_auth_error": "인증 실패", - "verification_failed": "조직 인증을 확인할 수 없습니다." - }, - "info": { - "organization_requires_auth": "조직에서 인증이 필요합니다." - } - }, "prompts": { "deleteMode": { "title": "사용자 정의 모드 삭제", diff --git a/src/i18n/locales/nl/common.json b/src/i18n/locales/nl/common.json index eba274c96ec..dabdd6e0aee 100644 --- a/src/i18n/locales/nl/common.json +++ b/src/i18n/locales/nl/common.json @@ -220,21 +220,6 @@ "rulesCleanupFailed": "Modus succesvol verwijderd, maar het verwijderen van de regelsmap op {{rulesFolderPath}} is mislukt. Je moet deze mogelijk handmatig verwijderen." } }, - "mdm": { - "errors": { - "cloud_auth_required": "Je organisatie vereist Roo Code Cloud-authenticatie. Log in om door te gaan.", - "organization_mismatch": "Je moet geauthenticeerd zijn met het Roo Code Cloud-account van je organisatie.", - "manual_url_empty": "Voer een geldige callback-URL in", - "manual_url_no_query": "Ongeldige callback-URL: query-parameters ontbreken", - "manual_url_missing_params": "Ongeldige callback-URL: vereiste parameters ontbreken (code en state)", - "manual_url_auth_failed": "Handmatige URL-authenticatie mislukt", - "manual_url_auth_error": "Authenticatie mislukt", - "verification_failed": "Kan organisatie-authenticatie niet verifiëren." - }, - "info": { - "organization_requires_auth": "Je organisatie vereist authenticatie." - } - }, "prompts": { "deleteMode": { "title": "Aangepaste modus verwijderen", diff --git a/src/i18n/locales/pl/common.json b/src/i18n/locales/pl/common.json index 20b568281bb..ac2cfddc03c 100644 --- a/src/i18n/locales/pl/common.json +++ b/src/i18n/locales/pl/common.json @@ -220,21 +220,6 @@ "rulesCleanupFailed": "Tryb został pomyślnie usunięty, ale nie udało się usunąć folderu reguł w {{rulesFolderPath}}. Może być konieczne ręczne usunięcie." } }, - "mdm": { - "errors": { - "cloud_auth_required": "Twoja organizacja wymaga uwierzytelnienia Roo Code Cloud. Zaloguj się, aby kontynuować.", - "organization_mismatch": "Musisz być uwierzytelniony kontem Roo Code Cloud swojej organizacji.", - "manual_url_empty": "Wprowadź prawidłowy URL callback", - "manual_url_no_query": "Nieprawidłowy URL callback: brak parametrów zapytania", - "manual_url_missing_params": "Nieprawidłowy URL callback: brak wymaganych parametrów (code i state)", - "manual_url_auth_failed": "Ręczne uwierzytelnienie URL nie powiodło się", - "manual_url_auth_error": "Uwierzytelnienie nie powiodło się", - "verification_failed": "Nie można zweryfikować uwierzytelnienia organizacji." - }, - "info": { - "organization_requires_auth": "Twoja organizacja wymaga uwierzytelnienia." - } - }, "prompts": { "deleteMode": { "title": "Usuń tryb niestandardowy", diff --git a/src/i18n/locales/pt-BR/common.json b/src/i18n/locales/pt-BR/common.json index 38abc8c8047..7c47318e87e 100644 --- a/src/i18n/locales/pt-BR/common.json +++ b/src/i18n/locales/pt-BR/common.json @@ -220,21 +220,6 @@ "rulesCleanupFailed": "O modo foi removido com sucesso, mas falhou ao excluir a pasta de regras em {{rulesFolderPath}}. Você pode precisar excluí-la manualmente." } }, - "mdm": { - "errors": { - "cloud_auth_required": "Sua organização requer autenticação do Roo Code Cloud. Faça login para continuar.", - "organization_mismatch": "Você deve estar autenticado com a conta Roo Code Cloud da sua organização.", - "manual_url_empty": "Por favor, insira uma URL de callback válida", - "manual_url_no_query": "URL de callback inválida: parâmetros de consulta ausentes", - "manual_url_missing_params": "URL de callback inválida: parâmetros obrigatórios ausentes (code e state)", - "manual_url_auth_failed": "Autenticação manual por URL falhou", - "manual_url_auth_error": "Falha na autenticação", - "verification_failed": "Não foi possível verificar a autenticação da organização." - }, - "info": { - "organization_requires_auth": "Sua organização requer autenticação." - } - }, "prompts": { "deleteMode": { "title": "Excluir Modo Personalizado", diff --git a/src/i18n/locales/ru/common.json b/src/i18n/locales/ru/common.json index d124f597318..a934e89298e 100644 --- a/src/i18n/locales/ru/common.json +++ b/src/i18n/locales/ru/common.json @@ -220,21 +220,6 @@ "rulesCleanupFailed": "Режим успешно удален, но не удалось удалить папку правил в {{rulesFolderPath}}. Возможно, вам придется удалить ее вручную." } }, - "mdm": { - "errors": { - "cloud_auth_required": "Ваша организация требует аутентификации Roo Code Cloud. Войдите в систему, чтобы продолжить.", - "organization_mismatch": "Вы должны быть аутентифицированы с учетной записью Roo Code Cloud вашей организации.", - "manual_url_empty": "Введи действительный URL обратного вызова", - "manual_url_no_query": "Недействительный URL обратного вызова: отсутствуют параметры запроса", - "manual_url_missing_params": "Недействительный URL обратного вызова: отсутствуют обязательные параметры (code и state)", - "manual_url_auth_failed": "Ручная аутентификация по URL не удалась", - "manual_url_auth_error": "Аутентификация не удалась", - "verification_failed": "Не удается проверить аутентификацию организации." - }, - "info": { - "organization_requires_auth": "Ваша организация требует аутентификации." - } - }, "prompts": { "deleteMode": { "title": "Удалить пользовательский режим", diff --git a/src/i18n/locales/tr/common.json b/src/i18n/locales/tr/common.json index 00dcf6fc33d..c7dd358ad62 100644 --- a/src/i18n/locales/tr/common.json +++ b/src/i18n/locales/tr/common.json @@ -220,21 +220,6 @@ "rulesCleanupFailed": "Mod başarıyla kaldırıldı, ancak {{rulesFolderPath}} konumundaki kurallar klasörü silinemedi. Manuel olarak silmeniz gerekebilir." } }, - "mdm": { - "errors": { - "cloud_auth_required": "Kuruluşunuz Roo Code Cloud kimlik doğrulaması gerektiriyor. Devam etmek için giriş yapın.", - "organization_mismatch": "Kuruluşunuzun Roo Code Cloud hesabıyla kimlik doğrulaması yapmalısınız.", - "manual_url_empty": "Lütfen geçerli bir callback URL'si girin", - "manual_url_no_query": "Geçersiz callback URL'si: sorgu parametreleri eksik", - "manual_url_missing_params": "Geçersiz callback URL'si: gerekli parametreler eksik (code ve state)", - "manual_url_auth_failed": "Manuel URL kimlik doğrulama başarısız", - "manual_url_auth_error": "Kimlik doğrulama başarısız", - "verification_failed": "Kuruluş kimlik doğrulaması doğrulanamıyor." - }, - "info": { - "organization_requires_auth": "Kuruluşunuz kimlik doğrulaması gerektiriyor." - } - }, "prompts": { "deleteMode": { "title": "Özel Modu Sil", diff --git a/src/i18n/locales/vi/common.json b/src/i18n/locales/vi/common.json index decd4ff53ef..033cd994c7d 100644 --- a/src/i18n/locales/vi/common.json +++ b/src/i18n/locales/vi/common.json @@ -220,21 +220,6 @@ "rulesCleanupFailed": "Đã xóa chế độ thành công, nhưng không thể xóa thư mục quy tắc tại {{rulesFolderPath}}. Bạn có thể cần xóa thủ công." } }, - "mdm": { - "errors": { - "cloud_auth_required": "Tổ chức của bạn yêu cầu xác thực Roo Code Cloud. Vui lòng đăng nhập để tiếp tục.", - "organization_mismatch": "Bạn phải được xác thực bằng tài khoản Roo Code Cloud của tổ chức.", - "manual_url_empty": "Vui lòng nhập URL callback hợp lệ", - "manual_url_no_query": "URL callback không hợp lệ: thiếu tham số truy vấn", - "manual_url_missing_params": "URL callback không hợp lệ: thiếu tham số bắt buộc (code và state)", - "manual_url_auth_failed": "Xác thực URL thủ công thất bại", - "manual_url_auth_error": "Xác thực thất bại", - "verification_failed": "Không thể xác minh xác thực tổ chức." - }, - "info": { - "organization_requires_auth": "Tổ chức của bạn yêu cầu xác thực." - } - }, "prompts": { "deleteMode": { "title": "Xóa chế độ tùy chỉnh", diff --git a/src/i18n/locales/zh-CN/common.json b/src/i18n/locales/zh-CN/common.json index 6df1f78b167..6aaa40e960a 100644 --- a/src/i18n/locales/zh-CN/common.json +++ b/src/i18n/locales/zh-CN/common.json @@ -225,21 +225,6 @@ "rulesCleanupFailed": "模式已成功移除,但无法删除位于 {{rulesFolderPath}} 的规则文件夹。您可能需要手动删除。" } }, - "mdm": { - "errors": { - "cloud_auth_required": "您的组织需要 Roo Code Cloud 身份验证。请登录以继续。", - "organization_mismatch": "您必须使用组织的 Roo Code Cloud 账户进行身份验证。", - "manual_url_empty": "请输入有效的回调 URL", - "manual_url_no_query": "无效的回调 URL:缺少查询参数", - "manual_url_missing_params": "无效的回调 URL:缺少必需参数(code 和 state)", - "manual_url_auth_failed": "手动 URL 身份验证失败", - "manual_url_auth_error": "身份验证失败", - "verification_failed": "无法验证组织身份验证。" - }, - "info": { - "organization_requires_auth": "您的组织需要身份验证。" - } - }, "prompts": { "deleteMode": { "title": "删除自定义模式", diff --git a/src/i18n/locales/zh-TW/common.json b/src/i18n/locales/zh-TW/common.json index be4a76fc5b9..7e3d952e861 100644 --- a/src/i18n/locales/zh-TW/common.json +++ b/src/i18n/locales/zh-TW/common.json @@ -220,21 +220,6 @@ "rulesCleanupFailed": "模式已成功移除,但無法刪除位於 {{rulesFolderPath}} 的規則資料夾。您可能需要手動刪除。" } }, - "mdm": { - "errors": { - "cloud_auth_required": "您的組織需要 Roo Code Cloud 身份驗證。請登入以繼續。", - "organization_mismatch": "您必須使用組織的 Roo Code Cloud 帳戶進行身份驗證。", - "manual_url_empty": "請輸入有效的回呼 URL", - "manual_url_no_query": "無效的回呼 URL:缺少查詢參數", - "manual_url_missing_params": "無效的回呼 URL:缺少必要參數(code 和 state)", - "manual_url_auth_failed": "手動 URL 身份驗證失敗", - "manual_url_auth_error": "身份驗證失敗", - "verification_failed": "無法驗證組織身份驗證。" - }, - "info": { - "organization_requires_auth": "您的組織需要身份驗證。" - } - }, "prompts": { "deleteMode": { "title": "刪除自訂模式", diff --git a/src/services/mdm/MdmService.ts b/src/services/mdm/MdmService.ts deleted file mode 100644 index 63bdbe29fca..00000000000 --- a/src/services/mdm/MdmService.ts +++ /dev/null @@ -1,202 +0,0 @@ -import * as fs from "fs" -import * as path from "path" -import * as os from "os" -import { z } from "zod" - -import { CloudService, getClerkBaseUrl, PRODUCTION_CLERK_BASE_URL } from "@roo-code/cloud" - -import { t } from "../../i18n" - -// MDM Configuration Schema -const mdmConfigSchema = z.object({ - requireCloudAuth: z.boolean(), - organizationId: z.string().optional(), -}) - -export type MdmConfig = z.infer - -export type ComplianceResult = { compliant: true } | { compliant: false; reason: string } - -export class MdmService { - private static _instance: MdmService | null = null - private mdmConfig: MdmConfig | null = null - private log: (...args: unknown[]) => void - - private constructor(log?: (...args: unknown[]) => void) { - this.log = log || console.log - } - - /** - * Initialize the MDM service by loading configuration - */ - public async initialize(): Promise { - try { - this.mdmConfig = await this.loadMdmConfig() - - if (this.mdmConfig) { - this.log(`[MDM] Loaded MDM configuration: ${JSON.stringify(this.mdmConfig)}`) - } - } catch (error) { - this.log(`[MDM] Error loading MDM configuration: ${error instanceof Error ? error.message : String(error)}`) - // Don't throw - extension should work without MDM config. - } - } - - /** - * Check if cloud authentication is required by MDM policy - */ - public requiresCloudAuth(): boolean { - return this.mdmConfig?.requireCloudAuth ?? false - } - - /** - * Get the required organization ID from MDM policy - */ - public getRequiredOrganizationId(): string | undefined { - return this.mdmConfig?.organizationId - } - - /** - * Check if the current state is compliant with MDM policy - */ - public isCompliant(): ComplianceResult { - // If no MDM policy, always compliant - if (!this.requiresCloudAuth()) { - return { compliant: true } - } - - // Check if cloud service is available and has active or attempting session - if (!CloudService.hasInstance() || !CloudService.instance.hasOrIsAcquiringActiveSession()) { - return { - compliant: false, - reason: t("mdm.errors.cloud_auth_required"), - } - } - - // Check organization match if specified - const requiredOrgId = this.getRequiredOrganizationId() - if (requiredOrgId) { - try { - // First try to get from active session - let currentOrgId = CloudService.instance.getOrganizationId() - - // If no active session, check stored credentials - if (!currentOrgId) { - const storedOrgId = CloudService.instance.getStoredOrganizationId() - - // null means personal account, which is not compliant for org requirements - if (storedOrgId === null || storedOrgId !== requiredOrgId) { - return { - compliant: false, - reason: t("mdm.errors.organization_mismatch"), - } - } - - currentOrgId = storedOrgId - } - - if (currentOrgId !== requiredOrgId) { - return { - compliant: false, - reason: t("mdm.errors.organization_mismatch"), - } - } - } catch (error) { - this.log("[MDM] Error checking organization ID:", error) - return { - compliant: false, - reason: t("mdm.errors.verification_failed"), - } - } - } - - return { compliant: true } - } - - /** - * Load MDM configuration from system location - */ - private async loadMdmConfig(): Promise { - const configPath = this.getMdmConfigPath() - - try { - // Check if file exists - if (!fs.existsSync(configPath)) { - return null - } - - // Read and parse the configuration file - const configContent = fs.readFileSync(configPath, "utf-8") - const parsedConfig = JSON.parse(configContent) - - // Validate against schema - return mdmConfigSchema.parse(parsedConfig) - } catch (error) { - this.log(`[MDM] Error reading MDM config from ${configPath}:`, error) - return null - } - } - - /** - * Get the platform-specific MDM configuration file path - */ - private getMdmConfigPath(): string { - const platform = os.platform() - const isProduction = getClerkBaseUrl() === PRODUCTION_CLERK_BASE_URL - const configFileName = isProduction ? "mdm.json" : "mdm.dev.json" - - switch (platform) { - case "win32": { - // Windows: %ProgramData%\RooCode\mdm.json or mdm.dev.json - const programData = process.env.PROGRAMDATA || "C:\\ProgramData" - return path.join(programData, "RooCode", configFileName) - } - - case "darwin": - // macOS: /Library/Application Support/RooCode/mdm.json or mdm.dev.json - return `/Library/Application Support/RooCode/${configFileName}` - - case "linux": - default: - // Linux: /etc/roo-code/mdm.json or mdm.dev.json - return `/etc/roo-code/${configFileName}` - } - } - - /** - * Get the singleton instance - */ - public static getInstance(): MdmService { - if (!this._instance) { - throw new Error("MdmService not initialized. Call createInstance() first.") - } - return this._instance - } - - /** - * Create and initialize the singleton instance - */ - public static async createInstance(log?: (...args: unknown[]) => void): Promise { - if (this._instance) { - throw new Error("MdmService instance already exists") - } - - this._instance = new MdmService(log) - await this._instance.initialize() - return this._instance - } - - /** - * Check if instance exists - */ - public static hasInstance(): boolean { - return this._instance !== null - } - - /** - * Reset the instance (for testing) - */ - public static resetInstance(): void { - this._instance = null - } -} diff --git a/src/services/mdm/__tests__/MdmService.spec.ts b/src/services/mdm/__tests__/MdmService.spec.ts deleted file mode 100644 index 31a7ba401e0..00000000000 --- a/src/services/mdm/__tests__/MdmService.spec.ts +++ /dev/null @@ -1,364 +0,0 @@ -import * as path from "path" - -// Mock dependencies -vi.mock("fs", () => ({ - existsSync: vi.fn(), - readFileSync: vi.fn(), -})) - -vi.mock("os", () => ({ - platform: vi.fn(), -})) - -vi.mock("@roo-code/cloud", () => ({ - CloudService: { - hasInstance: vi.fn(), - instance: { - hasActiveSession: vi.fn(), - hasOrIsAcquiringActiveSession: vi.fn(), - getOrganizationId: vi.fn(), - }, - }, - getClerkBaseUrl: vi.fn(), - PRODUCTION_CLERK_BASE_URL: "https://clerk.roocode.com", -})) - -vi.mock("vscode", () => ({ - workspace: { - getConfiguration: vi.fn(), - }, - ConfigurationTarget: { - Global: 1, - }, -})) - -vi.mock("../../../shared/package", () => ({ - Package: { - publisher: "roo-code", - name: "roo-cline", - version: "1.0.0", - outputChannel: "Roo-Code", - sha: undefined, - }, -})) - -vi.mock("../../../i18n", () => ({ - t: vi.fn((key: string) => { - const translations: Record = { - "mdm.errors.cloud_auth_required": - "Your organization requires Roo Code Cloud authentication. Please sign in to continue.", - "mdm.errors.organization_mismatch": - "You must be authenticated with your organization's Roo Code Cloud account.", - "mdm.errors.verification_failed": "Unable to verify organization authentication.", - } - return translations[key] || key - }), -})) - -import * as fs from "fs" -import * as os from "os" -import * as vscode from "vscode" -import { MdmService } from "../MdmService" -import { CloudService, getClerkBaseUrl, PRODUCTION_CLERK_BASE_URL } from "@roo-code/cloud" - -const mockFs = fs as any -const mockOs = os as any -const mockCloudService = CloudService as any -const mockVscode = vscode as any -const mockGetClerkBaseUrl = getClerkBaseUrl as any - -describe("MdmService", () => { - let originalPlatform: string - - beforeEach(() => { - // Reset singleton - MdmService.resetInstance() - - // Store original platform - originalPlatform = process.platform - - // Set default platform for tests - mockOs.platform.mockReturnValue("darwin") - - // Setup default mock for getClerkBaseUrl to return development URL - mockGetClerkBaseUrl.mockReturnValue("https://dev.clerk.roocode.com") - - // Setup VSCode mocks - const mockConfig = { - get: vi.fn().mockReturnValue(false), - update: vi.fn().mockResolvedValue(undefined), - } - mockVscode.workspace.getConfiguration.mockReturnValue(mockConfig) - - // Reset mocks - vi.clearAllMocks() - // Re-setup the default after clearing - mockGetClerkBaseUrl.mockReturnValue("https://dev.clerk.roocode.com") - }) - - afterEach(() => { - // Restore original platform - Object.defineProperty(process, "platform", { - value: originalPlatform, - }) - }) - - describe("initialization", () => { - it("should create instance successfully", async () => { - mockFs.existsSync.mockReturnValue(false) - - const service = await MdmService.createInstance() - expect(service).toBeInstanceOf(MdmService) - }) - - it("should load MDM config if file exists", async () => { - const mockConfig = { - requireCloudAuth: true, - organizationId: "test-org-123", - } - - mockFs.existsSync.mockReturnValue(true) - mockFs.readFileSync.mockReturnValue(JSON.stringify(mockConfig)) - - const service = await MdmService.createInstance() - - expect(service.requiresCloudAuth()).toBe(true) - expect(service.getRequiredOrganizationId()).toBe("test-org-123") - }) - - it("should handle missing MDM config file gracefully", async () => { - mockFs.existsSync.mockReturnValue(false) - - const service = await MdmService.createInstance() - - expect(service.requiresCloudAuth()).toBe(false) - expect(service.getRequiredOrganizationId()).toBeUndefined() - }) - - it("should handle invalid JSON gracefully", async () => { - mockFs.existsSync.mockReturnValue(true) - mockFs.readFileSync.mockReturnValue("invalid json") - - const service = await MdmService.createInstance() - - expect(service.requiresCloudAuth()).toBe(false) - }) - }) - - describe("platform-specific config paths", () => { - let originalNodeEnv: string | undefined - - beforeEach(() => { - originalNodeEnv = process.env.NODE_ENV - }) - - afterEach(() => { - if (originalNodeEnv !== undefined) { - process.env.NODE_ENV = originalNodeEnv - } else { - delete process.env.NODE_ENV - } - }) - - it("should use correct path for Windows in production", async () => { - mockOs.platform.mockReturnValue("win32") - process.env.PROGRAMDATA = "C:\\ProgramData" - mockGetClerkBaseUrl.mockReturnValue(PRODUCTION_CLERK_BASE_URL) - - mockFs.existsSync.mockReturnValue(false) - - await MdmService.createInstance() - - expect(mockFs.existsSync).toHaveBeenCalledWith(path.join("C:\\ProgramData", "RooCode", "mdm.json")) - }) - - it("should use correct path for Windows in development", async () => { - mockOs.platform.mockReturnValue("win32") - process.env.PROGRAMDATA = "C:\\ProgramData" - mockGetClerkBaseUrl.mockReturnValue("https://dev.clerk.roocode.com") - - mockFs.existsSync.mockReturnValue(false) - - await MdmService.createInstance() - - expect(mockFs.existsSync).toHaveBeenCalledWith(path.join("C:\\ProgramData", "RooCode", "mdm.dev.json")) - }) - - it("should use correct path for macOS in production", async () => { - mockOs.platform.mockReturnValue("darwin") - mockGetClerkBaseUrl.mockReturnValue(PRODUCTION_CLERK_BASE_URL) - - mockFs.existsSync.mockReturnValue(false) - - await MdmService.createInstance() - - expect(mockFs.existsSync).toHaveBeenCalledWith("/Library/Application Support/RooCode/mdm.json") - }) - - it("should use correct path for macOS in development", async () => { - mockOs.platform.mockReturnValue("darwin") - mockGetClerkBaseUrl.mockReturnValue("https://dev.clerk.roocode.com") - - mockFs.existsSync.mockReturnValue(false) - - await MdmService.createInstance() - - expect(mockFs.existsSync).toHaveBeenCalledWith("/Library/Application Support/RooCode/mdm.dev.json") - }) - - it("should use correct path for Linux in production", async () => { - mockOs.platform.mockReturnValue("linux") - mockGetClerkBaseUrl.mockReturnValue(PRODUCTION_CLERK_BASE_URL) - - mockFs.existsSync.mockReturnValue(false) - - await MdmService.createInstance() - - expect(mockFs.existsSync).toHaveBeenCalledWith("/etc/roo-code/mdm.json") - }) - - it("should use correct path for Linux in development", async () => { - mockOs.platform.mockReturnValue("linux") - mockGetClerkBaseUrl.mockReturnValue("https://dev.clerk.roocode.com") - - mockFs.existsSync.mockReturnValue(false) - - await MdmService.createInstance() - - expect(mockFs.existsSync).toHaveBeenCalledWith("/etc/roo-code/mdm.dev.json") - }) - - it("should default to dev config when NODE_ENV is not set", async () => { - mockOs.platform.mockReturnValue("darwin") - mockGetClerkBaseUrl.mockReturnValue("https://dev.clerk.roocode.com") - - mockFs.existsSync.mockReturnValue(false) - - await MdmService.createInstance() - - expect(mockFs.existsSync).toHaveBeenCalledWith("/Library/Application Support/RooCode/mdm.dev.json") - }) - }) - - describe("compliance checking", () => { - it("should be compliant when no MDM policy exists", async () => { - mockFs.existsSync.mockReturnValue(false) - - const service = await MdmService.createInstance() - const compliance = service.isCompliant() - - expect(compliance.compliant).toBe(true) - }) - - it("should be compliant when authenticated and no org requirement", async () => { - const mockConfig = { requireCloudAuth: true } - mockFs.existsSync.mockReturnValue(true) - mockFs.readFileSync.mockReturnValue(JSON.stringify(mockConfig)) - - mockCloudService.hasInstance.mockReturnValue(true) - mockCloudService.instance.hasOrIsAcquiringActiveSession.mockReturnValue(true) - - const service = await MdmService.createInstance() - const compliance = service.isCompliant() - - expect(compliance.compliant).toBe(true) - }) - - it("should be non-compliant when not authenticated", async () => { - const mockConfig = { requireCloudAuth: true } - mockFs.existsSync.mockReturnValue(true) - mockFs.readFileSync.mockReturnValue(JSON.stringify(mockConfig)) - - // Mock CloudService to indicate no instance or no active session - mockCloudService.hasInstance.mockReturnValue(false) - - const service = await MdmService.createInstance() - const compliance = service.isCompliant() - - expect(compliance.compliant).toBe(false) - if (!compliance.compliant) { - expect(compliance.reason).toContain("Your organization requires Roo Code Cloud authentication") - } - }) - - it("should be non-compliant when wrong organization", async () => { - const mockConfig = { - requireCloudAuth: true, - organizationId: "required-org-123", - } - mockFs.existsSync.mockReturnValue(true) - mockFs.readFileSync.mockReturnValue(JSON.stringify(mockConfig)) - - // Mock CloudService to have instance and active session but wrong org - mockCloudService.hasInstance.mockReturnValue(true) - mockCloudService.instance.hasOrIsAcquiringActiveSession.mockReturnValue(true) - mockCloudService.instance.getOrganizationId.mockReturnValue("different-org-456") - - const service = await MdmService.createInstance() - const compliance = service.isCompliant() - - expect(compliance.compliant).toBe(false) - if (!compliance.compliant) { - expect(compliance.reason).toContain( - "You must be authenticated with your organization's Roo Code Cloud account", - ) - } - }) - - it("should be compliant when correct organization", async () => { - const mockConfig = { - requireCloudAuth: true, - organizationId: "correct-org-123", - } - mockFs.existsSync.mockReturnValue(true) - mockFs.readFileSync.mockReturnValue(JSON.stringify(mockConfig)) - - mockCloudService.hasInstance.mockReturnValue(true) - mockCloudService.instance.hasOrIsAcquiringActiveSession.mockReturnValue(true) - mockCloudService.instance.getOrganizationId.mockReturnValue("correct-org-123") - - const service = await MdmService.createInstance() - const compliance = service.isCompliant() - - expect(compliance.compliant).toBe(true) - }) - - it("should be compliant when in attempting-session state", async () => { - const mockConfig = { requireCloudAuth: true } - mockFs.existsSync.mockReturnValue(true) - mockFs.readFileSync.mockReturnValue(JSON.stringify(mockConfig)) - - mockCloudService.hasInstance.mockReturnValue(true) - // Mock attempting session (not active, but acquiring) - mockCloudService.instance.hasOrIsAcquiringActiveSession.mockReturnValue(true) - - const service = await MdmService.createInstance() - const compliance = service.isCompliant() - - expect(compliance.compliant).toBe(true) - }) - }) - - describe("singleton pattern", () => { - it("should throw error when accessing instance before creation", () => { - expect(() => MdmService.getInstance()).toThrow("MdmService not initialized") - }) - - it("should throw error when creating instance twice", async () => { - mockFs.existsSync.mockReturnValue(false) - - await MdmService.createInstance() - - await expect(MdmService.createInstance()).rejects.toThrow("instance already exists") - }) - - it("should return same instance", async () => { - mockFs.existsSync.mockReturnValue(false) - - const service1 = await MdmService.createInstance() - const service2 = MdmService.getInstance() - - expect(service1).toBe(service2) - }) - }) -}) diff --git a/webview-ui/src/App.tsx b/webview-ui/src/App.tsx index cccb0422ca8..b06b6151f16 100644 --- a/webview-ui/src/App.tsx +++ b/webview-ui/src/App.tsx @@ -65,7 +65,6 @@ const App = () => { cloudApiUrl, cloudOrganizations, renderContext, - mdmCompliant, } = useExtensionState() // Create a persistent state manager @@ -91,27 +90,16 @@ const App = () => { const settingsRef = useRef(null) const chatViewRef = useRef(null) - const switchTab = useCallback( - (newTab: Tab) => { - // Only check MDM compliance if mdmCompliant is explicitly false (meaning there's an MDM policy and user is non-compliant) - // If mdmCompliant is undefined or true, allow tab switching - if (mdmCompliant === false && newTab !== "cloud") { - // Notify the user that authentication is required by their organization - vscode.postMessage({ type: "showMdmAuthRequiredNotification" }) - return - } - - setCurrentSection(undefined) - setCurrentMarketplaceTab(undefined) + const switchTab = useCallback((newTab: Tab) => { + setCurrentSection(undefined) + setCurrentMarketplaceTab(undefined) - if (settingsRef.current?.checkUnsaveChanges) { - settingsRef.current.checkUnsaveChanges(() => setTab(newTab)) - } else { - setTab(newTab) - } - }, - [mdmCompliant], - ) + if (settingsRef.current?.checkUnsaveChanges) { + settingsRef.current.checkUnsaveChanges(() => setTab(newTab)) + } else { + setTab(newTab) + } + }, []) const [currentSection, setCurrentSection] = useState(undefined) const [currentMarketplaceTab, setCurrentMarketplaceTab] = useState(undefined) diff --git a/webview-ui/src/context/ExtensionStateContext.tsx b/webview-ui/src/context/ExtensionStateContext.tsx index ce7a607d9a8..bd49b136b1c 100644 --- a/webview-ui/src/context/ExtensionStateContext.tsx +++ b/webview-ui/src/context/ExtensionStateContext.tsx @@ -48,7 +48,6 @@ export interface ExtensionStateContextType extends ExtensionState { cloudOrganizations?: CloudOrganizationMembership[] sharingEnabled: boolean publicSharingEnabled: boolean - mdmCompliant?: boolean hasOpenedModeSelector: boolean // New property to track if user has opened mode selector setHasOpenedModeSelector: (value: boolean) => void // Setter for the new property alwaysAllowFollowupQuestions: boolean // New property for follow-up questions auto-approve