2009-02-28 Gonzalo Paniagua Javier <gonzalo@novell.com>
[mono.git] / mcs / class / System.Web / System.Web / SiteMapProvider.cs
1 //
2 // System.Web.SiteMapProvider
3 //
4 // Authors:
5 //      Ben Maurer (bmaurer@users.sourceforge.net)
6 //      Lluis Sanchez Gual (lluis@novell.com)
7 //
8 // (C) 2003 Ben Maurer
9 // (C) 2005 Novell, Inc (http://www.novell.com)
10 //
11
12 //
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
20 // 
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
23 // 
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 //
32
33 #if NET_2_0
34 using System.ComponentModel;
35 using System.Collections;
36 using System.Collections.Specialized;
37 using System.Text;
38 using System.Configuration.Provider;
39 using System.Web.Util;
40 using System.Globalization;
41 using System.Web.Configuration;
42
43 namespace System.Web {
44         public abstract class SiteMapProvider : ProviderBase {
45                 static readonly object siteMapResolveEvent = new object ();
46                 
47                 internal object this_lock = new object ();
48                 
49                 bool enableLocalization;
50                 SiteMapProvider parentProvider;
51                 SiteMapProvider rootProviderCache;
52                 bool securityTrimming;
53                 object resolveLock = new Object();
54                 bool resolving;
55
56                 EventHandlerList events = new EventHandlerList ();
57                 
58                 public event SiteMapResolveEventHandler SiteMapResolve {
59                         add { events.AddHandler (siteMapResolveEvent, value); }
60                         remove { events.RemoveHandler (siteMapResolveEvent, value); }
61                 }
62                 
63                 protected virtual void AddNode (SiteMapNode node)
64                 {
65                         AddNode (node, null);
66                 }
67                 
68                 internal protected virtual void AddNode (SiteMapNode node, SiteMapNode parentNode)
69                 {
70                         throw new NotImplementedException ();
71                 }
72
73                 public virtual SiteMapNode FindSiteMapNode (HttpContext context)
74                 {
75                         if (context == null)
76                                 return null;
77                         
78                         SiteMapNode ret = this.FindSiteMapNode (context.Request.RawUrl);
79                         if (ret == null)
80                                 ret = this.FindSiteMapNode (context.Request.Path);
81                         return ret;
82                 }
83
84                 public abstract SiteMapNode FindSiteMapNode (string rawUrl);
85                 
86                 public virtual SiteMapNode FindSiteMapNodeFromKey (string key)
87                 {
88                         /* msdn2 says this function always returns
89                          * null, but it seems to just call
90                          * FindSiteMapNode(string rawUrl) */
91                         return FindSiteMapNode (key);
92                 }
93
94                 public abstract SiteMapNodeCollection GetChildNodes (SiteMapNode node);
95                 
96                 public virtual SiteMapNode GetCurrentNodeAndHintAncestorNodes (int upLevel)
97                 {
98                         if (upLevel < -1) throw new ArgumentOutOfRangeException ("upLevel");
99
100                         return CurrentNode;
101                 }
102                 
103                 public virtual SiteMapNode GetCurrentNodeAndHintNeighborhoodNodes (int upLevel, int downLevel)
104                 {
105                         if (upLevel < -1) throw new ArgumentOutOfRangeException ("upLevel");
106                         if (downLevel < -1) throw new ArgumentOutOfRangeException ("downLevel");
107                         
108                         return CurrentNode;
109                 }
110
111                 public abstract SiteMapNode GetParentNode (SiteMapNode node);
112                 
113                 public virtual SiteMapNode GetParentNodeRelativeToCurrentNodeAndHintDownFromParent (int walkupLevels, int relativeDepthFromWalkup)
114                 {
115                         if (walkupLevels < 0) throw new ArgumentOutOfRangeException ("walkupLevels");
116                         if (relativeDepthFromWalkup < 0) throw new ArgumentOutOfRangeException ("relativeDepthFromWalkup");
117                         
118                         SiteMapNode node = GetCurrentNodeAndHintAncestorNodes (walkupLevels);
119                         for (int n=0; n<walkupLevels && node != null; n++)
120                                 node = GetParentNode (node);
121                                 
122                         if (node == null) return null;
123
124                         HintNeighborhoodNodes (node, 0, relativeDepthFromWalkup);
125                         return node;
126                 }
127                 
128                 public virtual SiteMapNode GetParentNodeRelativeToNodeAndHintDownFromParent (SiteMapNode node, int walkupLevels, int relativeDepthFromWalkup)
129                 {
130                         if (walkupLevels < 0) throw new ArgumentOutOfRangeException ("walkupLevels");
131                         if (relativeDepthFromWalkup < 0) throw new ArgumentOutOfRangeException ("relativeDepthFromWalkup");
132                         if (node == null) throw new ArgumentNullException ("node");
133                         
134                         HintAncestorNodes (node, walkupLevels);
135                         for (int n=0; n<walkupLevels && node != null; n++)
136                                 node = GetParentNode (node);
137                                 
138                         if (node == null) return null;
139                         
140                         HintNeighborhoodNodes (node, 0, relativeDepthFromWalkup);
141                         return node;
142                 }
143                 
144                 protected internal abstract SiteMapNode GetRootNodeCore ();
145                 
146                 protected static SiteMapNode GetRootNodeCoreFromProvider (SiteMapProvider provider)
147                 {
148                         return provider.GetRootNodeCore ();
149                 }
150                 
151                 public virtual void HintAncestorNodes (SiteMapNode node, int upLevel)
152                 {
153                         if (upLevel < -1) throw new ArgumentOutOfRangeException ("upLevel");
154                         if (node == null) throw new ArgumentNullException ("node");
155                 }
156                 
157                 public virtual void HintNeighborhoodNodes (SiteMapNode node, int upLevel, int downLevel)
158                 {
159                         if (upLevel < -1) throw new ArgumentOutOfRangeException ("upLevel");
160                         if (downLevel < -1) throw new ArgumentOutOfRangeException ("downLevel");
161                         if (node == null) throw new ArgumentNullException ("node");
162                 }
163                 
164                 protected virtual void RemoveNode (SiteMapNode node)
165                 {
166                         throw new NotImplementedException ();
167                 }
168
169                 public override void Initialize (string name, NameValueCollection attributes)
170                 {
171                         base.Initialize (name, attributes);
172                         if (attributes ["securityTrimmingEnabled"] != null)
173                                 securityTrimming = (bool) Convert.ChangeType (attributes ["securityTrimmingEnabled"], typeof (bool));
174                 }
175                 
176                 [MonoTODO ("need to implement cases 2 and 3")]
177                 public virtual bool IsAccessibleToUser (HttpContext context, SiteMapNode node)
178                 {
179                         if (context == null) throw new ArgumentNullException ("context");
180                         if (node == null) throw new ArgumentNullException ("node");
181
182                         if (!SecurityTrimmingEnabled)
183                                 return true;
184
185                         /* The node is accessible (according to msdn2) if:
186                          *
187                          * 1. The Roles exists on node and the current user is in at least one of the specified roles.
188                          *
189                          * 2. The current thread has an associated WindowsIdentity that has file access to the requested URL and
190                          * the URL is located within the directory structure for the application.
191                          *
192                          * 3. The current user is authorized specifically for the requested URL in the authorization element for
193                          * the current application and the URL is located within the directory structure for the application. 
194                         */
195
196                         /* 1. */
197                         IList roles = node.Roles;
198                         if (roles != null && roles.Count > 0) {
199                                 foreach (string rolename in roles)
200                                         if (rolename == "*" || context.User.IsInRole (rolename))
201                                                 return true;
202                         }
203                         
204                         /* 2. */
205                         /* XXX */
206
207                         /* 3. */
208                         string url = node.Url;
209                         if(!String.IsNullOrEmpty(url)) {
210                                 // TODO check url is located within the current application
211
212                                 if (VirtualPathUtility.IsAppRelative (url) || !VirtualPathUtility.IsAbsolute (url))
213                                         url = VirtualPathUtility.Combine (VirtualPathUtility.AppendTrailingSlash (HttpRuntime.AppDomainAppVirtualPath), url);
214
215                                 AuthorizationSection config = (AuthorizationSection) WebConfigurationManager.GetSection (
216                                         "system.web/authorization",
217                                         url);
218                                 if (config != null)
219                                         return config.IsValidUser (context.User, context.Request.HttpMethod);
220                         }
221
222                         return false;
223                 }
224                 
225                 public virtual SiteMapNode CurrentNode {
226                         get {
227                                 if (HttpContext.Current != null) {
228                                         SiteMapNode ret = ResolveSiteMapNode (HttpContext.Current);
229                                         if (ret != null) return ret;
230                                         return FindSiteMapNode (HttpContext.Current);
231                                 } else
232                                         return null;
233                         }
234                 }
235                 
236                 public virtual SiteMapProvider ParentProvider {
237                         get { return parentProvider; }
238                         set { parentProvider = value; }
239                 }
240                 
241                 public virtual SiteMapProvider RootProvider {
242                         get {
243                                 lock (this_lock) {
244                                         if (rootProviderCache == null) {
245                                                 SiteMapProvider current = this;
246                                                 while (current.ParentProvider != null)
247                                                         current = current.ParentProvider;
248                                                 
249                                                 rootProviderCache = current;
250                                         }
251                                 }
252                                 return rootProviderCache;
253                         }
254                 }
255                 
256                 protected SiteMapNode ResolveSiteMapNode (HttpContext context)
257                 {
258                         SiteMapResolveEventHandler eh = events [siteMapResolveEvent] as SiteMapResolveEventHandler;
259
260                         if (eh != null) {
261                                 lock (resolveLock) {
262                                         if (resolving)
263                                                 return null;
264                                         resolving = true;
265                                         SiteMapResolveEventArgs args = new SiteMapResolveEventArgs (context, this);
266                                         SiteMapNode r = eh (this, args);
267                                         resolving = false;
268                                         return r;
269                                 }
270                         } else
271                                 return null;
272                 }
273                 
274                 public bool EnableLocalization {
275                         get { return enableLocalization; }
276                         set { enableLocalization = value; }
277                 }
278                 
279                 public bool SecurityTrimmingEnabled {
280                         get { return securityTrimming; }
281                 }
282
283                 string resourceKey;
284                 public string ResourceKey {
285                         get { return resourceKey; }
286                         set { resourceKey = value; }
287                 }
288
289                 public virtual SiteMapNode RootNode {
290                         get {
291                                 SiteMapNode node = GetRootNodeCore ();
292                                 return ReturnNodeIfAccessible (node);
293                         }
294                 }
295
296                 internal static SiteMapNode ReturnNodeIfAccessible (SiteMapNode node)
297                 {
298                         if (node.IsAccessibleToUser (HttpContext.Current))
299                                 return node;
300                         else
301                                 throw new InvalidOperationException (); /* need
302                                                                          * a
303                                                                          * message
304                                                                          * here */
305                 }
306         }
307 }
308 #endif
309