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));
+ }
+ }
+}