Inconvenient SPSecurityTrimmedControl revisited

, , , , , ,

About a year ago I wrote about the inconvenience of the SPSecurityTrimmedControl: a great idea within the SharePoint framework that unfortunately doesn’t work the way it should. Now, as we’re about to get a new release of SharePoint, I decided to check if things have changed.

What makes SPSecurityTrimmedControl so cool?

In case you didn’t know SharePoint, both 2007 and 2010, ships with a control that allows you to conditionally display/hide pieces of your site: the Microsoft.SharePoint.WebControls.SPSecurityTrimmedControl. You can display/hide the content using a few different criteria, like authentication (for anonymous/authenticated users only), page mode (page in display or edit mode) or current user’s permissions.

…so why it’s so uncool?

From what I’ve seen the control doesn’t work the way you would expect it. Let’s take for example the criteria to include some code for anonymous users only. This is a common way to include web analytics tracking codes. Using the SPSecurityTrimmedControl you would use the following code snippet in your Master Page/Page Layout:

<SharePoint:SPSecurityTrimmedControl runat="server" AuthenticationRestrictions="AnonymousUsersOnly">
Tracking code goes here...
</SharePoint:SPSecurityTrimmedControl>

Unfortunately the tracking code doesn’t get included: neither for authenticated nor for anonymous users. Similar thing applies to the PageModes property that would allow you to display some content in Edit Mode only (a bit like the EditModePanel from SharePoint server does). So far nothing has changed in SharePoint 2010.

And on top of that…

Even if the SPSecurityTrimmedControl worked as it should, it would still have a major flaw. SPSecurityTrimmedControl is meant to display conditionally content: this can be plain text, HTML or ASP.NET controls. The problem is with how it determines whether it should show its contents or not. As the SPSecurityTrimmedControls performs the check almost at the end of its lifecycle in the Render method, almost every method of every child control gets executed: even if it won’t be included in the generated HTML!

With all that I decided to create a custom SecurityTrimmedControl that would:

  1. Work as you would expect it to.
  2. Prevent the child controls from being executed if the whole control is hidden.

Proudly introducing the ImtechSecurityTrimmedControl:

[ParseChildren(true)]
public class ImtechSecurityTrimmedControl : Control
{
    public ITemplate ContentTemplate { get; set; }

    public AuthenticationRestrictions AuthenticationRestrictions { get; set; }
    public SPControlMode PageMode { get; set; }

    private static AuthenticationRestrictions CurrentAuthenticationRestriction
    {
        get
        {
            AuthenticationRestrictions currentAuthenticationRestriction = HttpContext.Current.Request.IsAuthenticated ? AuthenticationRestrictions.AuthenticatedUsersOnly : AuthenticationRestrictions.AnonymousUsersOnly;

            return currentAuthenticationRestriction;
        }
    }

    private static SPControlMode CurrentPageMode
    {
        get
        {
            SPControlMode pageMode = SPControlMode.Invalid;

            if (HttpContext.Current != null)
            {
                HttpRequest request = HttpContext.Current.Request;
                if (IsDocLibListItem)
                {
                    pageMode = (request.Form.Get("MSOAuthoringConsole_FormContext") == "1") ? SPControlMode.Edit : SPControlMode.Display;
                }

                if ((pageMode == SPControlMode.Display) && (request.QueryString.Get("ControlMode") == "Edit"))
                {
                    pageMode = SPControlMode.Edit;
                }
            }

            return pageMode;
        }
    }

    private static bool IsDocLibListItem
    {
        get
        {
            return SPContext.Current != null &&
                SPContext.Current.ListItem != null &&
                SPContext.Current.ItemId != 0;
        }
    }

    private bool ShouldRender
    {
        get
        {
            return AuthenticationRestrictionMatchesCurrentRequest(AuthenticationRestrictions) && PageIsInMode(PageMode);
        }
    }

    internal bool PageIsInMode(SPControlMode pageMode)
    {
        return pageMode == 0 ||
            CurrentPageMode == pageMode;
    }

    private static bool AuthenticationRestrictionMatchesCurrentRequest(AuthenticationRestrictions authenticationRestrictions)
    {
        return authenticationRestrictions == 0 ||
            authenticationRestrictions == AuthenticationRestrictions.AllUsers ||
            CurrentAuthenticationRestriction == authenticationRestrictions;
    }

    protected override void CreateChildControls()
    {
        base.CreateChildControls();

        if (ContentTemplate != null && ShouldRender)
        {
            Control container = new Control();
            ContentTemplate.InstantiateIn(container);
            Controls.Add(container);
        }
    }
}

The code is pretty straight-forward. After performing a few checks the control uses a template to instantiate its children if required. Because the ImtechSecurityTrimmedControl doesn’t have any HTML markup of its own, it derives from the System.Web.UI.Control base class. Because of that the ParseChildren attribute has to be explicitly set to true to make the control parse the ContentTemplate. If we inherited from the WebControl base class this would be done for us, but then we would end up with a pretty div in the generated HTML.

One thing that I didn’t include is the support for permissions. The reason for this is pretty simple: in all the projects I’ve worked on so far, I have never had a need to use that kind of functionality. And in case you need it: you can easily extend the control to support it.


Possibly related posts

6 Responses to “Inconvenient SPSecurityTrimmedControl revisited”

  1. Ari Bakker Says:

    Nice work Waldek. Could you also use the ASP.NET LoginView control to show content for anonymous users only or does it suffer from the same issue in that it performs the display check in the render method?

  2. Waldek Mastykarz Says:

    @Ari: Thanks. As far as I know the LoginView control uses templates as well, so there should be little penalty when showing content conditionally.

  3. Brad Porter Says:

    This is brilliant, Waldek! Would you mind giving details on how to compile/install and/or make available in your Tools section? Cheers!! Saved me hours of troubleshooting!

  4. Waldek Mastykarz Says:

    @Brad: The ImtechSecurityTrimmedControl is a control which needs to be compiled into an assembly. That assembly can be either deployed into GAC or into the bin directory of your Web Application where you want to make use of it. When working with SharePoint 2010 you can benefit of the new Visual Studio 2010 SharePoint Developer Tools to make the process of packaging and deploying the WSP easier. With SharePoint 2007 you can use WSPBuilder.

  5. When SPSecurityTrimmedControl AuthenticationRestrictions FAILs, simple workaround « Adisimon’s Weblog Says:

    [...] blog entry by Waldek Mastykarz mentioned this same problem. I figured that the SPSecurityTrimmedControl class [...]

  6. Using a different master page for authenticated users in SharePoint « Johan Leino Says:

    [...] you would use either a SPSecurityTrimmedControl or a [...]

Leave a Reply

Security Code:

WP Theme & Icons by N.Design Studio
Entries RSS Comments RSS
Copyright © 2007 - 2012 Waldek Mastykarz

Creative Commons License