Open Closed

Nested Permission Group #6274


User avatar
0
Lexuan.Zhou created
  • ABP Framework version: v7.3.2
  • UI Type: Blazor Server
  • Database System: EF Core (SQL Server)
  • Tiered (for MVC) or Auth Server Separated (for Angular): No

Is the a way to make a nested permission group ? i.e I want to make both of ListFullLimited & ListFull under ListReadOnlyLimited. Thanks


16 Answer(s)
  • User Avatar
    0
    liangshiwei created
    Support Team Fullstack Developer

    Hi,

    It should be supported, but not, it's a problem. I created an issue for this: https://github.com/abpframework/abp/issues/18338

    Will provide you with a temporary solution as soon as possible.

  • User Avatar
    0
    liangshiwei created
    Support Team Fullstack Developer

    The temporary solution:

    MyPermissionManagementModal.razor

    @using Microsoft.Extensions.Localization
    @using Volo.Abp.PermissionManagement.Localization
    @using Volo.Abp.PermissionManagement.Blazor.Components
    @using Volo.Abp.DependencyInjection
    @inherits PermissionManagementModal
    @attribute [ExposeServices(typeof(PermissionManagementModal))]
    @attribute [Dependency(ReplaceServices = true)]
    
    
    <Modal @ref="_modal" Closing="@ClosingModal">
        <ModalContent Size="ModalSize.Large" Centered="true">
            <ModalHeader>
                <ModalTitle>@L["Permissions"] - @_entityDisplayName</ModalTitle>
                <CloseButton Clicked="CloseModal" />
            </ModalHeader>
            <ModalBody MaxHeight="50">
                
                <Field>
                    <Check Disabled="_selectAllDisabled" Cursor="Cursor.Pointer" @bind-Checked="@GrantAll" TValue="bool">
                        @L["SelectAllInAllTabs"]
                    </Check>
                </Field>
                
                <Divider />
                
                @if (_groups != null)
                {
                    <Tabs TabPosition="TabPosition.Start" Pills="true" @bind-SelectedTab="@_selectedTabName">
                        <Items>
                            @foreach (var group in _groups)
                            {
                                <Tab Name="@GetNormalizedGroupName(group.Name)">
                                    @if (group.Permissions.Any(x => x.IsGranted))
                                    {
                                        <span>
                                            <b>@group.DisplayName ( @(group.Permissions.Count(x => x.IsGranted)) )</b>
                                        </span>
                                    }
                                    else
                                    {
                                        <span>
                                            @group.DisplayName ( @(group.Permissions.Count(x => x.IsGranted)) )
                                        </span>
                                    }
                                </Tab>
                            }
                        </Items>
                        <Content>
                            @foreach (var group in _groups)
                            {
                                <TabPanel Name="@GetNormalizedGroupName(group.Name)">
                                    <h4>@group.DisplayName</h4>
                                    
                                    <Divider />
                                    
                                    <Field>
                                        <Check
                                            Disabled="@(IsPermissionGroupDisabled(group))"
                                            Checked="@(group.Permissions.All(x => x.IsGranted))"
                                            Cursor="Cursor.Pointer"
                                            CheckedChanged="@(b => GroupGrantAllChanged(b, group))"
                                            TValue="bool">
                                            @L["SelectAllInThisTab"]
                                        </Check>
                                    </Field>
    
                                    <Divider />
                                    
                                    @foreach (var permission in group.Permissions)
                                    {
                                        <Field Style="@GetMarginStyle(group.Permissions, permission.ParentName)">
                                            <Check
                                                Disabled="@(IsDisabledPermission(permission))"
                                                Cursor="Cursor.Pointer"
                                                Checked="@permission.IsGranted"
                                                CheckedChanged="@(b => PermissionChanged(b, group, permission))"
                                                TValue="bool">
                                                @GetShownName(permission)
                                            </Check>
                                        </Field>
                                    }
                                    
                                </TabPanel>
                            }
                        </Content>
                    </Tabs>
                }
            </ModalBody>
            <ModalFooter>
                <Button Color="Color.Secondary" Clicked="CloseModal">@L["Cancel"]</Button>
                <Button Color="Color.Primary" Clicked="SaveAsync">@L["Save"]</Button>
            </ModalFooter>
        </ModalContent>
    </Modal>
    
    @code{
        
        protected virtual string GetMarginStyle(List<Volo.Abp.PermissionManagement.PermissionGrantInfoDto> permissions, string currentParent)
        {
            var currentDepth = GetDepth(permissions, currentParent, 0);
    
            return $"margin-inline-start: {currentDepth * 20}px;";
        }
    
        private int GetDepth(List<Volo.Abp.PermissionManagement.PermissionGrantInfoDto> permissions, string currentParent, int currentDepth)
        {
            foreach (var item in permissions)
            {
                if (item.Name == currentParent)
                {
                    currentDepth++;
                    if (item.ParentName != null)
                    {
                        currentDepth = GetDepth(permissions, item.ParentName, currentDepth);
                    }
    
                }
            }
    
            return currentDepth;
        }
    } 
    
  • User Avatar
    0
    Lexuan.Zhou created

    Thanks for the update, the UI nested structure works with the work around. But I found another issue along with it:

    The children's checkbox are supposed to checked off if the parent is checked off. Any solution for this case? thanks

  • User Avatar
    0
    liangshiwei created
    Support Team Fullstack Developer

    Hi,

    I could not reproduce the problem.

  • User Avatar
    0
    Lexuan.Zhou created

    This is the section I setup for the Activity, you should be able to reproduct if you try this.

    Also found another issue here: The second arrow is the checkbox i click on, but it doesn't toggle on the parent's parent's checkbox (first arrow)

  • User Avatar
    0
    liangshiwei created
    Support Team Fullstack Developer

    Hi,

    Ok, I can reproduce it now

  • User Avatar
    0
    liangshiwei created
    Support Team Fullstack Developer

    Hi,

    Will this work for you?

    @using Microsoft.Extensions.Localization
    @using Volo.Abp.PermissionManagement.Localization
    @using Volo.Abp.PermissionManagement.Blazor.Components
    @using Volo.Abp.DependencyInjection
    @using Volo.Abp.PermissionManagement
    @inherits PermissionManagementModal
    @attribute [ExposeServices(typeof(PermissionManagementModal))]
    @attribute [Dependency(ReplaceServices = true)]
    
    
    <Modal @ref="_modal" Closing="@ClosingModal">
        <ModalContent Size="ModalSize.Large" Centered="true">
            <ModalHeader>
                <ModalTitle>@L["Permissions"] - @_entityDisplayName</ModalTitle>
                <CloseButton Clicked="CloseModal" />
            </ModalHeader>
            <ModalBody MaxHeight="50">
                
                <Field>
                    <Check Disabled="_selectAllDisabled" Cursor="Cursor.Pointer" @bind-Checked="@GrantAll" TValue="bool">
                        @L["SelectAllInAllTabs"]
                    </Check>
                </Field>
                
                <Divider />
                
                @if (_groups != null)
                {
                    <Tabs TabPosition="TabPosition.Start" Pills="true" @bind-SelectedTab="@_selectedTabName">
                        <Items>
                            @foreach (var group in _groups)
                            {
                                <Tab Name="@GetNormalizedGroupName(group.Name)">
                                    @if (group.Permissions.Any(x => x.IsGranted))
                                    {
                                        <span>
                                            <b>@group.DisplayName ( @(group.Permissions.Count(x => x.IsGranted)) )</b>
                                        </span>
                                    }
                                    else
                                    {
                                        <span>
                                            @group.DisplayName ( @(group.Permissions.Count(x => x.IsGranted)) )
                                        </span>
                                    }
                                </Tab>
                            }
                        </Items>
                        <Content>
                            @foreach (var group in _groups)
                            {
                                <TabPanel Name="@GetNormalizedGroupName(group.Name)">
                                    <h4>@group.DisplayName</h4>
                                    
                                    <Divider />
                                    
                                    <Field>
                                        <Check
                                            Disabled="@(IsPermissionGroupDisabled(group))"
                                            Checked="@(group.Permissions.All(x => x.IsGranted))"
                                            Cursor="Cursor.Pointer"
                                            CheckedChanged="@(b => GroupGrantAllChanged(b, group))"
                                            TValue="bool">
                                            @L["SelectAllInThisTab"]
                                        </Check>
                                    </Field>
    
                                    <Divider />
                                    
                                    @foreach (var permission in group.Permissions)
                                    {
                                        <Field Style="@GetMarginStyle(group.Permissions, permission.ParentName)">
                                            <Check
                                                Disabled="@(IsDisabledPermission(permission))"
                                                Cursor="Cursor.Pointer"
                                                Checked="@permission.IsGranted"
                                                CheckedChanged="@(b => PermissionChanged(b, group, permission))"
                                                TValue="bool">
                                                @GetShownName(permission)
                                            </Check>
                                        </Field>
                                    }
                                    
                                </TabPanel>
                            }
                        </Content>
                    </Tabs>
                }
            </ModalBody>
            <ModalFooter>
                <Button Color="Color.Secondary" Clicked="CloseModal">@L["Cancel"]</Button>
                <Button Color="Color.Primary" Clicked="SaveAsync">@L["Save"]</Button>
            </ModalFooter>
        </ModalContent>
    </Modal>
    
    @code{
    
        protected virtual string GetMarginStyle(List<Volo.Abp.PermissionManagement.PermissionGrantInfoDto> permissions, string currentParent)
        {
            var currentDepth = GetDepth(permissions, currentParent, 0);
    
            return $"margin-inline-start: {currentDepth * 20}px;";
        }
    
        private int GetDepth(List<Volo.Abp.PermissionManagement.PermissionGrantInfoDto> permissions, string currentParent, int currentDepth)
        {
            foreach (var item in permissions)
            {
                if (item.Name == currentParent)
                {
                    currentDepth++;
                    if (item.ParentName != null)
                    {
                        currentDepth = GetDepth(permissions, item.ParentName, currentDepth);
                    }
    
                }
            }
    
            return currentDepth;
        }
    
        protected override void PermissionChanged(bool value, PermissionGroupDto permissionGroup, PermissionGrantInfoDto permission)
        {
            SetPermissionGrant(permission, value);
    
            if (value)
            {
                SetParentPermissionGrant(permissionGroup, permission);
            }
            else if (value == false)
            {
                var childPermissions = GetChildPermissions(permissionGroup, permission);
    
                foreach (var childPermission in childPermissions)
                {
                    SetPermissionGrant(childPermission, false);
                }
            }
        }
    
        private void SetParentPermissionGrant(PermissionGroupDto permissionGroup, PermissionGrantInfoDto permission)
        {
            if(permission.ParentName == null)
            {
                return;
            }
    
            var parentPermission = GetParentPermission(permissionGroup, permission);
            SetPermissionGrant(parentPermission, true);
    
            SetParentPermissionGrant(permissionGroup, parentPermission);
            
        }
    
        private void SetPermissionGrant(PermissionGrantInfoDto permission, bool value)
        {
            if (permission.IsGranted == value)
            {
                return;
            }
    
            if (value)
            {
                _grantedPermissionCount++;
                _notGrantedPermissionCount--;
            }
            else
            {
                _grantedPermissionCount--;
                _notGrantedPermissionCount++;
            }
    
            permission.IsGranted = value;
        }
    } 
    
  • User Avatar
    0
    Lexuan.Zhou created

    The latest solution you provided fix my second permission SecurityFilters section. But my Activities section still has the problem

    I don't quite understand the difference between them, the set up looks the same to me. Notice that the Activities' parent permission is disabled as you can see in the picture, is it possible something related to that? (also i don't understand why they are disabled, i log in as an admin, should have all the permissions)

  • User Avatar
    0
    liangshiwei created
    Support Team Fullstack Developer

    But my Activities section still has the problem

    Can you share the full steps to reproduce?

  • User Avatar
    0
    Lexuan.Zhou created

    Step 0: Step 1: (click child's checkbox)(I clicked Permission:ListFullLimitedCampaigns && Permission:ProspectAll) (Assert: the parent's checkbox all automatically checked) Step2: (click parent's checkbox)(I clicked Permissoin:ListFullLimited && Permission:Prospect) (Assert: the children's checkboxes should all automatically unchecked)

    In Step 2, apprently the ones in the activities section is wrong, you can tell the different between two section in this step

  • User Avatar
    0
    liangshiwei created
    Support Team Fullstack Developer

    Hi,

    Sorry, I could not reproduce the problem.

    public static class Activities
    {
        public const string Default = QaPermissions.GroupName +".Activities";
        public const string Edit = Default + ".Edit";
        public const string Delete = Default + ".Delete";
        public const string Create = Default + ".Create";
        private const string List = Default + ".List";
        public const string ListFull = List + ".Full";
        public const string ListFullLimitedCompaigns = List + ".FullLimitedCompaigns";
         public const string ListFullLimited= List + ".FullLimited";
         private const string Detail = Default + ".Detail";
         public const string DetailFull = Default + ".Full";
         public const string DetailFullLimitedCompaigns = Detail + ".FullLimitedCompaigns";
         public const string DetailFullLimited = Detail + ".FullLimited";
    }
    
    public static class SecurityFilters
    {
        public const string Default = QaPermissions.GroupName +".SecurityFilters";
        public const string Campaign = Default + ".Campaign";
        public const string CampaignAssigned = Campaign +".Assigned";
        public const string CampaignAll = Campaign +".ALl";
        public const string Prospect = Default +".Prospect";
        public const string ProspectAssigned = Prospect +".Assigned";
        public const string ProspectAll = Prospect +".ALL";
    }
    
    
    var permissionActivities = myGroup.AddPermission(Activities.Default, L("Permission:Activities"));
    permissionActivities.AddChild(Activities.Create, L("Create"));
    permissionActivities.AddChild(Activities.Delete, L("Delete"));
    permissionActivities.AddChild(Activities.Edit, L("Edit"));
    permissionActivities.AddChild(Activities.DetailFull, L("DetailFull"));
    permissionActivities.AddChild(Activities.DetailFullLimitedCompaigns, L("DetailFullLimitedCompaigns"));
    permissionActivities.AddChild(Activities.DetailFullLimited, L("DetailFullLimited"));
    
    var permissionActivitiesListFullLimited = permissionActivities.AddChild(Activities.ListFullLimited, L("ListFullLimited"));
    permissionActivitiesListFullLimited.AddChild(Activities.ListFullLimitedCompaigns, L("ListFullLimitedCompaigns"));
    permissionActivitiesListFullLimited.AddChild(Activities.ListFull, L("ListFull"));
    
    
    var securityFilters = myGroup.AddPermission(SecurityFilters.Default, L("SecurityFilters"));
    var securityFiltersCampaign = securityFilters.AddChild(SecurityFilters.Campaign, L("Campaign"));
    securityFiltersCampaign.AddChild(SecurityFilters.CampaignAssigned, L("CampaignAssigned"));
    securityFiltersCampaign.AddChild(SecurityFilters.CampaignAll, L("CampaignAll"));
    var securityFiltersProspect = securityFilters.AddChild(SecurityFilters.Prospect, L("Prospect"));
    securityFiltersProspect.AddChild(SecurityFilters.ProspectAssigned, L("ProspectAssigned"));
    securityFiltersProspect.AddChild(SecurityFilters.ProspectAll, L("ProspectAll"));
    

  • User Avatar
    0
    Lexuan.Zhou created

    Ok I figured out the work around solution, but the string value is not the proper one that I want

    This one works

    But this one doesn't (this is the string values that i want them to be)

    So the checkbox logic does rely on the actual string value to find the relation properly?

  • User Avatar
    0
    Lexuan.Zhou created

    As you can see from the recording provided above, the first child works as expected, but the second doesn't

  • User Avatar
    0
    liangshiwei created
    Support Team Fullstack Developer

    Hi,

    You can check the source code: https://github.com/abpframework/abp/blob/dev/modules/permission-management/src/Volo.Abp.PermissionManagement.Blazor/Components/PermissionManagementModal.razor.cs#L227

    The logic of the GetChildPermissions method is to get all sub-permissions according to the name convention.

    The name convention is structured like this:

    List
    List.Full
    List.FullLimited
    

    BTW, you can custom if you want:

    protected override void PermissionChanged(bool value, PermissionGroupDto permissionGroup, PermissionGrantInfoDto permission)
    {
        SetPermissionGrant(permission, value);
    
        if (value)
        {
            SetParentPermissionGrant(permissionGroup, permission);
        }
        else if (value == false)
        {
            var childPermissions = GetMyChildPermissions(permissionGroup, permission);
    
            foreach (var childPermission in childPermissions)
            {
                SetPermissionGrant(childPermission, false);
            }
        }
    }
    
    private List<PermissionGrantInfoDto> GetMyChildPermissions(PermissionGroupDto permissionGroup, PermissionGrantInfoDto permission)
    {
        // You can recursively query all sub-permissions here
    }
    
  • User Avatar
    0
    Lexuan.Zhou created

    Sure, thanks for providing the source code. Will hava a try

  • User Avatar
    0
    liangshiwei created
    Support Team Fullstack Developer

    : )

Made with ❤️ on ABP v9.1.0-preview. Updated on November 11, 2024, 11:11