sitemap support

Nov 12, 2009 at 3:10 PM

Hi Andrea,

I am wondering whether NetSqlAzman supports Sitemap?

Part of our application requirement ,we need to enable or diable menu links based logged in roles.

Thanks,

Satya.

 

 

 

Coordinator
Nov 12, 2009 at 7:58 PM

Hy Satya,

I have used Sitemap with NetSqlAzMan in a lot of cases, but you have to write your custom DynamicSiteMapProvider.
Here an example that use NetSqlAzMan WCF Cache Service (in this example I have populated into the global.asax the variable Session["AuthorizedItems"] with the WCF Cache Service Results ... see IsAccessibleToUser method below):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Xml;
using System.Collections.Specialized;
using NetSqlAzMan.Cache;
using NetSqlAzMan.Interfaces;
using NetSqlAzMan;
using System.Diagnostics;

namespace XXX.Framework.Security.Web
{
    /// <summary>
    /// DynamicSiteMapProvider per la generazione delle Voci di menu (in base alle autorizzazioni)
    /// </summary>
    public class DynamicSiteMapProvider : StaticSiteMapProvider
    {
        #region Constants & Fields
        private const String SiteMapNodeName = "siteMapNode";
        private String _siteMapFileName;
        private SiteMapNode _rootNode = null;
        #endregion Constants & Fields
        #region Private Methods
        private XmlDocument LoadSiteMapXml()
        {
            XmlDocument siteMapXml = new XmlDocument();
            siteMapXml.Load(AppDomain.CurrentDomain.BaseDirectory + _siteMapFileName);
            return siteMapXml;
        }
        private void AddDynamicNodes(XmlElement rootElement)
        {
            //XmlElement teams = AddDynamicChildElement(rootElement, "", "Nodo dinamico", "Nodo dinamico");
            //AddDynamicChildElement(teams, "~/SottonodoDinamico1.aspx", "Sotto nodo dinamico 1", "Sotto nodo dinamico 1");
            //AddDynamicChildElement(teams, "~/SottonodoDinamico2.aspx", "Sotto nodo dinamico 2", "Sotto nodo dinamico 2");
            //AddDynamicChildElement(teams, "~/SottonodoDinamico3.aspx", "Sotto nodo dinamico 3", "Sotto nodo dinamico 3");
        }
        private static XmlElement AddDynamicChildElement(XmlElement parentElement, string url, string title, string description, string resourceKey)
        {
            // Create new element from the parameters
            XmlElement childElement = parentElement.OwnerDocument.CreateElement(SiteMapNodeName);
            childElement.SetAttribute("url", url);
            childElement.SetAttribute("title", title);
            childElement.SetAttribute("description", description);
            childElement.SetAttribute("resourceKey", resourceKey);

            // Add it to the parent
            parentElement.AppendChild(childElement);
            return childElement;
        }
        private static XmlElement AddDynamicChildElement(XmlElement parentElement, string url, string title, string description)
        {
            return DynamicSiteMapProvider.AddDynamicChildElement(parentElement, url, title, description, String.Empty);
        }
        private void GenerateSiteMapNodes(XmlElement rootElement)
        {
            _rootNode = GetSiteMapNodeFromElement(rootElement);
            AddNode(_rootNode);
            CreateChildNodes(rootElement, _rootNode);
        }
        private void CreateChildNodes(XmlElement parentElement, SiteMapNode parentNode)
        {
            foreach (XmlNode xmlElement in parentElement.ChildNodes)
            {
                if (xmlElement.Name == SiteMapNodeName)
                {
                    SiteMapNode childNode = GetSiteMapNodeFromElement((XmlElement)xmlElement);
                    AddNode(childNode, parentNode);
                    CreateChildNodes((XmlElement)xmlElement, childNode);
                }
            }
        }
        private SiteMapNode GetSiteMapNodeFromElement(XmlElement rootElement)
        {
            SiteMapNode newSiteMapNode;
            string url = rootElement.GetAttribute("url");
            string title = rootElement.GetAttribute("title");
            string description = rootElement.GetAttribute("description");
            string resourceKey = rootElement.GetAttribute("resourceKey");
            //bool securityTrimmingEnabled = bool.Parse(rootElement.GetAttribute("securityTrimmingEnabled"));
            // The key needs to be unique, so hash the url and title.
            newSiteMapNode = new SiteMapNode(this, url == String.Empty ? Guid.NewGuid().ToString() : url, url, title, description);
            newSiteMapNode.ResourceKey = resourceKey;
            return newSiteMapNode;
        }

