Take the community feedback survey now.

Modify the Add User/Group dropdown options in the Access Right popup

Vote:
 

Hello,

I am trying to change the dropdown options that appear when a web editor attempts to set access rights to any content. After the popup opens that handles access rights, the dropdown appears after clicking on the "Add User/Group" button. Currently, the dropdown loads all users/groups without filtering according to the groups the signed-in user has access to. I need to filter the dropdown options according to the user roles. I found no documentation regarding this. So I assumed there is no existing configuration or option in the backend to achieve this.

I planned to create a new API to fetch dropdown options and integrate it into the Dojo component for the popup. After inspecting the existing codebase, I discovered that the Access Rights popup component is built using React. However, I have limited knowledge of how to override a React component that is embedded in Dojo. Any suggestions would be greatly appreciated. Additionally, please advise if it is possible to handle this entirely on the backend.

#340176
Sep 01, 2025 16:20
Vote:
 

You’re correct that there isn’t a supported configuration option for filtering the access rights dropdown. However, it can be handled on the backend by replacing the service that provides the dropdown data.

Please note that the class you’ll be overriding lives in the EPiServer.Cms.Shell.UI.Rest.Internal namespace. That means it’s considered an internal API and not guaranteed to be stable across versions. It works today, but Optimizely could change it in a future release.

The dropdown in both Edit view (“Set Access Rights” popup) and Admin mode relies on the SecurityEntityService. Specifically:

  • SearchNonVirtualRoles → fetches “real” roles from the membership/role provider

  • SearchVirtualRoles → fetches roles from the virtual role repository

By replacing this service, you can control what roles are returned to the UI. That way you don’t need to patch the React/Dojo frontend at all.

You can create a replacement class that delegates to the original logic but filters the results to only those roles the current user belongs to (principal.IsInRole). In this sample, users in the CmsAdmins group bypass filtering and see all roles:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;
using EPiServer.Cms.Shell.UI.Rest.Internal;
using EPiServer.Personalization.VisitorGroups;
using EPiServer.Security;
using Microsoft.AspNetCore.Http;

#nullable enable

namespace YourNamespace
{
    public class CustomSecurityEntityService : SecurityEntityService
    {
        private readonly IHttpContextAccessor _httpContextAccessor;
        private const string CmsAdminsRole = "CmsAdmins";

        public CustomSecurityEntityService(
            SecurityEntityProvider securityEntityProvider,
            IVirtualRoleRepository virtualRoleRepository,
            IVisitorGroupRepository visitorGroupRepository,
            IHttpContextAccessor httpContextAccessor)
            : base(securityEntityProvider, virtualRoleRepository, visitorGroupRepository)
        {
            _httpContextAccessor = httpContextAccessor;
        }

        public override async Task<IEnumerable<string>> SearchNonVirtualRoles(
            string partOfName,
            int startIndex,
            int maxRows)
        {
            var roles = await base.SearchNonVirtualRoles(partOfName, startIndex, maxRows);
            var principal = GetCurrentPrincipal();

            if (principal is null)
            {
                return Enumerable.Empty<string>();
            }

            // CmsAdmins see all roles
            if (principal.IsInRole(CmsAdminsRole))
            {
                return roles;
            }

            // Others only see roles they’re in
            return roles.Where(principal.IsInRole);
        }

        public override IEnumerable<string> SearchVirtualRoles(string partOfName)
        {
            var roles = base.SearchVirtualRoles(partOfName);
            var principal = GetCurrentPrincipal();

            if (principal is null)
            {
                return Enumerable.Empty<string>();
            }

            // CmsAdmins see all roles
            if (principal.IsInRole(CmsAdminsRole))
            {
                return roles;
            }

            // Others only see roles they’re in
            return roles.Where(principal.IsInRole);
        }

        private ClaimsPrincipal? GetCurrentPrincipal()
        {
            var principal = _httpContextAccessor.HttpContext?.User;
            if (principal is not null && principal.Identity is { IsAuthenticated: true })
            {
                return principal;
            }

            return Thread.CurrentPrincipal as ClaimsPrincipal;
        }
    }
}

Finally, register your replacement in Startup.cs after adding CMS services. The last registration wins in ASP.NET Core DI:

var mvcBuilder = services
            .AddCms();
 
services.AddTransient<SecurityEntityService, CustomSecurityEntityService>();

Hopefully this gives you some ideas to try.

#340305
Edited, Sep 09, 2025 12:27
Saad-Al- Muttakee - Sep 09, 2025 16:19
Thank you very much for the reply. This is exactly what I needed.
Your explanation has helped a lot. I was able to implement the class in a very short time.
* You are NOT allowed to include any hyperlinks in the post because your account hasn't associated to your company. User profile should be updated.