Contact Us

How to Implement Dynamic Permissions in ASP.NET Zero

How to Implement Dynamic Permissions in ASP.NET Zero

In this article, we will learn how to implement dynamic permissions in ASP.NET Zero. Dynamic permission is a feature that allows you to create permissions dynamically and assign them to users or roles. This feature is useful when you want to give users the ability to create custom permissions based on their requirements.

Sample source code for this article is available on GitHub at DynamicPermissionSampleDemo folder.

Create a New Entity for Dynamic Permissions

The first step is to create a new entity to store the dynamic permissions. Create a new class called DynamicPermission in the Core project under the DynamicPermissions folder and add the following code:

using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using Abp.Domain.Entities;

namespace DynamicPermissionSampleDemo.DynamicPermissions;

[Table("DynamicPermissions")]
public class DynamicPermission : Entity, IMayHaveTenant
{
    public virtual string Name { get; set; }

    public virtual string Description { get; set; }

    public virtual string DisplayName { get; set; }

    public virtual int? ParentId { get; set; }

    public int? TenantId { get; set; }

    [ForeignKey("ParentId")]
    public DynamicPermission Parent { get; set; }

    public List<DynamicPermission> Children { get; set; }

    public DynamicPermission()
    {
        Children = new List<DynamicPermission>();
    }
}

Update DbContext

Add this class to the DbContext in the EntityFrameworkCore project:

public virtual DbSet<DynamicPermission> DynamicPermissions { get; set; }

Apply Migration

After adding the entity to the DbContext, create a migration and update the database to add the DynamicPermission table.

Override PermissionManager

Step 1: Create DynamicPermissionManager

Create a new class called DynamicPermissionManager in the Core project under the DynamicPermissions folder. Add the following code:

using System.Collections.Generic;
using System.Linq;
using Abp.Authorization;
using Abp.Configuration.Startup;
using Abp.Dependency;
using Abp.Domain.Repositories;
using Abp.Domain.Uow;
using Abp.Localization;

namespace DynamicPermissionSampleDemo.DynamicPermissions;

public class DynamicPermissionManager : PermissionManager
{
    private readonly IRepository<DynamicPermission, int> _repository;
    private readonly IUnitOfWorkManager _uow;

    public DynamicPermissionManager(IIocManager iocManager, IAuthorizationConfiguration authorizationConfiguration,
        IUnitOfWorkManager unitOfWorkManager, IMultiTenancyConfig multiTenancy,
        IRepository<DynamicPermission, int> repository) 
        : base(iocManager, authorizationConfiguration, unitOfWorkManager, multiTenancy)
    {
        _repository = repository;
        _uow = unitOfWorkManager;
    }

    public override void Initialize()
    {
        Permissions.Clear();

        var databasePermissions = GetPermissionsFromDatabase();
        foreach (var permission in databasePermissions)
        {
            Permissions[permission.Name] = permission;
        }

        base.Initialize();
    }

    private List<Permission> GetPermissionsFromDatabase()
    {
        using var uow = _uow.Begin();
        var dynamicPermissions = _repository.GetAllList().ToList();

        var permissions = new List<Permission>();
        foreach (var item in dynamicPermissions)
        {
            var permission = new Permission(item.Name, L(item.DisplayName), L(item.Description));

            if (!item.ParentId.HasValue)
            {
                foreach (var child in item.Children)
                {
                    permission.CreateChildPermission(child.Name, L(child.DisplayName), L(child.Description));
                }
                permissions.Add(permission);
            }
        }

        uow.Complete();
        return permissions;
    }

    private static ILocalizableString L(string name)
    {
        return new LocalizableString(name, DynamicPermissionSampleDemoConsts.LocalizationSourceName);
    }
}

Step 2: Create DynamicPermissionManagerHandler

Create a new class called DynamicPermissionManagerHandler in the Core project under the DynamicPermissions folder. Add the following code:

using System;
using System.Linq;
using Abp.Authorization;
using Castle.MicroKernel;

namespace DynamicPermissionSampleDemo.DynamicPermissions;

public class DynamicPermissionManagerHandler : IHandlerSelector
{
    public bool HasOpinionAbout(string key, Type service)
    {
        return typeof(IPermissionManager) == service;
    }

    public IHandler SelectHandler(string key, Type service, IHandler[] handlers)
    {
        return handlers.FirstOrDefault(x => x.ComponentModel.Implementation == typeof(DynamicPermissionManager));
    }
}