        #endregion Private Methods
        #region Public Methods
        /// <summary>
        /// Gets the root <see cref="T:System.Web.SiteMapNode"/> object of the site map data that the current provider represents.
        /// </summary>
        /// <value></value>
        /// <returns>
        /// The root <see cref="T:System.Web.SiteMapNode"/> of the current site map data provider. The default implementation performs security trimming on the returned node.
        /// </returns>
        public override SiteMapNode RootNode
        {
            get { return BuildSiteMap(); }
        }

        /// <summary>
        /// Initializes the <see cref="T:System.Web.SiteMapProvider"/> implementation, including any resources that are needed to load site map data from persistent storage.
        /// </summary>
        /// <param name="name">The <see cref="P:System.Configuration.Provider.ProviderBase.Name"/> of the provider to initialize.</param>
        /// <param name="attributes">A <see cref="T:System.Collections.Specialized.NameValueCollection"/> that can contain additional attributes to help initialize the provider. These attributes are read from the site map provider configuration in the Web.config file.</param>
        public override void Initialize(string name, NameValueCollection attributes)
        {
            base.Initialize(name, attributes);
            _siteMapFileName = attributes["siteMapFile"];
        }
        /// <summary>
        /// When overridden in a derived class, loads the site map information from persistent storage and builds it in memory.
        /// </summary>
        /// <returns>
        /// The root <see cref="T:System.Web.SiteMapNode"/> of the site map navigation structure.
        /// </returns>
        public override SiteMapNode BuildSiteMap()
        {
            lock (this)
            {
                if (null == _rootNode)
                {
                    Clear();
                    // Load the sitemap's xml from the file.
                    XmlDocument siteMapXml = LoadSiteMapXml();
                    // Create the first site map item from the top node in the xml.
                    XmlElement rootElement =
                        (XmlElement)siteMapXml.GetElementsByTagName(
                        SiteMapNodeName)[0];
                    // This is the key method - add the dynamic nodes to the xml
                    this.AddDynamicNodes(rootElement);
                    // Now build up the site map structure from the xml
                    GenerateSiteMapNodes(rootElement);
                    //HttpContext context = HttpContext.Current;
                    //if (context != null & context.Session != null && context.Session["AuthorizedItems"] != null)
                    //{
                    //    //Clean Session Variable
                    //    context.Session["AuthorizedItems"] = null;
                    //    context.Session.Remove("AuthorizedItems");
                    //}
                }
            }
            return _rootNode;
        }
        #endregion Public Methods
        #region Protected Methods
        /// <summary>
        /// When overridden in a derived class, retrieves the root node of all the nodes that are currently managed by the current provider.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.Web.SiteMapNode"/> that represents the root node of the set of nodes that the current provider manages.
        /// </returns>
        protected override SiteMapNode GetRootNodeCore()
        {
            return RootNode;
        }

        /// <summary>
        /// Removes all elements in the collections of child and parent site map nodes that the <see cref="T:System.Web.StaticSiteMapProvider"/> tracks as part of its state.
        /// </summary>
        protected override void Clear()
        {
            lock (this)
            {
                _rootNode = null;
                base.Clear();
            }
        }

