1 //------------------------------------------------------------------------------
2 // <copyright file="LoginView.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //------------------------------------------------------------------------------
6 
7 namespace System.Web.UI.WebControls {
8 
9     using System.Collections;
10     using System.ComponentModel;
11     using System.Security.Permissions;
12     using System.Security.Principal;
13     using System.Web.Security;
14     using System.Web.UI;
15 
16 
17     /// <devdoc>
18     /// Renders exactly one of its templates, chosen by whether a user is logged in
19     /// and the roles that contain the user.
20     /// </devdoc>
21     [
22     Bindable(false),
23     ParseChildren(true),
24     PersistChildren(false),
25     Designer("System.Web.UI.Design.WebControls.LoginViewDesigner," + AssemblyRef.SystemDesign),
26     DefaultProperty("CurrentView"),
27     DefaultEvent("ViewChanged"),
28     Themeable(true),
29     ]
30     public class LoginView : Control, INamingContainer {
31 
32         private RoleGroupCollection _roleGroups;
33         private ITemplate _loggedInTemplate;
34         private ITemplate _anonymousTemplate;
35 
36         private int _templateIndex;
37 
38         private const int anonymousTemplateIndex = 0;
39         private const int loggedInTemplateIndex = 1;
40         private const int roleGroupStartingIndex = 2;
41 
42         private static readonly object EventViewChanging = new object();
43         private static readonly object EventViewChanged = new object();
44 
45 
46         /// <devdoc>
47         /// Template shown when no user is logged in.
48         /// </devdoc>
49         [
50         Browsable(false),
51         DefaultValue(null),
52         PersistenceMode(PersistenceMode.InnerProperty),
53         TemplateContainer(typeof(LoginView)),
54         ]
55         public virtual ITemplate AnonymousTemplate {
56             get {
57                 return _anonymousTemplate;
58             }
59             set {
60                 _anonymousTemplate = value;
61             }
62         }
63 
64         [
65         Browsable(true),
66         ]
67         public override bool EnableTheming {
68             get {
69                 return base.EnableTheming;
70             }
71             set {
72                 base.EnableTheming = value;
73             }
74         }
75 
76         [
77         Browsable(true),
78         ]
79         public override string SkinID {
80             get {
81                 return base.SkinID;
82             }
83             set {
84                 base.SkinID = value;
85             }
86         }
87 
88 
89 
90         /// <devdoc>
91         /// Copied from CompositeControl.  This control does not extend CompositeControl because it should not be a WebControl.
92         /// </devdoc>
93         public override ControlCollection Controls {
94             get {
95                 EnsureChildControls();
96                 return base.Controls;
97             }
98         }
99 
100 
101         /// <devdoc>
102         /// Copied from CompositeControl.  This control does not extend CompositeControl because it should not be a WebControl.
103         /// Does not call Base.DataBind(), since we need to call EnsureChildControls() between
104         /// OnDataBinding() and DataBindChildren().
105         /// </devdoc>
DataBind()106         public override void DataBind() {
107             // Do our own databinding
108             OnDataBinding(EventArgs.Empty);
109 
110             EnsureChildControls();
111 
112             // Do all of our children's databinding
113             DataBindChildren();
114         }
115 
116 
117         /// <devdoc>
118         /// Template shown when a user is logged in, but the user is not in any role associated with a template.
119         /// </devdoc>
120         [
121         Browsable(false),
122         DefaultValue(null),
123         PersistenceMode(PersistenceMode.InnerProperty),
124         TemplateContainer(typeof(LoginView)),
125         ]
126         public virtual ITemplate LoggedInTemplate {
127             get {
128                 return _loggedInTemplate;
129             }
130             set {
131                 _loggedInTemplate = value;
132             }
133         }
134 
135 
136         /// <devdoc>
137         /// Maps groups of roles to templates.
138         /// </devdoc>
139         [
140         WebCategory("Behavior"),
141         MergableProperty(false),
142         Themeable(false),
143         Filterable(false),
144         PersistenceMode(PersistenceMode.InnerProperty),
145         WebSysDescription(SR.LoginView_RoleGroups)
146         ]
147         public virtual RoleGroupCollection RoleGroups {
148             get {
149                 if (_roleGroups == null) {
150                     _roleGroups = new RoleGroupCollection();
151                 }
152                 return _roleGroups;
153             }
154         }
155 
156         /// <devdoc>
157         /// Index of the template rendered on the previous page load.  Saved in ControlState.
158         /// 0:   AnonymousTemplate
159         /// 1:   LoggedInTemplate
160         /// >=2: RoleGroup template with index n-2
161         /// </devdoc>
162         private int TemplateIndex {
163             get {
164                 return _templateIndex;
165             }
166             set {
167                 if (value != TemplateIndex) {
168                     OnViewChanging(EventArgs.Empty);
169                     _templateIndex = value;
170                     ChildControlsCreated = false;
171                     OnViewChanged(EventArgs.Empty);
172                 }
173             }
174         }
175 
176 
177         /// <devdoc>
178         /// Raised after the view is changed.
179         /// </devdoc>
180         [
181         WebCategory("Action"),
182         WebSysDescription(SR.LoginView_ViewChanged)
183         ]
184         public event EventHandler ViewChanged {
185             add {
186                 Events.AddHandler(EventViewChanged, value);
187             }
188             remove {
189                 Events.RemoveHandler(EventViewChanged, value);
190             }
191         }
192 
193 
194         /// <devdoc>
195         /// Raised before the view is changed.  Not cancellable, because the view is changed
196         /// when the logged-in user changes, and it wouldn't make sense to cancel this.
197         /// </devdoc>
198         [
199         WebCategory("Action"),
200         WebSysDescription(SR.LoginView_ViewChanging)
201         ]
202         public event EventHandler ViewChanging {
203             add {
204                 Events.AddHandler(EventViewChanging, value);
205             }
206             remove {
207                 Events.RemoveHandler(EventViewChanging, value);
208             }
209         }
210 
211 
212         /// <devdoc>
213         /// Instantiate the appropriate template.
214         /// </devdoc>
CreateChildControls()215         protected internal override void CreateChildControls() {
216             Controls.Clear();
217 
218             // For the first request, set _templateIndex now, so the correct template is
219             // instantiated and we do not raise the ViewChanging/ViewChanged events.
220             Page page = Page;
221             if (page != null && !page.IsPostBack && !DesignMode) {
222                 _templateIndex = GetTemplateIndex();
223             }
224 
225             int templateIndex = TemplateIndex;
226             ITemplate template = null;
227             switch (templateIndex) {
228                 case anonymousTemplateIndex:
229                     template = AnonymousTemplate;
230                     break;
231                 case loggedInTemplateIndex:
232                     template = LoggedInTemplate;
233                     break;
234                 default:
235                     int roleGroupIndex = templateIndex - roleGroupStartingIndex;
236                     RoleGroupCollection roleGroups = RoleGroups;
237                     if (0 <= roleGroupIndex && roleGroupIndex < roleGroups.Count) {
238                         template = roleGroups[roleGroupIndex].ContentTemplate;
239                     }
240                     break;
241             }
242 
243             if (template != null) {
244                 Control templateContainer = new Control();
245                 template.InstantiateIn(templateContainer);
246                 Controls.Add(templateContainer);
247             }
248         }
249 
250         [
251         EditorBrowsable(EditorBrowsableState.Never),
252         ]
Focus()253         public override void Focus() {
254             throw new NotSupportedException(SR.GetString(SR.NoFocusSupport, this.GetType().Name));
255         }
256 
257         /// <devdoc>
258         /// Loads the control state for those properties that should persist across postbacks
259         /// even when EnableViewState=false.
260         /// </devdoc>
LoadControlState(object savedState)261         protected internal override void LoadControlState(object savedState) {
262             if (savedState != null) {
263                 Pair state = (Pair)savedState;
264                 if (state.First != null) {
265                     base.LoadControlState(state.First);
266                 }
267                 if (state.Second != null) {
268                     _templateIndex = (int)state.Second;
269                 }
270             }
271         }
272 
273 
OnInit(EventArgs e)274         protected internal override void OnInit(EventArgs e) {
275             base.OnInit(e);
276             if (Page != null) {
277                 Page.RegisterRequiresControlState(this);
278             }
279         }
280 
281 
282         /// <devdoc>
283         /// Sets the TemplateIndex based on the current user.
284         /// </devdoc>
OnPreRender(EventArgs e)285         protected internal override void OnPreRender(EventArgs e) {
286             base.OnPreRender(e);
287 
288             TemplateIndex = GetTemplateIndex();
289 
290             // This is called in Control.PreRenderRecursiveInteral, but we need to call it again
291             // since we may have changed the TemplateIndex
292             EnsureChildControls();
293         }
294 
295 
296         /// <devdoc>
297         /// Raises the ViewChanged event.
298         /// </devdoc>
OnViewChanged(EventArgs e)299         protected virtual void OnViewChanged(EventArgs e) {
300             EventHandler handler = (EventHandler)Events[EventViewChanged];
301             if (handler != null) {
302                 handler(this, e);
303             }
304         }
305 
306 
307         /// <devdoc>
308         /// Raises the ViewChanging event.
309         /// </devdoc>
OnViewChanging(EventArgs e)310         protected virtual void OnViewChanging(EventArgs e) {
311             EventHandler handler = (EventHandler)Events[EventViewChanging];
312             if (handler != null) {
313                 handler(this, e);
314             }
315         }
316 
Render(HtmlTextWriter writer)317         protected internal override void Render(HtmlTextWriter writer) {
318             EnsureChildControls();
319             base.Render(writer);
320         }
321 
322         /// <devdoc>
323         /// Saves the control state for those properties that should persist across postbacks
324         /// even when EnableViewState=false.
325         /// </devdoc>
SaveControlState()326         protected internal override object SaveControlState() {
327             object baseState = base.SaveControlState();
328             if (baseState != null || _templateIndex != 0) {
329                 object templateIndexState = null;
330 
331                 if (_templateIndex != 0) {
332                     templateIndexState = _templateIndex;
333                 }
334                 return new Pair(baseState, templateIndexState);
335             }
336             return null;
337         }
338 
339 
340         /// <devdoc>
341         /// Allows the designer to set the TemplateIndex, so the different templates can be shown in the designer.
342         /// </devdoc>
343         [SecurityPermission(SecurityAction.Demand, Unrestricted = true)]
SetDesignModeState(IDictionary data)344         protected override void SetDesignModeState(IDictionary data) {
345             if (data != null) {
346                 object o = data["TemplateIndex"];
347                 if (o != null) {
348                     TemplateIndex = (int)o;
349 
350                     // Note: we always recreate the child controls in the designer to correctly handle the case of
351                     // the currently selected role group being deleted.  This is necessary because the
352                     // setter for TemplateIndex won't recreate the controls if the TemplateIndex is unchanged,
353                     // which is the case when deleting all but the last role group. [Fix for Bug 148406]
354                     ChildControlsCreated = false;
355                 }
356             }
357         }
358 
GetTemplateIndex()359         private int GetTemplateIndex() {
360             if (!DesignMode && Page != null && Page.Request.IsAuthenticated) {
361                 IPrincipal user = LoginUtil.GetUser(this);
362                 int roleGroupIndex = -1;
363 
364                 // Unlikely but possible for Page.Request.IsAuthenticated to be true and
365                 // user to be null.
366                 if (user != null) {
367                     roleGroupIndex = RoleGroups.GetMatchingRoleGroupInternal(user);
368                 }
369 
370                 if (roleGroupIndex >= 0) {
371                     return roleGroupIndex + roleGroupStartingIndex;
372                 }
373                 else {
374                     return loggedInTemplateIndex;
375                 }
376             }
377             else {
378                 return anonymousTemplateIndex;
379             }
380         }
381     }
382 }
383