Skip to content

Commit 143c101

Browse files
authored
[Identity] Move to use static web assets support. (#11029)
* Moves Identity UI to use Static Web Assets * Removes the static files as embedded content. * Stops plugging the static assets through the embedded file provider. * Selects the UI framework at build time instead of runtime.
1 parent 7a496ec commit 143c101

File tree

95 files changed

+723
-197
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

95 files changed

+723
-197
lines changed

src/Identity/IdentityNoDeps.slnf

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"solution": {
3+
"path": "Identity.sln",
4+
"projects": [
5+
"ApiAuthorization.IdentityServer\\samples\\ApiAuthSample\\ApiAuthSample.csproj",
6+
"ApiAuthorization.IdentityServer\\src\\Microsoft.AspNetCore.ApiAuthorization.IdentityServer.csproj",
7+
"ApiAuthorization.IdentityServer\\test\\Microsoft.AspNetCore.ApiAuthorization.IdentityServer.Tests.csproj",
8+
"Core\\src\\Microsoft.AspNetCore.Identity.csproj",
9+
"EntityFrameworkCore\\src\\Microsoft.AspNetCore.Identity.EntityFrameworkCore.csproj",
10+
"EntityFrameworkCore\\test\\EF.InMemory.Test\\Microsoft.AspNetCore.Identity.EntityFrameworkCore.InMemory.Test.csproj",
11+
"EntityFrameworkCore\\test\\EF.Test\\Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test.csproj",
12+
"Extensions.Core\\src\\Microsoft.Extensions.Identity.Core.csproj",
13+
"Extensions.Stores\\src\\Microsoft.Extensions.Identity.Stores.csproj",
14+
"Specification.Tests\\src\\Microsoft.AspNetCore.Identity.Specification.Tests.csproj",
15+
"UI\\src\\Microsoft.AspNetCore.Identity.UI.csproj",
16+
"samples\\IdentitySample.DefaultUI\\IdentitySample.DefaultUI.csproj",
17+
"samples\\IdentitySample.Mvc\\IdentitySample.Mvc.csproj",
18+
"test\\Identity.FunctionalTests\\Microsoft.AspNetCore.Identity.FunctionalTests.csproj",
19+
"test\\Identity.Test\\Microsoft.AspNetCore.Identity.Test.csproj",
20+
"test\\InMemory.Test\\Microsoft.AspNetCore.Identity.InMemory.Test.csproj",
21+
"testassets\\Identity.DefaultUI.WebSite\\Identity.DefaultUI.WebSite.csproj"
22+
]
23+
}
24+
}