        /// <summary>
        /// Retrieves a Boolean value indicating whether the specified <see cref="T:System.Web.SiteMapNode"/> object can be viewed by the user in the specified context.
        /// </summary>
        /// <param name="context">The <see cref="T:System.Web.HttpContext"/> that contains user information.</param>
        /// <param name="node">The <see cref="T:System.Web.SiteMapNode"/> that is requested by the user.</param>
        /// <returns>
        /// true if security trimming is enabled and <paramref name="node"/> can be viewed by the user or security trimming is not enabled; otherwise, false.
        /// </returns>
        /// <exception cref="T:System.ArgumentNullException">
        ///  <paramref name="context"/> is null.
        /// - or -
        /// <paramref name="node"/> is null.
        /// </exception>
        public override bool IsAccessibleToUser(HttpContext context, SiteMapNode node)
        {
            if (context!=null & context.AllErrors!=null && context.AllErrors.Length > 0)
                return true;
           
            //if (context.Cache[String.Format("{0} {1}", context.Session.SessionID, node.Key)] != null)
            //    return (bool)context.Cache[String.Format("{0} {1}", context.Session.SessionID, node.Key)];

            bool bresult = false;
            AuthorizedItem[] authorizedItems = (AuthorizedItem[])context.Session["AuthorizedItems"];
            string[] operationNames = node.ResourceKey.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
            if (operationNames.Length > 0)
            {
                AuthorizationType result = AuthorizationType.Neutral;
                foreach (string opName in operationNames)
                {
                    string operationName = opName.Trim();
                    var authorizedItem = (from t in authorizedItems
                                          where t.Name.Equals(operationName, StringComparison.CurrentCultureIgnoreCase)
                                          select t).FirstOrDefault();
                    if (authorizedItem != null)
                    {
                        result = AuthorizationHelper.MergeAuthorizations(result, authorizedItem.Authorization);
                    }
                    else
                    {
                        throw new ArgumentOutOfRangeException("resourceKey", String.Format("Operazione NetSqlAzMan '{0}' non valida (Node Title: {1}).", operationName, node.Title));
                    }
                }
                bresult = result == AuthorizationType.Allow || result == AuthorizationType.AllowWithDelegation;
            }
            else
            {
                bresult = true;
            }
            if (node.ParentNode != null)
                bresult = bresult & this.IsAccessibleToUser(context, node.ParentNode);
            //context.Cache.Add(String.Format("{0} {1}", context.Session.SessionID, node.Key), bresult,
            //    null,
            //    System.Web.Caching.Cache.NoAbsoluteExpiration,
            //    System.Web.Caching.Cache.NoSlidingExpiration,
            //    System.Web.Caching.CacheItemPriority.Default, null);
            return bresult;
        }
        #endregion Protected Methods
    }
}

 

Coordinator
Nov 12, 2009 at 8:00 PM

... and this is an example of the Web.sitemap file ... (I use the resourceKey attribute to set the NetSqlAzMan operation to check).

<?xml version="1.0" encoding="utf-8" ?>
<siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0">
  <siteMapNode url="~/Default.aspx" title="Home" description="" securityTrimmingEnabled="true">

    <siteMapNode url="~/Archivi OnLine/ArchiviOnLineMenuPage.aspx"      title="Archivi Online" description="" resourceKey="Accesso">
      <siteMapNode url="~/Archivi OnLine/Ricerca.aspx"          title="Ricerca" description="" resourceKey="Ricerca Semplice"/>
      <siteMapNode url="~/Archivi OnLine/Lavorazioni.aspx"      title="Lavorazioni" description="" resourceKey="Modifica Lavorazione"/>
      <siteMapNode url="~/Archivi OnLine/GestioneLotti.aspx"    title="Gestione Lotti" description="" resourceKey="Elimina Lotto"/>
    </siteMapNode>

    <!--<siteMapNode url="~/Amministrazione/Amministrazione.aspx" title="Amministrazione" description="" resourceKey="Accesso">-->
    <siteMapNode url="~/Amministrazione/Gestione Siti Clienti/GestioneSitiClientiMenuPage.aspx" title="Gestione Siti Clienti" description="" resourceKey="Inserisci e Modifica Sito">
      <siteMapNode url="~/Amministrazione/Gestione Siti Clienti/GestioneSitoCliente.aspx" title="Nuovo Sito Cliente" description="" />
      <siteMapNode url="~/Amministrazione/Gestione Siti Clienti/ElencoSitiClienti.aspx" title="Elenco Siti Clienti" description="" />
    </siteMapNode>
    <siteMapNode url="~/Amministrazione/Gestione Utenti/GestioneUtentiMenuPage.aspx" title="Gestione Utenti" description="" resourceKey="Gestione Utenti del Cliente">
      <siteMapNode url="~/Amministrazione/Gestione Utenti/GestioneUtente.aspx" title="Nuovo Utente" description=""/>
      <siteMapNode url="~/Amministrazione/Gestione Utenti/ElencoUtenti.aspx" title="Elenco Utenti" description=""/>
      <siteMapNode url="~/Amministrazione/Gestione Utenti/Impersonation.aspx" title="Impersonation" description="" resourceKey="Impersonation"/>
    </siteMapNode>

    <siteMapNode url="~/Help/HelpMenuPage.aspx" title="Help" description="" resourceKey="Help">
      <siteMapNode url="~/Help/FAQ.aspx" title="F.A.Q." description=""/>
      <siteMapNode url="~/Help/HelpOnLine.aspx" title="Help On-Line" description=""/>
      <siteMapNode url="~/Help/RichiediSupporto.aspx" title="Richiedi Supporto" description=""/>
    </siteMapNode>

    <siteMapNode url="~/Security/UserInfo.aspx" title="Informazioni Utente" description="Visualizza le impostazione dell'Utente" resourceKey="Accesso" />
  </siteMapNode>
