microsoft/teams.net

Public

mirrored fromhttps://github.com/microsoft/teams.netAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
samples/migration-bot

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

core/test/Microsoft.Teams.Bot.Core.UnitTests/Hosting/AddBotApplicationExtensionsTests.cs

326lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4using Microsoft.Extensions.Configuration;
5using Microsoft.Extensions.DependencyInjection;
6using Microsoft.Extensions.Options;
7using Microsoft.Identity.Abstractions;
8using Microsoft.Teams.Bot.Core.Hosting;
9
10namespace Microsoft.Teams.Bot.Core.UnitTests.Hosting;
11
12public class AddBotApplicationExtensionsTests
13{
14 private static ServiceProvider BuildServiceProvider(Dictionary<string, string?> configData, string? aadConfigSectionName = null)
15 {
16 IConfigurationRoot configuration = new ConfigurationBuilder()
17 .AddInMemoryCollection(configData)
18 .Build();
19
20 ServiceCollection services = new();
21 services.AddSingleton<IConfiguration>(configuration);
22 services.AddLogging();
23
24 if (aadConfigSectionName is null)
25 {
26 services.AddConversationClient();
27 }
28 else
29 {
30 services.AddConversationClient(aadConfigSectionName);
31 }
32
33 return services.BuildServiceProvider();
34 }
35
36 private static void AssertMsalOptions(ServiceProvider serviceProvider, string expectedClientId, string expectedTenantId, string expectedInstance = "https://login.microsoftonline.com/")
37 {
38 MicrosoftIdentityApplicationOptions msalOptions = serviceProvider
39 .GetRequiredService<IOptionsMonitor<MicrosoftIdentityApplicationOptions>>()
40 .Get(MsalConfigurationExtensions.MsalConfigKey);
41 Assert.Equal(expectedClientId, msalOptions.ClientId);
42 Assert.Equal(expectedTenantId, msalOptions.TenantId);
43 Assert.Equal(expectedInstance, msalOptions.Instance);
44 }
45
46 [Fact]
47 public void AddConversationClient_WithBotFrameworkConfig_ConfiguresClientSecret()
48 {
49 // Arrange
50 Dictionary<string, string?> configData = new()
51 {
52 ["MicrosoftAppId"] = "test-app-id",
53 ["MicrosoftAppTenantId"] = "test-tenant-id",
54 ["MicrosoftAppPassword"] = "test-secret"
55 };
56
57 // Act
58 ServiceProvider serviceProvider = BuildServiceProvider(configData);
59
60 // Assert
61 AssertMsalOptions(serviceProvider, "test-app-id", "test-tenant-id");
62 MicrosoftIdentityApplicationOptions msalOptions = serviceProvider
63 .GetRequiredService<IOptionsMonitor<MicrosoftIdentityApplicationOptions>>()
64 .Get(MsalConfigurationExtensions.MsalConfigKey);
65 Assert.NotNull(msalOptions.ClientCredentials);
66 Assert.Single(msalOptions.ClientCredentials);
67 CredentialDescription credential = msalOptions.ClientCredentials.First();
68 Assert.Equal(CredentialSource.ClientSecret, credential.SourceType);
69 Assert.Equal("test-secret", credential.ClientSecret);
70 }
71
72 [Fact]
73 public void AddConversationClient_WithCoreConfigAndClientSecret_ConfiguresClientSecret()
74 {
75 // Arrange
76 Dictionary<string, string?> configData = new()
77 {
78 ["CLIENT_ID"] = "test-client-id",
79 ["TENANT_ID"] = "test-tenant-id",
80 ["CLIENT_SECRET"] = "test-client-secret"
81 };
82
83 // Act
84 ServiceProvider serviceProvider = BuildServiceProvider(configData);
85
86 // Assert
87 AssertMsalOptions(serviceProvider, "test-client-id", "test-tenant-id");
88 MicrosoftIdentityApplicationOptions msalOptions = serviceProvider
89 .GetRequiredService<IOptionsMonitor<MicrosoftIdentityApplicationOptions>>()
90 .Get(MsalConfigurationExtensions.MsalConfigKey);
91 Assert.NotNull(msalOptions.ClientCredentials);
92 Assert.Single(msalOptions.ClientCredentials);
93 CredentialDescription credential = msalOptions.ClientCredentials.First();
94 Assert.Equal(CredentialSource.ClientSecret, credential.SourceType);
95 Assert.Equal("test-client-secret", credential.ClientSecret);
96 }
97
98 [Fact]
99 public void AddConversationClient_WithCoreConfigAndSystemAssignedMI_ConfiguresSystemAssignedFIC()
100 {
101 // Arrange
102 Dictionary<string, string?> configData = new()
103 {
104 ["CLIENT_ID"] = "test-client-id",
105 ["TENANT_ID"] = "test-tenant-id",
106 ["MANAGED_IDENTITY_CLIENT_ID"] = "system"
107 };
108
109 // Act
110 ServiceProvider serviceProvider = BuildServiceProvider(configData);
111
112 // Assert
113 AssertMsalOptions(serviceProvider, "test-client-id", "test-tenant-id");
114 MicrosoftIdentityApplicationOptions msalOptions = serviceProvider
115 .GetRequiredService<IOptionsMonitor<MicrosoftIdentityApplicationOptions>>()
116 .Get(MsalConfigurationExtensions.MsalConfigKey);
117 Assert.NotNull(msalOptions.ClientCredentials);
118 Assert.Single(msalOptions.ClientCredentials);
119 CredentialDescription credential = msalOptions.ClientCredentials.First();
120 Assert.Equal(CredentialSource.SignedAssertionFromManagedIdentity, credential.SourceType);
121 Assert.Null(credential.ManagedIdentityClientId); // System-assigned
122
123 ManagedIdentityOptions managedIdentityOptions = serviceProvider.GetRequiredService<IOptions<ManagedIdentityOptions>>().Value;
124 Assert.Null(managedIdentityOptions.UserAssignedClientId);
125 }
126
127 [Fact]
128 public void AddConversationClient_WithCoreConfigAndUserAssignedMI_ConfiguresUserAssignedFIC()
129 {
130 // Arrange
131 Dictionary<string, string?> configData = new()
132 {
133 ["CLIENT_ID"] = "test-client-id",
134 ["TENANT_ID"] = "test-tenant-id",
135 ["MANAGED_IDENTITY_CLIENT_ID"] = "umi-client-id" // Different from CLIENT_ID means FIC
136 };
137
138 // Act
139 ServiceProvider serviceProvider = BuildServiceProvider(configData);
140
141 // Assert
142 AssertMsalOptions(serviceProvider, "test-client-id", "test-tenant-id");
143 MicrosoftIdentityApplicationOptions msalOptions = serviceProvider
144 .GetRequiredService<IOptionsMonitor<MicrosoftIdentityApplicationOptions>>()
145 .Get(MsalConfigurationExtensions.MsalConfigKey);
146 Assert.NotNull(msalOptions.ClientCredentials);
147 Assert.Single(msalOptions.ClientCredentials);
148 CredentialDescription credential = msalOptions.ClientCredentials.First();
149 Assert.Equal(CredentialSource.SignedAssertionFromManagedIdentity, credential.SourceType);
150 Assert.Equal("umi-client-id", credential.ManagedIdentityClientId);
151
152 ManagedIdentityOptions managedIdentityOptions = serviceProvider.GetRequiredService<IOptions<ManagedIdentityOptions>>().Value;
153 Assert.Null(managedIdentityOptions.UserAssignedClientId);
154 }
155
156 [Fact]
157 public void AddConversationClient_WithCoreConfigAndNoManagedIdentity_ConfiguresUMIWithClientId()
158 {
159 // Arrange
160 Dictionary<string, string?> configData = new()
161 {
162 ["CLIENT_ID"] = "test-client-id",
163 ["TENANT_ID"] = "test-tenant-id"
164 };
165
166 // Act
167 ServiceProvider serviceProvider = BuildServiceProvider(configData);
168
169 // Assert
170 AssertMsalOptions(serviceProvider, "test-client-id", "test-tenant-id");
171 MicrosoftIdentityApplicationOptions msalOptions = serviceProvider
172 .GetRequiredService<IOptionsMonitor<MicrosoftIdentityApplicationOptions>>()
173 .Get(MsalConfigurationExtensions.MsalConfigKey);
174 Assert.Null(msalOptions.ClientCredentials);
175
176 ManagedIdentityOptions managedIdentityOptions = serviceProvider.GetRequiredService<IOptions<ManagedIdentityOptions>>().Value;
177 Assert.Equal("test-client-id", managedIdentityOptions.UserAssignedClientId);
178 }
179
180 [Fact]
181 public void AddConversationClient_WithDefaultSection_ConfiguresFromSection()
182 {
183 // AzureAd is the default Section Name
184 // Arrange
185 Dictionary<string, string?> configData = new()
186 {
187 ["AzureAd:ClientId"] = "azuread-client-id",
188 ["AzureAd:TenantId"] = "azuread-tenant-id",
189 ["AzureAd:Instance"] = "https://login.microsoftonline.com/"
190 };
191
192 // Act
193 ServiceProvider serviceProvider = BuildServiceProvider(configData);
194
195 // Assert
196 AssertMsalOptions(serviceProvider, "azuread-client-id", "azuread-tenant-id");
197 }
198
199 [Fact]
200 public void AddConversationClient_WithCustomSectionName_ConfiguresFromCustomSection()
201 {
202 // Arrange
203 Dictionary<string, string?> configData = new()
204 {
205 ["CustomAuth:ClientId"] = "custom-client-id",
206 ["CustomAuth:TenantId"] = "custom-tenant-id",
207 ["CustomAuth:Instance"] = "https://login.microsoftonline.com/"
208 };
209
210 // Act
211 ServiceProvider serviceProvider = BuildServiceProvider(configData, "CustomAuth");
212
213 // Assert
214 AssertMsalOptions(serviceProvider, "custom-client-id", "custom-tenant-id");
215 }
216
217 // --- BotApplicationOptions (AppId) tests ---
218
219 private static ServiceProvider BuildServiceProviderForBotApp(Dictionary<string, string?> configData, string? sectionName = null)
220 {
221 IConfigurationRoot configuration = new ConfigurationBuilder()
222 .AddInMemoryCollection(configData)
223 .Build();
224
225 ServiceCollection services = new();
226 services.AddSingleton<IConfiguration>(configuration);
227 services.AddLogging();
228
229 if (sectionName is null)
230 services.AddBotApplication();
231 else
232 services.AddBotApplication(sectionName);
233
234 return services.BuildServiceProvider();
235 }
236
237 private static string GetAppId(ServiceProvider serviceProvider) =>
238 serviceProvider.GetRequiredService<BotApplicationOptions>().AppId;
239
240 [Fact]
241 public void AddBotApplication_WithMicrosoftAppId_SetsAppIdFromMicrosoftAppId()
242 {
243 // Arrange
244 Dictionary<string, string?> configData = new()
245 {
246 ["MicrosoftAppId"] = "bf-app-id",
247 ["MicrosoftAppTenantId"] = "bf-tenant-id"
248 };
249
250 // Act
251 ServiceProvider serviceProvider = BuildServiceProviderForBotApp(configData);
252
253 // Assert
254 Assert.Equal("bf-app-id", GetAppId(serviceProvider));
255 }
256
257 [Fact]
258 public void AddBotApplication_WithClientId_SetsAppIdFromClientId()
259 {
260 // Arrange
261 Dictionary<string, string?> configData = new()
262 {
263 ["CLIENT_ID"] = "core-client-id",
264 ["TENANT_ID"] = "core-tenant-id"
265 };
266
267 // Act
268 ServiceProvider serviceProvider = BuildServiceProviderForBotApp(configData);
269
270 // Assert
271 Assert.Equal("core-client-id", GetAppId(serviceProvider));
272 }
273
274 [Fact]
275 public void AddBotApplication_WithAzureAdSection_SetsAppIdFromSection()
276 {
277 // Arrange
278 Dictionary<string, string?> configData = new()
279 {
280 ["AzureAd:ClientId"] = "azuread-client-id",
281 ["AzureAd:TenantId"] = "azuread-tenant-id"
282 };
283
284 // Act
285 ServiceProvider serviceProvider = BuildServiceProviderForBotApp(configData);
286
287 // Assert
288 Assert.Equal("azuread-client-id", GetAppId(serviceProvider));
289 }
290
291 [Fact]
292 public void AddBotApplication_WithCustomSection_SetsAppIdFromCustomSection()
293 {
294 // Arrange
295 Dictionary<string, string?> configData = new()
296 {
297 ["CustomAuth:ClientId"] = "custom-client-id",
298 ["CustomAuth:TenantId"] = "custom-tenant-id"
299 };
300
301 // Act
302 ServiceProvider serviceProvider = BuildServiceProviderForBotApp(configData, "CustomAuth");
303
304 // Assert
305 Assert.Equal("custom-client-id", GetAppId(serviceProvider));
306 }
307
308 [Fact]
309 public void AddBotApplication_ClientIdTakesPrecedenceOverMicrosoftAppId()
310 {
311 // Arrange — both keys present; CLIENT_ID is highest priority
312 Dictionary<string, string?> configData = new()
313 {
314 ["MicrosoftAppId"] = "bf-app-id",
315 ["MicrosoftAppTenantId"] = "bf-tenant-id",
316 ["CLIENT_ID"] = "core-client-id",
317 ["TENANT_ID"] = "core-tenant-id"
318 };
319
320 // Act
321 ServiceProvider serviceProvider = BuildServiceProviderForBotApp(configData);
322
323 // Assert
324 Assert.Equal("core-client-id", GetAppId(serviceProvider));
325 }
326}
327