It's no secret that I'm a fan of Content Apps. I've created a few of them since the documentation page was published on how to create them.

My favourite one that I've written so far is the one I co-wrote with Nik Rimington, called ImageFilter.

One thing I don't like is how the decision on who gets to see the Content App is made by the package owner, in that they can code it in to only allow for admin roles etc.

Also there is not much room for too many Content Apps so if we can manage who gets to see them ourselves then it will be a real help.

So I've made a way to allow you to be able to control which groups can see a specific Content App.

The idea is, you add this code to your site, or install the NuGet package when I publish it and then you configure the permissions in a single app setting value.

If we take my Image Filter content app as an example, I can add an app setting which says only show the content app titled 'Filter' to people who are in a group called 'Filter' or 'Administrators'.

<!-- Enter the Content app and allowed groups in this format: 
  ContentApp1Name[AllowedGroup1|AllowedGroup2],ContentApp2Name[AllowedGroup1] -->

<add key="ContentAppsRestrictedByGroup" value="Filter[Filter|Administrators]"/>

I can then create a Group called 'Filter' and add the relevant users to that group.

Now only users who are in the group 'Filter' or 'Administrators' can see the Content App.

Here is the code:

using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Web.Http.Filters;
using Umbraco.Core;
using Umbraco.Core.Composing;
using Umbraco.Core.Models.ContentEditing;
using Umbraco.Core.Models.Membership;
using Umbraco.Web.Editors;
using Umbraco.Web.Models.ContentEditing;

namespace Our.Umbraco.ContentAppGroupPermissions.Core.Compose
{
    [RuntimeLevel(MinLevel = RuntimeLevel.Run)]
    public class ContentAppGroupPermissionsComposer : ComponentComposer<ContentAppGroupPermissionsComponent>
    {
    }

    public class ContentAppGroupPermissionsComponent : IComponent
    {
        public void Initialize()
        {
            EditorModelEventManager.SendingMediaModel += EditorModelEventManager_SendingMediaModel;
            EditorModelEventManager.SendingContentModel += EditorModelEventManager_SendingContentModel;
        }

        public void Terminate()
        {
        }

        private void EditorModelEventManager_SendingMediaModel(HttpActionExecutedContext sender, EditorModelEventArgs<MediaItemDisplay> e)
        {
            e.Model.ContentApps = GetAllowedContentApps(e.UmbracoContext.Security.CurrentUser, e.Model.ContentApps);
        }

        private void EditorModelEventManager_SendingContentModel(HttpActionExecutedContext sender, EditorModelEventArgs<ContentItemDisplay> e)
        {
            e.Model.ContentApps = GetAllowedContentApps(e.UmbracoContext.Security.CurrentUser, e.Model.ContentApps);
        }

        private IEnumerable<ContentApp> GetAllowedContentApps(IUser user, IEnumerable<ContentApp> contentApps)
        {
            // Read the app setting for restricting content apps
            var contentAppsRestrictedByGroups = ConfigurationManager.AppSettings["ContentAppsRestrictedByGroup"]?.Split(',');

            if (contentAppsRestrictedByGroups != null)
            {
                // Loop through the content app settings to find the content app name and allowed group list
                foreach (var contentAppSetting in contentAppsRestrictedByGroups.Where(x => !string.IsNullOrWhiteSpace(x)))
                {
                    var contentAppName = contentAppSetting.Split('[').FirstOrDefault();
                    var allowedGroupList = GetAllowedGroupList(contentAppSetting);
                    if (!string.IsNullOrWhiteSpace(contentAppName) && allowedGroupList != null)
                    {
                        // check if the user is in any of the allowed groups
                        // if they are not, then remove the content app from the list
                        contentApps = UserIsInAnyOfTheseGroups(user, allowedGroupList)
                            ? contentApps
                            : contentApps.Where(x => x.Name != contentAppName);
                    }
                }
            }

            return contentApps;
        }

        // Reads the app setting and returns a list of group names that are allowed to use this content app
        private static string[] GetAllowedGroupList(string contentAppSetting)
        {
            var groupNamesStart = contentAppSetting.IndexOf('[');
            var groupNamesEnd = contentAppSetting.IndexOf(']');
            if (groupNamesStart != -1 && groupNamesEnd != -1)
            {
                var allowedGroups = contentAppSetting.Substring(groupNamesStart + 1, (groupNamesEnd - groupNamesStart) - 1);
                var allowedGroupList = allowedGroups.Split('|');
                return allowedGroupList;
            }

            return null;
        }

        private static bool UserIsInAnyOfTheseGroups(IUser user, IEnumerable<string> groupNames)
        {
            return user?.Groups?.Any(x => groupNames.Contains(x.Name)) ?? false;
        }
    }
}

As you can see, the idea is that if the user is not a member of any of the groups in the app setting for the Content App then it gets removed from the list.

In theory, the Content tab and Info tab are both Content Apps, so you can use this to restrict them too

<!-- Enter the Content app and allowed groups in this format: 
  ContentApp1Name[AllowedGroup1|AllowedGroup2],ContentApp2Name[AllowedGroup1] -->
<add key="ContentAppsRestrictedByGroup" value="Filter[Filter|Administrators],Content[Administrators],Info[Sensitive data]"/>

Paul Seal

Umbraco MVP and .NET Web Developer from Derby (UK) who specialises in building Content Management System (CMS) websites using MVC with Umbraco as a framework. Paul is passionate about web development and programming as a whole. Apart from when he's with his wife and son, if he's not writing code, he's thinking about it or listening to a podcast about it.

Proudly sponsored by

Moriyama

  • Moriyama build, support and deploy Umbraco, Azure and ASP.NET websites and applications.
AppVeyor

  • CI/CD service for Windows, Linux and macOS
  • Build, test, deploy your apps faster, on any platform.
elmah.io

  • elmah.io is the easy error logging and uptime monitoring service for .NET.
  • Take back control of your errors with support for all .NET web and logging frameworks.
uSync Complete

  • uSync.Complete gives you all the uSync packages, allowing you to completely control how your Umbraco settings, content and media is stored, transferred and managed across all your Umbraco Installations.
uSkinned

  • More than a theme for Umbraco CMS, take full control of your content and design with a feature-rich, award-nominated & content editor focused website platform.
UmbHost

  • Affordable, Geo-Redundant, Umbraco hosting which gives back to the community by sponsoring an Umbraco Open Source Developer with each hosting package sold.