55using System . Collections . Generic ;
66using System . Reflection ;
77using System . Threading . Tasks ;
8- using Microsoft . AspNetCore . Components ;
8+ using Microsoft . AspNetCore . Authorization ;
9+ using Microsoft . AspNetCore . Components . Auth ;
10+ using Microsoft . AspNetCore . Components . Internal ;
911using Microsoft . AspNetCore . Components . Layouts ;
12+ using Microsoft . AspNetCore . Components . RenderTree ;
1013
1114namespace Microsoft . AspNetCore . Components
1215{
@@ -16,9 +19,6 @@ namespace Microsoft.AspNetCore.Components
1619 /// </summary>
1720 public class PageDisplay : IComponent
1821 {
19- internal const string NameOfPage = nameof ( Page ) ;
20- internal const string NameOfPageParameters = nameof ( PageParameters ) ;
21-
2222 private RenderHandle _renderHandle ;
2323
2424 /// <summary>
@@ -34,6 +34,18 @@ public class PageDisplay : IComponent
3434 [ Parameter ]
3535 public IDictionary < string , object > PageParameters { get ; private set ; }
3636
37+ /// <summary>
38+ /// The content that will be displayed if the user is not authorized.
39+ /// </summary>
40+ [ Parameter ]
41+ public RenderFragment < AuthenticationState > NotAuthorizedContent { get ; private set ; }
42+
43+ /// <summary>
44+ /// The content that will be displayed while asynchronous authorization is in progress.
45+ /// </summary>
46+ [ Parameter ]
47+ public RenderFragment AuthorizingContent { get ; private set ; }
48+
3749 /// <inheritdoc />
3850 public void Configure ( RenderHandle renderHandle )
3951 {
@@ -50,41 +62,80 @@ public Task SetParametersAsync(ParameterCollection parameters)
5062
5163 private void Render ( )
5264 {
53- // In the middle, we render the requested page
54- var fragment = RenderComponentWithBody ( Page , bodyParam : null ) ;
65+ // In the middle goes the requested page
66+ var fragment = ( RenderFragment ) RenderPageWithParameters ;
67+
68+ // Around that goes an AuthorizeViewCore
69+ fragment = WrapInAuthorizeViewCore ( fragment ) ;
5570
56- // Repeatedly wrap it in each layer of nested layout until we get
71+ // Then repeatedly wrap that in each layer of nested layout until we get
5772 // to a layout that has no parent
5873 Type layoutType = Page ;
5974 while ( ( layoutType = GetLayoutType ( layoutType ) ) != null )
6075 {
61- fragment = RenderComponentWithBody ( layoutType , fragment ) ;
76+ fragment = WrapInLayout ( layoutType , fragment ) ;
6277 }
6378
6479 _renderHandle . Render ( fragment ) ;
6580 }
6681
67- private RenderFragment RenderComponentWithBody ( Type componentType , RenderFragment bodyParam ) => builder =>
82+ private RenderFragment WrapInLayout ( Type layoutType , RenderFragment bodyParam ) => builder =>
6883 {
69- builder . OpenComponent ( 0 , componentType ) ;
70- if ( bodyParam != null )
71- {
72- builder . AddAttribute ( 1 , LayoutComponentBase . BodyPropertyName , bodyParam ) ;
73- }
74- else
84+ builder . OpenComponent ( 0 , layoutType ) ;
85+ builder . AddAttribute ( 1 , LayoutComponentBase . BodyPropertyName , bodyParam ) ;
86+ builder . CloseComponent ( ) ;
87+ } ;
88+
89+ private void RenderPageWithParameters ( RenderTreeBuilder builder )
90+ {
91+ builder . OpenComponent ( 0 , Page ) ;
92+
93+ if ( PageParameters != null )
7594 {
76- if ( PageParameters != null )
95+ foreach ( var kvp in PageParameters )
7796 {
78- foreach ( var kvp in PageParameters )
79- {
80- builder . AddAttribute ( 1 , kvp . Key , kvp . Value ) ;
81- }
97+ builder . AddAttribute ( 1 , kvp . Key , kvp . Value ) ;
8298 }
8399 }
100+
84101 builder . CloseComponent ( ) ;
85- } ;
102+ }
103+
104+ private RenderFragment WrapInAuthorizeViewCore ( RenderFragment pageFragment )
105+ {
106+ var authorizeData = AttributeAuthorizeDataCache . GetAuthorizeDataForType ( Page ) ;
107+ if ( authorizeData == null )
108+ {
109+ // No authorization, so no need to wrap the fragment
110+ return pageFragment ;
111+ }
86112
87- private Type GetLayoutType ( Type type )
113+ // Some authorization data exists, so we do need to wrap the fragment
114+ RenderFragment < AuthenticationState > authorizedContent = context => pageFragment ;
115+ return builder =>
116+ {
117+ builder . OpenComponent < AuthorizeViewWithSuppliedData > ( 0 ) ;
118+ builder . AddAttribute ( 1 , nameof ( AuthorizeViewWithSuppliedData . AuthorizeDataParam ) , authorizeData ) ;
119+ builder . AddAttribute ( 2 , nameof ( AuthorizeViewWithSuppliedData . Authorized ) , authorizedContent ) ;
120+ builder . AddAttribute ( 3 , nameof ( AuthorizeViewWithSuppliedData . NotAuthorized ) , NotAuthorizedContent ?? DefaultNotAuthorizedContent ) ;
121+ builder . AddAttribute ( 4 , nameof ( AuthorizeViewWithSuppliedData . Authorizing ) , AuthorizingContent ) ;
122+ builder . CloseComponent ( ) ;
123+ } ;
124+ }
125+
126+ private static Type GetLayoutType ( Type type )
88127 => type . GetCustomAttribute < LayoutAttribute > ( ) ? . LayoutType ;
128+
129+ private class AuthorizeViewWithSuppliedData : AuthorizeViewCore
130+ {
131+ [ Parameter ] public IAuthorizeData [ ] AuthorizeDataParam { get ; private set ; }
132+
133+ protected override IAuthorizeData [ ] AuthorizeData => AuthorizeDataParam ;
134+ }
135+
136+ // There has to be some default content. If we render blank by default, developers
137+ // will find it hard to guess why their UI isn't appearing.
138+ private static RenderFragment DefaultNotAuthorizedContent ( AuthenticationState authenticationState )
139+ => builder => builder . AddContent ( 0 , "Not authorized" ) ;
89140 }
90141}
0 commit comments