2 // System.Web.SiteMapProvider
5 // Ben Maurer (bmaurer@users.sourceforge.net)
6 // Lluis Sanchez Gual (lluis@novell.com)
9 // (C) 2005-2009 Novell, Inc (http://www.novell.com)
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:
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
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.
33 using System.ComponentModel;
34 using System.Collections;
35 using System.Collections.Specialized;
37 using System.Configuration.Provider;
38 using System.Web.Util;
39 using System.Globalization;
40 using System.Web.Configuration;
44 public abstract class SiteMapProvider : ProviderBase
46 static readonly object siteMapResolveEvent = new object ();
48 internal object this_lock = new object ();
50 bool enableLocalization;
51 SiteMapProvider parentProvider;
52 SiteMapProvider rootProviderCache;
53 bool securityTrimming;
54 object resolveLock = new Object();
57 EventHandlerList events = new EventHandlerList ();
59 public event SiteMapResolveEventHandler SiteMapResolve {
60 add { events.AddHandler (siteMapResolveEvent, value); }
61 remove { events.RemoveHandler (siteMapResolveEvent, value); }
64 protected virtual void AddNode (SiteMapNode node)
69 internal protected virtual void AddNode (SiteMapNode node, SiteMapNode parentNode)
71 throw new NotImplementedException ();
74 public virtual SiteMapNode FindSiteMapNode (HttpContext context)
79 HttpRequest req = context.Request;
83 SiteMapNode ret = this.FindSiteMapNode (req.RawUrl);
85 ret = this.FindSiteMapNode (req.Path);
89 public abstract SiteMapNode FindSiteMapNode (string rawUrl);
91 public virtual SiteMapNode FindSiteMapNodeFromKey (string key)
93 /* msdn2 says this function always returns
94 * null, but it seems to just call
95 * FindSiteMapNode(string rawUrl) */
96 return FindSiteMapNode (key);
99 public abstract SiteMapNodeCollection GetChildNodes (SiteMapNode node);
101 public virtual SiteMapNode GetCurrentNodeAndHintAncestorNodes (int upLevel)
103 if (upLevel < -1) throw new ArgumentOutOfRangeException ("upLevel");
108 public virtual SiteMapNode GetCurrentNodeAndHintNeighborhoodNodes (int upLevel, int downLevel)
110 if (upLevel < -1) throw new ArgumentOutOfRangeException ("upLevel");
111 if (downLevel < -1) throw new ArgumentOutOfRangeException ("downLevel");
116 public abstract SiteMapNode GetParentNode (SiteMapNode node);
118 public virtual SiteMapNode GetParentNodeRelativeToCurrentNodeAndHintDownFromParent (int walkupLevels, int relativeDepthFromWalkup)
120 if (walkupLevels < 0) throw new ArgumentOutOfRangeException ("walkupLevels");
121 if (relativeDepthFromWalkup < 0) throw new ArgumentOutOfRangeException ("relativeDepthFromWalkup");
123 SiteMapNode node = GetCurrentNodeAndHintAncestorNodes (walkupLevels);
124 for (int n=0; n<walkupLevels && node != null; n++)
125 node = GetParentNode (node);
127 if (node == null) return null;
129 HintNeighborhoodNodes (node, 0, relativeDepthFromWalkup);
133 public virtual SiteMapNode GetParentNodeRelativeToNodeAndHintDownFromParent (SiteMapNode node, int walkupLevels, int relativeDepthFromWalkup)
135 if (walkupLevels < 0) throw new ArgumentOutOfRangeException ("walkupLevels");
136 if (relativeDepthFromWalkup < 0) throw new ArgumentOutOfRangeException ("relativeDepthFromWalkup");
137 if (node == null) throw new ArgumentNullException ("node");
139 HintAncestorNodes (node, walkupLevels);
140 for (int n=0; n<walkupLevels && node != null; n++)
141 node = GetParentNode (node);
143 if (node == null) return null;
145 HintNeighborhoodNodes (node, 0, relativeDepthFromWalkup);
149 protected internal abstract SiteMapNode GetRootNodeCore ();
151 protected static SiteMapNode GetRootNodeCoreFromProvider (SiteMapProvider provider)
153 return provider.GetRootNodeCore ();
156 public virtual void HintAncestorNodes (SiteMapNode node, int upLevel)
158 if (upLevel < -1) throw new ArgumentOutOfRangeException ("upLevel");
159 if (node == null) throw new ArgumentNullException ("node");
162 public virtual void HintNeighborhoodNodes (SiteMapNode node, int upLevel, int downLevel)
164 if (upLevel < -1) throw new ArgumentOutOfRangeException ("upLevel");
165 if (downLevel < -1) throw new ArgumentOutOfRangeException ("downLevel");
166 if (node == null) throw new ArgumentNullException ("node");
169 protected virtual void RemoveNode (SiteMapNode node)
171 throw new NotImplementedException ();
174 public override void Initialize (string name, NameValueCollection attributes)
176 base.Initialize (name, attributes);
177 if (attributes ["securityTrimmingEnabled"] != null)
178 securityTrimming = (bool) Convert.ChangeType (attributes ["securityTrimmingEnabled"], typeof (bool));
181 [MonoTODO ("need to implement cases 2 and 3")]
182 public virtual bool IsAccessibleToUser (HttpContext context, SiteMapNode node)
184 if (context == null) throw new ArgumentNullException ("context");
185 if (node == null) throw new ArgumentNullException ("node");
187 if (!SecurityTrimmingEnabled)
190 /* The node is accessible (according to msdn2) if:
192 * 1. The Roles exists on node and the current user is in at least one of the specified roles.
194 * 2. The current thread has an associated WindowsIdentity that has file access to the requested URL and
195 * the URL is located within the directory structure for the application.
197 * 3. The current user is authorized specifically for the requested URL in the authorization element for
198 * the current application and the URL is located within the directory structure for the application.
202 IList roles = node.Roles;
203 if (roles != null && roles.Count > 0) {
204 foreach (string rolename in roles)
205 if (rolename == "*" || context.User.IsInRole (rolename))
213 string url = node.Url;
214 if(!String.IsNullOrEmpty(url)) {
215 // TODO check url is located within the current application
217 if (VirtualPathUtility.IsAppRelative (url) || !VirtualPathUtility.IsAbsolute (url))
218 url = VirtualPathUtility.Combine (VirtualPathUtility.AppendTrailingSlash (HttpRuntime.AppDomainAppVirtualPath), url);
220 AuthorizationSection config = (AuthorizationSection) WebConfigurationManager.GetSection (
221 "system.web/authorization",
224 return config.IsValidUser (context.User, context.Request.HttpMethod);
230 public virtual SiteMapNode CurrentNode {
232 if (HttpContext.Current != null) {
233 SiteMapNode ret = ResolveSiteMapNode (HttpContext.Current);
234 if (ret != null) return ret;
235 return FindSiteMapNode (HttpContext.Current);
241 public virtual SiteMapProvider ParentProvider {
242 get { return parentProvider; }
243 set { parentProvider = value; }
246 public virtual SiteMapProvider RootProvider {
249 if (rootProviderCache == null) {
250 SiteMapProvider current = this;
251 while (current.ParentProvider != null)
252 current = current.ParentProvider;
254 rootProviderCache = current;
257 return rootProviderCache;
261 protected SiteMapNode ResolveSiteMapNode (HttpContext context)
263 SiteMapResolveEventHandler eh = events [siteMapResolveEvent] as SiteMapResolveEventHandler;
270 SiteMapResolveEventArgs args = new SiteMapResolveEventArgs (context, this);
271 SiteMapNode r = eh (this, args);
279 public bool EnableLocalization {
280 get { return enableLocalization; }
281 set { enableLocalization = value; }
284 public bool SecurityTrimmingEnabled {
285 get { return securityTrimming; }
289 public string ResourceKey {
290 get { return resourceKey; }
291 set { resourceKey = value; }
294 public virtual SiteMapNode RootNode {
296 SiteMapNode node = GetRootNodeCore ();
297 return ReturnNodeIfAccessible (node);
301 internal static SiteMapNode ReturnNodeIfAccessible (SiteMapNode node)
303 if (node.IsAccessibleToUser (HttpContext.Current))
306 throw new InvalidOperationException (); /* need