Base solution for your next web application
Open Closed

Using multi-organisation units for entity access permissions #10008


User avatar
0
Astech created

Hi,

We are relatively new to Zero and are wanting to add a permission based system to the entities that we create. We have seen the following URL which suggests using Organisation Units:

https://support.aspnetzero.com/QA/Questions/7163/More-detailed-permissions-per-entity

It links to this guide:

https://aspnetboilerplate.com/Pages/Documents/Articles\How-To\add-custom-data-filter-ef-core

The guide details how to have a single OU associated to a user, however in our business model both users and entities can have multiple OUs associated. So for example, an entity may have 3 OUs associated that can access it. A user may be in 5 OUs. As long as one of the OUs matches, they will have access.

Is there a method/guide for acheiving this?

As an additional question is there a premade UI control that we can plug into for the entity OU selection? We have spotted the lookup modal. Should we use this to lookup OUs and allow multi selection of them for each entity or is there a more "built in" way of handling such entity permissions?

We are using the latest Zero version for .NET Core/Jquery.

Thanks in advance

Scott


4 Answer(s)
  • User Avatar
    0
    Astech created

    As an addition point, we have attempted to follow the instructions in the following guide:

    https://aspnetboilerplate.com/Pages/Documents/Articles\How-To\add-custom-data-filter-ef-core

    However user.OrganizationUnitId is always null in the UserClaimsPrincipalFactory?

    We also noticed that there was already a list of OrganizationUnits in the User.cs class:

    public List<UserOrganizationUnit> OrganizationUnits { get; set; }

    However it also seems to be null in UserClaimsPrincipalFactory depite the User being in an OU.

    Are we missing something?

  • User Avatar
    0
    Astech created

    No replies but I shall keep the thread updated with my progress in case anyone else is attempting to do the same in the future (or for if anyone has any input or suggestions).

    I could not get the user.OrganizationUnitId or user.OrganizationUnits to populate and they always appear to be null. Therefore I am manually getting them by modifying the CreateAsync method from the guide that I sent a link to above:

    public override async Task CreateAsync(User user)
    {
        var claim = await base.CreateAsync(user);
    
        var organizationUnits = await _userManager.GetOrganizationUnitsAsync(user);
        var organizationUnitIds = organizationUnits.Select(ou => ou.Id);
    
        foreach (var ou in organizationUnitIds)
        {
    	claim.Identities.First().AddClaim(new Claim("Application_OrganizationUnitId", ou.ToString()));
        }
    
        return claim;
    }
    

    This seems to work in terms of getting the OU's to create claims however I still need to test the performance hit of doing it this way, as CreateAsync seems to be called multiple times per page load.

    This isn't too far away from what we need. However, the more problematic step now is being able to assign multiple OU's against entities. We are also wanting a allow/deny system whereby a user can select a OU for any entity and state whether that OU is allowed or denied access to it. A deny rule will always override an allow.

    We are currently looking into this by modifying our entities to have lists of essentially a join table type (called EntityOrganizationUnit). One for allowed and one for denied:

    public class Team : FullAuditedEntity, IMayHaveTenant, INeedOrganizationUnitAuthorization
    {
        public int? TenantId { get; set; }
        [Required]
        [StringLength(TeamConsts.MaxNameLength, MinimumLength = TeamConsts.MinNameLength)]
        public virtual string Name { get; set; }
    
        [StringLength(TeamConsts.MaxDescriptionLength, MinimumLength = TeamConsts.MinDescriptionLength)]
        public virtual string Description { get; set; }
    
        public virtual DateTime StartDate { get; set; }
    
        public virtual DateTime EndDate { get; set; }
    
        public List<EntityOrganizationUnit> AllowedOrganizationUnitIds { get; set; }
    
        public List<EntityOrganizationUnit> DeniedOrganizationUnitIds { get; set; }
    }
    

    This EntityOrganizationUnit class will then bridge the gap between any entity (be it a team, player, coach) and it's OU permissions:

    public class EntityOrganizationUnit : FullAuditedEntity, IMayHaveTenant
    {
        public int? TenantId { get; set; }
        public int EntityTypeId { get; set; }
        public int EntityId { get; set; }
        public int OrganizationUnitId { get; set; } 
        public bool IsAllowOrDeny { get; set; }  //TODO - change to enum
    }
    

    This is currently where we are up to and the next stage will involve trying to bring the OU and entities (any entities - not just team) tables together with this join table to create a situation whereby any item added on the system by an end user can have multiple OUs assigned to it to define who (and who cannot) have access to it.

    Shall keep you updated with the progress that we make. And if anyone has any comments, suggestions or advice on any of the above, we would love to hear your thoughts.

    Thanks, Scott

  • User Avatar
    0
    Astech created

    @ismcagdas Is it possible to get your input on this please?

    Thank you

  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi @Astech,

    Sorry for the late reply. You can use OrganizationTree component for OU selection for entities. You can find it under wwwroot/view-resources/Areas/AppAreaName/Views/Common/_OrganizationTree.js.

    For filtering entities by OUs, you can follow this document https://aspnetboilerplate.com/Pages/Documents/Articles\How-To\add-custom-data-filter-ef-core. In this way, entities which implement INeedOrganizationUnitAuthorization can be filtered automatically.

    You can also use generic repositories and write a single query in CreateAsync method if you think two round-trip to database will be a performance problem.