Tuesday, August 29, 2017

View Trash Content Specific To Site For User in EPiServer

In one of our project we had very strict access rights policy for EPiServer projects.
We had about 16 sites and each of the sites had editor which could not see the content of other sites.

We had proper access rights setup on site hierarchy but Recycle Bin EPiServer sits inside the Root Page. The root page and recycle bin had access to everyone therefore, editors could see the deleted content of other editors. Therefore, we had to come up with some approach to restrict the content in recycle bin to editor of that specific site.

What we decided to create virtual role and make sure content is linked to logged in user site.
As the actual content is physically moved underneath the recycle bin. Therefore, there was no way to find the actual site information. However, before deleting content and moving it to under recycle bin EPiServer saves the actual parent deleted content in Dynamic Data Store named ParentRestoreEntity  and can use IParentRestoreRepository to fetch parent link. You can check the view dbo.VW_EPiParentRestoreStore on database as well.


In order to figure out whether, user has access to deleted content. we will fetch the original parent of deleted content and make sure logged in user has access to that parent content validating that the current logged in user has access to this deleted content as well.

Below is the Virtual Role that we created

 namespace NRL.Web.Business.VirtualRoles
{
    using System.Security.Principal;
    using EPiServer.Core;
    using EPiServer.DataAbstraction;
    using EPiServer.Security;
    using EPiServer.Web;
    using NRL.Shared.Extensions;
    using NRL.Web.Business.Extensions;
    using NRL.Web.Business.Models.Pages;
    using EPiServer.Filters;

    public class SiteTrashRole : VirtualRoleProviderBase
    {
        private IVirtualRoleRepository _repository;
        private IParentRestoreRepository _parentRestoreRepository;
        private ISiteDefinitionResolver _siteDefinitionResolver;

        private const string DefaultRoleName = "SiteTrashRole";
        private static string _roleName;

        public override string Name
        {
            get
            {
                return base.Name ?? "SiteTrashRole";
            }

            set
            {
                base.Name = value;
            }
        }

        /// <summary>Gets or sets gets the name of the role.</summary>
        /// <value>The name of the role.</value>
        public static string RoleName
        {
            get
            {
                return SiteTrashRole._roleName ?? (SiteTrashRole._roleName = "SiteTrashRole");
            }

            set
            {
                SiteTrashRole._roleName = value;
            }
        }

        /// <summary>
        /// Creates a new instance of <see cref="T:EPiServer.Security.CreatorRole" /></summary>
        public SiteTrashRole(IVirtualRoleRepository repository, IParentRestoreRepository parentRestoreRepository, ISiteDefinitionResolver siteDefinitionResolver)
        {
            this.EnableIsInRoleCache = false;
            this.SupportsClaims = false;
            this._repository = repository;
            this._parentRestoreRepository = parentRestoreRepository;
            this._siteDefinitionResolver = siteDefinitionResolver;
        }

        /// <summary>
        /// Determines whether the specified principal is a member of this role.
        /// </summary>
        /// <param name="principal">The principal.</param>
        /// <param name="context">The context.</param>
        /// <returns>
        /// <c>true</c> if the specified principal is in the virtual role; otherwise, <c>false</c>.
        ///     </returns>
        /// <note>
        /// Any implementation of IsInVirtualRole must be fully thread-safe since only one instance of the class
        /// is created and any role checks are made against the same instance.
        /// </note>
        public override bool IsInVirtualRole(IPrincipal principal, object context)
        {
            if (!principal.Identity.IsAuthenticated)
                return false;

            if (context is ContentAccessControlList cacl)
            {
                if (cacl.ContentLink == ContentReference.WasteBasket)
                    return true;

                var originalParentLink = _parentRestoreRepository.GetParentLink(cacl.ContentLink);

                /* Check parent access */
                var parentContent = originalParentLink?.Get<IContent>();

                if (parentContent == null)
                    return false;

                if (parentContent.QueryDistinctAccess(AccessLevel.Read))
                    return true;
            }

            return false;
        }
    }
}



The Recylce bin role setup looked like

  <virtualRoles addClaims="true">
      <providers>
         <add name="SiteTrashRole" type="NRL.Web.Business.VirtualRoles.SiteTrashRole, NRL.Web.Business" />
      </providers>
    </virtualRoles>    



Now, when user opens up the recycle bin then it will see content of site it belonged to.



@Note: We have give only read/change access to editors so they can see the lists. The restore and delete would not work as it requires delete access rights. The problem with given delete access rights is that delete button becomes enabled and then editor would be able to delete content of other sites.
If you try to restore you will get below message

No comments:

Post a Comment