src/Identity/UI/ref/Microsoft.AspNetCore.Identity.UI.netcoreapp3.0.cs

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,15 @@ namespace Microsoft.AspNetCore.Identity
66
public static partial class IdentityBuilderUIExtensions
77
{
88
public static Microsoft.AspNetCore.Identity.IdentityBuilder AddDefaultUI(this Microsoft.AspNetCore.Identity.IdentityBuilder builder) { throw null; }
9-
public static Microsoft.AspNetCore.Identity.IdentityBuilder AddDefaultUI(this Microsoft.AspNetCore.Identity.IdentityBuilder builder, Microsoft.AspNetCore.Identity.UI.UIFramework framework) { throw null; }
109
}
1110
}
1211
namespace Microsoft.AspNetCore.Identity.UI
1312
{
14-
public partial class DefaultUIOptions
13+
[System.AttributeUsageAttribute(System.AttributeTargets.Assembly, Inherited=false, AllowMultiple=false)]
14+
public sealed partial class UIFrameworkAttribute : System.Attribute
1515
{
16-
public DefaultUIOptions() { }
17-
public Microsoft.AspNetCore.Identity.UI.UIFramework UIFramework { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
18-
}
19-
public enum UIFramework
20-
{
21-
Bootstrap3 = 0,
22-
Bootstrap4 = 1,
16+
public UIFrameworkAttribute(string uiFramework) { }
17+
public string UIFramework { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
2318
}
2419
}
2520
namespace Microsoft.AspNetCore.Identity.UI.Services

src/Identity/UI/src/DefaultUIOptions.cs

Lines changed: 0 additions & 16 deletions
This file was deleted.

src/Identity/UI/src/IdentityBuilderUIExtensions.cs

Lines changed: 49 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Collections.Generic;
66
using System.Linq;
77
using System.Reflection;
8+
using Microsoft.AspNetCore.Hosting;
89
using Microsoft.AspNetCore.Identity.UI;
910
using Microsoft.AspNetCore.Identity.UI.Services;
1011
using Microsoft.AspNetCore.Mvc.ApplicationModels;
@@ -30,35 +31,16 @@ public static class IdentityBuilderUIExtensions
3031
/// </remarks>
3132
/// <param name="builder">The <see cref="IdentityBuilder"/>.</param>
3233
/// <returns>The <see cref="IdentityBuilder"/>.</returns>
33-
public static IdentityBuilder AddDefaultUI(this IdentityBuilder builder) => builder.AddDefaultUI(UIFramework.Bootstrap4);
34-
35-
36-
/// <summary>
37-
/// Adds a default, self-contained UI for Identity to the application using
38-
/// Razor Pages in an area named Identity.
39-
/// </summary>
40-
/// <remarks>
41-
/// In order to use the default UI, the application must be using <see cref="Microsoft.AspNetCore.Mvc"/>,
42-
/// <see cref="Microsoft.AspNetCore.StaticFiles"/> and contain a <c>_LoginPartial</c> partial view that
43-
/// can be found by the application.
44-
/// </remarks>
45-
/// <param name="builder">The <see cref="IdentityBuilder"/>.</param>
46-
/// <param name="framework">The <see cref="UIFramework"/>.</param>
47-
/// <returns>The <see cref="IdentityBuilder"/>.</returns>
48-
public static IdentityBuilder AddDefaultUI(
49-
this IdentityBuilder builder,
50-
UIFramework framework)
34+
public static IdentityBuilder AddDefaultUI(this IdentityBuilder builder)
5135
{
5236
builder.AddSignInManager();
53-
AddRelatedParts(builder, framework);
37+
AddRelatedParts(builder);
5438

5539
builder.Services.ConfigureOptions(
5640
typeof(IdentityDefaultUIConfigureOptions<>)
5741
.MakeGenericType(builder.UserType));
5842
builder.Services.TryAddTransient<IEmailSender, EmailSender>();
5943

60-
builder.Services.Configure<DefaultUIOptions>(o => o.UIFramework = framework);
61-
6244
return builder;
6345
}
6446

@@ -69,8 +51,20 @@ public static IdentityBuilder AddDefaultUI(
6951
[UIFramework.Bootstrap4] = "Microsoft.AspNetCore.Identity.UI.Views.V4",
7052
};
7153

72-
private static void AddRelatedParts(IdentityBuilder builder, UIFramework framework)
54+
private static void AddRelatedParts(IdentityBuilder builder)
7355
{
56+
// We try to resolve the UI framework that was used by looking at the entry assembly.
57+
// When an app runs, the entry assembly will point to the built app. In some rare cases
58+
// (functional testing) the app assembly will be different, and we'll try to locate it through
59+
// the same mechanism that MVC uses today.
60+
// Finally, if for some reason we aren't able to find the assembly, we'll use our default value
61+
// (Bootstrap4)
62+
if (!TryResolveUIFramework(Assembly.GetEntryAssembly(), out var framework) ||
63+
!TryResolveUIFramework(GetApplicationAssembly(builder), out framework))
64+
{
65+
framework = default;
66+
}
67+
7468
var mvcBuilder = builder.Services
7569
.AddMvc()
7670
.ConfigureApplicationPartManager(partManager =>
@@ -131,5 +125,38 @@ void AddParts(
131125
}
132126
});
133127
}
128+
129+
private static Assembly GetApplicationAssembly(IdentityBuilder builder)
130+
{
131+
// Whis is the same logic that MVC follows to find the application assembly.
132+
var environment = builder.Services.Where(d => d.ServiceType == typeof(IWebHostEnvironment)).ToArray();
133+
var applicationName = ((IWebHostEnvironment)environment.LastOrDefault()?.ImplementationInstance)
134+
.ApplicationName;
135+
136+
var appAssembly = Assembly.Load(applicationName);
137+
return appAssembly;
138+
}
139+
140+
private static bool TryResolveUIFramework(Assembly assembly, out UIFramework uiFramework)
141+
{
142+
uiFramework = default;
143+
144+
var metadata = assembly.GetCustomAttributes<UIFrameworkAttribute>()
145+
.SingleOrDefault()?.UIFramework; // Bootstrap4 is the default
146+
if (metadata == null)
147+
{
148+
return false;
149+
}
150+
151+
// If we find the metadata there must be a valid framework here.
152+
if (!Enum.TryParse<UIFramework>(metadata, ignoreCase: true, out var uIFramework))
153+
{
154+
var enumValues = string.Join(", ", Enum.GetNames(typeof(UIFramework)).Select(v => $"'{v}'"));
155+
throw new InvalidOperationException(
156+
$"Found an invalid value for the 'IdentityUIFrameworkVersion'. Valid values are {enumValues}");
157+
}
158+
159+
return true;
160+
}
134161
}
135162
}

