When Public Access isn't quite enough

Heads Up!

This article is several years old now, and much has happened since then, so please keep that in mind while reading it.

Much has been written in the last few days about Membership sites on Umbraco.  Mike and Pawel's articles focus on the APIs needed for creating and updating Members in Umbraco at a fairly detailed and complex level.  

Here I'm going  to take a step back from their deep dive and show that it is possible to get a Membership site up and running quickly, with a decidedly Level 1 Developer focus (my comfort zone).  I'll also show that with just a little extra code you can extend Umbraco's 2-state Public Access feature to allow n-states of access for a page.

We're big fans of Umbraco Public Access feature - even though it's name is counter intuitive to most of the Editors we work with.  Public Access is a great way to secure content to certain parts of a site to only Logged in and Authorised Members.  Essentially Editors need to be provided with a Login Page and an Error Page and then Locking down access to a secured area is an entirely Editor managed task.

In early, WebForms versions of Umbraco creating the Login Page was simple, just dropping a <ASP:Login/> control on a template was good enough.

Life is a little more complex in today's MVC world, but the code to create registration, login and logout pages is there as out of the box code in Umbraco.  Steve Morgan wrote about how to setup a site with Public Access without Coding.  It's still very relevant today if you've got some Level 1 developer skills and aren't comfortable in writing controllers and the like (me included).

Getting your site up and running with Public access simply involves the creation of couple of Partial Views to show a Login Form and a Logout Button, using code snippets provided in Umbraco by default.  These make use of controllers that are already available in the Umbraco core (have a look if you want to learn a little more about the core code UmbLoginController.cs and UmbLoginStatusController.cs). You then need to create Login and Error pages.

I've built a really simple demo site built in a couple of hours that includes all the DocTypes, Views, Partials to get a simple membership site up and running without any real coding required.  The critical login, logout and registration code was provided by the Snippets in Umbraco and I only edited them to add a bit of styling to work with the Foundation framework.

The demo site is available on the 2StateMembership branch of my repo.

This gives each Course page controlled by Public Access two states:

  • Logged in & authorised:  Umbraco will display the default template for the page

  • Not logged in, or not authorised:

    • Not logged in -- Umbraco will display the Login Page of the editors choice

    • Logged in, not authorised -- Umbraco will display the Error Page of the editors choice

However, we've found on a couple of recent sites that the two states that Public Access provides are not enough to encompass the functionality we wanted to implement.

To give an example, we're currently building a learning management system that incorporates part sales function (transacted through Merchello) and part course delivery system. I wanted to maintain a single course directory with a node for each course, that node should present different content depending on the state of the current member.

  • Not logged in -- Umbraco will display the "Sales" view of the content node

  • Logged in, course not purchased -- Ditto above

  • Logged in, course purchased -- Umbraco will display the "Deliver course" view of the node

It is the fact that the page is always accessible but showing different content depending on the current state of the logged in member and their properties, that means simple public access isn't any good.  

We could have achieved this by maintaining a set of publicly accessible course directory content nodes and a corresponding set of course content nodes secured using Public Access, but we'd need to maintain relationships between them.  But that's a complexity for Editors which we've done in previous sites we've built that we wanted to improve on.

The simplest solution we've found is to do away with allowing Editors to manage access using Public Access.  Instead the default template on the n-State Public Access pages checks the current state of the Membership and displays different code as a result.

@using Umbraco.Core;
@using Umbraco.Core.Models;
@using Umbraco.Core.Services;
@inherits Umbraco.Web.Mvc.UmbracoTemplatePage

@{
    Layout = "ContentMaster.cshtml";

    // Check if logged in and if authorised
    bool loggedIn = false;
    bool authorised = false;

    if (loggedIn = Members.IsLoggedIn())
    {
        // Retrieve all the members with a Role matching the current page name
        // Then check if the current member is a part of that set
        
        var memberService = ApplicationContext.Current.Services.MemberService;

        string pageName = CurrentPage.Name;
        var matchingMembers = memberService.GetMembersInRole(pageName);
        authorised = matchingMembers.Any(x => x.Id == Members.GetCurrentMember().Id);
    }
}
<div class="row">
    <div class="large-12 columns">
        <div class="callout large">
            <h2>@Umbraco.Field("coursetitle")</h2>

            @if (loggedIn && authorised)
            {
                @Umbraco.Field("coursecontent")
            }
            else
            {
                @Umbraco.Field("salesdescription")
                
                <h3>Next steps</h3>
                if (!loggedIn)
                {
                    <p>To access this course you need to login</p>
                    <a href="~/login" class="button">login</a>
                }
                else
                {
                    <p>To access this course you need to arrange for access</p>
                    <a href="mailto:no-reply@orcare.com" class="button">Email us</a>
                }
            }

        </div>
    </div>
</div>

The updated demo can be found on the 3StateMembership branch of the repo.

My example above is a much simplified version of what we use in the real world.  For example, we wouldn't normally use Member Groups as a mechanism for fine grain page authorisation.  Instead on our current project we use a table of Course Licenses, to determine if a Member is authorised to view a page.

Hopefully, this small example has helped to show that it is possible to get a Membership based Umbraco site up and running with little coding and out of the box functionality, then extend this to enable pages to have n-State Public Access.  As we use this n-State Public Access pattern more, we're seeing more use cases for it, e.g. on a different ecommerce site we add additional functionality to our product pages depending on the logged in state, and add an additional level of data on pages depending on attributes of the Member.

A big thanks and Merry Christmas to Alex Meyers and Jacob Polden who helped me put this article together.

 

Paul Marden

Paul is on Twitter as