</siteMap>

 

Nov 13, 2009 at 1:31 PM

Thanks Andrea!

I can not use above code as it is as it missing AuthorizationHelper reference. Would you mind sharing that code as well?

 

Thanks,

Satya.

 

 

Coordinator
Nov 13, 2009 at 1:45 PM

Hi,

I have used only this static method (inside AuthorizationHelper):

/// <summary>

        /// Merges the authorizations.

        /// </summary>

        /// <param name="auth1">The auth1.</param>

        /// <param name="auth2">The auth2.</param>

        /// <returns></returns>

        public static AuthorizationType MergeAuthorizations(AuthorizationType auth1, AuthorizationType auth2)

        {

            if (auth1 == AuthorizationType.AllowWithDelegation)

            {

                if (auth2 == AuthorizationType.AllowWithDelegation)

                {

                    return AuthorizationType.AllowWithDelegation;

                }

                else if (auth2 == AuthorizationType.Allow)

                {

                    return AuthorizationType.AllowWithDelegation;

                }

                else if (auth2 == AuthorizationType.Deny)

                {

                    return AuthorizationType.Deny;

                }

                else if (auth2 == AuthorizationType.Neutral)

                {

                    return AuthorizationType.AllowWithDelegation;

                }

            }

            else if (auth1 == AuthorizationType.Allow)

            {

                if (auth2 == AuthorizationType.AllowWithDelegation)

                {

                    return AuthorizationType.AllowWithDelegation;

                }

                else if (auth2 == AuthorizationType.Allow)

                {

                    return AuthorizationType.Allow;

                }

                else if (auth2 == AuthorizationType.Deny)

                {

                    return AuthorizationType.Deny;

                }

                else if (auth2 == AuthorizationType.Neutral)

                {

                    return AuthorizationType.Allow;

                }

            }

            else if (auth1 == AuthorizationType.Deny)

            {

                return AuthorizationType.Deny;

            }

            //else if (auth1 == AuthorizationType.Neutral)

            //{

            return auth2;

            //}

        }

__________________________________
Andrea Ferendeles
NetSqlAzMan Project Coordinator  
E-mail aferende@hotmail.com Web http://netsqlazman.codeplex.com

Nov 13, 2009 at 6:19 PM

Hello Andrea,

Thanks again for your source code and I appreciate it. However I do have list of questions regarding dynamic Sitemap provider implementation

From your code I have made couple of assumptions from my side, and would like to make sure whether I am thinking on same guide lines as you coded.

1. Retrieving user roles using GetAuthorizedItems and then cache them as  AuthorizedItems

2. Resourcekey would be holding roles authorised  for a given Sitemap Node . In below code roles will be replaced with  Resourcekey?

<siteMapNode title="Home" description="Home"  url="~/default.aspx"  >
    <siteMapNode title="Products"
         description="Our products"
         roles="*" >
      <siteMapNode title="Hardware"
           description="Hardware we offer"
           url="~/Hardware.aspx" />
      <siteMapNode title="Software"
           description="Software for sale"
           url="~/Software.aspx" />
      <siteMapNode title="Discounts"
           description="Employee Discounts"
           url="~/EmployeesOnly/Discounts.aspx" />
    </siteMapNode>

3. AddDynamicNodes() method is only required if I want to add some nodes(nodes not avialble in sitemap file) at runtime ,otherwise AddDynamicNodes() could be ignored in BuildSiteMap() method?

4. what happens if my website has more than one sitemap file?

Hope to hear back from you soon and thanks for your time!!!

Satya.

Coordinator
Nov 13, 2009 at 8:18 PM
Hello Satya,
you have understood well:
1) Yes, using the WCF Cache Service ... I collect user authorizations one time per session.
2) Yes, I'm using resourseKey to map each sitemap node to a specific "operation". I dont want to use Roles ... to avoid strong coupluing between Roles and my application.
3) Yes ... use it only if you need to dynamically compose your sitemap at run time (and check authorizations at run-time too).
4) You can change your sitemap by changing the sitemap source into the web.config file:
<siteMap defaultProvider="MySiteMapProvider">
<providers>
<add siteMapFile="Web.sitemap1" name="Default" type="My.Framework.Security.Web.DynamicSiteMapProvider1" enabled="false" />
<add siteMapFile="Web.sitemap2" name="MySiteMapProvider" type="My.Framework.Security.Web.DynamicSiteMapProvider2" enabled="true" securityTrimmingEnabled="true" />
</providers>
</siteMap>
Regards,
Andrea.

