BlazorWasmTemplateAuthTest.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. // Licensed to the .NET Foundation under one or more agreements.
  2. // The .NET Foundation licenses this file to you under the MIT license.
  3. using System;
  4. using System.Collections.Generic;
  5. using System.IO;
  6. using System.Linq;
  7. using System.Text.Json;
  8. using System.Text.RegularExpressions;
  9. using System.Threading.Tasks;
  10. using Microsoft.AspNetCore.Testing;
  11. using Newtonsoft.Json.Linq;
  12. using Templates.Test.Helpers;
  13. using Xunit;
  14. using Xunit.Abstractions;
  15. using Xunit.Sdk;
  16. namespace Templates.Blazor.Test;
  17. public class BlazorWasmTemplateAuthTest : BlazorTemplateTest
  18. {
  19. public BlazorWasmTemplateAuthTest(ProjectFactoryFixture projectFactory)
  20. : base(projectFactory) { }
  21. public override string ProjectType { get; } = "blazorwasm";
  22. [ConditionalFact]
  23. [SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/34554", Queues = "Windows.10.Arm64v8.Open")]
  24. // LocalDB doesn't work on non Windows platforms
  25. [OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX)]
  26. public Task BlazorWasmHostedTemplate_IndividualAuth_Works_WithLocalDB()
  27. => BlazorWasmHostedTemplate_IndividualAuth_Works(true, false, false);
  28. [ConditionalFact]
  29. [SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/34554", Queues = "Windows.10.Arm64v8.Open")]
  30. // LocalDB doesn't work on non Windows platforms
  31. [OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX)]
  32. public Task BlazorWasmHostedTemplate_IndividualAuth_NoHttps_Works_WithLocalDB()
  33. => BlazorWasmHostedTemplate_IndividualAuth_Works(true, false, true);
  34. [ConditionalFact]
  35. [SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/34554", Queues = "Windows.10.Arm64v8.Open")]
  36. public Task BlazorWasmHostedTemplate_IndividualAuth_Works_WithOutLocalDB()
  37. => BlazorWasmHostedTemplate_IndividualAuth_Works(false, false, false);
  38. [ConditionalFact]
  39. [SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/34554", Queues = "Windows.10.Arm64v8.Open")]
  40. public Task BlazorWasmHostedTemplate_IndividualAuth_NoHttps_Works_WithOutLocalDB()
  41. => BlazorWasmHostedTemplate_IndividualAuth_Works(false, false, false);
  42. [ConditionalFact]
  43. [SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/34554", Queues = "Windows.10.Arm64v8.Open")]
  44. // LocalDB doesn't work on non Windows platforms
  45. [OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX)]
  46. public Task BlazorWasmHostedTemplate_IndividualAuth_Works_WithLocalDB_ProgramMain()
  47. => BlazorWasmHostedTemplate_IndividualAuth_Works(true, true, false);
  48. [ConditionalFact]
  49. [SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/34554", Queues = "Windows.10.Arm64v8.Open")]
  50. // LocalDB doesn't work on non Windows platforms
  51. [OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX)]
  52. public Task BlazorWasmHostedTemplate_IndividualAuth_NoHttps_Works_WithLocalDB_ProgramMain()
  53. => BlazorWasmHostedTemplate_IndividualAuth_Works(true, true, true);
  54. [ConditionalFact]
  55. [SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/34554", Queues = "Windows.10.Arm64v8.Open")]
  56. public Task BlazorWasmHostedTemplate_IndividualAuth_Works_WithOutLocalDB_ProgramMain()
  57. => BlazorWasmHostedTemplate_IndividualAuth_Works(false, true, false);
  58. [ConditionalFact]
  59. [SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/34554", Queues = "Windows.10.Arm64v8.Open")]
  60. public Task BlazorWasmHostedTemplate_IndividualAuth_NoHttps_Works_WithOutLocalDB_ProgramMain()
  61. => BlazorWasmHostedTemplate_IndividualAuth_Works(false, true, true);
  62. private async Task<Project> CreateBuildPublishIndividualAuthProject(bool useLocalDb, bool useProgramMain = false, bool noHttps = false)
  63. {
  64. // Additional arguments are needed. See: https://github.com/dotnet/aspnetcore/issues/24278
  65. Environment.SetEnvironmentVariable("EnableDefaultScopedCssItems", "true");
  66. var args = new[] { ArgConstants.Hosted, useLocalDb ? "-uld" : "", useProgramMain ? ArgConstants.UseProgramMain : "", noHttps ? ArgConstants.NoHttps : "" };
  67. var project = await CreateBuildPublishAsync("Individual", args: args);
  68. var serverProject = GetSubProject(project, "Server", $"{project.ProjectName}.Server");
  69. var serverProjectFileContents = ReadFile(serverProject.TemplateOutputDir, $"{serverProject.ProjectName}.csproj");
  70. if (!useLocalDb)
  71. {
  72. Assert.Contains(".db", serverProjectFileContents);
  73. }
  74. var appSettings = ReadFile(serverProject.TemplateOutputDir, "appsettings.json");
  75. var element = JsonSerializer.Deserialize<JsonElement>(appSettings);
  76. var clientsProperty = element.GetProperty("IdentityServer").EnumerateObject().Single().Value.EnumerateObject().Single();
  77. var replacedSection = element.GetRawText().Replace(clientsProperty.Name, serverProject.ProjectName.Replace(".Server", ".Client"));
  78. var appSettingsPath = Path.Combine(serverProject.TemplateOutputDir, "appsettings.json");
  79. File.WriteAllText(appSettingsPath, replacedSection);
  80. var publishResult = await serverProject.RunDotNetPublishAsync();
  81. Assert.True(0 == publishResult.ExitCode, ErrorMessages.GetFailedProcessMessage("publish", serverProject, publishResult));
  82. // Run dotnet build after publish. The reason is that one uses Config = Debug and the other uses Config = Release
  83. // The output from publish will go into bin/Release/netcoreappX.Y/publish and won't be affected by calling build
  84. // later, while the opposite is not true.
  85. var buildResult = await serverProject.RunDotNetBuildAsync();
  86. Assert.True(0 == buildResult.ExitCode, ErrorMessages.GetFailedProcessMessage("build", serverProject, buildResult));
  87. var migrationsResult = await serverProject.RunDotNetEfCreateMigrationAsync("blazorwasm");
  88. Assert.True(0 == migrationsResult.ExitCode, ErrorMessages.GetFailedProcessMessage("run EF migrations", serverProject, migrationsResult));
  89. serverProject.AssertEmptyMigration("blazorwasm");
  90. return project;
  91. }
  92. private async Task BlazorWasmHostedTemplate_IndividualAuth_Works(bool useLocalDb, bool useProgramMain, bool noHttps)
  93. {
  94. var project = await CreateBuildPublishIndividualAuthProject(useLocalDb: useLocalDb, useProgramMain: useProgramMain, noHttps: noHttps);
  95. var serverProject = GetSubProject(project, "Server", $"{project.ProjectName}.Server");
  96. }
  97. [Fact]
  98. public async Task BlazorWasmStandaloneTemplate_IndividualAuth_CreateBuildPublish()
  99. {
  100. var project = await CreateBuildPublishAsync("Individual", args: new[] {
  101. "--authority",
  102. "https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration",
  103. ArgConstants.ClientId,
  104. "sample-client-id"
  105. });
  106. }
  107. [Fact]
  108. public async Task BlazorWasmStandaloneTemplate_NoHttps_IndividualAuth_CreateBuildPublish()
  109. {
  110. var project = await CreateBuildPublishAsync("Individual", args: new[] {
  111. "--authority",
  112. "https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration",
  113. ArgConstants.ClientId,
  114. "sample-client-id",
  115. ArgConstants.NoHttps
  116. });
  117. }
  118. public static TheoryData<TemplateInstance> TemplateDataIndividualB2C => new TheoryData<TemplateInstance>
  119. {
  120. new TemplateInstance("blazorwasmhostedaadb2c", "IndividualB2C",
  121. ArgConstants.Hosted,
  122. ArgConstants.AadB2cInstance, "example.b2clogin.com",
  123. "-ssp", "b2c_1_siupin",
  124. ArgConstants.ClientId, "clientId",
  125. ArgConstants.Domain, "my-domain",
  126. ArgConstants.DefaultScope, "full",
  127. ArgConstants.AppIdUri, "ApiUri",
  128. ArgConstants.AppIdClientId, "1234123413241324"),
  129. new TemplateInstance("blazorwasmhostedaadb2c_program_main", "IndividualB2C",
  130. ArgConstants.Hosted,
  131. ArgConstants.AadB2cInstance, "example.b2clogin.com",
  132. "-ssp", "b2c_1_siupin",
  133. ArgConstants.ClientId, "clientId",
  134. ArgConstants.Domain, "my-domain",
  135. ArgConstants.DefaultScope, "full",
  136. ArgConstants.AppIdUri, "ApiUri",
  137. ArgConstants.AppIdClientId, "1234123413241324",
  138. ArgConstants.UseProgramMain),
  139. new TemplateInstance("blazorwasmstandaloneaadb2c", "IndividualB2C",
  140. ArgConstants.AadB2cInstance, "example.b2clogin.com",
  141. "-ssp", "b2c_1_siupin",
  142. ArgConstants.ClientId, "clientId",
  143. ArgConstants.Domain, "my-domain"),
  144. new TemplateInstance("blazorwasmstandaloneaadb2c_program_main", "IndividualB2C",
  145. ArgConstants.AadB2cInstance, "example.b2clogin.com",
  146. "-ssp", "b2c_1_siupin",
  147. ArgConstants.ClientId, "clientId",
  148. ArgConstants.Domain, "my-domain",
  149. ArgConstants.UseProgramMain),
  150. };
  151. public static TheoryData<TemplateInstance> TemplateDataSingleOrg => new TheoryData<TemplateInstance>
  152. {
  153. new TemplateInstance("blazorwasmhostedaad", "SingleOrg",
  154. ArgConstants.Hosted,
  155. ArgConstants.Domain, "my-domain",
  156. ArgConstants.TenantId, "tenantId",
  157. ArgConstants.ClientId, "clientId",
  158. ArgConstants.DefaultScope, "full",
  159. ArgConstants.AppIdUri, "ApiUri",
  160. ArgConstants.AppIdClientId, "1234123413241324"),
  161. new TemplateInstance("blazorwasmhostedaadgraph", "SingleOrg",
  162. ArgConstants.Hosted,
  163. ArgConstants.CallsGraph,
  164. ArgConstants.Domain, "my-domain",
  165. ArgConstants.TenantId, "tenantId",
  166. ArgConstants.ClientId, "clientId",
  167. ArgConstants.DefaultScope, "full",
  168. ArgConstants.AppIdUri, "ApiUri",
  169. ArgConstants.AppIdClientId, "1234123413241324"),
  170. new TemplateInstance("blazorwasmhostedaadapi", "SingleOrg",
  171. ArgConstants.Hosted,
  172. ArgConstants.CalledApiUrl, "\"https://graph.microsoft.com\"",
  173. ArgConstants.CalledApiScopes, "user.readwrite",
  174. ArgConstants.Domain, "my-domain",
  175. ArgConstants.TenantId, "tenantId",
  176. ArgConstants.ClientId, "clientId",
  177. ArgConstants.DefaultScope, "full",
  178. ArgConstants.AppIdUri, "ApiUri",
  179. ArgConstants.AppIdClientId, "1234123413241324"),
  180. new TemplateInstance("blazorwasmstandaloneaad", "SingleOrg",
  181. ArgConstants.Domain, "my-domain",
  182. ArgConstants.TenantId, "tenantId",
  183. ArgConstants.ClientId, "clientId"),
  184. };
  185. public static TheoryData<TemplateInstance> TemplateDataSingleOrgProgramMain => new TheoryData<TemplateInstance>
  186. {
  187. new TemplateInstance("blazorwasmhostedaad_program_main", "SingleOrg",
  188. ArgConstants.Hosted,
  189. ArgConstants.Domain, "my-domain",
  190. ArgConstants.TenantId, "tenantId",
  191. ArgConstants.ClientId, "clientId",
  192. ArgConstants.DefaultScope, "full",
  193. ArgConstants.AppIdUri, "ApiUri",
  194. ArgConstants.AppIdClientId, "1234123413241324",
  195. ArgConstants.UseProgramMain),
  196. new TemplateInstance("blazorwasmhostedaadgraph_program_main", "SingleOrg",
  197. ArgConstants.Hosted,
  198. ArgConstants.CallsGraph,
  199. ArgConstants.Domain, "my-domain",
  200. ArgConstants.TenantId, "tenantId",
  201. ArgConstants.ClientId, "clientId",
  202. ArgConstants.DefaultScope, "full",
  203. ArgConstants.AppIdUri, "ApiUri",
  204. ArgConstants.AppIdClientId, "1234123413241324",
  205. ArgConstants.UseProgramMain),
  206. new TemplateInstance("blazorwasmhostedaadapi_program_main", "SingleOrg",
  207. ArgConstants.Hosted,
  208. ArgConstants.CalledApiUrl, "\"https://graph.microsoft.com\"",
  209. ArgConstants.CalledApiScopes, "user.readwrite",
  210. ArgConstants.Domain, "my-domain",
  211. ArgConstants.TenantId, "tenantId",
  212. ArgConstants.ClientId, "clientId",
  213. ArgConstants.DefaultScope, "full",
  214. ArgConstants.AppIdUri, "ApiUri",
  215. ArgConstants.AppIdClientId, "1234123413241324",
  216. ArgConstants.UseProgramMain),
  217. new TemplateInstance("blazorwasmstandaloneaad_program_main", "SingleOrg",
  218. ArgConstants.Domain, "my-domain",
  219. ArgConstants.TenantId, "tenantId",
  220. ArgConstants.ClientId, "clientId",
  221. ArgConstants.UseProgramMain),
  222. };
  223. public class TemplateInstance
  224. {
  225. public TemplateInstance(string name, string auth, params string[] arguments)
  226. {
  227. Name = name;
  228. Auth = auth;
  229. Arguments = arguments;
  230. }
  231. public string Name { get; }
  232. public string Auth { get; }
  233. public string[] Arguments { get; }
  234. }
  235. [ConditionalTheory]
  236. [MemberData(nameof(TemplateDataIndividualB2C))]
  237. public Task BlazorWasmHostedTemplate_AzureActiveDirectoryTemplate_IndividualB2C_Works(TemplateInstance instance)
  238. => CreateBuildPublishAsync(auth: instance.Auth, args: instance.Arguments, targetFramework: "netstandard2.1");
  239. [ConditionalTheory]
  240. [MemberData(nameof(TemplateDataIndividualB2C))]
  241. public Task BlazorWasmHostedTemplate_AzureActiveDirectoryTemplate_IndividualB2C_NoHttps_Works(TemplateInstance instance)
  242. => CreateBuildPublishAsync(auth: instance.Auth, args: instance.Arguments.Union(new[] { ArgConstants.NoHttps }).ToArray(), targetFramework: "netstandard2.1");
  243. [ConditionalTheory]
  244. [MemberData(nameof(TemplateDataSingleOrg))]
  245. public Task BlazorWasmHostedTemplate_AzureActiveDirectoryTemplate_SingleOrg_Works(TemplateInstance instance)
  246. => CreateBuildPublishAsync(auth: instance.Auth, args: instance.Arguments, targetFramework: "netstandard2.1");
  247. [ConditionalTheory]
  248. [MemberData(nameof(TemplateDataSingleOrg))]
  249. public Task BlazorWasmHostedTemplate_AzureActiveDirectoryTemplate_SingleOrg_NoHttps_Works(TemplateInstance instance)
  250. => CreateBuildPublishAsync(auth: instance.Auth, args: instance.Arguments.Union(new[] { ArgConstants.NoHttps }).ToArray(), targetFramework: "netstandard2.1");
  251. [ConditionalTheory]
  252. [MemberData(nameof(TemplateDataSingleOrgProgramMain))]
  253. public Task BlazorWasmHostedTemplate_AzureActiveDirectoryTemplate_SingleOrg_ProgramMain_Works(TemplateInstance instance)
  254. => CreateBuildPublishAsync(auth: instance.Auth, args: instance.Arguments, targetFramework: "netstandard2.1");
  255. [ConditionalTheory]
  256. [MemberData(nameof(TemplateDataSingleOrgProgramMain))]
  257. public Task BlazorWasmHostedTemplate_AzureActiveDirectoryTemplate_SingleOrg_NoHttps_ProgramMain_Works(TemplateInstance instance)
  258. => CreateBuildPublishAsync(auth: instance.Auth, args: instance.Arguments.Union(new[] { ArgConstants.NoHttps }).ToArray(), targetFramework: "netstandard2.1");
  259. private static void UpdatePublishedSettings(Project serverProject)
  260. {
  261. // Hijack here the config file to use the development key during publish.
  262. var appSettings = JObject.Parse(File.ReadAllText(Path.Combine(serverProject.TemplateOutputDir, "appsettings.json")));
  263. var appSettingsDevelopment = JObject.Parse(File.ReadAllText(Path.Combine(serverProject.TemplateOutputDir, "appsettings.Development.json")));
  264. ((JObject)appSettings["IdentityServer"]).Merge(appSettingsDevelopment["IdentityServer"]);
  265. ((JObject)appSettings["IdentityServer"]).Merge(new
  266. {
  267. IdentityServer = new
  268. {
  269. Key = new
  270. {
  271. FilePath = "./tempkey.json"
  272. }
  273. }
  274. });
  275. var testAppSettings = appSettings.ToString();
  276. File.WriteAllText(Path.Combine(serverProject.TemplatePublishDir, "appsettings.json"), testAppSettings);
  277. }
  278. }