Step 3: Update DynamicPermissionSampleDemoCoreModule

Add to PreInitialize

In the DynamicPermissionSampleDemoCoreModule class at *.Core project, add the following code to the PreInitialize method:

IocManager.IocContainer.Kernel.AddHandlerSelector(new DynamicPermissionManagerHandler());

Add to PostInitialize

In the DynamicPermissionSampleDemoWebCoreModule class at *.Web.Core, add the following code to the PostInitialize method:

IocManager.Resolve<DynamicPermissionManager>().Initialize();

Create Application Service for Dynamic Permissions

Step 1: Define the Interface

Create a new class called IDynamicPermissionAppService in the Application.Shared project under the DynamicPermissions folder and add the following code:

using System.Threading.Tasks;
using Abp.Application.Services;
using Abp.Application.Services.Dto;
using DynamicPermissionSampleDemo.Notifications.Dto;

namespace DynamicPermissionSampleDemo.DynamicPermissions;

public interface IDynamicPermissionsAppService : IApplicationService
{
    Task<PagedResultDto<GetDynamicPermissionForViewDto>> GetAll();
    
    Task CreateOrEdit(CreateOrEditDynamicPermissionDto input);

    Task<GetDynamicPermissionForEditOutput> GetDynamicPermissionForEdit(EntityDto input);

    Task Delete(EntityDto input);

    Task<PagedResultDto<DynamicPermissionDynamicPermissionLookupTableDto>> GetAllDynamicPermissionForLookupTable(GetAllForLookupTableInput input);
}

public class GetDynamicPermissionForViewDto
{
    public DynamicPermissionDto DynamicPermission { get; set; }

    public string DynamicPermissionName { get; set; }
}

public class DynamicPermissionDto : EntityDto
{
    public string Name { get; set; }

    public string Description { get; set; }

    public string DisplayName { get; set; }

    public int? ParentId { get; set; }
}

public class CreateOrEditDynamicPermissionDto : EntityDto<int?>
{
    public string Name { get; set; }

    public string Description { get; set; }

    public string DisplayName { get; set; }

    public int? ParentId { get; set; }
}

public class GetDynamicPermissionForEditOutput
{
    public CreateOrEditDynamicPermissionDto DynamicPermission { get; set; }

    public string DynamicPermissionName { get; set; }
}

public class DynamicPermissionDynamicPermissionLookupTableDto
{
    public int Id { get; set; }

    public string DisplayName { get; set; }
}

Note: You can separate the DTOs into individual files if needed.

Step 2: Implement the Service

Create a new class called DynamicPermissionsAppService in the Application project under the DynamicPermissions folder and add the following code:

using System.Linq;
using System.Linq.Dynamic.Core;
using Abp.Linq.Extensions;
using System.Collections.Generic;
using System.Threading.Tasks;
using Abp.Domain.Repositories;
using Abp.Application.Services.Dto;
using DynamicPermissionSampleDemo.Notifications.Dto;
using Microsoft.EntityFrameworkCore;

namespace DynamicPermissionSampleDemo.DynamicPermissions;

public class DynamicPermissionsAppService : DynamicPermissionSampleDemoAppServiceBase, IDynamicPermissionsAppService
{
    private readonly IRepository<DynamicPermission> _dynamicPermissionRepository;
    private readonly DynamicPermissionManager _permissionManager;

    public DynamicPermissionsAppService(IRepository<DynamicPermission> dynamicPermissionRepository, DynamicPermissionManager permissionManager)
    {
        _dynamicPermissionRepository = dynamicPermissionRepository;
        _permissionManager = permissionManager;
    }
    
    public async Task<PagedResultDto<GetDynamicPermissionForViewDto>> GetAll(PagedAndSortedResultRequestDto input)
    {
        var filteredDynamicPermissions = _dynamicPermissionRepository.GetAll()
            .Include(e => e.Parent);

        var pagedAndFilteredDynamicPermissions = filteredDynamicPermissions
            .OrderBy("id asc")
            .PageBy(input);

        var dynamicPermissions = from o in pagedAndFilteredDynamicPermissions
            join o1 in _dynamicPermissionRepository.GetAll() on o.ParentId equals o1.Id into j1
            from s1 in j1.DefaultIfEmpty()
            select new
            {
                o.Name,
                o.Description,
                o.DisplayName,
                Id = o.Id,
                DynamicPermissionName = s1 == null || s1.Name == null ? "" : s1.Name.ToString()
            };

        var totalCount = await filteredDynamicPermissions.CountAsync();

        var dbList = await dynamicPermissions.ToListAsync();
        var results = new List<GetDynamicPermissionForViewDto>();

        foreach (var o in dbList)
        {
            var res = new GetDynamicPermissionForViewDto()
            {
                DynamicPermission = new DynamicPermissionDto
                {
                    Name = o.Name,
                    Description = o.Description,
                    DisplayName = o.DisplayName,
                    Id = o.Id,
                },
                DynamicPermissionName = o.DynamicPermissionName
            };

            results.Add(res);
        }

        return new PagedResultDto<GetDynamicPermissionForViewDto>(
            totalCount,
            results
        );
    }