Nov 16, 2009 at 8:32 PM

 

Hello Andrea,

Above code working fine for single Sitemap file  however it does not work for more than one file.I have added sitemap files to web.config file like below but my provider reading only Web.sitemap data

<siteMap defaultProvider="DynamicSiteMapProvider" enabled="true">
   <providers>
         <add name="DynamicSiteMapProvider1" description="Default SiteMap provider1." type="WebSite2.DynamicSiteMapProvider" siteMapFile="Web2.sitemap" securityTrimmingEnabled="true"/>
         <add name="DynamicSiteMapProvider" description="Default SiteMap provider." type="WebSite2.DynamicSiteMapProvider" siteMapFile="Web.sitemap" securityTrimmingEnabled="true"/> 
   </providers>
  </siteMap>

Any ideas? 

Right now my application using SitemapDatasource and Treeview controls to dispaly sitemap files data , I am wondering how to replace them with my dynamic sitemap and place it on same location as my previous sitemap on page.

Thanks,

Satya. 

 

 

Coordinator
Nov 16, 2009 at 8:37 PM
See if this can be useful:
Regards,
Andrea.

Nov 16, 2009 at 8:57 PM

 

I get below error for

<siteMap defaultProvider="DynamicSiteMapProvider" enabled="true">
   <providers>
         <add name="DynamicSiteMapProvider1" description="Default SiteMap provider1." type="WebSite2.DynamicSiteMapProvider" siteMapFile="Web2.sitemap" securityTrimmingEnabled="true"/>
        <!-- <add name="DynamicSiteMapProvider" description="Default SiteMap provider." type="WebSite2.DynamicSiteMapProvider" siteMapFile="Web.sitemap" securityTrimmingEnabled="true"/>-->
   </providers>
  </siteMap>

 

Configuration Error

Description: An error occurred during the processing of a configuration file required to service this request. Please review the specific error details below and modify your configuration file appropriately.

Parser Error Message: The provider 'DynamicSiteMapProvider' specified for the defaultProvider does not exist in the providers collection.

Source Error:

Line 103:			<add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
Line 104:		</httpModules>
Line 105:		<siteMap defaultProvider="DynamicSiteMapProvider" enabled="true">
Line 106:			<providers>
Line 107:         <add name="DynamicSiteMapProvider1" description="Default SiteMap provider1." type="WebSite2.DynamicSiteMapProvider" siteMapFile="Web2.sitemap" securityTrimmingEnabled="true"/>
Coordinator
Nov 17, 2009 at 7:16 AM

You have this error because you have commented the second provider.

You are declaring that you are using a default provider that is “commented”. J

__________________________________
Andrea Ferendeles
NetSqlAzMan Project Coordinator  
E-mail aferende@hotmail.com Web http://netsqlazman.codeplex.com

Nov 19, 2009 at 2:31 PM

Hello Andrea,

I made it work this way....

In web config file I configured SiteMapProvider.... 

<siteMap defaultProvider="DynamicSiteMapProvider" enabled="true">
   <providers>
         <add name="DynamicSiteMapProvider1" description="Default SiteMap provider1." type="WebSite2.DynamicSiteMapProvider" siteMapFile="Web.sitemap" securityTrimmingEnabled="true"/>
         <add name="DynamicSiteMapProvider" description="Default SiteMap provider." type="WebSite2.DynamicSiteMapProvider" siteMapFile="Web2.sitemap" securityTrimmingEnabled="true"/>
   </providers>
  </siteMap>

in side .aspx page I added below code that's it ....

<body>
    <form id="form1" runat="server">
    <div>
   
    </div>
    <asp:SiteMapDataSource ID="SiteMapDataSource1" SiteMapProvider = "DynamicSiteMapProvider1" runat="server" />
    <asp:TreeView ID="TreeView1" runat="server" DataSourceID="SiteMapDataSource1">
    </asp:TreeView>
   
    <asp:TreeView ID="TreeView2" runat="server" DataSourceID="SiteMapDataSource2">
    </asp:TreeView>
    <asp:SiteMapDataSource ID="SiteMapDataSource2" SiteMapProvider = "DynamicSiteMapProvider" runat="server" />
   
    </form>
  
</body>

the above code allows me to shows two sitemaps at one time with DynamicSiteMapProvider and also secuiry trimming enabled to true.

Thanks,

Satya.

 

 

 

Coordinator
Dec 2, 2009 at 7:14 AM

Good work !

Andrea.