diff --git a/src/Identity/IdentityNoDeps.slnf b/src/Identity/IdentityNoDeps.slnf new file mode 100644 index 0000000000..8a75678d34 --- /dev/null +++ b/src/Identity/IdentityNoDeps.slnf @@ -0,0 +1,24 @@ +{ + "solution": { + "path": "Identity.sln", + "projects": [ + "ApiAuthorization.IdentityServer\\samples\\ApiAuthSample\\ApiAuthSample.csproj", + "ApiAuthorization.IdentityServer\\src\\Microsoft.AspNetCore.ApiAuthorization.IdentityServer.csproj", + "ApiAuthorization.IdentityServer\\test\\Microsoft.AspNetCore.ApiAuthorization.IdentityServer.Tests.csproj", + "Core\\src\\Microsoft.AspNetCore.Identity.csproj", + "EntityFrameworkCore\\src\\Microsoft.AspNetCore.Identity.EntityFrameworkCore.csproj", + "EntityFrameworkCore\\test\\EF.InMemory.Test\\Microsoft.AspNetCore.Identity.EntityFrameworkCore.InMemory.Test.csproj", + "EntityFrameworkCore\\test\\EF.Test\\Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test.csproj", + "Extensions.Core\\src\\Microsoft.Extensions.Identity.Core.csproj", + "Extensions.Stores\\src\\Microsoft.Extensions.Identity.Stores.csproj", + "Specification.Tests\\src\\Microsoft.AspNetCore.Identity.Specification.Tests.csproj", + "UI\\src\\Microsoft.AspNetCore.Identity.UI.csproj", + "samples\\IdentitySample.DefaultUI\\IdentitySample.DefaultUI.csproj", + "samples\\IdentitySample.Mvc\\IdentitySample.Mvc.csproj", + "test\\Identity.FunctionalTests\\Microsoft.AspNetCore.Identity.FunctionalTests.csproj", + "test\\Identity.Test\\Microsoft.AspNetCore.Identity.Test.csproj", + "test\\InMemory.Test\\Microsoft.AspNetCore.Identity.InMemory.Test.csproj", + "testassets\\Identity.DefaultUI.WebSite\\Identity.DefaultUI.WebSite.csproj" + ] + } +} \ No newline at end of file diff --git a/src/Identity/UI/ref/Microsoft.AspNetCore.Identity.UI.netcoreapp3.0.cs b/src/Identity/UI/ref/Microsoft.AspNetCore.Identity.UI.netcoreapp3.0.cs index 8be2ba8ab6..946e9c778c 100644 --- a/src/Identity/UI/ref/Microsoft.AspNetCore.Identity.UI.netcoreapp3.0.cs +++ b/src/Identity/UI/ref/Microsoft.AspNetCore.Identity.UI.netcoreapp3.0.cs @@ -6,20 +6,15 @@ namespace Microsoft.AspNetCore.Identity public static partial class IdentityBuilderUIExtensions { public static Microsoft.AspNetCore.Identity.IdentityBuilder AddDefaultUI(this Microsoft.AspNetCore.Identity.IdentityBuilder builder) { throw null; } - public static Microsoft.AspNetCore.Identity.IdentityBuilder AddDefaultUI(this Microsoft.AspNetCore.Identity.IdentityBuilder builder, Microsoft.AspNetCore.Identity.UI.UIFramework framework) { throw null; } } } namespace Microsoft.AspNetCore.Identity.UI { - public partial class DefaultUIOptions + [System.AttributeUsageAttribute(System.AttributeTargets.Assembly, Inherited=false, AllowMultiple=false)] + public sealed partial class UIFrameworkAttribute : System.Attribute { - public DefaultUIOptions() { } - public Microsoft.AspNetCore.Identity.UI.UIFramework UIFramework { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } - } - public enum UIFramework - { - Bootstrap3 = 0, - Bootstrap4 = 1, + public UIFrameworkAttribute(string uiFramework) { } + public string UIFramework { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } } } namespace Microsoft.AspNetCore.Identity.UI.Services diff --git a/src/Identity/UI/src/DefaultUIOptions.cs b/src/Identity/UI/src/DefaultUIOptions.cs deleted file mode 100644 index 3242941dcf..0000000000 --- a/src/Identity/UI/src/DefaultUIOptions.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -namespace Microsoft.AspNetCore.Identity.UI -{ - /// - /// Options for the default Identity UI - /// - public class DefaultUIOptions - { - /// - /// Gets or sets the to use with the default UI. - /// - public UIFramework UIFramework { get; set; } - } -} diff --git a/src/Identity/UI/src/IdentityBuilderUIExtensions.cs b/src/Identity/UI/src/IdentityBuilderUIExtensions.cs index 0f959c7fb2..6cf1ed958f 100644 --- a/src/Identity/UI/src/IdentityBuilderUIExtensions.cs +++ b/src/Identity/UI/src/IdentityBuilderUIExtensions.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Reflection; +using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Identity.UI; using Microsoft.AspNetCore.Identity.UI.Services; using Microsoft.AspNetCore.Mvc.ApplicationModels; @@ -30,35 +31,16 @@ namespace Microsoft.AspNetCore.Identity /// /// The . /// The . - public static IdentityBuilder AddDefaultUI(this IdentityBuilder builder) => builder.AddDefaultUI(UIFramework.Bootstrap4); - - - /// - /// Adds a default, self-contained UI for Identity to the application using - /// Razor Pages in an area named Identity. - /// - /// - /// In order to use the default UI, the application must be using , - /// and contain a _LoginPartial partial view that - /// can be found by the application. - /// - /// The . - /// The . - /// The . - public static IdentityBuilder AddDefaultUI( - this IdentityBuilder builder, - UIFramework framework) + public static IdentityBuilder AddDefaultUI(this IdentityBuilder builder) { builder.AddSignInManager(); - AddRelatedParts(builder, framework); + AddRelatedParts(builder); builder.Services.ConfigureOptions( typeof(IdentityDefaultUIConfigureOptions<>) .MakeGenericType(builder.UserType)); builder.Services.TryAddTransient(); - builder.Services.Configure(o => o.UIFramework = framework); - return builder; } @@ -69,8 +51,20 @@ namespace Microsoft.AspNetCore.Identity [UIFramework.Bootstrap4] = "Microsoft.AspNetCore.Identity.UI.Views.V4", }; - private static void AddRelatedParts(IdentityBuilder builder, UIFramework framework) + private static void AddRelatedParts(IdentityBuilder builder) { + // We try to resolve the UI framework that was used by looking at the entry assembly. + // When an app runs, the entry assembly will point to the built app. In some rare cases + // (functional testing) the app assembly will be different, and we'll try to locate it through + // the same mechanism that MVC uses today. + // Finally, if for some reason we aren't able to find the assembly, we'll use our default value + // (Bootstrap4) + if (!TryResolveUIFramework(Assembly.GetEntryAssembly(), out var framework) || + !TryResolveUIFramework(GetApplicationAssembly(builder), out framework)) + { + framework = default; + } + var mvcBuilder = builder.Services .AddMvc() .ConfigureApplicationPartManager(partManager => @@ -131,5 +125,38 @@ namespace Microsoft.AspNetCore.Identity } }); } + + private static Assembly GetApplicationAssembly(IdentityBuilder builder) + { + // Whis is the same logic that MVC follows to find the application assembly. + var environment = builder.Services.Where(d => d.ServiceType == typeof(IWebHostEnvironment)).ToArray(); + var applicationName = ((IWebHostEnvironment)environment.LastOrDefault()?.ImplementationInstance) + .ApplicationName; + + var appAssembly = Assembly.Load(applicationName); + return appAssembly; + } + + private static bool TryResolveUIFramework(Assembly assembly, out UIFramework uiFramework) + { + uiFramework = default; + + var metadata = assembly.GetCustomAttributes() + .SingleOrDefault()?.UIFramework; // Bootstrap4 is the default + if (metadata == null) + { + return false; + } + + // If we find the metadata there must be a valid framework here. + if (!Enum.TryParse(metadata, ignoreCase: true, out var uIFramework)) + { + var enumValues = string.Join(", ", Enum.GetNames(typeof(UIFramework)).Select(v => $"'{v}'")); + throw new InvalidOperationException( + $"Found an invalid value for the 'IdentityUIFrameworkVersion'. Valid values are {enumValues}"); + } + + return true; + } } } diff --git a/src/Identity/UI/src/IdentityDefaultUIConfigureOptions.cs b/src/Identity/UI/src/IdentityDefaultUIConfigureOptions.cs index 3ef8438ba5..f8d68d92fc 100644 --- a/src/Identity/UI/src/IdentityDefaultUIConfigureOptions.cs +++ b/src/Identity/UI/src/IdentityDefaultUIConfigureOptions.cs @@ -3,34 +3,26 @@ using System; using Microsoft.AspNetCore.Authentication.Cookies; -using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Identity.UI.Areas.Identity.Filters; using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.AspNetCore.StaticFiles; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Options; namespace Microsoft.AspNetCore.Identity.UI { internal class IdentityDefaultUIConfigureOptions : IPostConfigureOptions, - IPostConfigureOptions, IConfigureNamedOptions where TUser : class { private const string IdentityUIDefaultAreaName = "Identity"; public IdentityDefaultUIConfigureOptions( - IWebHostEnvironment environment, - IOptions uiOptions) - { + IWebHostEnvironment environment) { Environment = environment; - UiOptions = uiOptions; } public IWebHostEnvironment Environment { get; } - public IOptions UiOptions { get; } public void PostConfigure(string name, RazorPagesOptions options) { @@ -50,30 +42,10 @@ namespace Microsoft.AspNetCore.Identity.UI pam => pam.Filters.Add(new ExternalLoginsPageFilter())); } - public void PostConfigure(string name, StaticFileOptions options) - { - name = name ?? throw new ArgumentNullException(nameof(name)); - options = options ?? throw new ArgumentNullException(nameof(options)); - - // Basic initialization in case the options weren't initialized by any other component - options.ContentTypeProvider = options.ContentTypeProvider ?? new FileExtensionContentTypeProvider(); - if (options.FileProvider == null && Environment.WebRootFileProvider == null) - { - throw new InvalidOperationException("Missing FileProvider."); - } - - options.FileProvider = options.FileProvider ?? Environment.WebRootFileProvider; - - var basePath = UiOptions.Value.UIFramework == UIFramework.Bootstrap3 ? "wwwroot/V3" : - "wwwroot/V4"; - - // Add our provider - var filesProvider = new ManifestEmbeddedFileProvider(GetType().Assembly, basePath); - options.FileProvider = new CompositeFileProvider(options.FileProvider, filesProvider); + public void Configure(CookieAuthenticationOptions options) { + // Nothing to do here as Configure(string name, CookieAuthenticationOptions options) is hte one setting things up. } - public void Configure(CookieAuthenticationOptions options) { } - public void Configure(string name, CookieAuthenticationOptions options) { name = name ?? throw new ArgumentNullException(nameof(name)); diff --git a/src/Identity/UI/src/Microsoft.AspNetCore.Identity.UI.csproj b/src/Identity/UI/src/Microsoft.AspNetCore.Identity.UI.csproj index 49f3f003b6..7fe773e2c6 100644 --- a/src/Identity/UI/src/Microsoft.AspNetCore.Identity.UI.csproj +++ b/src/Identity/UI/src/Microsoft.AspNetCore.Identity.UI.csproj @@ -1,4 +1,4 @@ - + ASP.NET Core Identity UI is the default Razor Pages built-in UI for the ASP.NET Core Identity framework. @@ -7,23 +7,30 @@ true aspnetcore;identity;membership;razorpages true - true Microsoft.AspNetCore.Mvc.ApplicationParts.NullApplicationPartFactory, Microsoft.AspNetCore.Mvc.Core false false false true true + + true + true + + + $(GetCurrentProjectStaticWebAssetsDependsOn); + _UpdatedIdentityUIStaticWebAssets + + + Bootstrap4 + - - - + + + - - - @@ -40,6 +47,10 @@ + + + + @@ -63,6 +74,7 @@ + - + - + @@ -90,19 +100,15 @@ - <_RazorGenerate - Include="Areas\Identity\Pages\$(UIFrameworkVersion)\**\*.cshtml" /> + <_RazorGenerate Include="Areas\Identity\Pages\$(UIFrameworkVersion)\**\*.cshtml" /> - + - + <_GeneratedRazorViews Include="$(TargetDir)$(TargetName).Views.%(UIFrameworkVersionMoniker.Identity).dll" /> @@ -117,15 +123,13 @@ - + - + @@ -136,8 +140,34 @@ - + + + + + + + + + <_V3Content Include="wwwroot\V3\**" /> + <_V4Content Include="wwwroot\V4\**" /> + + + + Microsoft.AspNetCore.Identity.UI + $([MSBuild]::NormalizePath('$(MSBuildThisFileDirectory)wwwroot/V3')) + /Identity + %(RecursiveDir)%(FileName)%(Extension) + + + + + Microsoft.AspNetCore.Identity.UI + $([MSBuild]::NormalizePath('$(MSBuildThisFileDirectory)wwwroot/V4')) + /Identity + %(RecursiveDir)%(FileName)%(Extension) + + + diff --git a/src/Identity/UI/src/UIFramework.cs b/src/Identity/UI/src/UIFramework.cs index 7112e4c221..4637bd4312 100644 --- a/src/Identity/UI/src/UIFramework.cs +++ b/src/Identity/UI/src/UIFramework.cs @@ -1,21 +1,13 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace Microsoft.AspNetCore.Identity.UI { - /// - /// The list of supported presentation frameworks for the default UI - /// - public enum UIFramework + internal enum UIFramework { - /// - /// Bootstrap 3 - /// - Bootstrap3 = 0, - - /// - /// Bootstrap 4 - /// - Bootstrap4 = 1 + // The default framework for a given release must be 0. + // So this needs to be updated in the future if we include more frameworks. + Bootstrap4 = 0, + Bootstrap3 = 1, } } diff --git a/src/Identity/UI/src/UIFrameworkAttribute.cs b/src/Identity/UI/src/UIFrameworkAttribute.cs new file mode 100644 index 0000000000..e36aae2e73 --- /dev/null +++ b/src/Identity/UI/src/UIFrameworkAttribute.cs @@ -0,0 +1,29 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; + +namespace Microsoft.AspNetCore.Identity.UI +{ + /// + /// The UIFramework Identity UI will use on the application. + /// + [AttributeUsage(AttributeTargets.Assembly, Inherited = false, AllowMultiple = false)] + public sealed class UIFrameworkAttribute : Attribute + { + + /// + /// Initializes a new instance of . + /// + /// + public UIFrameworkAttribute(string uiFramework) + { + UIFramework = uiFramework; + } + + /// + /// The UI Framework Identity UI will use. + /// + public string UIFramework { get; } + } +} diff --git a/src/Identity/UI/src/build/Microsoft.AspNetCore.Identity.UI.props b/src/Identity/UI/src/build/Microsoft.AspNetCore.Identity.UI.props new file mode 100644 index 0000000000..75248f571e --- /dev/null +++ b/src/Identity/UI/src/build/Microsoft.AspNetCore.Identity.UI.props @@ -0,0 +1,31 @@ + + + Bootstrap4 + + + + + <_Parameter1>$(IdentityUIFrameworkVersion) + + + + + + Package + Microsoft.AspNetCore.Identity.UI + $([MSBuild]::NormalizePath('$(MSBuildThisFileDirectory)..\staticwebassets\V3')) + /Identity + %(RecursiveDir)%(FileName)%(Extension) + + + + + + Package + Microsoft.AspNetCore.Identity.UI + $([MSBuild]::NormalizePath('$(MSBuildThisFileDirectory)..\staticwebassets\V4')) + /Identity + %(RecursiveDir)%(FileName)%(Extension) + + + diff --git a/src/Identity/UI/src/wwwroot/V3/Identity/css/site.css b/src/Identity/UI/src/wwwroot/V3/css/site.css similarity index 100% rename from src/Identity/UI/src/wwwroot/V3/Identity/css/site.css rename to src/Identity/UI/src/wwwroot/V3/css/site.css diff --git a/src/Identity/UI/src/wwwroot/V3/Identity/js/site.js b/src/Identity/UI/src/wwwroot/V3/js/site.js similarity index 100% rename from src/Identity/UI/src/wwwroot/V3/Identity/js/site.js rename to src/Identity/UI/src/wwwroot/V3/js/site.js diff --git a/src/Identity/UI/src/wwwroot/V3/Identity/lib/bootstrap/LICENSE b/src/Identity/UI/src/wwwroot/V3/lib/bootstrap/LICENSE similarity index 100% rename from src/Identity/UI/src/wwwroot/V3/Identity/lib/bootstrap/LICENSE rename to src/Identity/UI/src/wwwroot/V3/lib/bootstrap/LICENSE diff --git a/src/Identity/UI/src/wwwroot/V3/Identity/lib/bootstrap/dist/css/bootstrap-theme.css b/src/Identity/UI/src/wwwroot/V3/lib/bootstrap/dist/css/bootstrap-theme.css similarity index 100% rename from src/Identity/UI/src/wwwroot/V3/Identity/lib/bootstrap/dist/css/bootstrap-theme.css rename to src/Identity/UI/src/wwwroot/V3/lib/bootstrap/dist/css/bootstrap-theme.css diff --git a/src/Identity/UI/src/wwwroot/V3/Identity/lib/bootstrap/dist/css/bootstrap-theme.css.map b/src/Identity/UI/src/wwwroot/V3/lib/bootstrap/dist/css/bootstrap-theme.css.map similarity index 100% rename from src/Identity/UI/src/wwwroot/V3/Identity/lib/bootstrap/dist/css/bootstrap-theme.css.map rename to src/Identity/UI/src/wwwroot/V3/lib/bootstrap/dist/css/bootstrap-theme.css.map diff --git a/src/Identity/UI/src/wwwroot/V3/Identity/lib/bootstrap/dist/css/bootstrap-theme.min.css b/src/Identity/UI/src/wwwroot/V3/lib/bootstrap/dist/css/bootstrap-theme.min.css similarity index 100% rename from src/Identity/UI/src/wwwroot/V3/Identity/lib/bootstrap/dist/css/bootstrap-theme.min.css rename to src/Identity/UI/src/wwwroot/V3/lib/bootstrap/dist/css/bootstrap-theme.min.css diff --git a/src/Identity/UI/src/wwwroot/V3/Identity/lib/bootstrap/dist/css/bootstrap-theme.min.css.map b/src/Identity/UI/src/wwwroot/V3/lib/bootstrap/dist/css/bootstrap-theme.min.css.map similarity index 100% rename from src/Identity/UI/src/wwwroot/V3/Identity/lib/bootstrap/dist/css/bootstrap-theme.min.css.map rename to src/Identity/UI/src/wwwroot/V3/lib/bootstrap/dist/css/bootstrap-theme.min.css.map diff --git a/src/Identity/UI/src/wwwroot/V3/Identity/lib/bootstrap/dist/css/bootstrap.css b/src/Identity/UI/src/wwwroot/V3/lib/bootstrap/dist/css/bootstrap.css similarity index 100% rename from src/Identity/UI/src/wwwroot/V3/Identity/lib/bootstrap/dist/css/bootstrap.css rename to src/Identity/UI/src/wwwroot/V3/lib/bootstrap/dist/css/bootstrap.css diff --git a/src/Identity/UI/src/wwwroot/V3/Identity/lib/bootstrap/dist/css/bootstrap.css.map b/src/Identity/UI/src/wwwroot/V3/lib/bootstrap/dist/css/bootstrap.css.map similarity index 100% rename from src/Identity/UI/src/wwwroot/V3/Identity/lib/bootstrap/dist/css/bootstrap.css.map rename to src/Identity/UI/src/wwwroot/V3/lib/bootstrap/dist/css/bootstrap.css.map diff --git a/src/Identity/UI/src/wwwroot/V3/Identity/lib/bootstrap/dist/css/bootstrap.min.css b/src/Identity/UI/src/wwwroot/V3/lib/bootstrap/dist/css/bootstrap.min.css similarity index 100% rename from src/Identity/UI/src/wwwroot/V3/Identity/lib/bootstrap/dist/css/bootstrap.min.css rename to src/Identity/UI/src/wwwroot/V3/lib/bootstrap/dist/css/bootstrap.min.css diff --git a/src/Identity/UI/src/wwwroot/V3/Identity/lib/bootstrap/dist/css/bootstrap.min.css.map b/src/Identity/UI/src/wwwroot/V3/lib/bootstrap/dist/css/bootstrap.min.css.map similarity index 100% rename from src/Identity/UI/src/wwwroot/V3/Identity/lib/bootstrap/dist/css/bootstrap.min.css.map rename to src/Identity/UI/src/wwwroot/V3/lib/bootstrap/dist/css/bootstrap.min.css.map diff --git a/src/Identity/UI/src/wwwroot/V3/Identity/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.eot b/src/Identity/UI/src/wwwroot/V3/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.eot similarity index 100% rename from src/Identity/UI/src/wwwroot/V3/Identity/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.eot rename to src/Identity/UI/src/wwwroot/V3/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.eot diff --git a/src/Identity/UI/src/wwwroot/V3/Identity/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.svg b/src/Identity/UI/src/wwwroot/V3/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.svg similarity index 100% rename from src/Identity/UI/src/wwwroot/V3/Identity/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.svg rename to src/Identity/UI/src/wwwroot/V3/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.svg diff --git a/src/Identity/UI/src/wwwroot/V3/Identity/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.ttf b/src/Identity/UI/src/wwwroot/V3/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.ttf similarity index 100% rename from src/Identity/UI/src/wwwroot/V3/Identity/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.ttf rename to src/Identity/UI/src/wwwroot/V3/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.ttf diff --git a/src/Identity/UI/src/wwwroot/V3/Identity/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff b/src/Identity/UI/src/wwwroot/V3/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff similarity index 100% rename from src/Identity/UI/src/wwwroot/V3/Identity/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff rename to src/Identity/UI/src/wwwroot/V3/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff diff --git a/src/Identity/UI/src/wwwroot/V3/Identity/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff2 b/src/Identity/UI/src/wwwroot/V3/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff2 similarity index 100% rename from src/Identity/UI/src/wwwroot/V3/Identity/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff2 rename to src/Identity/UI/src/wwwroot/V3/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff2 diff --git a/src/Identity/UI/src/wwwroot/V3/Identity/lib/bootstrap/dist/js/bootstrap.js b/src/Identity/UI/src/wwwroot/V3/lib/bootstrap/dist/js/bootstrap.js similarity index 100% rename from src/Identity/UI/src/wwwroot/V3/Identity/lib/bootstrap/dist/js/bootstrap.js rename to src/Identity/UI/src/wwwroot/V3/lib/bootstrap/dist/js/bootstrap.js diff --git a/src/Identity/UI/src/wwwroot/V3/Identity/lib/bootstrap/dist/js/bootstrap.min.js b/src/Identity/UI/src/wwwroot/V3/lib/bootstrap/dist/js/bootstrap.min.js similarity index 100% rename from src/Identity/UI/src/wwwroot/V3/Identity/lib/bootstrap/dist/js/bootstrap.min.js rename to src/Identity/UI/src/wwwroot/V3/lib/bootstrap/dist/js/bootstrap.min.js diff --git a/src/Identity/UI/src/wwwroot/V3/Identity/lib/bootstrap/dist/js/npm.js b/src/Identity/UI/src/wwwroot/V3/lib/bootstrap/dist/js/npm.js similarity index 100% rename from src/Identity/UI/src/wwwroot/V3/Identity/lib/bootstrap/dist/js/npm.js rename to src/Identity/UI/src/wwwroot/V3/lib/bootstrap/dist/js/npm.js diff --git a/src/Identity/UI/src/wwwroot/V3/Identity/lib/jquery-validation-unobtrusive/LICENSE.txt b/src/Identity/UI/src/wwwroot/V3/lib/jquery-validation-unobtrusive/LICENSE.txt similarity index 100% rename from src/Identity/UI/src/wwwroot/V3/Identity/lib/jquery-validation-unobtrusive/LICENSE.txt rename to src/Identity/UI/src/wwwroot/V3/lib/jquery-validation-unobtrusive/LICENSE.txt diff --git a/src/Identity/UI/src/wwwroot/V3/Identity/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js b/src/Identity/UI/src/wwwroot/V3/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js similarity index 100% rename from src/Identity/UI/src/wwwroot/V3/Identity/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js rename to src/Identity/UI/src/wwwroot/V3/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js diff --git a/src/Identity/UI/src/wwwroot/V3/Identity/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js b/src/Identity/UI/src/wwwroot/V3/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js similarity index 100% rename from src/Identity/UI/src/wwwroot/V3/Identity/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js rename to src/Identity/UI/src/wwwroot/V3/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js diff --git a/src/Identity/UI/src/wwwroot/V3/Identity/lib/jquery-validation/LICENSE.md b/src/Identity/UI/src/wwwroot/V3/lib/jquery-validation/LICENSE.md similarity index 100% rename from src/Identity/UI/src/wwwroot/V3/Identity/lib/jquery-validation/LICENSE.md rename to src/Identity/UI/src/wwwroot/V3/lib/jquery-validation/LICENSE.md diff --git a/src/Identity/UI/src/wwwroot/V3/Identity/lib/jquery-validation/dist/additional-methods.js b/src/Identity/UI/src/wwwroot/V3/lib/jquery-validation/dist/additional-methods.js similarity index 100% rename from src/Identity/UI/src/wwwroot/V3/Identity/lib/jquery-validation/dist/additional-methods.js rename to src/Identity/UI/src/wwwroot/V3/lib/jquery-validation/dist/additional-methods.js diff --git a/src/Identity/UI/src/wwwroot/V3/Identity/lib/jquery-validation/dist/additional-methods.min.js b/src/Identity/UI/src/wwwroot/V3/lib/jquery-validation/dist/additional-methods.min.js similarity index 100% rename from src/Identity/UI/src/wwwroot/V3/Identity/lib/jquery-validation/dist/additional-methods.min.js rename to src/Identity/UI/src/wwwroot/V3/lib/jquery-validation/dist/additional-methods.min.js diff --git a/src/Identity/UI/src/wwwroot/V3/Identity/lib/jquery-validation/dist/jquery.validate.js b/src/Identity/UI/src/wwwroot/V3/lib/jquery-validation/dist/jquery.validate.js similarity index 100% rename from src/Identity/UI/src/wwwroot/V3/Identity/lib/jquery-validation/dist/jquery.validate.js rename to src/Identity/UI/src/wwwroot/V3/lib/jquery-validation/dist/jquery.validate.js diff --git a/src/Identity/UI/src/wwwroot/V3/Identity/lib/jquery-validation/dist/jquery.validate.min.js b/src/Identity/UI/src/wwwroot/V3/lib/jquery-validation/dist/jquery.validate.min.js similarity index 100% rename from src/Identity/UI/src/wwwroot/V3/Identity/lib/jquery-validation/dist/jquery.validate.min.js rename to src/Identity/UI/src/wwwroot/V3/lib/jquery-validation/dist/jquery.validate.min.js diff --git a/src/Identity/UI/src/wwwroot/V3/Identity/lib/jquery/LICENSE.txt b/src/Identity/UI/src/wwwroot/V3/lib/jquery/LICENSE.txt similarity index 100% rename from src/Identity/UI/src/wwwroot/V3/Identity/lib/jquery/LICENSE.txt rename to src/Identity/UI/src/wwwroot/V3/lib/jquery/LICENSE.txt diff --git a/src/Identity/UI/src/wwwroot/V3/Identity/lib/jquery/dist/jquery.js b/src/Identity/UI/src/wwwroot/V3/lib/jquery/dist/jquery.js similarity index 100% rename from src/Identity/UI/src/wwwroot/V3/Identity/lib/jquery/dist/jquery.js rename to src/Identity/UI/src/wwwroot/V3/lib/jquery/dist/jquery.js diff --git a/src/Identity/UI/src/wwwroot/V3/Identity/lib/jquery/dist/jquery.min.js b/src/Identity/UI/src/wwwroot/V3/lib/jquery/dist/jquery.min.js similarity index 100% rename from src/Identity/UI/src/wwwroot/V3/Identity/lib/jquery/dist/jquery.min.js rename to src/Identity/UI/src/wwwroot/V3/lib/jquery/dist/jquery.min.js diff --git a/src/Identity/UI/src/wwwroot/V3/Identity/lib/jquery/dist/jquery.min.map b/src/Identity/UI/src/wwwroot/V3/lib/jquery/dist/jquery.min.map similarity index 100% rename from src/Identity/UI/src/wwwroot/V3/Identity/lib/jquery/dist/jquery.min.map rename to src/Identity/UI/src/wwwroot/V3/lib/jquery/dist/jquery.min.map diff --git a/src/Identity/UI/src/wwwroot/V4/Identity/css/site.css b/src/Identity/UI/src/wwwroot/V4/css/site.css similarity index 100% rename from src/Identity/UI/src/wwwroot/V4/Identity/css/site.css rename to src/Identity/UI/src/wwwroot/V4/css/site.css diff --git a/src/Identity/UI/src/wwwroot/V4/Identity/favicon.ico b/src/Identity/UI/src/wwwroot/V4/favicon.ico similarity index 100% rename from src/Identity/UI/src/wwwroot/V4/Identity/favicon.ico rename to src/Identity/UI/src/wwwroot/V4/favicon.ico diff --git a/src/Identity/UI/src/wwwroot/V4/Identity/js/site.js b/src/Identity/UI/src/wwwroot/V4/js/site.js similarity index 100% rename from src/Identity/UI/src/wwwroot/V4/Identity/js/site.js rename to src/Identity/UI/src/wwwroot/V4/js/site.js diff --git a/src/Identity/UI/src/wwwroot/V4/Identity/lib/bootstrap/LICENSE b/src/Identity/UI/src/wwwroot/V4/lib/bootstrap/LICENSE similarity index 100% rename from src/Identity/UI/src/wwwroot/V4/Identity/lib/bootstrap/LICENSE rename to src/Identity/UI/src/wwwroot/V4/lib/bootstrap/LICENSE diff --git a/src/Identity/UI/src/wwwroot/V4/Identity/lib/bootstrap/dist/css/bootstrap-grid.css b/src/Identity/UI/src/wwwroot/V4/lib/bootstrap/dist/css/bootstrap-grid.css similarity index 100% rename from src/Identity/UI/src/wwwroot/V4/Identity/lib/bootstrap/dist/css/bootstrap-grid.css rename to src/Identity/UI/src/wwwroot/V4/lib/bootstrap/dist/css/bootstrap-grid.css diff --git a/src/Identity/UI/src/wwwroot/V4/Identity/lib/bootstrap/dist/css/bootstrap-grid.css.map b/src/Identity/UI/src/wwwroot/V4/lib/bootstrap/dist/css/bootstrap-grid.css.map similarity index 100% rename from src/Identity/UI/src/wwwroot/V4/Identity/lib/bootstrap/dist/css/bootstrap-grid.css.map rename to src/Identity/UI/src/wwwroot/V4/lib/bootstrap/dist/css/bootstrap-grid.css.map diff --git a/src/Identity/UI/src/wwwroot/V4/Identity/lib/bootstrap/dist/css/bootstrap-grid.min.css b/src/Identity/UI/src/wwwroot/V4/lib/bootstrap/dist/css/bootstrap-grid.min.css similarity index 100% rename from src/Identity/UI/src/wwwroot/V4/Identity/lib/bootstrap/dist/css/bootstrap-grid.min.css rename to src/Identity/UI/src/wwwroot/V4/lib/bootstrap/dist/css/bootstrap-grid.min.css diff --git a/src/Identity/UI/src/wwwroot/V4/Identity/lib/bootstrap/dist/css/bootstrap-grid.min.css.map b/src/Identity/UI/src/wwwroot/V4/lib/bootstrap/dist/css/bootstrap-grid.min.css.map similarity index 100% rename from src/Identity/UI/src/wwwroot/V4/Identity/lib/bootstrap/dist/css/bootstrap-grid.min.css.map rename to src/Identity/UI/src/wwwroot/V4/lib/bootstrap/dist/css/bootstrap-grid.min.css.map diff --git a/src/Identity/UI/src/wwwroot/V4/Identity/lib/bootstrap/dist/css/bootstrap-reboot.css b/src/Identity/UI/src/wwwroot/V4/lib/bootstrap/dist/css/bootstrap-reboot.css similarity index 100% rename from src/Identity/UI/src/wwwroot/V4/Identity/lib/bootstrap/dist/css/bootstrap-reboot.css rename to src/Identity/UI/src/wwwroot/V4/lib/bootstrap/dist/css/bootstrap-reboot.css diff --git a/src/Identity/UI/src/wwwroot/V4/Identity/lib/bootstrap/dist/css/bootstrap-reboot.css.map b/src/Identity/UI/src/wwwroot/V4/lib/bootstrap/dist/css/bootstrap-reboot.css.map similarity index 100% rename from src/Identity/UI/src/wwwroot/V4/Identity/lib/bootstrap/dist/css/bootstrap-reboot.css.map rename to src/Identity/UI/src/wwwroot/V4/lib/bootstrap/dist/css/bootstrap-reboot.css.map diff --git a/src/Identity/UI/src/wwwroot/V4/Identity/lib/bootstrap/dist/css/bootstrap-reboot.min.css b/src/Identity/UI/src/wwwroot/V4/lib/bootstrap/dist/css/bootstrap-reboot.min.css similarity index 100% rename from src/Identity/UI/src/wwwroot/V4/Identity/lib/bootstrap/dist/css/bootstrap-reboot.min.css rename to src/Identity/UI/src/wwwroot/V4/lib/bootstrap/dist/css/bootstrap-reboot.min.css diff --git a/src/Identity/UI/src/wwwroot/V4/Identity/lib/bootstrap/dist/css/bootstrap-reboot.min.css.map b/src/Identity/UI/src/wwwroot/V4/lib/bootstrap/dist/css/bootstrap-reboot.min.css.map similarity index 100% rename from src/Identity/UI/src/wwwroot/V4/Identity/lib/bootstrap/dist/css/bootstrap-reboot.min.css.map rename to src/Identity/UI/src/wwwroot/V4/lib/bootstrap/dist/css/bootstrap-reboot.min.css.map diff --git a/src/Identity/UI/src/wwwroot/V4/Identity/lib/bootstrap/dist/css/bootstrap.css b/src/Identity/UI/src/wwwroot/V4/lib/bootstrap/dist/css/bootstrap.css similarity index 100% rename from src/Identity/UI/src/wwwroot/V4/Identity/lib/bootstrap/dist/css/bootstrap.css rename to src/Identity/UI/src/wwwroot/V4/lib/bootstrap/dist/css/bootstrap.css diff --git a/src/Identity/UI/src/wwwroot/V4/Identity/lib/bootstrap/dist/css/bootstrap.css.map b/src/Identity/UI/src/wwwroot/V4/lib/bootstrap/dist/css/bootstrap.css.map similarity index 100% rename from src/Identity/UI/src/wwwroot/V4/Identity/lib/bootstrap/dist/css/bootstrap.css.map rename to src/Identity/UI/src/wwwroot/V4/lib/bootstrap/dist/css/bootstrap.css.map diff --git a/src/Identity/UI/src/wwwroot/V4/Identity/lib/bootstrap/dist/css/bootstrap.min.css b/src/Identity/UI/src/wwwroot/V4/lib/bootstrap/dist/css/bootstrap.min.css similarity index 100% rename from src/Identity/UI/src/wwwroot/V4/Identity/lib/bootstrap/dist/css/bootstrap.min.css rename to src/Identity/UI/src/wwwroot/V4/lib/bootstrap/dist/css/bootstrap.min.css diff --git a/src/Identity/UI/src/wwwroot/V4/Identity/lib/bootstrap/dist/css/bootstrap.min.css.map b/src/Identity/UI/src/wwwroot/V4/lib/bootstrap/dist/css/bootstrap.min.css.map similarity index 100% rename from src/Identity/UI/src/wwwroot/V4/Identity/lib/bootstrap/dist/css/bootstrap.min.css.map rename to src/Identity/UI/src/wwwroot/V4/lib/bootstrap/dist/css/bootstrap.min.css.map diff --git a/src/Identity/UI/src/wwwroot/V4/Identity/lib/bootstrap/dist/js/bootstrap.bundle.js b/src/Identity/UI/src/wwwroot/V4/lib/bootstrap/dist/js/bootstrap.bundle.js similarity index 100% rename from src/Identity/UI/src/wwwroot/V4/Identity/lib/bootstrap/dist/js/bootstrap.bundle.js rename to src/Identity/UI/src/wwwroot/V4/lib/bootstrap/dist/js/bootstrap.bundle.js diff --git a/src/Identity/UI/src/wwwroot/V4/Identity/lib/bootstrap/dist/js/bootstrap.bundle.js.map b/src/Identity/UI/src/wwwroot/V4/lib/bootstrap/dist/js/bootstrap.bundle.js.map similarity index 100% rename from src/Identity/UI/src/wwwroot/V4/Identity/lib/bootstrap/dist/js/bootstrap.bundle.js.map rename to src/Identity/UI/src/wwwroot/V4/lib/bootstrap/dist/js/bootstrap.bundle.js.map diff --git a/src/Identity/UI/src/wwwroot/V4/Identity/lib/bootstrap/dist/js/bootstrap.bundle.min.js b/src/Identity/UI/src/wwwroot/V4/lib/bootstrap/dist/js/bootstrap.bundle.min.js similarity index 100% rename from src/Identity/UI/src/wwwroot/V4/Identity/lib/bootstrap/dist/js/bootstrap.bundle.min.js rename to src/Identity/UI/src/wwwroot/V4/lib/bootstrap/dist/js/bootstrap.bundle.min.js diff --git a/src/Identity/UI/src/wwwroot/V4/Identity/lib/bootstrap/dist/js/bootstrap.bundle.min.js.map b/src/Identity/UI/src/wwwroot/V4/lib/bootstrap/dist/js/bootstrap.bundle.min.js.map similarity index 100% rename from src/Identity/UI/src/wwwroot/V4/Identity/lib/bootstrap/dist/js/bootstrap.bundle.min.js.map rename to src/Identity/UI/src/wwwroot/V4/lib/bootstrap/dist/js/bootstrap.bundle.min.js.map diff --git a/src/Identity/UI/src/wwwroot/V4/Identity/lib/bootstrap/dist/js/bootstrap.js b/src/Identity/UI/src/wwwroot/V4/lib/bootstrap/dist/js/bootstrap.js similarity index 100% rename from src/Identity/UI/src/wwwroot/V4/Identity/lib/bootstrap/dist/js/bootstrap.js rename to src/Identity/UI/src/wwwroot/V4/lib/bootstrap/dist/js/bootstrap.js diff --git a/src/Identity/UI/src/wwwroot/V4/Identity/lib/bootstrap/dist/js/bootstrap.js.map b/src/Identity/UI/src/wwwroot/V4/lib/bootstrap/dist/js/bootstrap.js.map similarity index 100% rename from src/Identity/UI/src/wwwroot/V4/Identity/lib/bootstrap/dist/js/bootstrap.js.map rename to src/Identity/UI/src/wwwroot/V4/lib/bootstrap/dist/js/bootstrap.js.map diff --git a/src/Identity/UI/src/wwwroot/V4/Identity/lib/bootstrap/dist/js/bootstrap.min.js b/src/Identity/UI/src/wwwroot/V4/lib/bootstrap/dist/js/bootstrap.min.js similarity index 100% rename from src/Identity/UI/src/wwwroot/V4/Identity/lib/bootstrap/dist/js/bootstrap.min.js rename to src/Identity/UI/src/wwwroot/V4/lib/bootstrap/dist/js/bootstrap.min.js diff --git a/src/Identity/UI/src/wwwroot/V4/Identity/lib/bootstrap/dist/js/bootstrap.min.js.map b/src/Identity/UI/src/wwwroot/V4/lib/bootstrap/dist/js/bootstrap.min.js.map similarity index 100% rename from src/Identity/UI/src/wwwroot/V4/Identity/lib/bootstrap/dist/js/bootstrap.min.js.map rename to src/Identity/UI/src/wwwroot/V4/lib/bootstrap/dist/js/bootstrap.min.js.map diff --git a/src/Identity/UI/src/wwwroot/V4/Identity/lib/jquery-validation-unobtrusive/LICENSE.txt b/src/Identity/UI/src/wwwroot/V4/lib/jquery-validation-unobtrusive/LICENSE.txt similarity index 100% rename from src/Identity/UI/src/wwwroot/V4/Identity/lib/jquery-validation-unobtrusive/LICENSE.txt rename to src/Identity/UI/src/wwwroot/V4/lib/jquery-validation-unobtrusive/LICENSE.txt diff --git a/src/Identity/UI/src/wwwroot/V4/Identity/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js b/src/Identity/UI/src/wwwroot/V4/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js similarity index 100% rename from src/Identity/UI/src/wwwroot/V4/Identity/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js rename to src/Identity/UI/src/wwwroot/V4/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js diff --git a/src/Identity/UI/src/wwwroot/V4/Identity/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js b/src/Identity/UI/src/wwwroot/V4/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js similarity index 100% rename from src/Identity/UI/src/wwwroot/V4/Identity/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js rename to src/Identity/UI/src/wwwroot/V4/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js diff --git a/src/Identity/UI/src/wwwroot/V4/Identity/lib/jquery-validation/LICENSE.md b/src/Identity/UI/src/wwwroot/V4/lib/jquery-validation/LICENSE.md similarity index 100% rename from src/Identity/UI/src/wwwroot/V4/Identity/lib/jquery-validation/LICENSE.md rename to src/Identity/UI/src/wwwroot/V4/lib/jquery-validation/LICENSE.md diff --git a/src/Identity/UI/src/wwwroot/V4/Identity/lib/jquery-validation/dist/additional-methods.js b/src/Identity/UI/src/wwwroot/V4/lib/jquery-validation/dist/additional-methods.js similarity index 100% rename from src/Identity/UI/src/wwwroot/V4/Identity/lib/jquery-validation/dist/additional-methods.js rename to src/Identity/UI/src/wwwroot/V4/lib/jquery-validation/dist/additional-methods.js diff --git a/src/Identity/UI/src/wwwroot/V4/Identity/lib/jquery-validation/dist/additional-methods.min.js b/src/Identity/UI/src/wwwroot/V4/lib/jquery-validation/dist/additional-methods.min.js similarity index 100% rename from src/Identity/UI/src/wwwroot/V4/Identity/lib/jquery-validation/dist/additional-methods.min.js rename to src/Identity/UI/src/wwwroot/V4/lib/jquery-validation/dist/additional-methods.min.js diff --git a/src/Identity/UI/src/wwwroot/V4/Identity/lib/jquery-validation/dist/jquery.validate.js b/src/Identity/UI/src/wwwroot/V4/lib/jquery-validation/dist/jquery.validate.js similarity index 100% rename from src/Identity/UI/src/wwwroot/V4/Identity/lib/jquery-validation/dist/jquery.validate.js rename to src/Identity/UI/src/wwwroot/V4/lib/jquery-validation/dist/jquery.validate.js diff --git a/src/Identity/UI/src/wwwroot/V4/Identity/lib/jquery-validation/dist/jquery.validate.min.js b/src/Identity/UI/src/wwwroot/V4/lib/jquery-validation/dist/jquery.validate.min.js similarity index 100% rename from src/Identity/UI/src/wwwroot/V4/Identity/lib/jquery-validation/dist/jquery.validate.min.js rename to src/Identity/UI/src/wwwroot/V4/lib/jquery-validation/dist/jquery.validate.min.js diff --git a/src/Identity/UI/src/wwwroot/V4/Identity/lib/jquery/LICENSE.txt b/src/Identity/UI/src/wwwroot/V4/lib/jquery/LICENSE.txt similarity index 100% rename from src/Identity/UI/src/wwwroot/V4/Identity/lib/jquery/LICENSE.txt rename to src/Identity/UI/src/wwwroot/V4/lib/jquery/LICENSE.txt diff --git a/src/Identity/UI/src/wwwroot/V4/Identity/lib/jquery/dist/jquery.js b/src/Identity/UI/src/wwwroot/V4/lib/jquery/dist/jquery.js similarity index 100% rename from src/Identity/UI/src/wwwroot/V4/Identity/lib/jquery/dist/jquery.js rename to src/Identity/UI/src/wwwroot/V4/lib/jquery/dist/jquery.js diff --git a/src/Identity/UI/src/wwwroot/V4/Identity/lib/jquery/dist/jquery.min.js b/src/Identity/UI/src/wwwroot/V4/lib/jquery/dist/jquery.min.js similarity index 100% rename from src/Identity/UI/src/wwwroot/V4/Identity/lib/jquery/dist/jquery.min.js rename to src/Identity/UI/src/wwwroot/V4/lib/jquery/dist/jquery.min.js diff --git a/src/Identity/UI/src/wwwroot/V4/Identity/lib/jquery/dist/jquery.min.map b/src/Identity/UI/src/wwwroot/V4/lib/jquery/dist/jquery.min.map similarity index 100% rename from src/Identity/UI/src/wwwroot/V4/Identity/lib/jquery/dist/jquery.min.map rename to src/Identity/UI/src/wwwroot/V4/lib/jquery/dist/jquery.min.map diff --git a/src/Identity/samples/IdentitySample.DefaultUI/Areas/Identity/Pages/_ViewStart.cshtml b/src/Identity/samples/IdentitySample.DefaultUI/Areas/Identity/Pages/_ViewStart.cshtml deleted file mode 100644 index c4284f6c20..0000000000 --- a/src/Identity/samples/IdentitySample.DefaultUI/Areas/Identity/Pages/_ViewStart.cshtml +++ /dev/null @@ -1,3 +0,0 @@ -@{ - Layout = "/Views/Shared/_Layout.cshtml"; -} diff --git a/src/Identity/samples/IdentitySample.DefaultUI/IdentitySample.DefaultUI.csproj b/src/Identity/samples/IdentitySample.DefaultUI/IdentitySample.DefaultUI.csproj index ecfe756f9f..5512886233 100644 --- a/src/Identity/samples/IdentitySample.DefaultUI/IdentitySample.DefaultUI.csproj +++ b/src/Identity/samples/IdentitySample.DefaultUI/IdentitySample.DefaultUI.csproj @@ -1,12 +1,14 @@ - + Identity sample MVC application on ASP.NET Core using the default UI netcoreapp3.0 aspnetcore-2ff9bc27-5e8c-4484-90ca-e3aace89b72a + Bootstrap4 + @@ -26,6 +28,7 @@ + @@ -34,4 +37,23 @@ + + + + <_Parameter1>$(IdentityUIFrameworkVersion) + + + + + _SetBootstrapFrameworkVersion + + + + + <_StaticWebAssetsProjectReference Condition="'%(FileName)' == 'Microsoft.AspNetCore.identity.UI'"> + IdentityUIFrameworkVersion=$(IdentityUIFrameworkVersion) + + + + diff --git a/src/Identity/samples/IdentitySample.DefaultUI/Program.cs b/src/Identity/samples/IdentitySample.DefaultUI/Program.cs index 6a8f2a7048..2669c06b7f 100644 --- a/src/Identity/samples/IdentitySample.DefaultUI/Program.cs +++ b/src/Identity/samples/IdentitySample.DefaultUI/Program.cs @@ -1,5 +1,6 @@ using System.IO; using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; namespace IdentitySample.DefaultUI { @@ -7,17 +8,19 @@ namespace IdentitySample.DefaultUI { public static void Main(string[] args) { - var host = CreateWebHostBuilder(args) - .Build(); - - host.Run(); + CreateHostBuilder(args).Build().Run(); } - public static IWebHostBuilder CreateWebHostBuilder(string[] args) => - new WebHostBuilder() - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) - .UseIISIntegration() - .UseStartup(); + public static bool UseStartup { get; set; } = true; + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + if (UseStartup) + { + webBuilder.UseStartup(); + } + }); } -} \ No newline at end of file +} diff --git a/src/Identity/test/Identity.FunctionalTests/Bootstrap3Tests/Bootstrap3AuthorizationTests.cs b/src/Identity/test/Identity.FunctionalTests/Bootstrap3Tests/Bootstrap3AuthorizationTests.cs index 580d1f5440..e5323fa388 100644 --- a/src/Identity/test/Identity.FunctionalTests/Bootstrap3Tests/Bootstrap3AuthorizationTests.cs +++ b/src/Identity/test/Identity.FunctionalTests/Bootstrap3Tests/Bootstrap3AuthorizationTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using Identity.DefaultUI.WebSite; @@ -6,10 +6,11 @@ using Identity.DefaultUI.WebSite.Data; namespace Microsoft.AspNetCore.Identity.FunctionalTests.IdentityUserTests { - public class Bootstrap3AuthorizationTests : AuthorizationTests + public class Bootstrap3AuthorizationTests : AuthorizationTests { - public Bootstrap3AuthorizationTests(ServerFactory serverFactory) : base(serverFactory) + public Bootstrap3AuthorizationTests(ServerFactory serverFactory) : base(serverFactory) { + serverFactory.BootstrapFrameworkVersion = "V3"; } } } diff --git a/src/Identity/test/Identity.FunctionalTests/Bootstrap3Tests/Bootstrap3ManagementTests.cs b/src/Identity/test/Identity.FunctionalTests/Bootstrap3Tests/Bootstrap3ManagementTests.cs index 999a479261..1070b3f0f0 100644 --- a/src/Identity/test/Identity.FunctionalTests/Bootstrap3Tests/Bootstrap3ManagementTests.cs +++ b/src/Identity/test/Identity.FunctionalTests/Bootstrap3Tests/Bootstrap3ManagementTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using Identity.DefaultUI.WebSite; @@ -6,10 +6,11 @@ using Identity.DefaultUI.WebSite.Data; namespace Microsoft.AspNetCore.Identity.FunctionalTests.Bootstrap3Tests { - public class Bootstrap3ManagementTests : ManagementTests + public class Bootstrap3ManagementTests : ManagementTests { - public Bootstrap3ManagementTests(ServerFactory serverFactory) : base(serverFactory) + public Bootstrap3ManagementTests(ServerFactory serverFactory) : base(serverFactory) { + serverFactory.BootstrapFrameworkVersion = "V3"; } } } diff --git a/src/Identity/test/Identity.FunctionalTests/Bootstrap3Tests/Bootstrap3RegistrationTests.cs b/src/Identity/test/Identity.FunctionalTests/Bootstrap3Tests/Bootstrap3RegistrationTests.cs index 325811dd93..7fd7c2e0a3 100644 --- a/src/Identity/test/Identity.FunctionalTests/Bootstrap3Tests/Bootstrap3RegistrationTests.cs +++ b/src/Identity/test/Identity.FunctionalTests/Bootstrap3Tests/Bootstrap3RegistrationTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using Identity.DefaultUI.WebSite; @@ -6,10 +6,11 @@ using Identity.DefaultUI.WebSite.Data; namespace Microsoft.AspNetCore.Identity.FunctionalTests.Bootstrap3Tests { - public class Bootstrap3RegistrationTests : RegistrationTests + public class Bootstrap3RegistrationTests : RegistrationTests { - public Bootstrap3RegistrationTests(ServerFactory serverFactory) : base(serverFactory) + public Bootstrap3RegistrationTests(ServerFactory serverFactory) : base(serverFactory) { + serverFactory.BootstrapFrameworkVersion = "V3"; } } } diff --git a/src/Identity/test/Identity.FunctionalTests/Bootstrap3Tests/Bootstrap3UserLoginTests.cs b/src/Identity/test/Identity.FunctionalTests/Bootstrap3Tests/Bootstrap3UserLoginTests.cs index b4cb9be42e..31c66f28a6 100644 --- a/src/Identity/test/Identity.FunctionalTests/Bootstrap3Tests/Bootstrap3UserLoginTests.cs +++ b/src/Identity/test/Identity.FunctionalTests/Bootstrap3Tests/Bootstrap3UserLoginTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using Identity.DefaultUI.WebSite; @@ -6,10 +6,11 @@ using Identity.DefaultUI.WebSite.Data; namespace Microsoft.AspNetCore.Identity.FunctionalTests.Bootstrap3Tests { - public class Bootstrap3LoginTests : LoginTests + public class Bootstrap3LoginTests : LoginTests { - public Bootstrap3LoginTests(ServerFactory serverFactory) : base(serverFactory) + public Bootstrap3LoginTests(ServerFactory serverFactory) : base(serverFactory) { + serverFactory.BootstrapFrameworkVersion = "V3"; } } } diff --git a/src/Identity/test/Identity.FunctionalTests/Infrastructure/ServerFactory.cs b/src/Identity/test/Identity.FunctionalTests/Infrastructure/ServerFactory.cs index 7082dfb442..95fafbedd1 100644 --- a/src/Identity/test/Identity.FunctionalTests/Infrastructure/ServerFactory.cs +++ b/src/Identity/test/Identity.FunctionalTests/Infrastructure/ServerFactory.cs @@ -2,19 +2,26 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; using Identity.DefaultUI.WebSite; -using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Hosting.StaticWebAssets; +using Microsoft.AspNetCore.Identity.UI; +using Microsoft.AspNetCore.Mvc.ApplicationParts; using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.AspNetCore.TestHost; using Microsoft.Data.Sqlite; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Hosting; namespace Microsoft.AspNetCore.Identity.FunctionalTests { - public class ServerFactory: WebApplicationFactory + public class ServerFactory : WebApplicationFactory where TStartup : class where TContext : DbContext { @@ -29,6 +36,10 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests ClientOptions.BaseAddress = new Uri("https://localhost"); } + public string BootstrapFrameworkVersion { get; set; } = "V4"; + private bool IsHelixCI => typeof(ServerFactory<,>).Assembly.GetCustomAttributes() + .Any(a => a.Key == "Microsoft.AspNetCore.Testing.IsHelixCI"); + protected override IHostBuilder CreateHostBuilder() { Program.UseStartup = false; @@ -48,6 +59,70 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests // several places to pass important data in post-redirect-get flows. .AddCookieTempDataProvider(o => o.Cookie.IsEssential = true); }); + + UpdateStaticAssets(builder); + UpdateApplicationParts(builder); + } + + private void UpdateApplicationParts(IWebHostBuilder builder) => + builder.ConfigureServices(services => AddRelatedParts(services, BootstrapFrameworkVersion)); + + private void UpdateStaticAssets(IWebHostBuilder builder) + { + var manifestPath = Path.GetDirectoryName(new Uri(typeof(ServerFactory<,>).Assembly.CodeBase).LocalPath); + builder.ConfigureAppConfiguration((ctx, cb) => + { + if (ctx.HostingEnvironment.WebRootFileProvider is CompositeFileProvider composite) + { + var originalWebRoot = composite.FileProviders.First(); + ctx.HostingEnvironment.WebRootFileProvider = originalWebRoot; + } + }); + + string versionedPath = Path.Combine(manifestPath, $"Testing.DefaultWebSite.StaticWebAssets.{BootstrapFrameworkVersion}.xml"); + UpdateManifest(versionedPath); + + builder.ConfigureAppConfiguration((context, configBuilder) => + { + using (var manifest = File.OpenRead(versionedPath)) + { + typeof(StaticWebAssetsLoader) + .GetMethod("UseStaticWebAssetsCore", BindingFlags.NonPublic | BindingFlags.Static) + .Invoke(null, new object[] { context.HostingEnvironment, manifest }); + } + }); + } + + private void UpdateManifest(string versionedPath) + { + var content = File.ReadAllText(versionedPath); + var path = typeof(ServerFactory<,>).Assembly.GetCustomAttributes() + .Single(a => a.Key == "Microsoft.AspNetCore.Testing.IdentityUIProjectPath").Value; + + path = Directory.Exists(path) ? Path.Combine(path, "wwwroot") : Path.Combine(FindHelixSlnFileDirectory(), "UI", "wwwroot"); + + var updatedContent = content.Replace("{TEST_PLACEHOLDER}", path); + + File.WriteAllText(versionedPath, updatedContent); + } + + private string FindHelixSlnFileDirectory() + { + var applicationPath = Path.GetDirectoryName(typeof(ServerFactory<,>).Assembly.Location); + var directoryInfo = new DirectoryInfo(applicationPath); + do + { + var solutionPath = Directory.EnumerateFiles(directoryInfo.FullName, "*.sln").FirstOrDefault(); + if (solutionPath != null) + { + return directoryInfo.FullName; + } + + directoryInfo = directoryInfo.Parent; + } + while (directoryInfo.Parent != null); + + throw new InvalidOperationException($"Solution root could not be located using application root {applicationPath}."); } protected override IHost CreateHost(IHostBuilder builder) @@ -78,5 +153,76 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests base.Dispose(disposing); } + + private static void AddRelatedParts(IServiceCollection services, string framework) + { + var _assemblyMap = + new Dictionary() + { + [UIFramework.Bootstrap3] = "Microsoft.AspNetCore.Identity.UI.Views.V3", + [UIFramework.Bootstrap4] = "Microsoft.AspNetCore.Identity.UI.Views.V4", + }; + + var mvcBuilder = services + .AddMvc() + .ConfigureApplicationPartManager(partManager => + { + var thisAssembly = typeof(IdentityBuilderUIExtensions).Assembly; + var relatedAssemblies = RelatedAssemblyAttribute.GetRelatedAssemblies(thisAssembly, throwOnError: true); + var relatedParts = relatedAssemblies.ToDictionary( + ra => ra, + CompiledRazorAssemblyApplicationPartFactory.GetDefaultApplicationParts); + + var selectedFrameworkAssembly = _assemblyMap[framework == "V3" ? UIFramework.Bootstrap3 : UIFramework.Bootstrap4]; + + foreach (var kvp in relatedParts) + { + var assemblyName = kvp.Key.GetName().Name; + if (!IsAssemblyForFramework(selectedFrameworkAssembly, assemblyName)) + { + RemoveParts(partManager, kvp.Value); + } + else + { + AddParts(partManager, kvp.Value); + } + } + + bool IsAssemblyForFramework(string frameworkAssembly, string assemblyName) => + string.Equals(assemblyName, frameworkAssembly, StringComparison.OrdinalIgnoreCase); + + void RemoveParts( + ApplicationPartManager manager, + IEnumerable partsToRemove) + { + for (var i = 0; i < manager.ApplicationParts.Count; i++) + { + var part = manager.ApplicationParts[i]; + if (partsToRemove.Any(p => string.Equals( + p.Name, + part.Name, + StringComparison.OrdinalIgnoreCase))) + { + manager.ApplicationParts.Remove(part); + } + } + } + + void AddParts( + ApplicationPartManager manager, + IEnumerable partsToAdd) + { + foreach (var part in partsToAdd) + { + if (!manager.ApplicationParts.Any(p => p.GetType() == part.GetType() && + string.Equals(p.Name, part.Name, StringComparison.OrdinalIgnoreCase))) + { + manager.ApplicationParts.Add(part); + } + } + } + }); + } + } } diff --git a/src/Identity/test/Identity.FunctionalTests/Microsoft.AspNetCore.Identity.FunctionalTests.csproj b/src/Identity/test/Identity.FunctionalTests/Microsoft.AspNetCore.Identity.FunctionalTests.csproj index 379ee6603a..534f1b50a5 100644 --- a/src/Identity/test/Identity.FunctionalTests/Microsoft.AspNetCore.Identity.FunctionalTests.csproj +++ b/src/Identity/test/Identity.FunctionalTests/Microsoft.AspNetCore.Identity.FunctionalTests.csproj @@ -7,9 +7,10 @@ - + + @@ -27,6 +28,39 @@ + + + Always + + + Always + + + Always + + + + + + <_IdentitUIDefaultWebSite Include="@(ReferencePath)" Condition="'%(ReferencePath.FileName)' == 'Identity.DefaultUI.WebSite'" /> + + <_Parameter1>Microsoft.AspNetCore.Testing.DefaultWebSiteProjectPath + <_Parameter2>$([System.IO.Path]::GetDirectoryName('%(_IdentitUIDefaultWebSite.MSBuildSourceProjectFile)')) + + + <_IdentitUIProjectPath Include="@(ReferencePath)" Condition="'%(ReferencePath.FileName)' == 'Microsoft.AspNetCore.Identity.UI'" /> + + <_Parameter1>Microsoft.AspNetCore.Testing.IdentityUIProjectPath + <_Parameter2>$([System.IO.Path]::GetDirectoryName('%(_IdentitUIProjectPath.MSBuildSourceProjectFile)')) + + + + <_Parameter1>Microsoft.AspNetCore.Testing.IsHelixCI + <_Parameter2>true + + + + <_PublishFiles Include="$(ArtifactsBinDir)Microsoft.AspNetCore.Identity.UI\$(Configuration)\netcoreapp3.0\Microsoft.AspNetCore.Identity.UI.Views.*.dll" /> @@ -34,22 +68,16 @@ <_PublishFiles Include="$(ArtifactsBinDir)Identity.DefaultUI.WebSite\$(Configuration)\netcoreapp3.0\Identity.DefaultUI.WebSite.deps.json" /> <_wwwrootFiles Include="$(MSBuildThisFileDirectory)..\..\testassets\Identity.DefaultUI.WebSite\wwwroot\**\*.*" /> <_PagesFiles Include="$(MSBuildThisFileDirectory)..\..\testassets\Identity.DefaultUI.WebSite\Pages\**\*.*" /> + <_IdentityUIContent Include="$(MSBuildThisFileDirectory)..\..\UI\src\wwwroot\**\*" /> + <_IdentityUIPages Include="$(MSBuildThisFileDirectory)..\..\UI\src\Areas\Identity\Pages\**\*" /> - - - + + + + + - + diff --git a/src/Identity/test/Identity.FunctionalTests/Testing.DefaultWebSite.StaticWebAssets.V3.xml b/src/Identity/test/Identity.FunctionalTests/Testing.DefaultWebSite.StaticWebAssets.V3.xml new file mode 100644 index 0000000000..3776c285c5 --- /dev/null +++ b/src/Identity/test/Identity.FunctionalTests/Testing.DefaultWebSite.StaticWebAssets.V3.xml @@ -0,0 +1,3 @@ + + + diff --git a/src/Identity/test/Identity.FunctionalTests/Testing.DefaultWebSite.StaticWebAssets.V4.xml b/src/Identity/test/Identity.FunctionalTests/Testing.DefaultWebSite.StaticWebAssets.V4.xml new file mode 100644 index 0000000000..462ea618ce --- /dev/null +++ b/src/Identity/test/Identity.FunctionalTests/Testing.DefaultWebSite.StaticWebAssets.V4.xml @@ -0,0 +1,3 @@ + + + diff --git a/src/Identity/test/Identity.Test/IdentityUIScriptsTest.cs b/src/Identity/test/Identity.Test/IdentityUIScriptsTest.cs index 8ff722cbe3..d6217cedff 100644 --- a/src/Identity/test/Identity.Test/IdentityUIScriptsTest.cs +++ b/src/Identity/test/Identity.Test/IdentityUIScriptsTest.cs @@ -15,6 +15,7 @@ using Microsoft.AspNetCore.Testing; using Microsoft.AspNetCore.Testing.xunit; using Xunit; using Xunit.Abstractions; +using System.Reflection; namespace Microsoft.AspNetCore.Identity.Test { @@ -83,11 +84,11 @@ namespace Microsoft.AspNetCore.Identity.Test [Flaky("https://github.com/aspnet/AspNetCore-Internal/issues/2267", FlakyOn.AzP.macOS)] public async Task IdentityUI_ScriptTags_FallbackSourceContent_Matches_CDNContent(ScriptTag scriptTag) { - var wwwrootDir = Path.Combine(AppContext.BaseDirectory, "UI", "src", "wwwroot", scriptTag.Version); + var wwwrootDir = Path.Combine(GetProjectBasePath(), "wwwroot", scriptTag.Version); var cdnContent = await _httpClient.GetStringAsync(scriptTag.Src); var fallbackSrcContent = File.ReadAllText( - Path.Combine(wwwrootDir, scriptTag.FallbackSrc.TrimStart('~').TrimStart('/'))); + Path.Combine(wwwrootDir, scriptTag.FallbackSrc.Replace("Identity", "").TrimStart('~').TrimStart('/'))); Assert.Equal(RemoveLineEndings(cdnContent), RemoveLineEndings(fallbackSrcContent)); } @@ -108,8 +109,8 @@ namespace Microsoft.AspNetCore.Identity.Test private static List GetScriptTags() { - var uiDirV3 = Path.Combine(AppContext.BaseDirectory, "UI", "src", "Areas", "Identity", "Pages", "V3"); - var uiDirV4 = Path.Combine(AppContext.BaseDirectory, "UI", "src", "Areas", "Identity", "Pages", "V4"); + var uiDirV3 = Path.Combine(GetProjectBasePath(), "Areas", "Identity", "Pages", "V3"); + var uiDirV4 = Path.Combine(GetProjectBasePath(), "Areas", "Identity", "Pages", "V4"); var cshtmlFiles = GetRazorFiles(uiDirV3).Concat(GetRazorFiles(uiDirV4)); var scriptTags = new List(); @@ -163,6 +164,32 @@ namespace Microsoft.AspNetCore.Identity.Test _httpClient.Dispose(); } + private static string GetProjectBasePath() + { + var projectPath = typeof(IdentityUIScriptsTest).Assembly.GetCustomAttributes() + .Single(a => a.Key == "Microsoft.AspNetCore.Testing.DefaultUIProjectPath").Value; + return Directory.Exists(projectPath) ? projectPath : Path.Combine(FindHelixSlnFileDirectory(), "UI"); + } + + private static string FindHelixSlnFileDirectory() + { + var applicationPath = Path.GetDirectoryName(typeof(IdentityUIScriptsTest).Assembly.Location); + var directoryInfo = new DirectoryInfo(applicationPath); + do + { + var solutionPath = Directory.EnumerateFiles(directoryInfo.FullName, "*.sln").FirstOrDefault(); + if (solutionPath != null) + { + return directoryInfo.FullName; + } + + directoryInfo = directoryInfo.Parent; + } + while (directoryInfo.Parent != null); + + throw new InvalidOperationException($"Solution root could not be located using application root {applicationPath}."); + } + class RetryHandler : DelegatingHandler { public RetryHandler(HttpMessageHandler innerHandler) : base(innerHandler) { } diff --git a/src/Identity/test/Identity.Test/Microsoft.AspNetCore.Identity.Test.csproj b/src/Identity/test/Identity.Test/Microsoft.AspNetCore.Identity.Test.csproj index 3e34340ad8..9ed81cbcb8 100644 --- a/src/Identity/test/Identity.Test/Microsoft.AspNetCore.Identity.Test.csproj +++ b/src/Identity/test/Identity.Test/Microsoft.AspNetCore.Identity.Test.csproj @@ -7,8 +7,6 @@ - - @@ -17,8 +15,31 @@ + + + + <_IdentitUIDefaultUI Include="@(ReferencePath)" Condition="'%(ReferencePath.FileName)' == 'Microsoft.AspNetCore.Identity.UI'" /> + + <_Parameter1>Microsoft.AspNetCore.Testing.DefaultUIProjectPath + <_Parameter2>$([System.IO.Path]::GetDirectoryName('%(_IdentitUIDefaultUI.MSBuildSourceProjectFile)')) + + + + + + + <_IdentityUIContent Include="$(MSBuildThisFileDirectory)..\..\UI\src\wwwroot\**\*" /> + <_IdentityUIPages Include="$(MSBuildThisFileDirectory)..\..\UI\src\Areas\Identity\Pages\**\*" /> + + + + + + + + diff --git a/src/Identity/testassets/Identity.DefaultUI.WebSite/Bootstrap3Startup.cs b/src/Identity/testassets/Identity.DefaultUI.WebSite/Bootstrap3Startup.cs deleted file mode 100644 index 4670238288..0000000000 --- a/src/Identity/testassets/Identity.DefaultUI.WebSite/Bootstrap3Startup.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Identity.UI; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; - -namespace Identity.DefaultUI.WebSite -{ - public class Bootstrap3Startup : ApplicationUserStartup - { - public Bootstrap3Startup(IConfiguration configuration) : base(configuration) - { - } - - public override UIFramework Framework => UIFramework.Bootstrap3; - } -} diff --git a/src/Identity/testassets/Identity.DefaultUI.WebSite/Identity.DefaultUI.WebSite.csproj b/src/Identity/testassets/Identity.DefaultUI.WebSite/Identity.DefaultUI.WebSite.csproj index 199d523d7d..ad7f35bb53 100644 --- a/src/Identity/testassets/Identity.DefaultUI.WebSite/Identity.DefaultUI.WebSite.csproj +++ b/src/Identity/testassets/Identity.DefaultUI.WebSite/Identity.DefaultUI.WebSite.csproj @@ -3,6 +3,7 @@ netcoreapp3.0 aspnet-Identity.DefaultUI.WebSite-80C658D8-CED7-467F-9B47-75DA3BC1A16D + Bootstrap3 @@ -43,4 +44,15 @@ + + _SetBootstrapFrameworkVersion + + + + + <_StaticWebAssetsProjectReference Condition="'%(FileName)' == 'Microsoft.AspNetCore.Identity.UI'"> + IdentityDefaultUIFramework=$(IdentityDefaultUIFramework) + + + diff --git a/src/Identity/testassets/Identity.DefaultUI.WebSite/PocoUserStartup.cs b/src/Identity/testassets/Identity.DefaultUI.WebSite/PocoUserStartup.cs index df60be2eed..a16c7a7e80 100644 --- a/src/Identity/testassets/Identity.DefaultUI.WebSite/PocoUserStartup.cs +++ b/src/Identity/testassets/Identity.DefaultUI.WebSite/PocoUserStartup.cs @@ -27,7 +27,6 @@ namespace Identity.DefaultUI.WebSite }); services.AddDefaultIdentity() - .AddDefaultUI(UIFramework.Bootstrap4) .AddUserManager>(); services.AddSingleton, InMemoryUserStore>(); diff --git a/src/Identity/testassets/Identity.DefaultUI.WebSite/StartupBase.cs b/src/Identity/testassets/Identity.DefaultUI.WebSite/StartupBase.cs index b6372bbc2b..77706eeb79 100644 --- a/src/Identity/testassets/Identity.DefaultUI.WebSite/StartupBase.cs +++ b/src/Identity/testassets/Identity.DefaultUI.WebSite/StartupBase.cs @@ -28,8 +28,6 @@ namespace Identity.DefaultUI.WebSite public IConfiguration Configuration { get; } - public virtual UIFramework Framework { get; } = UIFramework.Bootstrap4; - // This method gets called by the runtime. Use this method to add services to the container. public virtual void ConfigureServices(IServiceCollection services) { @@ -49,7 +47,6 @@ namespace Identity.DefaultUI.WebSite .UseSqlite("DataSource=:memory:")); services.AddDefaultIdentity() - .AddDefaultUI(Framework) .AddRoles() .AddEntityFrameworkStores(); diff --git a/src/ProjectTemplates/test/Helpers/Project.cs b/src/ProjectTemplates/test/Helpers/Project.cs index f2e5a94f36..5d2ffcc237 100644 --- a/src/ProjectTemplates/test/Helpers/Project.cs +++ b/src/ProjectTemplates/test/Helpers/Project.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; @@ -41,11 +42,19 @@ namespace Templates.Test.Helpers public ITestOutputHelper Output { get; set; } public IMessageSink DiagnosticsMessageSink { get; set; } - internal async Task RunDotNetNewAsync(string templateName, string auth = null, string language = null, bool useLocalDB = false, bool noHttps = false, string[] args = null) + internal async Task RunDotNetNewAsync( + string templateName, + string auth = null, + string language = null, + bool useLocalDB = false, + bool noHttps = false, + string[] args = null, + // Used to set special options in MSBuild + IDictionary environmentVariables = null) { var hiveArg = $"--debug:custom-hive \"{TemplatePackageInstaller.CustomHivePath}\""; var argString = $"new {templateName} {hiveArg}"; - + environmentVariables ??= new Dictionary(); if (!string.IsNullOrEmpty(auth)) { argString += $" --auth {auth}"; @@ -86,7 +95,7 @@ namespace Templates.Test.Helpers await DotNetNewLock.WaitAsync(); try { - var execution = ProcessEx.Run(Output, AppContext.BaseDirectory, DotNetMuxer.MuxerPathOrDefault(), argString); + var execution = ProcessEx.Run(Output, AppContext.BaseDirectory, DotNetMuxer.MuxerPathOrDefault(), argString, environmentVariables); await execution.Exited; return execution; } @@ -96,7 +105,7 @@ namespace Templates.Test.Helpers } } - internal async Task RunDotNetPublishAsync(bool takeNodeLock = false) + internal async Task RunDotNetPublishAsync(bool takeNodeLock = false, IDictionary packageOptions = null) { Output.WriteLine("Publishing ASP.NET application..."); @@ -112,7 +121,7 @@ namespace Templates.Test.Helpers await effectiveLock.WaitAsync(); try { - var result = ProcessEx.Run(Output, TemplateOutputDir, DotNetMuxer.MuxerPathOrDefault(), $"publish -c Release {extraArgs}"); + var result = ProcessEx.Run(Output, TemplateOutputDir, DotNetMuxer.MuxerPathOrDefault(), $"publish -c Release {extraArgs}", packageOptions); await result.Exited; return result; } @@ -122,7 +131,7 @@ namespace Templates.Test.Helpers } } - internal async Task RunDotNetBuildAsync(bool takeNodeLock = false) + internal async Task RunDotNetBuildAsync(bool takeNodeLock = false, IDictionary packageOptions = null) { Output.WriteLine("Building ASP.NET application..."); @@ -133,7 +142,7 @@ namespace Templates.Test.Helpers await effectiveLock.WaitAsync(); try { - var result = ProcessEx.Run(Output, TemplateOutputDir, DotNetMuxer.MuxerPathOrDefault(), "build -c Debug"); + var result = ProcessEx.Run(Output, TemplateOutputDir, DotNetMuxer.MuxerPathOrDefault(), "build -c Debug", packageOptions); await result.Exited; return result; } diff --git a/src/ProjectTemplates/test/IdentityUIPackageTest.cs b/src/ProjectTemplates/test/IdentityUIPackageTest.cs new file mode 100644 index 0000000000..2620a5dc4c --- /dev/null +++ b/src/ProjectTemplates/test/IdentityUIPackageTest.cs @@ -0,0 +1,190 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Threading.Tasks; +using Templates.Test.Helpers; +using Xunit; +using Xunit.Abstractions; + +namespace Templates.Test +{ + public class IdentityUIPackageTest + { + public IdentityUIPackageTest(ProjectFactoryFixture projectFactory, ITestOutputHelper output) + { + ProjectFactory = projectFactory; + Output = output; + } + + public Project Project { get; set; } + + public ProjectFactoryFixture ProjectFactory { get; set; } + + public ITestOutputHelper Output { get; } + + public static TheoryData, string, string[]> MSBuildIdentityUIPackageOptions + { + get + { + var data = new TheoryData, string, string[]>(); + + data.Add(new Dictionary + { + ["IdentityUIFrameworkVersion"] = "Bootstrap3" + }, + "Bootstrap v3.4.1", + Bootstrap3ContentFiles); + + data.Add(new Dictionary(), "Bootstrap v4.3.1", Bootstrap4ContentFiles); + + return data; + } + } + + public static string[] Bootstrap3ContentFiles { get; } = new string[] + { + "Identity/css/site.css", + "Identity/js/site.js", + "Identity/lib/bootstrap/dist/css/bootstrap-theme.css", + "Identity/lib/bootstrap/dist/css/bootstrap-theme.css.map", + "Identity/lib/bootstrap/dist/css/bootstrap-theme.min.css", + "Identity/lib/bootstrap/dist/css/bootstrap-theme.min.css.map", + "Identity/lib/bootstrap/dist/css/bootstrap.css", + "Identity/lib/bootstrap/dist/css/bootstrap.css.map", + "Identity/lib/bootstrap/dist/css/bootstrap.min.css", + "Identity/lib/bootstrap/dist/css/bootstrap.min.css.map", + "Identity/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.eot", + "Identity/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.svg", + "Identity/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.ttf", + "Identity/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff", + "Identity/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff2", + "Identity/lib/bootstrap/dist/js/bootstrap.js", + "Identity/lib/bootstrap/dist/js/bootstrap.min.js", + "Identity/lib/bootstrap/dist/js/npm.js", + "Identity/lib/jquery/LICENSE.txt", + "Identity/lib/jquery/dist/jquery.js", + "Identity/lib/jquery/dist/jquery.min.js", + "Identity/lib/jquery/dist/jquery.min.map", + "Identity/lib/jquery-validation/LICENSE.md", + "Identity/lib/jquery-validation/dist/additional-methods.js", + "Identity/lib/jquery-validation/dist/additional-methods.min.js", + "Identity/lib/jquery-validation/dist/jquery.validate.js", + "Identity/lib/jquery-validation/dist/jquery.validate.min.js", + "Identity/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js", + "Identity/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js", + "Identity/lib/jquery-validation-unobtrusive/LICENSE.txt", + }; + + public static string[] Bootstrap4ContentFiles { get; } = new string[] + { + "Identity/favicon.ico", + "Identity/css/site.css", + "Identity/js/site.js", + "Identity/lib/bootstrap/dist/css/bootstrap-grid.css", + "Identity/lib/bootstrap/dist/css/bootstrap-grid.css.map", + "Identity/lib/bootstrap/dist/css/bootstrap-grid.min.css", + "Identity/lib/bootstrap/dist/css/bootstrap-grid.min.css.map", + "Identity/lib/bootstrap/dist/css/bootstrap-reboot.css", + "Identity/lib/bootstrap/dist/css/bootstrap-reboot.css.map", + "Identity/lib/bootstrap/dist/css/bootstrap-reboot.min.css", + "Identity/lib/bootstrap/dist/css/bootstrap-reboot.min.css.map", + "Identity/lib/bootstrap/dist/css/bootstrap.css", + "Identity/lib/bootstrap/dist/css/bootstrap.css.map", + "Identity/lib/bootstrap/dist/css/bootstrap.min.css", + "Identity/lib/bootstrap/dist/css/bootstrap.min.css.map", + "Identity/lib/bootstrap/dist/js/bootstrap.bundle.js", + "Identity/lib/bootstrap/dist/js/bootstrap.bundle.js.map", + "Identity/lib/bootstrap/dist/js/bootstrap.bundle.min.js", + "Identity/lib/bootstrap/dist/js/bootstrap.bundle.min.js.map", + "Identity/lib/bootstrap/dist/js/bootstrap.js", + "Identity/lib/bootstrap/dist/js/bootstrap.js.map", + "Identity/lib/bootstrap/dist/js/bootstrap.min.js", + "Identity/lib/bootstrap/dist/js/bootstrap.min.js.map", + "Identity/lib/jquery/LICENSE.txt", + "Identity/lib/jquery/dist/jquery.js", + "Identity/lib/jquery/dist/jquery.min.js", + "Identity/lib/jquery/dist/jquery.min.map", + "Identity/lib/jquery-validation/LICENSE.md", + "Identity/lib/jquery-validation/dist/additional-methods.js", + "Identity/lib/jquery-validation/dist/additional-methods.min.js", + "Identity/lib/jquery-validation/dist/jquery.validate.js", + "Identity/lib/jquery-validation/dist/jquery.validate.min.js", + "Identity/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js", + "Identity/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js", + "Identity/lib/jquery-validation-unobtrusive/LICENSE.txt", + }; + + [Theory] + [MemberData(nameof(MSBuildIdentityUIPackageOptions))] + public async Task IdentityUIPackage_WorksWithDifferentOptions(IDictionary packageOptions, string versionValidator, string[] expectedFiles) + { + Project = await ProjectFactory.GetOrCreateProject("identityuipackage" + string.Concat(packageOptions.Values), Output); + var useLocalDB = false; + + var createResult = await Project.RunDotNetNewAsync("razor", auth: "Individual", useLocalDB: useLocalDB, environmentVariables: packageOptions); + Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", Project, createResult)); + + var projectFileContents = ReadFile(Project.TemplateOutputDir, $"{Project.ProjectName}.csproj"); + Assert.Contains(".db", projectFileContents); + + var publishResult = await Project.RunDotNetPublishAsync(packageOptions: packageOptions); + Assert.True(0 == publishResult.ExitCode, ErrorMessages.GetFailedProcessMessage("publish", Project, publishResult)); + + // Run dotnet build after publish. The reason is that one uses Config = Debug and the other uses Config = Release + // The output from publish will go into bin/Release/netcoreapp3.0/publish and won't be affected by calling build + // later, while the opposite is not true. + + var buildResult = await Project.RunDotNetBuildAsync(packageOptions: packageOptions); + Assert.True(0 == buildResult.ExitCode, ErrorMessages.GetFailedProcessMessage("build", Project, buildResult)); + + var migrationsResult = await Project.RunDotNetEfCreateMigrationAsync("razorpages"); + Assert.True(0 == migrationsResult.ExitCode, ErrorMessages.GetFailedProcessMessage("run EF migrations", Project, migrationsResult)); + Project.AssertEmptyMigration("razorpages"); + + using (var aspNetProcess = Project.StartBuiltProjectAsync()) + { + Assert.False( + aspNetProcess.Process.HasExited, + ErrorMessages.GetFailedProcessMessageOrEmpty("Run built project", Project, aspNetProcess.Process)); + + var response = await aspNetProcess.SendRequest("/Identity/lib/bootstrap/dist/css/bootstrap.css"); + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Contains(versionValidator, await response.Content.ReadAsStringAsync()); + await ValidatePublishedFiles(aspNetProcess, expectedFiles); + } + + using (var aspNetProcess = Project.StartPublishedProjectAsync()) + { + Assert.False( + aspNetProcess.Process.HasExited, + ErrorMessages.GetFailedProcessMessageOrEmpty("Run built project", Project, aspNetProcess.Process)); + + var response = await aspNetProcess.SendRequest("/Identity/lib/bootstrap/dist/css/bootstrap.css"); + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Contains(versionValidator, await response.Content.ReadAsStringAsync()); + await ValidatePublishedFiles(aspNetProcess, expectedFiles); + } + } + + private async Task ValidatePublishedFiles(AspNetProcess aspNetProcess, string[] expectedContentFiles) + { + foreach (var file in expectedContentFiles) + { + var response = await aspNetProcess.SendRequest(file); + Assert.True(response?.StatusCode == HttpStatusCode.OK, $"Couldn't find file '{file}'"); + } + } + + private string ReadFile(string basePath, string path) + { + var fullPath = Path.Combine(basePath, path); + var doesExist = File.Exists(fullPath); + + Assert.True(doesExist, $"Expected file to exist, but it doesn't: {path}"); + return File.ReadAllText(Path.Combine(basePath, path)); + } + } +}