Security Trimming and SiteMapNode attributes

Feb 11, 2010 at 9:50 AM
Edited Feb 11, 2010 at 9:50 AM

Hey, two questions:

Firstly: I'm trying to make use of Security Trimming but can't get it to work.

Taking the example project, if I put the [Authorize(Roles="testing")] attribute on either the Index action in the products controller, or the product controller class itself, it still shows up in the menu. Should this be the case? I thought I'd read that MVCSiteMap should pick up on the attribute and determine that the node shouldn't be shown as no one is logged in / doesn't have permission?

Secondly: Is it possible to build a site map solely using [SiteMapNode] attributes? If I leave out the web.sitemap or leave it empty, I keep getting exceptions.

Thanks!

Feb 11, 2010 at 11:05 AM
Edited Feb 11, 2010 at 11:10 AM

I have had the same Problems! I like the concept of MVC Sitemap - and in former version it works. Im looking for the reason an solve the problem with the following code in the MvcSiteMapProvider.cs modul:

the changes are marked with 

//Security trimming
I don't know wether this is a optimal solution but it works for me!

 

    protected SiteMapNode GetMvcSiteMapNodeFromMvcSiteMapNodeAttribute(IMvcSiteMapNodeAttribute attribute, Type type, MethodInfo methodInfo)
        {
            if (attribute == null)
            {
                throw new ArgumentNullException("attribute");
            }
            if (type == null)
            {
                throw new ArgumentNullException("type");
            }

            if (methodInfo == null) // try to find Index action
            {
                var ms = type.FindMembers(MemberTypes.Method, BindingFlags.Instance | BindingFlags.Public, (mi, o) => mi != null && string.Equals(mi.Name, "Index"), null);
                if (ms != null)
                {
                    foreach (MethodInfo m in ms.OfType<MethodInfo>())
                    {
                        var pars = m.GetParameters();
                        if (pars == null || pars.Length == 0)
                        {
                            methodInfo = m;
                            break;
                        }
                    }
                }
            }

            var area = attribute.Area;
            if (string.IsNullOrEmpty(area))
            {
                var parts = type.Namespace.Split('.');
                area = parts[parts.Length - 2];

                var assemblyParts = type.Assembly.FullName.Split(',');

                if (type.Namespace == assemblyParts[0] + ".Controllers" || type.Namespace.StartsWith(area))
                {
                    // Is in default areaName...
                    area = null;
                }
            }
            var controller = type.Name.Replace("Controller", "") ?? "Home";
            var action = (methodInfo != null ? methodInfo.Name : null) ?? "Index";
            if (methodInfo != null) // handle custom action name
            {
                
                var actionNameAttribute = methodInfo.GetCustomAttributes(typeof(ActionNameAttribute), true).FirstOrDefault() as ActionNameAttribute;
                if (actionNameAttribute != null)
                {
                    action = actionNameAttribute.Name;
                }
            }

            // Generate key for node
            var key = !string.IsNullOrEmpty(attribute.Key) ? attribute.Key :
                methodInfo != null ? (area ?? "") + "_" + controller + "_" + action + "_" + attribute.Title : Guid.NewGuid().ToString();
            
            
            

            // Handle title and description globalization
            var explicitResourceKeys = new NameValueCollection();
            var title = attribute.Title;
            var description = attribute.Description;
            HandleResourceAttribute("title", ref title, ref explicitResourceKeys);
            HandleResourceAttribute("description", ref description, ref explicitResourceKeys);

            // Create a new SiteMap node, setting the key and url
            var siteMapNode = new MvcSiteMapNode(this, key, explicitResourceKeys);
          
            // Security trimming
            // read the Attributs  for the controller       

            var types=methodInfo.Module.GetTypes();
            foreach (var typ in types)
            {
                foreach (Attribute au in typ.GetCustomAttributes(typeof(AuthorizeAttribute), true))
                {
                    if (methodInfo.DeclaringType.FullName == typ.FullName)
                    {
                        if (au.GetType() == typeof(AuthorizeAttribute))
                        {
                            AuthorizeAttribute ax = (AuthorizeAttribute)au;
                            string[] rolesx = ax.Roles.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries);
                            foreach (string rol in rolesx)
                            {
                                siteMapNode.Roles.Add(rol);
                            }
                        }
                    }
                }
            }



            // read the modul Attributs
            foreach (Attribute authatt in methodInfo.GetCustomAttributes(typeof(AuthorizeAttribute), true))
            {
                if (authatt.GetType()==typeof(AuthorizeAttribute))
                {
                    AuthorizeAttribute autAtt = (AuthorizeAttribute)authatt;
                    string[] roles = autAtt.Roles.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries);
                    foreach (string rol in roles)
                    {
                        siteMapNode.Roles.Add(rol);
                    }
                    
                
                }
            }
            // Ende des von mir eingefügten Codes!
            // End Security Trimming

            // Set the properties on siteMapNode.
            siteMapNode.Title = title;
            siteMapNode.Description = description;
            siteMapNode.Target = attribute.Target;
            siteMapNode.ImageUrl = attribute.ImageUrl;
            siteMapNode.Area = area;
            siteMapNode.Controller = controller;
            siteMapNode.Action = action;
            
            
            if (!string.IsNullOrEmpty(attribute.Url))
                siteMapNode.Url = attribute.Url;

            siteMapNode.Visibility = attribute.Visibility;
            siteMapNode.ResourceKey = attribute.ResourceKey;

            if (attribute.Visibility == MvcSiteMapNodeVisibility.Full && siteMapNode.IsDynamic)
            {
                siteMapNode.Visibility = MvcSiteMapNodeVisibility.InSiteMapPathOnly;
            }

            if (attribute.DetectIsDynamic && methodInfo != null)
            {
                var pars = methodInfo.GetParameters();
                siteMapNode.IsDynamic = pars != null && pars.Length > 0;
                if (siteMapNode.IsDynamic)
                {
                    foreach (ParameterInfo parameter in pars)
                    {
                        siteMapNode.DynamicParameters.Add(parameter.Name);
                    }
                }
            }
            else
            {
                siteMapNode.IsDynamic = attribute.IsDynamic;
                if (siteMapNode.IsDynamic && !string.IsNullOrEmpty(attribute.DynamicParameters))
                {
                    siteMapNode.DynamicParameters.AddRange(attribute.DynamicParameters.Split(';', ','));
                }
            }

            // Set node properties
            siteMapNode["controller"] = siteMapNode.Controller;
            siteMapNode["action"] = siteMapNode.Action;

            // Create a route data dictionary
            IDictionary<string, object> routeValues = new Dictionary<string, object>();
            routeValues.Add("area", siteMapNode.Area);
            routeValues.Add("controller", siteMapNode.Controller);
            routeValues.Add("action", siteMapNode.Action);

            // Add route values to sitemap node
            siteMapNode.RouteValues = routeValues;
            
            return siteMapNode;
        }

        #endregion

        #region Helpers

        /// <summary>
        /// Given an XAttribute, will either return an empty string if its value is
        /// null or the actual value.
        /// </summary>
        /// <param name="attribute">The attribe to get the value for.</param>
        /// <returns></returns>
        public string GetAttributeValue(XAttribute attribute)
        {
            return attribute != null ? attribute.Value : null;
        }

        /// <summary>
        /// Get controller from controller cache
        /// </summary>
        /// <param name="context">Request context</param>
        /// <param name="controllerName">Name of the controller.</param>
        /// <param name="areaName">Name of the areaName.</param>
        /// <returns>Controller instance</returns>
        public IController GetController(RequestContext context, string controllerName, string areaName)
        {
            if (controllerName == "/") controllerName = "Home";
            var cacheKey = String.Format("{0}/{1}", areaName, controllerName);
            if (!controllerCache.ContainsKey(cacheKey))
            {
                lock (controllerCache)
                {
                    if (!controllerCache.ContainsKey(cacheKey))
                    {
                        var controller = ControllerBuilder.Current.GetControllerFactory().CreateController(context, controllerName);
                        controllerCache.Add(cacheKey, controller);

                        return controller;
                    }
                }
            }

            return (IController)controllerCache[cacheKey];
        }

 

 

Feb 13, 2010 at 12:02 PM

I've post a new solution here: http://mvcsitemap.codeplex.com/Thread/View.aspx?ThreadId=85250

Regards,

Minorello

Feb 14, 2010 at 3:04 PM
Edited Feb 14, 2010 at 4:02 PM

Hi minerello,

it´s a small 3 line solution - but this seems to break the concept. maybe im wrong, but if we have a filed "roles" inside the Sitemapnodes so we have no relation  to the concept of

AuthorizeAttribute. So we have to look for Authorize AND for the sitemapnodes "Roles" 
Regards thilo