src/Identity/UI/src/IdentityDefaultUIConfigureOptions.cs

Lines changed: 3 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,34 +3,26 @@
33

44
using System;
55
using Microsoft.AspNetCore.Authentication.Cookies;
6-
using Microsoft.AspNetCore.Builder;
76
using Microsoft.AspNetCore.Hosting;
87
using Microsoft.AspNetCore.Identity.UI.Areas.Identity.Filters;
98
using Microsoft.AspNetCore.Mvc.RazorPages;
10-
using Microsoft.AspNetCore.StaticFiles;
119
using Microsoft.Extensions.DependencyInjection;
12-
using Microsoft.Extensions.FileProviders;
1310
using Microsoft.Extensions.Options;
1411

1512
namespace Microsoft.AspNetCore.Identity.UI
1613
{
1714
internal class IdentityDefaultUIConfigureOptions<TUser> :
1815
IPostConfigureOptions<RazorPagesOptions>,
19-
IPostConfigureOptions<StaticFileOptions>,
2016
IConfigureNamedOptions<CookieAuthenticationOptions> where TUser : class
2117
{
2218
private const string IdentityUIDefaultAreaName = "Identity";
2319

2420
public IdentityDefaultUIConfigureOptions(
25-
IWebHostEnvironment environment,
26-
IOptions<DefaultUIOptions> uiOptions)
27-
{
21+
IWebHostEnvironment environment) {
2822
Environment = environment;
29-
UiOptions = uiOptions;
3023
}
3124

3225
public IWebHostEnvironment Environment { get; }
33-
public IOptions<DefaultUIOptions> UiOptions { get; }
3426

3527
public void PostConfigure(string name, RazorPagesOptions options)
3628
{
@@ -50,30 +42,10 @@ public void PostConfigure(string name, RazorPagesOptions options)
5042
pam => pam.Filters.Add(new ExternalLoginsPageFilter<TUser>()));
5143
}
5244

53-
public void PostConfigure(string name, StaticFileOptions options)
54-
{
55-
name = name ?? throw new ArgumentNullException(nameof(name));
56-
options = options ?? throw new ArgumentNullException(nameof(options));
57-
58-
// Basic initialization in case the options weren't initialized by any other component
59-
options.ContentTypeProvider = options.ContentTypeProvider ?? new FileExtensionContentTypeProvider();
60-
if (options.FileProvider == null && Environment.WebRootFileProvider == null)
61-
{
62-
throw new InvalidOperationException("Missing FileProvider.");
63-
}
64-
65-
options.FileProvider = options.FileProvider ?? Environment.WebRootFileProvider;
66-
67-
var basePath = UiOptions.Value.UIFramework == UIFramework.Bootstrap3 ? "wwwroot/V3" :
68-
"wwwroot/V4";
69-
70-
// Add our provider
71-
var filesProvider = new ManifestEmbeddedFileProvider(GetType().Assembly, basePath);
72-
options.FileProvider = new CompositeFileProvider(options.FileProvider, filesProvider);
45+
public void Configure(CookieAuthenticationOptions options) {
46+
// Nothing to do here as Configure(string name, CookieAuthenticationOptions options) is hte one setting things up.
7347
}
7448

75-
public void Configure(CookieAuthenticationOptions options) { }
76-
7749
public void Configure(string name, CookieAuthenticationOptions options)
7850
{
7951
name = name ?? throw new ArgumentNullException(nameof(name));

src/Identity/UI/src/Microsoft.AspNetCore.Identity.UI.csproj

Lines changed: 55 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<Project Sdk="Microsoft.NET.Sdk.Razor">
1+
<Project Sdk="Microsoft.NET.Sdk.Razor">
22

33
<PropertyGroup>
44
<Description>ASP.NET Core Identity UI is the default Razor Pages built-in UI for the ASP.NET Core Identity framework.</Description>
@@ -7,23 +7,30 @@
77
<GenerateDocumentationFile>true</GenerateDocumentationFile>
88
<PackageTags>aspnetcore;identity;membership;razorpages</PackageTags>
99
<IsShippingPackage>true</IsShippingPackage>
10-
<GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>
1110
<ProvideApplicationPartFactoryAttributeTypeName>Microsoft.AspNetCore.Mvc.ApplicationParts.NullApplicationPartFactory, Microsoft.AspNetCore.Mvc.Core</ProvideApplicationPartFactoryAttributeTypeName>
1211
<RazorCompileOnBuild>false</RazorCompileOnBuild>
1312
<RazorCompileOnPublish>false</RazorCompileOnPublish>
1413
<EnableDefaultRazorGenerateItems>false</EnableDefaultRazorGenerateItems>
1514
<DisableFastUpToDateCheck>true</DisableFastUpToDateCheck>
1615
<AddRazorSupportForMvc>true</AddRazorSupportForMvc>
16+
17+
<DisableStaticWebAssetsBuildPropsFileGeneration>true</DisableStaticWebAssetsBuildPropsFileGeneration>
18+
<StaticWebAssetsDisableProjectBuildPropsFileGeneration>true</StaticWebAssetsDisableProjectBuildPropsFileGeneration>
19+
20+
<GetCurrentProjectStaticWebAssetsDependsOn>
21+
$(GetCurrentProjectStaticWebAssetsDependsOn);
22+
_UpdatedIdentityUIStaticWebAssets
23+
</GetCurrentProjectStaticWebAssetsDependsOn>
24+
25+
<IdentityUIFrameworkVersion Condition="'$(IdentityUIFrameworkVersion)' == ''">Bootstrap4</IdentityUIFrameworkVersion>
26+
1727
</PropertyGroup>
1828

1929
<ItemGroup>
20-
<EmbeddedResource Include="wwwroot/**/*" />
21-
<EmbeddedResource Remove="wwwroot/**/LICENSE*" />
22-
<None Remove="wwwroot/**/LICENSE*" />
30+
<Content Remove="@(Content)" />
31+
<Content Include="wwwroot\**\*" Pack="true" />
32+
<None Include="build\*" Pack="true" PackagePath="build\" />
2333
<None Include="THIRD-PARTY-NOTICES.txt" Pack="true" PackagePath="/THIRD-PARTY-NOTICES.txt" />
24-
25-
<Content Update="wwwroot/**/*" Pack="false" />
26-
<Content Update="**\*.cshtml" Pack="false" />
2734
</ItemGroup>
2835

2936
<ItemGroup>
@@ -40,6 +47,10 @@
4047
<UIFrameworkVersionMoniker Include="V4" />
4148
</ItemGroup>
4249

50+
<ItemGroup>
51+
<Folder Include="build\" />
52+
</ItemGroup>
53+
4354
<!-- Source build doesn't build this package -->
4455
<Target Name="BuildRazorViews" DependsOnTargets="Compile" BeforeTargets="Build" Condition="'$(DotNetBuildFromSource)' != 'true'">
4556
<Message Text="Building razor views assemblies" Importance="High" />
@@ -63,6 +74,7 @@
6374

6475
<Output TaskParameter="DestinationFiles" ItemName="FileWrites"/>
6576
<Output TaskParameter="DestinationFiles" ItemName="_RazorAssembly"/>
77+
6678
</Copy>
6779

6880
<Copy
@@ -75,12 +87,10 @@
7587
UseHardlinksIfPossible="$(CreateHardLinksForCopyFilesToOutputDirectoryIfPossible)"
7688
UseSymboliclinksIfPossible="$(CreateSymbolicLinksForCopyFilesToOutputDirectoryIfPossible)">
7789

78-
<Output TaskParameter="DestinationFiles" ItemName="FileWrites"/>
90+
<Output TaskParameter="DestinationFiles" ItemName="FileWrites" />
7991
</Copy>
8092

81-
<Message
82-
Importance="High"
83-
Text="$(MSBuildProjectName) -&gt; %(_RazorAssembly.FullPath)" />
93+
<Message Importance="High" Text="$(MSBuildProjectName) -&gt; %(_RazorAssembly.FullPath)" />
8494

8595
</Target>
8696

@@ -90,19 +100,15 @@
90100

91101
<Target Name="SetupRazorInputs">
92102
<ItemGroup>
93-
<_RazorGenerate
94-
Include="Areas\Identity\Pages\$(UIFrameworkVersion)\**\*.cshtml" />
103+
<_RazorGenerate Include="Areas\Identity\Pages\$(UIFrameworkVersion)\**\*.cshtml" />
95104

96-
<RazorGenerate
97-
Include="@(_RazorGenerate)"
98-
Link="Areas\Identity\Pages\%(RecursiveDir)%(Filename)%(Extension)" />
105+
<RazorGenerate Include="@(_RazorGenerate)" Link="Areas\Identity\Pages\%(RecursiveDir)%(Filename)%(Extension)" />
99106
</ItemGroup>
100107
</Target>
101108

102109
<Target Name="BuildForUI" DependsOnTargets="SetupRazorInputs;RazorCompile" />
103110

104-
<Target
105-
Name="_GetRazorDlls" BeforeTargets="GetCopyToOutputDirectoryItems">
111+
<Target Name="_GetRazorDlls" BeforeTargets="GetCopyToOutputDirectoryItems">
106112

107113
<ItemGroup>
108114
<_GeneratedRazorViews Include="$(TargetDir)$(TargetName).Views.%(UIFrameworkVersionMoniker.Identity).dll" />
@@ -117,15 +123,13 @@
117123

118124
<Target Name="_AddRazorDlls" BeforeTargets="BuiltProjectOutputGroup">
119125
<ItemGroup>
120-
<BuiltProjectOutputGroupOutput
121-
Include="$(IntermediateOutputPath)%(UIFrameworkVersionMoniker.Identity)\$(TargetName).Views.%(UIFrameworkVersionMoniker.Identity).dll" />
126+
<BuiltProjectOutputGroupOutput Include="$(IntermediateOutputPath)%(UIFrameworkVersionMoniker.Identity)\$(TargetName).Views.%(UIFrameworkVersionMoniker.Identity).dll" />
122127
</ItemGroup>
123128
</Target>
124129

125130
<Target Name="_AddRazorPdbs" BeforeTargets="DebugSymbolsProjectOutputGroup">
126131
<ItemGroup>
127-
<DebugSymbolsProjectOutputGroupOutput
128-
Include="$(IntermediateOutputPath)%(UIFrameworkVersionMoniker.Identity)\$(TargetName).Views.%(UIFrameworkVersionMoniker.Identity).pdb" />
132+
<DebugSymbolsProjectOutputGroupOutput Include="$(IntermediateOutputPath)%(UIFrameworkVersionMoniker.Identity)\$(TargetName).Views.%(UIFrameworkVersionMoniker.Identity).pdb" />
129133
</ItemGroup>
130134
</Target>
131135

@@ -136,8 +140,34 @@
136140
<ExpectedOutputFile Include="$(TargetDir)Microsoft.AspNetCore.Identity.UI.Views.V4.dll" />
137141
</ItemGroup>
138142

139-
<Error Text="Unable to find precompiled view file %(ExpectedOutputFile.Identity)"
140-
Condition="!Exists('%(ExpectedOutputFile.Identity)')" />
143+
<Error Text="Unable to find precompiled view file %(ExpectedOutputFile.Identity)" Condition="!Exists('%(ExpectedOutputFile.Identity)')" />
144+
</Target>
145+
146+
<Target Name="_UpdatedIdentityUIStaticWebAssets">
147+
148+
<ItemGroup>
149+
<StaticWebAsset Remove="@(StaticWebAsset)" />
150+
151+
<_V3Content Include="wwwroot\V3\**" />
152+
<_V4Content Include="wwwroot\V4\**" />
153+
154+
<StaticWebAsset Include="@(_V3Content->'%(FullPath)')" Condition="'$(IdentityUIFrameworkVersion)' == 'Bootstrap3'">
155+
<SourceType></SourceType>
156+
<SourceId>Microsoft.AspNetCore.Identity.UI</SourceId>
157+
<ContentRoot>$([MSBuild]::NormalizePath('$(MSBuildThisFileDirectory)wwwroot/V3'))</ContentRoot>
158+
<BasePath>/Identity</BasePath>
159+
<RelativePath>%(RecursiveDir)%(FileName)%(Extension)</RelativePath>
160+
</StaticWebAsset>
161+
162+
<StaticWebAsset Include="@(_V4Content->'%(FullPath)')" Condition="'$(IdentityUIFrameworkVersion)' == 'Bootstrap4'">
163+
<SourceType></SourceType>
164+
<SourceId>Microsoft.AspNetCore.Identity.UI</SourceId>
165+
<ContentRoot>$([MSBuild]::NormalizePath('$(MSBuildThisFileDirectory)wwwroot/V4'))</ContentRoot>
166+
<BasePath>/Identity</BasePath>
167+
<RelativePath>%(RecursiveDir)%(FileName)%(Extension)</RelativePath>
168+
</StaticWebAsset>
169+
</ItemGroup>
170+
141171
</Target>
142172

143173
</Project>

0 commit comments

Comments
 (0)