    public async Task CreateOrEdit(CreateOrEditDynamicPermissionDto input)
    {
        if (input.Id == null)
        {
            await Create(input);
        }
        else
        {
            await Update(input);
        }
        
        await UnitOfWorkManager.Current.SaveChangesAsync();
        
        _permissionManager.Initialize();
    }
    
    protected virtual async Task Create(CreateOrEditDynamicPermissionDto input)
    {
        var dynamicPermission = ObjectMapper.Map<DynamicPermission>(input);

        if (AbpSession.TenantId != null)
        {
            dynamicPermission.TenantId = AbpSession.TenantId;
        }

        await _dynamicPermissionRepository.InsertAsync(dynamicPermission);
    }
    
    protected virtual async Task Update(CreateOrEditDynamicPermissionDto input)
    {
        var dynamicPermission = await _dynamicPermissionRepository.FirstOrDefaultAsync((int)input.Id);
        ObjectMapper.Map(input, dynamicPermission);
    }

    public async Task<GetDynamicPermissionForEditOutput> GetDynamicPermissionForEdit(EntityDto input)
    {
        var dynamicPermission = await _dynamicPermissionRepository.FirstOrDefaultAsync(input.Id);

        var output = new GetDynamicPermissionForEditOutput
            { DynamicPermission = ObjectMapper.Map<CreateOrEditDynamicPermissionDto>(dynamicPermission) };

        if (output.DynamicPermission.ParentId != null)
        {
            var lookupDynamicPermission =
                await _dynamicPermissionRepository.FirstOrDefaultAsync((int)output.DynamicPermission
                    .ParentId);
            output.DynamicPermissionName = lookupDynamicPermission?.Name;
        }

        return output;
    }

    public async Task Delete(EntityDto input)
    {
        await _dynamicPermissionRepository.DeleteAsync(input.Id);
        
        await UnitOfWorkManager.Current.SaveChangesAsync();
        
        _permissionManager.Initialize();
    }

    public async Task<PagedResultDto<DynamicPermissionDynamicPermissionLookupTableDto>> GetAllDynamicPermissionForLookupTable(GetAllForLookupTableInput input)
    {
        var query = _dynamicPermissionRepository.GetAll().WhereIf(
            !string.IsNullOrWhiteSpace(input.Filter),
            e => e.Name != null && e.Name.Contains(input.Filter)
        );

        var totalCount = await query.CountAsync();

        var dynamicPermissionList = await query
            .PageBy(input)
            .ToListAsync();

        var lookupTableDtoList = new List<DynamicPermissionDynamicPermissionLookupTableDto>();
        foreach (var dynamicPermission in dynamicPermissionList)
        {
            lookupTableDtoList.Add(new DynamicPermissionDynamicPermissionLookupTableDto
            {
                Id = dynamicPermission.Id,
                DisplayName = dynamicPermission.Name?.ToString()
            });
        }

        return new PagedResultDto<DynamicPermissionDynamicPermissionLookupTableDto>(
            totalCount,
            lookupTableDtoList
        );
    }
}

Note: For brevity, the interface implementation is not included here but can be extended as needed. You may also refer to the sample project for additional details.

For example, you can create a new dynamic permission using the following code.

abp.auth.hasPermission('Pages.Administration.Users.Unlock' + user.department)

This code will check if the user has the permission to unlock the user based on the department. If the user has the permission, the code will return true, otherwise false. You can create many departments and assign different permissions to each department.

Conclusion

In this article, we learned how to implement dynamic permissions in ASP.NET Zero. Dynamic permissions allow you to create custom permissions based on your requirements. This feature is useful when you want to give users the ability to create custom permissions dynamically. You can extend this feature further by adding more functionality to the dynamic permissions.

ASP.NET Zero Awarded As One of the Best 5 Application Development Tools by Get App Logo Learn More