Umbraco’s Public Access is a great feature, but it is not very suitable in some cases.

For example if you are working on a site with multiple environments like Development, Staging and Production, you might find some obstacles:

  • The Public Access settings are stored in ~/App_Data/access.config and not in the database – this is a problem if your environments are distributed using DFS. In this case you need to make sure that DFS will copy the updated settings between the servers.
  • The protected pages are stored using their IDs – you might get ID mismatch between the Dev, Staging and Production installations.
  • Courier does not transfer the access.config out of the box (I haven’t tested this myself, but I checked in the community) and event if you manage to transfer it, it is very likely that you will have ID problems.

Public Access Alternative

Following is a sample access.config file:

<access>
  <page id="8930" loginPage="8472" noRightsPage="9054" simple="False">
    <group id="Administrator" />
  </page>
</access>

I’ve come up with a more flexible design of the XML contents:

<access>
  <page path="Site Root/Page1/Page2"
    loginPath="Site Root/Login"
    errorPath="Site Root/Unauthorized"
    groups="Administrator" />
</access>

Note that the path attributes represent the names of the nodes in the content tree. This way you won’t have to worry about IDs between the different environments and you can store this file in source control.

Parsing the access.xml

Following is the code to parse the XML on application startup. When the application has started, the code will check if App_Data contains the access.xml file. If it exists, it will parse the content and use the APIs that Public Access uses to protect the pages. In my case I only needed roles based protection, so I didn’t implement single user protection.

public class PublicAccessSetup : ApplicationEventHandler
{
    protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
    {
        var accessXmlFile = HttpContext.Current.Server.MapPath("~/App_Data/access.xml");
        if (System.IO.File.Exists(accessXmlFile))
        {
            // Clear the old access.config.
            System.IO.File.Delete(HttpContext.Current.Server.MapPath("~/App_Data/access.config"));

            var protectedPages = XDocument.Load(accessXmlFile).XPathSelectElements("/access/page");
            foreach (var protectedPage in protectedPages)
            {
                try
                {
                    var page = GetPageByUrl(protectedPage.Attribute("path").Value);
                    var loginPage = GetPageByUrl(protectedPage.Attribute("loginPath").Value);
                    var errorPage = GetPageByUrl(protectedPage.Attribute("errorPath").Value);
                    var groups = protectedPage.Attribute("groups").Value.Split(';');

                    Access.ProtectPage(false, page.Id, loginPage.Id, errorPage.Id);
                    foreach (var group in groups)
                    {
                        Access.AddMembershipRoleToDocument(page.Id, group);
                    }
                }
                catch
                {
                    // Log
                }
            }
        }
    }

    private static IContent GetPageByUrl(string url)
    {
        var pages = url.ToLower().Split('/');
        if (pages.Any())
        {
            var parent = ApplicationContext.Current.Services.ContentService
             .GetRootContent()
             .First(x => x.Name.ToLower() == pages.First());
            foreach (var page in pages.Skip(1))
            {
                parent = parent.Children().First(x => x.Name.ToLower() == page);
            }

            return parent;
        }

        return null;
    }
}

0 Comments

Leave a Reply

Your email address will not be published. Required fields are marked *