From f586819ff19a5afe557fc8e5ddb42f261657ccfd Mon Sep 17 00:00:00 2001 From: Dhruvkumar-Microsoft Date: Fri, 9 Oct 2026 15:12:49 +0530 Subject: [PATCH] resolve the securoty incident --- infra/avm/main.bicep | 4 + infra/avm/main.json | 120 +++++----- infra/bicep/main.bicep | 4 + infra/bicep/main.json | 92 ++++---- infra/main.json | 216 +++++++++--------- src/backend/agents/agent_template.py | 10 +- src/backend/common/config/app_config.py | 18 +- src/mcp_server/services/image_service.py | 8 +- .../agents/test_agent_template_integration.py | 8 +- .../backend/agents/test_agent_template.py | 20 +- .../backend/common/config/test_app_config.py | 80 +++---- src/tests/mcp_server/test_image_service.py | 29 +++ 12 files changed, 329 insertions(+), 280 deletions(-) diff --git a/infra/avm/main.bicep b/infra/avm/main.bicep index e3985c195..412fb3146 100644 --- a/infra/avm/main.bicep +++ b/infra/avm/main.bicep @@ -1351,6 +1351,10 @@ module containerAppMcp './modules/compute/container-app.bicep' = { name: 'AZURE_STORAGE_IMAGES_CONTAINER' value: 'generated-images' } + { + name: 'APP_ENV' + value: 'prod' + } { name: 'BACKEND_URL' value: 'https://${containerAppName}.${containerAppEnvironment.outputs.defaultDomain}' diff --git a/infra/avm/main.json b/infra/avm/main.json index 7589f1da6..1acd08718 100644 --- a/infra/avm/main.json +++ b/infra/avm/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "15518088603134439540" + "version": "0.46.1.21595", + "templateHash": "6761827456905004696" }, "name": "Multi-Agent Custom Automation Engine - AVM", "description": "AVM orchestrator for the Multi-Agent Custom Automation Engine accelerator. Deploys the same logical resources and preserves the same outputs as infra\\main.bicep using local AVM wrapper modules." @@ -749,8 +749,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "14054590446517938662" + "version": "0.46.1.21595", + "templateHash": "657680309568566554" } }, "parameters": { @@ -4045,8 +4045,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "5783288507350094145" + "version": "0.46.1.21595", + "templateHash": "1077806126224374403" } }, "parameters": { @@ -4975,8 +4975,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "17026638034178729316" + "version": "0.46.1.21595", + "templateHash": "14375623146044395014" } }, "definitions": { @@ -7838,8 +7838,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "13928883839220797481" + "version": "0.46.1.21595", + "templateHash": "4613446280405157854" } }, "parameters": { @@ -9742,8 +9742,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "1680554660663753024" + "version": "0.46.1.21595", + "templateHash": "8420279536133240728" } }, "parameters": { @@ -10282,8 +10282,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "8672261192488019749" + "version": "0.46.1.21595", + "templateHash": "11252391644211280586" } }, "parameters": { @@ -11739,8 +11739,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "14627790702698637496" + "version": "0.46.1.21595", + "templateHash": "13006336383696924611" } }, "parameters": { @@ -12239,8 +12239,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "11335675658907249239" + "version": "0.46.1.21595", + "templateHash": "11653979101297837047" } }, "parameters": { @@ -21588,8 +21588,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "8816372137748980601" + "version": "0.46.1.21595", + "templateHash": "11076758087490966511" } }, "parameters": { @@ -25078,8 +25078,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "17491996720911703910" + "version": "0.46.1.21595", + "templateHash": "12857967541128499898" } }, "parameters": { @@ -25682,8 +25682,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "15126235746263324029" + "version": "0.46.1.21595", + "templateHash": "8266777043110372909" } }, "parameters": { @@ -25807,8 +25807,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "17292566980983106747" + "version": "0.46.1.21595", + "templateHash": "8582571445356860028" } }, "parameters": { @@ -28584,8 +28584,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "13733831222630151234" + "version": "0.46.1.21595", + "templateHash": "16072437499473558707" } }, "parameters": { @@ -28765,8 +28765,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "16421784581434471074" + "version": "0.46.1.21595", + "templateHash": "15329304533697998689" } }, "parameters": { @@ -31103,8 +31103,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "14126031085782385090" + "version": "0.46.1.21595", + "templateHash": "14116608463414189185" } }, "parameters": { @@ -31290,8 +31290,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "4231043916662566766" + "version": "0.46.1.21595", + "templateHash": "1338804329793217019" } }, "parameters": { @@ -39731,8 +39731,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "10123896303259078883" + "version": "0.46.1.21595", + "templateHash": "9044076711049835657" } }, "parameters": { @@ -45921,8 +45921,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "15183605406224107229" + "version": "0.46.1.21595", + "templateHash": "62199279976532843" } }, "parameters": { @@ -47307,8 +47307,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "8816372137748980601" + "version": "0.46.1.21595", + "templateHash": "11076758087490966511" } }, "parameters": { @@ -50804,8 +50804,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "6663544911550543480" + "version": "0.46.1.21595", + "templateHash": "3852019752607765790" } }, "parameters": { @@ -55039,8 +55039,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "10535819316717631477" + "version": "0.46.1.21595", + "templateHash": "12104474316605623480" } }, "parameters": { @@ -56962,6 +56962,10 @@ "name": "AZURE_STORAGE_IMAGES_CONTAINER", "value": "generated-images" }, + { + "name": "APP_ENV", + "value": "prod" + }, { "name": "BACKEND_URL", "value": "[format('https://{0}.{1}', variables('containerAppName'), reference('containerAppEnvironment').outputs.defaultDomain.value)]" @@ -56978,8 +56982,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "10535819316717631477" + "version": "0.46.1.21595", + "templateHash": "12104474316605623480" } }, "parameters": { @@ -58797,8 +58801,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "6179990525873682101" + "version": "0.46.1.21595", + "templateHash": "16438169678559886054" } }, "parameters": { @@ -59708,8 +59712,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "9504607717010385151" + "version": "0.46.1.21595", + "templateHash": "6805363261673404556" } }, "definitions": { @@ -75258,8 +75262,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "8115715631921296017" + "version": "0.46.1.21595", + "templateHash": "3868776181492554939" } }, "parameters": { @@ -75539,8 +75543,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "17425188716359064959" + "version": "0.46.1.21595", + "templateHash": "3243956187915273142" } }, "parameters": { @@ -75632,8 +75636,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "17425188716359064959" + "version": "0.46.1.21595", + "templateHash": "3243956187915273142" } }, "parameters": { @@ -75725,8 +75729,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "17425188716359064959" + "version": "0.46.1.21595", + "templateHash": "3243956187915273142" } }, "parameters": { diff --git a/infra/bicep/main.bicep b/infra/bicep/main.bicep index 0a40138a9..a5f4d548e 100644 --- a/infra/bicep/main.bicep +++ b/infra/bicep/main.bicep @@ -772,6 +772,10 @@ module mcp_container_app './modules/compute/container-app.bicep' = { name: 'AZURE_STORAGE_IMAGES_CONTAINER' value: storageContainerNameGeneratedImages } + { + name: 'APP_ENV' + value: 'prod' + } { name: 'BACKEND_URL' value: 'https://${backendContainerAppName}.${container_app_environment.outputs.defaultDomain}' diff --git a/infra/bicep/main.json b/infra/bicep/main.json index 8690715a3..b1c494c0b 100644 --- a/infra/bicep/main.json +++ b/infra/bicep/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "12836920652912180859" + "version": "0.46.1.21595", + "templateHash": "17455885082861327851" }, "name": "Multi-Agent Custom Automation Engine - Vanilla Bicep", "description": "Vanilla Bicep orchestrator for the Multi-Agent Custom Automation Engine accelerator. This deployment intentionally excludes WAF features such as private networking, scale-out, redundancy, bastion, and VM resources while keeping router-compatible outputs." @@ -454,8 +454,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "12095091241389333540" + "version": "0.46.1.21595", + "templateHash": "4864573048477524742" } }, "parameters": { @@ -575,8 +575,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "3199910078817503702" + "version": "0.46.1.21595", + "templateHash": "2461614014827306322" } }, "parameters": { @@ -739,8 +739,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "18283443262150133120" + "version": "0.46.1.21595", + "templateHash": "4217214652648169338" } }, "parameters": { @@ -845,8 +845,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "13273853593348360979" + "version": "0.46.1.21595", + "templateHash": "1051211235176310329" } }, "parameters": { @@ -1078,8 +1078,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "11426307275851055409" + "version": "0.46.1.21595", + "templateHash": "8348022486141626008" } }, "parameters": { @@ -1218,8 +1218,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "13733831222630151234" + "version": "0.46.1.21595", + "templateHash": "16072437499473558707" } }, "parameters": { @@ -1364,8 +1364,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "1497887351577571547" + "version": "0.46.1.21595", + "templateHash": "16419237328425639575" } }, "parameters": { @@ -1551,8 +1551,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "10414963800025954513" + "version": "0.46.1.21595", + "templateHash": "4341123824109406580" } }, "parameters": { @@ -1790,8 +1790,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "2631611108118541891" + "version": "0.46.1.21595", + "templateHash": "18385120077114102431" } }, "parameters": { @@ -2009,8 +2009,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "1776329168736674430" + "version": "0.46.1.21595", + "templateHash": "4080902883682302811" } }, "parameters": { @@ -2202,8 +2202,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "12583934720074784298" + "version": "0.46.1.21595", + "templateHash": "3134427457835312304" } }, "parameters": { @@ -2364,8 +2364,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "14126031085782385090" + "version": "0.46.1.21595", + "templateHash": "14116608463414189185" } }, "parameters": { @@ -2536,8 +2536,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "9689542388193984627" + "version": "0.46.1.21595", + "templateHash": "683271241027273054" } }, "parameters": { @@ -2865,8 +2865,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "9640201944207164847" + "version": "0.46.1.21595", + "templateHash": "15791443493449132254" } }, "parameters": { @@ -3198,6 +3198,10 @@ "name": "AZURE_STORAGE_IMAGES_CONTAINER", "value": "[parameters('storageContainerNameGeneratedImages')]" }, + { + "name": "APP_ENV", + "value": "prod" + }, { "name": "BACKEND_URL", "value": "[format('https://{0}.{1}', variables('backendContainerAppName'), reference(resourceId('Microsoft.Resources/deployments', take(format('module.container-app-environment.{0}', variables('solutionSuffix')), 64)), '2025-04-01').outputs.defaultDomain.value)]" @@ -3214,8 +3218,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "9640201944207164847" + "version": "0.46.1.21595", + "templateHash": "15791443493449132254" } }, "parameters": { @@ -3451,8 +3455,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "9486671591976114621" + "version": "0.46.1.21595", + "templateHash": "12803318155313142608" } }, "parameters": { @@ -3626,8 +3630,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "1320403547799686545" + "version": "0.46.1.21595", + "templateHash": "18008001815439926123" } }, "parameters": { @@ -3934,8 +3938,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "5361107554306540209" + "version": "0.46.1.21595", + "templateHash": "15955615998864557844" } }, "parameters": { @@ -4323,8 +4327,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "3414629451173950961" + "version": "0.46.1.21595", + "templateHash": "13837304634713780607" } }, "parameters": { @@ -4416,8 +4420,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "3414629451173950961" + "version": "0.46.1.21595", + "templateHash": "13837304634713780607" } }, "parameters": { @@ -4509,8 +4513,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "3414629451173950961" + "version": "0.46.1.21595", + "templateHash": "13837304634713780607" } }, "parameters": { diff --git a/infra/main.json b/infra/main.json index 20e76ec2a..79fcd9a4c 100644 --- a/infra/main.json +++ b/infra/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "13731968440732364764" + "version": "0.46.1.21595", + "templateHash": "6576863771473835220" }, "name": "Multi-Agent Custom Automation Engine - Deployment Router", "description": "Deployment router for the Multi-Agent Custom Automation Engine accelerator. Routes to either the AVM or vanilla Bicep orchestrator and preserves a unified deployment contract." @@ -560,8 +560,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "15518088603134439540" + "version": "0.46.1.21595", + "templateHash": "6761827456905004696" }, "name": "Multi-Agent Custom Automation Engine - AVM", "description": "AVM orchestrator for the Multi-Agent Custom Automation Engine accelerator. Deploys the same logical resources and preserves the same outputs as infra\\main.bicep using local AVM wrapper modules." @@ -1304,8 +1304,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "14054590446517938662" + "version": "0.46.1.21595", + "templateHash": "657680309568566554" } }, "parameters": { @@ -4600,8 +4600,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "5783288507350094145" + "version": "0.46.1.21595", + "templateHash": "1077806126224374403" } }, "parameters": { @@ -5530,8 +5530,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "17026638034178729316" + "version": "0.46.1.21595", + "templateHash": "14375623146044395014" } }, "definitions": { @@ -8393,8 +8393,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "13928883839220797481" + "version": "0.46.1.21595", + "templateHash": "4613446280405157854" } }, "parameters": { @@ -10297,8 +10297,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "1680554660663753024" + "version": "0.46.1.21595", + "templateHash": "8420279536133240728" } }, "parameters": { @@ -10837,8 +10837,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "8672261192488019749" + "version": "0.46.1.21595", + "templateHash": "11252391644211280586" } }, "parameters": { @@ -12294,8 +12294,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "14627790702698637496" + "version": "0.46.1.21595", + "templateHash": "13006336383696924611" } }, "parameters": { @@ -12794,8 +12794,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "11335675658907249239" + "version": "0.46.1.21595", + "templateHash": "11653979101297837047" } }, "parameters": { @@ -22143,8 +22143,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "8816372137748980601" + "version": "0.46.1.21595", + "templateHash": "11076758087490966511" } }, "parameters": { @@ -25633,8 +25633,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "17491996720911703910" + "version": "0.46.1.21595", + "templateHash": "12857967541128499898" } }, "parameters": { @@ -26237,8 +26237,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "15126235746263324029" + "version": "0.46.1.21595", + "templateHash": "8266777043110372909" } }, "parameters": { @@ -26362,8 +26362,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "17292566980983106747" + "version": "0.46.1.21595", + "templateHash": "8582571445356860028" } }, "parameters": { @@ -29139,8 +29139,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "13733831222630151234" + "version": "0.46.1.21595", + "templateHash": "16072437499473558707" } }, "parameters": { @@ -29320,8 +29320,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "16421784581434471074" + "version": "0.46.1.21595", + "templateHash": "15329304533697998689" } }, "parameters": { @@ -31658,8 +31658,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "14126031085782385090" + "version": "0.46.1.21595", + "templateHash": "14116608463414189185" } }, "parameters": { @@ -31845,8 +31845,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "4231043916662566766" + "version": "0.46.1.21595", + "templateHash": "1338804329793217019" } }, "parameters": { @@ -40286,8 +40286,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "10123896303259078883" + "version": "0.46.1.21595", + "templateHash": "9044076711049835657" } }, "parameters": { @@ -46476,8 +46476,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "15183605406224107229" + "version": "0.46.1.21595", + "templateHash": "62199279976532843" } }, "parameters": { @@ -47862,8 +47862,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "8816372137748980601" + "version": "0.46.1.21595", + "templateHash": "11076758087490966511" } }, "parameters": { @@ -51359,8 +51359,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "6663544911550543480" + "version": "0.46.1.21595", + "templateHash": "3852019752607765790" } }, "parameters": { @@ -55594,8 +55594,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "10535819316717631477" + "version": "0.46.1.21595", + "templateHash": "12104474316605623480" } }, "parameters": { @@ -57517,6 +57517,10 @@ "name": "AZURE_STORAGE_IMAGES_CONTAINER", "value": "generated-images" }, + { + "name": "APP_ENV", + "value": "prod" + }, { "name": "BACKEND_URL", "value": "[format('https://{0}.{1}', variables('containerAppName'), reference('containerAppEnvironment').outputs.defaultDomain.value)]" @@ -57533,8 +57537,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "10535819316717631477" + "version": "0.46.1.21595", + "templateHash": "12104474316605623480" } }, "parameters": { @@ -59352,8 +59356,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "6179990525873682101" + "version": "0.46.1.21595", + "templateHash": "16438169678559886054" } }, "parameters": { @@ -60263,8 +60267,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "9504607717010385151" + "version": "0.46.1.21595", + "templateHash": "6805363261673404556" } }, "definitions": { @@ -75813,8 +75817,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "8115715631921296017" + "version": "0.46.1.21595", + "templateHash": "3868776181492554939" } }, "parameters": { @@ -76094,8 +76098,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "17425188716359064959" + "version": "0.46.1.21595", + "templateHash": "3243956187915273142" } }, "parameters": { @@ -76187,8 +76191,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "17425188716359064959" + "version": "0.46.1.21595", + "templateHash": "3243956187915273142" } }, "parameters": { @@ -76280,8 +76284,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "17425188716359064959" + "version": "0.46.1.21595", + "templateHash": "3243956187915273142" } }, "parameters": { @@ -76887,8 +76891,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "12836920652912180859" + "version": "0.46.1.21595", + "templateHash": "17455885082861327851" }, "name": "Multi-Agent Custom Automation Engine - Vanilla Bicep", "description": "Vanilla Bicep orchestrator for the Multi-Agent Custom Automation Engine accelerator. This deployment intentionally excludes WAF features such as private networking, scale-out, redundancy, bastion, and VM resources while keeping router-compatible outputs." @@ -77337,8 +77341,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "12095091241389333540" + "version": "0.46.1.21595", + "templateHash": "4864573048477524742" } }, "parameters": { @@ -77458,8 +77462,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "3199910078817503702" + "version": "0.46.1.21595", + "templateHash": "2461614014827306322" } }, "parameters": { @@ -77622,8 +77626,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "18283443262150133120" + "version": "0.46.1.21595", + "templateHash": "4217214652648169338" } }, "parameters": { @@ -77728,8 +77732,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "13273853593348360979" + "version": "0.46.1.21595", + "templateHash": "1051211235176310329" } }, "parameters": { @@ -77961,8 +77965,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "11426307275851055409" + "version": "0.46.1.21595", + "templateHash": "8348022486141626008" } }, "parameters": { @@ -78101,8 +78105,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "13733831222630151234" + "version": "0.46.1.21595", + "templateHash": "16072437499473558707" } }, "parameters": { @@ -78247,8 +78251,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "1497887351577571547" + "version": "0.46.1.21595", + "templateHash": "16419237328425639575" } }, "parameters": { @@ -78434,8 +78438,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "10414963800025954513" + "version": "0.46.1.21595", + "templateHash": "4341123824109406580" } }, "parameters": { @@ -78673,8 +78677,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "2631611108118541891" + "version": "0.46.1.21595", + "templateHash": "18385120077114102431" } }, "parameters": { @@ -78892,8 +78896,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "1776329168736674430" + "version": "0.46.1.21595", + "templateHash": "4080902883682302811" } }, "parameters": { @@ -79085,8 +79089,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "12583934720074784298" + "version": "0.46.1.21595", + "templateHash": "3134427457835312304" } }, "parameters": { @@ -79247,8 +79251,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "14126031085782385090" + "version": "0.46.1.21595", + "templateHash": "14116608463414189185" } }, "parameters": { @@ -79419,8 +79423,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "9689542388193984627" + "version": "0.46.1.21595", + "templateHash": "683271241027273054" } }, "parameters": { @@ -79748,8 +79752,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "9640201944207164847" + "version": "0.46.1.21595", + "templateHash": "15791443493449132254" } }, "parameters": { @@ -80081,6 +80085,10 @@ "name": "AZURE_STORAGE_IMAGES_CONTAINER", "value": "[parameters('storageContainerNameGeneratedImages')]" }, + { + "name": "APP_ENV", + "value": "prod" + }, { "name": "BACKEND_URL", "value": "[format('https://{0}.{1}', variables('backendContainerAppName'), reference(resourceId('Microsoft.Resources/deployments', take(format('module.container-app-environment.{0}', variables('solutionSuffix')), 64)), '2025-04-01').outputs.defaultDomain.value)]" @@ -80097,8 +80105,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "9640201944207164847" + "version": "0.46.1.21595", + "templateHash": "15791443493449132254" } }, "parameters": { @@ -80334,8 +80342,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "9486671591976114621" + "version": "0.46.1.21595", + "templateHash": "12803318155313142608" } }, "parameters": { @@ -80509,8 +80517,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "1320403547799686545" + "version": "0.46.1.21595", + "templateHash": "18008001815439926123" } }, "parameters": { @@ -80817,8 +80825,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "5361107554306540209" + "version": "0.46.1.21595", + "templateHash": "15955615998864557844" } }, "parameters": { @@ -81206,8 +81214,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "3414629451173950961" + "version": "0.46.1.21595", + "templateHash": "13837304634713780607" } }, "parameters": { @@ -81299,8 +81307,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "3414629451173950961" + "version": "0.46.1.21595", + "templateHash": "13837304634713780607" } }, "parameters": { @@ -81392,8 +81400,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.44.1.10279", - "templateHash": "3414629451173950961" + "version": "0.46.1.21595", + "templateHash": "13837304634713780607" } }, "parameters": { diff --git a/src/backend/agents/agent_template.py b/src/backend/agents/agent_template.py index 63e1e5eee..8b4b2aac2 100644 --- a/src/backend/agents/agent_template.py +++ b/src/backend/agents/agent_template.py @@ -24,7 +24,7 @@ from azure.ai.projects.models import (CodeInterpreterTool, FileSearchTool, MCPTool, PromptAgentDefinition) from azure.core.exceptions import HttpResponseError, ResourceNotFoundError -from azure.identity.aio import (DefaultAzureCredential, +from azure.identity.aio import (AzureCliCredential, ManagedIdentityCredential) from common.database.database_base import DatabaseBase from common.models.messages import CurrentTeamAgent, TeamConfiguration @@ -73,7 +73,7 @@ def __init__( self.logger = logging.getLogger(__name__) - self._credential: Optional[DefaultAzureCredential] = None + self._credential: AzureCliCredential | ManagedIdentityCredential | None = None self._stack: Optional[AsyncExitStack] = None self._agent: Optional[Agent] = None self._resolved_vector_store_id: str | None = None @@ -89,14 +89,12 @@ async def open(self) -> "AgentTemplate": self._stack = AsyncExitStack() - # Use ManagedIdentityCredential with explicit client_id when deployed - # (APP_ENV != 'dev') to avoid DefaultAzureCredential chain issues with - # user-assigned MI. Locally, DefaultAzureCredential uses CLI login. + # Use Azure CLI login locally and user-assigned managed identity when deployed. import os app_env = os.environ.get("APP_ENV", "prod").lower() client_id = os.environ.get("AZURE_CLIENT_ID") if app_env == "dev": - self._credential = DefaultAzureCredential() + self._credential = AzureCliCredential() else: self._credential = ManagedIdentityCredential(client_id=client_id) await self._stack.enter_async_context(self._credential) diff --git a/src/backend/common/config/app_config.py b/src/backend/common/config/app_config.py index 913fe750f..749b406d6 100644 --- a/src/backend/common/config/app_config.py +++ b/src/backend/common/config/app_config.py @@ -5,9 +5,9 @@ from azure.ai.projects.aio import AIProjectClient from azure.cosmos import CosmosClient -from azure.identity import DefaultAzureCredential, ManagedIdentityCredential +from azure.identity import AzureCliCredential, ManagedIdentityCredential from azure.identity.aio import ( - DefaultAzureCredential as DefaultAzureCredentialAsync, + AzureCliCredential as AzureCliCredentialAsync, ManagedIdentityCredential as ManagedIdentityCredentialAsync, ) from dotenv import load_dotenv @@ -121,18 +121,17 @@ def get_azure_credential(self, client_id=None): """ Returns an Azure credential based on the application environment. - If the environment is 'dev', it uses DefaultAzureCredential with exclude_environment_credential=True - to avoid EnvironmentCredential exceptions in Application Insights traces. + If the environment is 'dev', it uses the local Azure CLI login. Otherwise, it uses ManagedIdentityCredential. Args: client_id (str, optional): The client ID for the Managed Identity Credential. Returns: - Credential object: Either DefaultAzureCredential or ManagedIdentityCredential. + Credential object: Either AzureCliCredential or ManagedIdentityCredential. """ if self.APP_ENV == "dev": - return DefaultAzureCredential(exclude_environment_credential=True) # CodeQL [SM05139]: DefaultAzureCredential is safe here + return AzureCliCredential() else: return ManagedIdentityCredential(client_id=client_id) @@ -140,18 +139,17 @@ def get_azure_credential_async(self, client_id=None): """ Returns an async Azure credential based on the application environment. - If the environment is 'dev', it uses DefaultAzureCredential (async) with exclude_environment_credential=True - to avoid EnvironmentCredential exceptions in Application Insights traces. + If the environment is 'dev', it uses the local Azure CLI login. Otherwise, it uses ManagedIdentityCredential (async). Args: client_id (str, optional): The client ID for the Managed Identity Credential. Returns: - Async Credential object: Either DefaultAzureCredentialAsync or ManagedIdentityCredentialAsync. + Async Credential object: Either AzureCliCredentialAsync or ManagedIdentityCredentialAsync. """ if self.APP_ENV == "dev": - return DefaultAzureCredentialAsync(exclude_environment_credential=True) + return AzureCliCredentialAsync() else: return ManagedIdentityCredentialAsync(client_id=client_id) diff --git a/src/mcp_server/services/image_service.py b/src/mcp_server/services/image_service.py index e5779f8b2..977d70f6b 100644 --- a/src/mcp_server/services/image_service.py +++ b/src/mcp_server/services/image_service.py @@ -13,7 +13,7 @@ import httpx -from azure.identity import DefaultAzureCredential, ManagedIdentityCredential, get_bearer_token_provider +from azure.identity import AzureCliCredential, ManagedIdentityCredential, get_bearer_token_provider from azure.storage.blob import ( BlobServiceClient, ContentSettings, @@ -28,11 +28,11 @@ -def _get_credential(): - """Return a credential based on environment (dev vs deployed).""" +def _get_credential() -> AzureCliCredential | ManagedIdentityCredential: + """Use Azure CLI locally and managed identity when deployed.""" app_env = (config.app_env or os.environ.get("APP_ENV") or "prod").lower() if app_env == "dev": - return DefaultAzureCredential() + return AzureCliCredential() return ManagedIdentityCredential(client_id=config.azure_client_id) diff --git a/src/tests/agents/test_agent_template_integration.py b/src/tests/agents/test_agent_template_integration.py index 4e4f1d0b0..80d68bc44 100644 --- a/src/tests/agents/test_agent_template_integration.py +++ b/src/tests/agents/test_agent_template_integration.py @@ -237,7 +237,7 @@ async def _empty_async_iter(): mock_credential.__aexit__ = AsyncMock(return_value=False) with ( - patch("agents.agent_template.DefaultAzureCredential", return_value=mock_credential), + patch("agents.agent_template.AzureCliCredential", return_value=mock_credential), patch("agents.agent_template.AIProjectClient", return_value=mock_project_client), patch("agents.agent_template.FoundryChatClient", return_value=mock_chat_client), patch("agents.agent_template.Agent", return_value=mock_agent), @@ -306,7 +306,7 @@ async def _existing_agent_iter(): mock_credential.__aexit__ = AsyncMock(return_value=False) with ( - patch("agents.agent_template.DefaultAzureCredential", return_value=mock_credential), + patch("agents.agent_template.AzureCliCredential", return_value=mock_credential), patch("agents.agent_template.AIProjectClient", return_value=mock_project_client), patch("agents.agent_template.FoundryChatClient", return_value=mock_chat_client), patch("agents.agent_template.Agent", return_value=mock_agent), @@ -383,7 +383,7 @@ async def _empty_async_iter(): mock_credential.__aexit__ = AsyncMock(return_value=False) with ( - patch("agents.agent_template.DefaultAzureCredential", return_value=mock_credential), + patch("agents.agent_template.AzureCliCredential", return_value=mock_credential), patch("agents.agent_template.AIProjectClient", return_value=mock_project_client), patch("agents.agent_template.FoundryChatClient", return_value=mock_chat_client), patch("agents.agent_template.Agent", return_value=mock_agent), @@ -456,7 +456,7 @@ async def _empty_async_iter(): mock_credential.__aexit__ = AsyncMock(return_value=False) with ( - patch("agents.agent_template.DefaultAzureCredential", return_value=mock_credential), + patch("agents.agent_template.AzureCliCredential", return_value=mock_credential), patch("agents.agent_template.AIProjectClient", return_value=mock_project_client), patch("agents.agent_template.FoundryChatClient", return_value=mock_chat_client), patch("agents.agent_template.Agent", return_value=mock_agent), diff --git a/src/tests/backend/agents/test_agent_template.py b/src/tests/backend/agents/test_agent_template.py index bf0b2722a..7abcce823 100644 --- a/src/tests/backend/agents/test_agent_template.py +++ b/src/tests/backend/agents/test_agent_template.py @@ -328,7 +328,7 @@ async def test_open_creates_agent_when_not_found(self, basic_kwargs): chat_client_mock.get_toolbox = AsyncMock(return_value=Mock()) with ( - patch("backend.agents.agent_template.DefaultAzureCredential", return_value=cred), + patch("backend.agents.agent_template.AzureCliCredential", return_value=cred), patch("backend.agents.agent_template.AIProjectClient", return_value=project_client), patch("backend.agents.agent_template.FoundryChatClient", return_value=chat_client_mock), patch("backend.agents.agent_template.Agent", return_value=agent_cm), @@ -358,7 +358,7 @@ async def test_open_reuses_agent_when_found(self, basic_kwargs): chat_client_mock.get_toolbox = AsyncMock(return_value=Mock()) with ( - patch("backend.agents.agent_template.DefaultAzureCredential", return_value=cred), + patch("backend.agents.agent_template.AzureCliCredential", return_value=cred), patch("backend.agents.agent_template.AIProjectClient", return_value=project_client), patch("backend.agents.agent_template.FoundryChatClient", return_value=chat_client_mock), patch("backend.agents.agent_template.Agent", return_value=agent_cm) as mock_agent_cls, @@ -381,7 +381,7 @@ async def test_open_no_toolbox_when_no_tools(self, basic_kwargs): chat_client_mock = Mock() with ( - patch("backend.agents.agent_template.DefaultAzureCredential", return_value=cred), + patch("backend.agents.agent_template.AzureCliCredential", return_value=cred), patch("backend.agents.agent_template.AIProjectClient", return_value=project_client), patch("backend.agents.agent_template.FoundryChatClient", return_value=chat_client_mock), patch("backend.agents.agent_template.Agent", return_value=agent_cm) as mock_agent_cls, @@ -409,7 +409,7 @@ async def test_open_creates_toolbox_with_mcp(self, basic_kwargs, mcp_config): mcp_tool_cm.__aexit__ = AsyncMock(return_value=False) with ( - patch("backend.agents.agent_template.DefaultAzureCredential", return_value=cred), + patch("backend.agents.agent_template.AzureCliCredential", return_value=cred), patch("backend.agents.agent_template.AIProjectClient", return_value=project_client), patch("backend.agents.agent_template.FoundryChatClient", return_value=chat_client_mock), patch("backend.agents.agent_template.Agent", return_value=agent_cm) as mock_agent_cls, @@ -431,7 +431,7 @@ async def test_open_registers_agent(self, basic_kwargs): agent_cm = _make_agent_cm_mock() with ( - patch("backend.agents.agent_template.DefaultAzureCredential", return_value=cred), + patch("backend.agents.agent_template.AzureCliCredential", return_value=cred), patch("backend.agents.agent_template.AIProjectClient", return_value=project_client), patch("backend.agents.agent_template.FoundryChatClient", return_value=Mock()), patch("backend.agents.agent_template.Agent", return_value=agent_cm), @@ -449,7 +449,7 @@ async def test_open_is_idempotent(self, basic_kwargs): agent_cm = _make_agent_cm_mock() with ( - patch("backend.agents.agent_template.DefaultAzureCredential", return_value=cred), + patch("backend.agents.agent_template.AzureCliCredential", return_value=cred), patch("backend.agents.agent_template.AIProjectClient", return_value=project_client), patch("backend.agents.agent_template.FoundryChatClient", return_value=Mock()), patch("backend.agents.agent_template.Agent", return_value=agent_cm), @@ -470,7 +470,7 @@ async def test_open_cleans_up_on_error(self, basic_kwargs): project_client.agents.get = AsyncMock(side_effect=RuntimeError("boom")) with ( - patch("backend.agents.agent_template.DefaultAzureCredential", return_value=cred), + patch("backend.agents.agent_template.AzureCliCredential", return_value=cred), patch("backend.agents.agent_template.AIProjectClient", return_value=project_client), patch("backend.agents.agent_template.agent_registry"), ): @@ -495,7 +495,7 @@ async def test_close_clears_state(self, basic_kwargs): agent_cm = _make_agent_cm_mock() with ( - patch("backend.agents.agent_template.DefaultAzureCredential", return_value=cred), + patch("backend.agents.agent_template.AzureCliCredential", return_value=cred), patch("backend.agents.agent_template.AIProjectClient", return_value=project_client), patch("backend.agents.agent_template.FoundryChatClient", return_value=Mock()), patch("backend.agents.agent_template.Agent", return_value=agent_cm), @@ -522,7 +522,7 @@ async def test_context_manager(self, basic_kwargs): agent_cm = _make_agent_cm_mock() with ( - patch("backend.agents.agent_template.DefaultAzureCredential", return_value=cred), + patch("backend.agents.agent_template.AzureCliCredential", return_value=cred), patch("backend.agents.agent_template.AIProjectClient", return_value=project_client), patch("backend.agents.agent_template.FoundryChatClient", return_value=Mock()), patch("backend.agents.agent_template.Agent", return_value=agent_cm), @@ -561,7 +561,7 @@ async def _fake_run(message, *, stream=False): agent_cm = _make_agent_cm_mock(inner=mock_inner) with ( - patch("backend.agents.agent_template.DefaultAzureCredential", return_value=cred), + patch("backend.agents.agent_template.AzureCliCredential", return_value=cred), patch("backend.agents.agent_template.AIProjectClient", return_value=project_client), patch("backend.agents.agent_template.FoundryChatClient", return_value=Mock()), patch("backend.agents.agent_template.Agent", return_value=agent_cm), diff --git a/src/tests/backend/common/config/test_app_config.py b/src/tests/backend/common/config/test_app_config.py index 814b6159e..e58986772 100644 --- a/src/tests/backend/common/config/test_app_config.py +++ b/src/tests/backend/common/config/test_app_config.py @@ -250,18 +250,18 @@ def _get_minimal_env(self): "AZURE_AI_AGENT_ENDPOINT": "https://test.ai.azure.com" } - @patch('backend.common.config.app_config.DefaultAzureCredential') - def test_get_azure_credential_dev_environment(self, mock_default_credential): - """Test get_azure_credential method in dev environment with exclude_environment_credential.""" + @patch('backend.common.config.app_config.AzureCliCredential') + def test_get_azure_credential_dev_environment(self, mock_cli_credential): + """Test get_azure_credential method in dev environment uses Azure CLI credential.""" mock_credential = MagicMock() - mock_default_credential.return_value = mock_credential + mock_cli_credential.return_value = mock_credential with patch.dict(os.environ, self._get_minimal_env()): config = AppConfig() result = config.get_azure_credential() - # Verify it's called with exclude_environment_credential=True in dev - mock_default_credential.assert_called_once_with(exclude_environment_credential=True) + # Verify Azure CLI credential is used in dev + mock_cli_credential.assert_called_once_with() assert result == mock_credential @patch('backend.common.config.app_config.ManagedIdentityCredential') @@ -281,11 +281,11 @@ def test_get_azure_credential_prod_environment(self, mock_managed_credential): mock_managed_credential.assert_called_once_with(client_id="test-client-id") assert result == mock_credential - @patch('backend.common.config.app_config.DefaultAzureCredential') - def test_get_azure_credentials_caching(self, mock_default_credential): + @patch('backend.common.config.app_config.AzureCliCredential') + def test_get_azure_credentials_caching(self, mock_cli_credential): """Test that get_azure_credentials caches the credential.""" mock_credential = MagicMock() - mock_default_credential.return_value = mock_credential + mock_cli_credential.return_value = mock_credential with patch.dict(os.environ, self._get_minimal_env()): config = AppConfig() @@ -296,18 +296,18 @@ def test_get_azure_credentials_caching(self, mock_default_credential): # Second call should return cached credential result2 = config.get_azure_credentials() - mock_default_credential.assert_called_once() + mock_cli_credential.assert_called_once() assert result1 == result2 == mock_credential - @patch('backend.common.config.app_config.DefaultAzureCredential') - def test_get_access_token_success(self, mock_default_credential): + @patch('backend.common.config.app_config.AzureCliCredential') + def test_get_access_token_success(self, mock_cli_credential): """Test successful access token retrieval.""" mock_token = MagicMock() mock_token.token = "test-access-token" mock_credential = MagicMock() mock_credential.get_token.return_value = mock_token - mock_default_credential.return_value = mock_credential + mock_cli_credential.return_value = mock_credential with patch.dict(os.environ, self._get_minimal_env()): config = AppConfig() @@ -319,12 +319,12 @@ def test_get_access_token_success(self, mock_default_credential): assert token.token == "test-access-token" mock_credential.get_token.assert_called_once_with(config.AZURE_COGNITIVE_SERVICES) - @patch('backend.common.config.app_config.DefaultAzureCredential') - def test_get_access_token_failure(self, mock_default_credential): + @patch('backend.common.config.app_config.AzureCliCredential') + def test_get_access_token_failure(self, mock_cli_credential): """Test access token retrieval failure.""" mock_credential = MagicMock() mock_credential.get_token.side_effect = Exception("Token retrieval failed") - mock_default_credential.return_value = mock_credential + mock_cli_credential.return_value = mock_credential with patch.dict(os.environ, self._get_minimal_env()): config = AppConfig() @@ -335,18 +335,18 @@ def test_get_access_token_failure(self, mock_default_credential): with pytest.raises(Exception, match="Token retrieval failed"): credential.get_token(config.AZURE_COGNITIVE_SERVICES) - @patch('backend.common.config.app_config.DefaultAzureCredentialAsync') - def test_get_azure_credential_async_dev_environment(self, mock_default_credential_async): - """Test get_azure_credential_async method in dev environment with exclude_environment_credential.""" + @patch('backend.common.config.app_config.AzureCliCredentialAsync') + def test_get_azure_credential_async_dev_environment(self, mock_cli_credential_async): + """Test get_azure_credential_async method in dev environment uses Azure CLI credential.""" mock_credential = MagicMock() - mock_default_credential_async.return_value = mock_credential + mock_cli_credential_async.return_value = mock_credential with patch.dict(os.environ, self._get_minimal_env()): config = AppConfig() result = config.get_azure_credential_async() - # Verify it's called with exclude_environment_credential=True in dev - mock_default_credential_async.assert_called_once_with(exclude_environment_credential=True) + # Verify Azure CLI credential is used in dev + mock_cli_credential_async.assert_called_once_with() assert result == mock_credential @patch('backend.common.config.app_config.ManagedIdentityCredentialAsync') @@ -406,11 +406,11 @@ def _get_minimal_env(self): } @patch('backend.common.config.app_config.CosmosClient') - @patch('backend.common.config.app_config.DefaultAzureCredential') - def test_get_cosmos_database_client_success(self, mock_default_credential, mock_cosmos_client): + @patch('backend.common.config.app_config.AzureCliCredential') + def test_get_cosmos_database_client_success(self, mock_cli_credential, mock_cosmos_client): """Test successful Cosmos DB client creation.""" mock_credential = MagicMock() - mock_default_credential.return_value = mock_credential + mock_cli_credential.return_value = mock_credential mock_cosmos_instance = MagicMock() mock_database_client = MagicMock() @@ -430,11 +430,11 @@ def test_get_cosmos_database_client_success(self, mock_default_credential, mock_ assert result == mock_database_client @patch('backend.common.config.app_config.CosmosClient') - @patch('backend.common.config.app_config.DefaultAzureCredential') - def test_get_cosmos_database_client_caching(self, mock_default_credential, mock_cosmos_client): + @patch('backend.common.config.app_config.AzureCliCredential') + def test_get_cosmos_database_client_caching(self, mock_cli_credential, mock_cosmos_client): """Test that Cosmos DB client is cached.""" mock_credential = MagicMock() - mock_default_credential.return_value = mock_credential + mock_cli_credential.return_value = mock_credential mock_cosmos_instance = MagicMock() mock_database_client = MagicMock() @@ -456,11 +456,11 @@ def test_get_cosmos_database_client_caching(self, mock_default_credential, mock_ assert result1 == result2 == mock_database_client @patch('backend.common.config.app_config.CosmosClient') - @patch('backend.common.config.app_config.DefaultAzureCredential') - def test_get_cosmos_database_client_failure(self, mock_default_credential, mock_cosmos_client): + @patch('backend.common.config.app_config.AzureCliCredential') + def test_get_cosmos_database_client_failure(self, mock_cli_credential, mock_cosmos_client): """Test Cosmos DB client creation failure.""" mock_credential = MagicMock() - mock_default_credential.return_value = mock_credential + mock_cli_credential.return_value = mock_credential mock_cosmos_client.side_effect = Exception("Cosmos connection failed") @@ -474,11 +474,11 @@ def test_get_cosmos_database_client_failure(self, mock_default_credential, mock_ mock_logger.assert_called_once() @patch('backend.common.config.app_config.AIProjectClient') - @patch('backend.common.config.app_config.DefaultAzureCredential') - def test_get_ai_project_client_success(self, mock_default_credential, mock_ai_client): + @patch('backend.common.config.app_config.AzureCliCredential') + def test_get_ai_project_client_success(self, mock_cli_credential, mock_ai_client): """Test successful AI Project client creation.""" mock_credential = MagicMock() - mock_default_credential.return_value = mock_credential + mock_cli_credential.return_value = mock_credential mock_ai_instance = MagicMock() mock_ai_client.return_value = mock_ai_instance @@ -498,11 +498,11 @@ def test_get_ai_project_client_success(self, mock_default_credential, mock_ai_cl assert result == mock_ai_instance @patch('backend.common.config.app_config.AIProjectClient') - @patch('backend.common.config.app_config.DefaultAzureCredential') - def test_get_ai_project_client_caching(self, mock_default_credential, mock_ai_client): + @patch('backend.common.config.app_config.AzureCliCredential') + def test_get_ai_project_client_caching(self, mock_cli_credential, mock_ai_client): """Test that AI Project client is cached.""" mock_credential = MagicMock() - mock_default_credential.return_value = mock_credential + mock_cli_credential.return_value = mock_credential mock_ai_instance = MagicMock() mock_ai_client.return_value = mock_ai_instance @@ -532,11 +532,11 @@ def test_get_ai_project_client_credential_failure(self, mock_ai_client): config.get_ai_project_client() @patch('backend.common.config.app_config.AIProjectClient') - @patch('backend.common.config.app_config.DefaultAzureCredential') - def test_get_ai_project_client_creation_failure(self, mock_default_credential, mock_ai_client): + @patch('backend.common.config.app_config.AzureCliCredential') + def test_get_ai_project_client_creation_failure(self, mock_cli_credential, mock_ai_client): """Test AI Project client creation failure.""" mock_credential = MagicMock() - mock_default_credential.return_value = mock_credential + mock_cli_credential.return_value = mock_credential mock_ai_client.side_effect = Exception("AI client creation failed") diff --git a/src/tests/mcp_server/test_image_service.py b/src/tests/mcp_server/test_image_service.py index 42e5f4bc5..06b76ba00 100644 --- a/src/tests/mcp_server/test_image_service.py +++ b/src/tests/mcp_server/test_image_service.py @@ -8,6 +8,7 @@ """ from typing import Self +from unittest.mock import Mock import pytest @@ -23,6 +24,34 @@ ) +@pytest.mark.parametrize("app_env", ["dev", "DEV"]) +def test_get_credential_uses_cli_locally(monkeypatch, app_env) -> None: + cli = Mock() + managed_identity = Mock() + monkeypatch.setattr(image_service_mod.config, "app_env", app_env) + monkeypatch.setattr(image_service_mod, "AzureCliCredential", cli) + monkeypatch.setattr(image_service_mod, "ManagedIdentityCredential", managed_identity) + + assert image_service_mod._get_credential() is cli.return_value + cli.assert_called_once_with() + managed_identity.assert_not_called() + + +@pytest.mark.parametrize("app_env", ["prod", "Prod", "staging", ""]) +def test_get_credential_uses_managed_identity_when_not_dev(monkeypatch, app_env) -> None: + cli = Mock() + managed_identity = Mock() + monkeypatch.delenv("APP_ENV", raising=False) + monkeypatch.setattr(image_service_mod.config, "app_env", app_env) + monkeypatch.setattr(image_service_mod.config, "azure_client_id", "managed-client-id") + monkeypatch.setattr(image_service_mod, "AzureCliCredential", cli) + monkeypatch.setattr(image_service_mod, "ManagedIdentityCredential", managed_identity) + + assert image_service_mod._get_credential() is managed_identity.return_value + managed_identity.assert_called_once_with(client_id="managed-client-id") + cli.assert_not_called() + + class _FakeResponse: """Stand-in httpx response with a fixed status code and JSON payload."""