2010-04-21 Marek Habersack <mhabersack@novell.com>
[mono.git] / mcs / class / System.Web / System.Web / SiteMapProvider.cs
index f65a07687c09fb8d1f1e740e8753ba0476e36e68..8d005f2aa494e772b318cbbd7e46b891f92ea788 100644 (file)
@@ -3,8 +3,10 @@
 //
 // Authors:
 //     Ben Maurer (bmaurer@users.sourceforge.net)
+//     Lluis Sanchez Gual (lluis@novell.com)
 //
 // (C) 2003 Ben Maurer
+// (C) 2005-2009 Novell, Inc (http://www.novell.com)
 //
 
 //
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
 
-#if NET_2_0
+using System.ComponentModel;
 using System.Collections;
 using System.Collections.Specialized;
 using System.Text;
 using System.Configuration.Provider;
 using System.Web.Util;
 using System.Globalization;
+using System.Web.Configuration;
 
-namespace System.Web {
-       public abstract class SiteMapProvider : ProviderBase {
+namespace System.Web
+{
+       public abstract class SiteMapProvider : ProviderBase
+       {
+               static readonly object siteMapResolveEvent = new object ();
+               
+               internal object this_lock = new object ();
                
                bool enableLocalization;
+               SiteMapProvider parentProvider;
+               SiteMapProvider rootProviderCache;
+               bool securityTrimming;
+               object resolveLock = new Object();
+               bool resolving;
+
+               EventHandlerList events = new EventHandlerList ();
                
-               public void AddNode (SiteMapNode node)
+               public event SiteMapResolveEventHandler SiteMapResolve {
+                       add { events.AddHandler (siteMapResolveEvent, value); }
+                       remove { events.RemoveHandler (siteMapResolveEvent, value); }
+               }
+               
+               protected virtual void AddNode (SiteMapNode node)
                {
                        AddNode (node, null);
                }
                
-               public void AddNode (SiteMapNode node, SiteMapNode parentNode)
+               internal protected virtual void AddNode (SiteMapNode node, SiteMapNode parentNode)
                {
-                       if (node == null)
-                               throw new ArgumentNullException ("node");
-                       
-                       lock (this) {
-                               string url = node.Url;
-                               if (url != null && url.Length > 0) {
-                                       
-                                       
-                                               if (UrlUtils.IsRelativeUrl (url))
-                                                       url = UrlUtils.Combine (HttpRuntime.AppDomainAppVirtualPath, url);
-                                               else
-                                                       url = UrlUtils.ResolveVirtualPathFromAppAbsolute (url);
-                                               
-                                               if (FindSiteMapNode (url) != null)
-                                                       throw new InvalidOperationException ();
-                                       
-                                       UrlToNode [url] = node;
-                               }
-                               
-                               if (parentNode != null) {
-                                       NodeToParent [node] = parentNode;
-                                       if (NodeToChildren [parentNode] == null)
-                                               NodeToChildren [parentNode] = new SiteMapNodeCollection ();
-                                       
-                                       ((SiteMapNodeCollection) NodeToChildren [parentNode]).Add (node);
-                               }
-                       }
+                       throw new NotImplementedException ();
                }
-               
-               Hashtable nodeToParent;
-               Hashtable NodeToParent {
-                       get {
-                               if (nodeToParent == null) {
-                                       lock (this) {
-                                               if (nodeToParent == null)
-                                                       nodeToParent = new Hashtable ();
-                                       }
-                               }
-                               return nodeToParent;
-                       }
+
+               public virtual SiteMapNode FindSiteMapNode (HttpContext context)
+               {
+                       if (context == null)
+                               return null;
+
+                       HttpRequest req = context.Request;
+                       if (req == null)
+                               return null;
+                       
+                       SiteMapNode ret = this.FindSiteMapNode (req.RawUrl);
+                       if (ret == null)
+                               ret = this.FindSiteMapNode (req.Path);
+                       return ret;
                }
+
+               public abstract SiteMapNode FindSiteMapNode (string rawUrl);
                
-               Hashtable nodeToChildren;
-               Hashtable NodeToChildren {
-                       get {
-                               if (nodeToChildren == null) {
-                                       lock (this) {
-                                               if (nodeToChildren == null)
-                                                       nodeToChildren = new Hashtable ();
-                                       }
-                               }
-                               return nodeToChildren;
-                       }
+               public virtual SiteMapNode FindSiteMapNodeFromKey (string key)
+               {
+                       /* msdn2 says this function always returns
+                        * null, but it seems to just call
+                        * FindSiteMapNode(string rawUrl) */
+                       return FindSiteMapNode (key);
                }
+
+               public abstract SiteMapNodeCollection GetChildNodes (SiteMapNode node);
                
-               Hashtable urlToNode;
-               Hashtable UrlToNode {
-                       get {
-                               if (urlToNode == null) {
-                                       lock (this) {
-                                               if (urlToNode == null) {
-                                                       urlToNode = new Hashtable (
-                                                               CaseInsensitiveHashCodeProvider.DefaultInvariant,
-                                                               CaseInsensitiveComparer.DefaultInvariant
-                                                       );
-                                               }
-                                       }
-                               }
-                               return urlToNode;
-                       }
+               public virtual SiteMapNode GetCurrentNodeAndHintAncestorNodes (int upLevel)
+               {
+                       if (upLevel < -1) throw new ArgumentOutOfRangeException ("upLevel");
+
+                       return CurrentNode;
                }
                
-               protected virtual void Clear ()
+               public virtual SiteMapNode GetCurrentNodeAndHintNeighborhoodNodes (int upLevel, int downLevel)
                {
-                       lock (this) {
-                               if (urlToNode != null)
-                                       urlToNode.Clear ();
-                               if (nodeToChildren != null)
-                                       nodeToChildren.Clear ();
-                               if (nodeToParent != null)
-                                       nodeToParent.Clear ();
-                       }
+                       if (upLevel < -1) throw new ArgumentOutOfRangeException ("upLevel");
+                       if (downLevel < -1) throw new ArgumentOutOfRangeException ("downLevel");
+                       
+                       return CurrentNode;
                }
 
-               public virtual SiteMapNode FindSiteMapNode (string rawUrl)
+               public abstract SiteMapNode GetParentNode (SiteMapNode node);
+               
+               public virtual SiteMapNode GetParentNodeRelativeToCurrentNodeAndHintDownFromParent (int walkupLevels, int relativeDepthFromWalkup)
                {
-                       if (rawUrl == null)
-                               throw new ArgumentNullException ("rawUrl");
+                       if (walkupLevels < 0) throw new ArgumentOutOfRangeException ("walkupLevels");
+                       if (relativeDepthFromWalkup < 0) throw new ArgumentOutOfRangeException ("relativeDepthFromWalkup");
                        
-                       if (rawUrl.Length > 0) {
-                               this.BuildSiteMap();
-                               rawUrl = UrlUtils.ResolveVirtualPathFromAppAbsolute (rawUrl);
-                               return (SiteMapNode) UrlToNode [rawUrl];
-                       }
-                       return null;
+                       SiteMapNode node = GetCurrentNodeAndHintAncestorNodes (walkupLevels);
+                       for (int n=0; n<walkupLevels && node != null; n++)
+                               node = GetParentNode (node);
+                               
+                       if (node == null) return null;
+
+                       HintNeighborhoodNodes (node, 0, relativeDepthFromWalkup);
+                       return node;
                }
                
-               public virtual SiteMapNodeCollection GetChildNodes (SiteMapNode node)
+               public virtual SiteMapNode GetParentNodeRelativeToNodeAndHintDownFromParent (SiteMapNode node, int walkupLevels, int relativeDepthFromWalkup)
                {
-                       if (node == null)
-                               throw new ArgumentNullException ("node");
-                       
-                       this.BuildSiteMap();
-                       SiteMapNodeCollection ret = (SiteMapNodeCollection) NodeToChildren [node];
+                       if (walkupLevels < 0) throw new ArgumentOutOfRangeException ("walkupLevels");
+                       if (relativeDepthFromWalkup < 0) throw new ArgumentOutOfRangeException ("relativeDepthFromWalkup");
+                       if (node == null) throw new ArgumentNullException ("node");
                        
-                       if (ret != null)
-                               return SiteMapNodeCollection.ReadOnly (ret);
+                       HintAncestorNodes (node, walkupLevels);
+                       for (int n=0; n<walkupLevels && node != null; n++)
+                               node = GetParentNode (node);
+                               
+                       if (node == null) return null;
                        
-                       return null;
+                       HintNeighborhoodNodes (node, 0, relativeDepthFromWalkup);
+                       return node;
                }
                
-               public virtual SiteMapNode GetParentNode(SiteMapNode node) {
-                       if (node == null)
-                               throw new ArgumentNullException ("node");
-                       this.BuildSiteMap();
-                       return (SiteMapNode) NodeToParent [node];
+               protected internal abstract SiteMapNode GetRootNodeCore ();
+               
+               protected static SiteMapNode GetRootNodeCoreFromProvider (SiteMapProvider provider)
+               {
+                       return provider.GetRootNodeCore ();
                }
                
-               public void RemoveNode (SiteMapNode node)
+               public virtual void HintAncestorNodes (SiteMapNode node, int upLevel)
                {
-       
-                       if (node == null)
-                               throw new ArgumentNullException("node");
-                       
-                       lock (this) {
-                               SiteMapNode parent = (SiteMapNode) NodeToParent [node];
-                               if (NodeToParent.Contains (node))
-                                       NodeToParent.Remove (node);
-                               
-                               if (node.Url != null && node.Url.Length > 0 && UrlToNode.Contains (node.Url))
-                                       UrlToNode.Remove (node.Url);
-                               
-                               if (parent != null) {
-                                       SiteMapNodeCollection siblings = (SiteMapNodeCollection) NodeToChildren [node];
-                                       if (siblings != null && siblings.Contains (node))
-                                               siblings.Remove (node);
-                               }
-                       }
+                       if (upLevel < -1) throw new ArgumentOutOfRangeException ("upLevel");
+                       if (node == null) throw new ArgumentNullException ("node");
+               }
+               
+               public virtual void HintNeighborhoodNodes (SiteMapNode node, int upLevel, int downLevel)
+               {
+                       if (upLevel < -1) throw new ArgumentOutOfRangeException ("upLevel");
+                       if (downLevel < -1) throw new ArgumentOutOfRangeException ("downLevel");
+                       if (node == null) throw new ArgumentNullException ("node");
+               }
+               
+               protected virtual void RemoveNode (SiteMapNode node)
+               {
+                       throw new NotImplementedException ();
                }
 
                public override void Initialize (string name, NameValueCollection attributes)
-               { 
-                       if (attributes != null)
-                               description = attributes ["description"];
+               {
+                       base.Initialize (name, attributes);
+                       if (attributes ["securityTrimmingEnabled"] != null)
+                               securityTrimming = (bool) Convert.ChangeType (attributes ["securityTrimmingEnabled"], typeof (bool));
+               }
                
+               [MonoTODO ("need to implement cases 2 and 3")]
+               public virtual bool IsAccessibleToUser (HttpContext context, SiteMapNode node)
+               {
+                       if (context == null) throw new ArgumentNullException ("context");
+                       if (node == null) throw new ArgumentNullException ("node");
+
+                       if (!SecurityTrimmingEnabled)
+                               return true;
+
+                       /* The node is accessible (according to msdn2) if:
+                        *
+                        * 1. The Roles exists on node and the current user is in at least one of the specified roles.
+                        *
+                        * 2. The current thread has an associated WindowsIdentity that has file access to the requested URL and
+                        * the URL is located within the directory structure for the application.
+                        *
+                        * 3. The current user is authorized specifically for the requested URL in the authorization element for
+                        * the current application and the URL is located within the directory structure for the application. 
+                       */
+
+                       /* 1. */
+                       IList roles = node.Roles;
+                       if (roles != null && roles.Count > 0) {
+                               foreach (string rolename in roles)
+                                       if (rolename == "*" || context.User.IsInRole (rolename))
+                                               return true;
+                       }
+                       
+                       /* 2. */
+                       /* XXX */
+
+                       /* 3. */
+                       string url = node.Url;
+                       if(!String.IsNullOrEmpty(url)) {
+                               // TODO check url is located within the current application
+
+                               if (VirtualPathUtility.IsAppRelative (url) || !VirtualPathUtility.IsAbsolute (url))
+                                       url = VirtualPathUtility.Combine (VirtualPathUtility.AppendTrailingSlash (HttpRuntime.AppDomainAppVirtualPath), url);
+
+                               AuthorizationSection config = (AuthorizationSection) WebConfigurationManager.GetSection (
+                                       "system.web/authorization",
+                                       url);
+                               if (config != null)
+                                       return config.IsValidUser (context.User, context.Request.HttpMethod);
+                       }
+
+                       return false;
                }
                
                public virtual SiteMapNode CurrentNode {
                        get {
-                               SiteMapNode ret;
-                               
                                if (HttpContext.Current != null) {
-                                       ret = this.FindSiteMapNode (HttpContext.Current.Request.RawUrl);
-                                       if (ret == null)
-                                               ret = this.FindSiteMapNode (HttpContext.Current.Request.Path);
-
-                                       return ret;
-                               }
-                               
-                               return null;
+                                       SiteMapNode ret = ResolveSiteMapNode (HttpContext.Current);
+                                       if (ret != null) return ret;
+                                       return FindSiteMapNode (HttpContext.Current);
+                               } else
+                                       return null;
                        }
                }
                
-               string description;
-               public virtual string Description {
-                       get { return description != null ? description : "SiteMapProvider"; }
-               }
-               
-               SiteMapProvider parentProvider;
                public virtual SiteMapProvider ParentProvider {
                        get { return parentProvider; }
                        set { parentProvider = value; }
                }
                
-               SiteMapProvider rootProviderCache;
                public virtual SiteMapProvider RootProvider {
                        get {
-                               if (rootProviderCache == null) {
-                                       lock (this) {
-                                               if (rootProviderCache == null) {
-                                                       SiteMapProvider current = this;
-                                                       while (current.ParentProvider != null)
-                                                               current = current.ParentProvider;
-                                                       
-                                                       rootProviderCache = current;
-                                               }
+                               lock (this_lock) {
+                                       if (rootProviderCache == null) {
+                                               SiteMapProvider current = this;
+                                               while (current.ParentProvider != null)
+                                                       current = current.ParentProvider;
+                                               
+                                               rootProviderCache = current;
                                        }
                                }
                                return rootProviderCache;
                        }
                }
                
+               protected SiteMapNode ResolveSiteMapNode (HttpContext context)
+               {
+                       SiteMapResolveEventHandler eh = events [siteMapResolveEvent] as SiteMapResolveEventHandler;
+
+                       if (eh != null) {
+                               lock (resolveLock) {
+                                       if (resolving)
+                                               return null;
+                                       resolving = true;
+                                       SiteMapResolveEventArgs args = new SiteMapResolveEventArgs (context, this);
+                                       SiteMapNode r = eh (this, args);
+                                       resolving = false;
+                                       return r;
+                               }
+                       } else
+                               return null;
+               }
+               
                public bool EnableLocalization {
                        get { return enableLocalization; }
                        set { enableLocalization = value; }
                }
+               
+               public bool SecurityTrimmingEnabled {
+                       get { return securityTrimming; }
+               }
+
+               string resourceKey;
+               public string ResourceKey {
+                       get { return resourceKey; }
+                       set { resourceKey = value; }
+               }
+
+               public virtual SiteMapNode RootNode {
+                       get {
+                               SiteMapNode node = GetRootNodeCore ();
+                               return ReturnNodeIfAccessible (node);
+                       }
+               }
 
-               public abstract SiteMapNode BuildSiteMap ();
-               public abstract SiteMapNode RootNode { get; }
-       
+               internal static SiteMapNode ReturnNodeIfAccessible (SiteMapNode node)
+               {
+                       if (node.IsAccessibleToUser (HttpContext.Current))
+                               return node;
+                       else
+                               throw new InvalidOperationException (); /* need
+                                                                        * a
+                                                                        * message
+                                                                        * here */
+               }
        }
 }
-#endif
+