83 changed files with 2654 additions and 0 deletions
			
			
		| @ -0,0 +1,3 @@ | ||||
| <Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> | ||||
|   <ConfigureAwait ContinueOnCapturedContext="false" /> | ||||
| </Weavers> | ||||
| @ -0,0 +1,21 @@ | ||||
| <Project Sdk="Microsoft.NET.Sdk"> | ||||
| 
 | ||||
| 	<Import Project="..\..\..\configureawait.props" /> | ||||
| 	<Import Project="..\..\..\common.props" /> | ||||
| 
 | ||||
| 	<PropertyGroup> | ||||
| 		<TargetFramework>netstandard2.0</TargetFramework> | ||||
| 		<RootNamespace /> | ||||
| 	</PropertyGroup> | ||||
| 
 | ||||
| 	<ItemGroup> | ||||
| 		<PackageReference Include="Volo.Abp.Ddd.Application.Contracts" Version="$(VoloAbpVersion)" /> | ||||
| 		<PackageReference Include="Volo.Abp.Authorization" Version="$(VoloAbpVersion)" /> | ||||
| 		<PackageReference Include="Volo.Abp.PermissionManagement.Application.Contracts" Version="$(VoloAbpVersion)" /> | ||||
| 	</ItemGroup> | ||||
| 
 | ||||
| 	<ItemGroup> | ||||
| 	  <ProjectReference Include="..\Sanhe.Abp.MenuManagement.Domain.Shared\Sanhe.Abp.MenuManagement.Domain.Shared.csproj" /> | ||||
| 	</ItemGroup> | ||||
| 	 | ||||
| </Project> | ||||
| @ -0,0 +1,23 @@ | ||||
| using System.Collections.Generic; | ||||
| 
 | ||||
| namespace Sanhe.Abp.MenuManagement | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 授权策略 | ||||
|     /// </summary> | ||||
|     public class AuthPolicyDto | ||||
|     { | ||||
|         /// <summary> | ||||
|         /// 名称 | ||||
|         /// </summary> | ||||
|         public string Name { get; set; } | ||||
|         /// <summary> | ||||
|         /// 显示名称 | ||||
|         /// </summary> | ||||
|         public string DisplayName { get; set; } | ||||
|         /// <summary> | ||||
|         /// 子策略 | ||||
|         /// </summary> | ||||
|         public List<AuthPolicyDto> Children { get; set; } = new List<AuthPolicyDto>(); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,97 @@ | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.ComponentModel.DataAnnotations; | ||||
| using Volo.Abp.Application.Dtos; | ||||
| using Volo.Abp.MultiTenancy; | ||||
| 
 | ||||
| namespace Sanhe.Abp.MenuManagement | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 创建修改菜单 | ||||
|     /// </summary> | ||||
|     public class CreateOrUpdateMenuDto : EntityDto, IValidatableObject | ||||
|     { | ||||
|         /// <summary> | ||||
|         /// 名称 | ||||
|         /// </summary> | ||||
|         public string Name { get; set; } | ||||
|         /// <summary> | ||||
|         /// 显示名称 | ||||
|         /// </summary> | ||||
|         public string DisplayName { get; set; } | ||||
|         /// <summary> | ||||
|         /// 组件路径 | ||||
|         /// </summary> | ||||
|         public string ComponentPath { get; set; } | ||||
|         /// <summary> | ||||
|         /// 路由路径 | ||||
|         /// </summary> | ||||
|         public string RouterPath { get; set; } | ||||
|         /// <summary> | ||||
|         /// 父Id | ||||
|         /// </summary> | ||||
|         public Guid? ParentId { get; set; } | ||||
|         /// <summary> | ||||
|         /// 菜单类型 | ||||
|         /// </summary> | ||||
|         public MenuEnumType MenuType { get; set; } | ||||
|         /// <summary> | ||||
|         /// 图标 | ||||
|         /// </summary> | ||||
|         public string Icon { get; set; } | ||||
|         /// <summary> | ||||
|         /// 排序 | ||||
|         /// </summary> | ||||
|         public string Sort { get; set; } | ||||
|         /// <summary> | ||||
|         /// window.open _blank  | ||||
|         /// </summary> | ||||
|         public string TargetUrl { get; set; } | ||||
|         /// <summary> | ||||
|         /// 此菜单关联的权限key | ||||
|         /// </summary> | ||||
|         public string PermissionKey { get; set; } | ||||
|         /// <summary> | ||||
|         /// 表示多租户应用程序中的所属方 | ||||
|         /// </summary> | ||||
|         public MultiTenancySides MultiTenancySide { get; set; } | ||||
| 
 | ||||
|         public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) | ||||
|         { | ||||
|             if (MenuType == MenuEnumType.Menu) | ||||
|             { | ||||
|                 if (Name.IsNullOrWhiteSpace()) | ||||
|                 { | ||||
|                     yield return new ValidationResult("Name 不能为空", new[] { "Name" }); | ||||
|                 } | ||||
| 
 | ||||
|                 if (DisplayName.IsNullOrWhiteSpace()) | ||||
|                 { | ||||
|                     yield return new ValidationResult("DisplayName 不能为空", new[] { "DisplayName" }); | ||||
|                 } | ||||
| 
 | ||||
|                 if (RouterPath.IsNullOrWhiteSpace()) | ||||
|                 { | ||||
|                     yield return new ValidationResult("RouterPath 不能为空", new[] { "RouterPath" }); | ||||
|                 } | ||||
| 
 | ||||
|                 if (ComponentPath.IsNullOrWhiteSpace()) | ||||
|                 { | ||||
|                     yield return new ValidationResult("ComponentPath 不能为空", new[] { "ComponentPath" }); | ||||
|                 } | ||||
|             } | ||||
|             else if (MenuType == MenuEnumType.Permission) | ||||
|             { | ||||
|                 if (DisplayName.IsNullOrWhiteSpace()) | ||||
|                 { | ||||
|                     yield return new ValidationResult("DisplayName 不能为空", new[] { "DisplayName" }); | ||||
|                 } | ||||
| 
 | ||||
|                 if (!ParentId.HasValue) | ||||
|                 { | ||||
|                     yield return new ValidationResult("ParentId 不能为空", new[] { "ParentId" }); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,44 @@ | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| 
 | ||||
| namespace Sanhe.Abp.MenuManagement | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 前端菜单 | ||||
|     /// </summary> | ||||
|     public class FrontMenu | ||||
|     { | ||||
|         /// <summary> | ||||
|         /// Id | ||||
|         /// </summary> | ||||
|         public Guid Id { get; set; } | ||||
|         /// <summary> | ||||
|         /// 名称 | ||||
|         /// </summary> | ||||
|         public string Name { get; set; } | ||||
|         /// <summary> | ||||
|         /// 组件 | ||||
|         /// </summary> | ||||
|         public string Component { get; set; } | ||||
|         /// <summary> | ||||
|         /// 路径 | ||||
|         /// </summary> | ||||
|         public string Path { get; set; } | ||||
|         /// <summary> | ||||
|         /// window.open _blank  | ||||
|         /// </summary> | ||||
|         public string TargetUrl { get; set; } | ||||
|         /// <summary> | ||||
|         /// 此菜单关联的权限key. | ||||
|         /// </summary> | ||||
|         public string PermissionKey { get; set; } | ||||
|         /// <summary> | ||||
|         /// 菜单标签 | ||||
|         /// </summary> | ||||
|         public MenuMeta Meta { get; set; } | ||||
|         /// <summary> | ||||
|         /// 子菜单 | ||||
|         /// </summary> | ||||
|         public List<FrontMenu> Children { get; set; } = new List<FrontMenu>(); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,10 @@ | ||||
| using System.Collections.Generic; | ||||
| 
 | ||||
| namespace Sanhe.Abp.MenuManagement | ||||
| { | ||||
|     public class GetMenuGrantListResultDto | ||||
|     { | ||||
|         public List<MenuGrantInfoDto> MenuGrants { get; set; } | ||||
|         public List<MenuDto> Menus { get; set; } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,10 @@ | ||||
| using System.Collections.Generic; | ||||
| 
 | ||||
| namespace Sanhe.Abp.MenuManagement | ||||
| { | ||||
|     public class GetMenuResultDto | ||||
|     { | ||||
|         public List<string> PermissionGrants { get; set; } | ||||
|         public List<FrontMenu> Menus { get; set; } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,16 @@ | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Threading.Tasks; | ||||
| using Volo.Abp.Application.Services; | ||||
| 
 | ||||
| namespace Sanhe.Abp.MenuManagement | ||||
| { | ||||
|     public interface IMenuAppService : ICrudAppService<MenuDto, Guid, MenuRequestDto, CreateOrUpdateMenuDto> | ||||
|     { | ||||
|         /// <summary> | ||||
|         /// 获取授权策略 | ||||
|         /// </summary> | ||||
|         /// <returns></returns> | ||||
|         Task<List<AuthPolicyDto>> GetAuthPolicies(); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,13 @@ | ||||
| using JetBrains.Annotations; | ||||
| using System.Threading.Tasks; | ||||
| using Volo.Abp.Application.Services; | ||||
| 
 | ||||
| namespace Sanhe.Abp.MenuManagement | ||||
| { | ||||
|     public interface IMenuGrantAppService : IApplicationService | ||||
|     { | ||||
|         Task<GetMenuResultDto> GetListAsync(); | ||||
|         Task<GetMenuGrantListResultDto> GetAsync([NotNull] string providerName, [NotNull] string providerKey); | ||||
|         Task UpdateAsync([NotNull] string providerName, [NotNull] string providerKey, UpdateMenuGrantsDto input); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,63 @@ | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using Volo.Abp.Application.Dtos; | ||||
| using Volo.Abp.MultiTenancy; | ||||
| 
 | ||||
| namespace Sanhe.Abp.MenuManagement | ||||
| { | ||||
|     public class MenuDto : AuditedEntityDto<Guid> | ||||
|     { | ||||
|         /// <summary> | ||||
|         /// 名称 | ||||
|         /// </summary> | ||||
|         public string Name { get; set; } | ||||
|         /// <summary> | ||||
|         /// 显示名称 | ||||
|         /// </summary> | ||||
|         public string DisplayName { get; set; } | ||||
|         /// <summary> | ||||
|         /// 菜单类型 | ||||
|         /// </summary> | ||||
|         public MenuEnumType MenuType { get; set; } | ||||
|         /// <summary> | ||||
|         /// 组件路径 | ||||
|         /// </summary> | ||||
|         public string ComponentPath { get; set; } | ||||
|         /// <summary> | ||||
|         /// 路由路径 | ||||
|         /// </summary> | ||||
|         public string RouterPath { get; set; } | ||||
|         /// <summary> | ||||
|         /// 父级显示名称 | ||||
|         /// </summary> | ||||
|         public string ParentDisplayName { get; set; } | ||||
|         /// <summary> | ||||
|         /// 父Id | ||||
|         /// </summary> | ||||
|         public Guid? ParentId { get; set; } | ||||
|         /// <summary> | ||||
|         /// 图标 | ||||
|         /// </summary> | ||||
|         public string Icon { get; set; } | ||||
|         /// <summary> | ||||
|         /// 排序 | ||||
|         /// </summary> | ||||
|         public string Sort { get; set; } | ||||
|         /// <summary> | ||||
|         /// window.open _blank  | ||||
|         /// </summary> | ||||
|         public string TargetUrl { get; set; } | ||||
|         /// <summary> | ||||
|         /// 此菜单关联的权限key | ||||
|         /// </summary> | ||||
|         public string PermissionKey { get; set; } | ||||
|         /// <summary> | ||||
|         /// 表示多租户应用程序中的所属方 | ||||
|         /// </summary> | ||||
|         public MultiTenancySides MultiTenancySide { get; set; } | ||||
|         /// <summary> | ||||
|         /// 子菜单 | ||||
|         /// </summary> | ||||
|         public List<MenuDto> Children { get; set; } = new List<MenuDto>(); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,17 @@ | ||||
| using Volo.Abp.Application; | ||||
| using Volo.Abp.Authorization; | ||||
| using Volo.Abp.Modularity; | ||||
| using Volo.Abp.PermissionManagement; | ||||
| 
 | ||||
| namespace Sanhe.Abp.MenuManagement; | ||||
| 
 | ||||
| [DependsOn( | ||||
|     typeof(AbpPermissionManagementApplicationContractsModule), | ||||
|     typeof(MenuManagementDomainSharedModule), | ||||
|     typeof(AbpDddApplicationContractsModule), | ||||
|     typeof(AbpAuthorizationModule) | ||||
|     )] | ||||
| public class MenuManagementApplicationContractsModule : AbpModule | ||||
| { | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,8 @@ | ||||
| namespace Sanhe.Abp.MenuManagement; | ||||
| 
 | ||||
| public class MenuManagementRemoteServiceConsts | ||||
| { | ||||
|     public const string RemoteServiceName = "MenuManagement"; | ||||
| 
 | ||||
|     public const string ModuleName = "menuManagement"; | ||||
| } | ||||
| @ -0,0 +1,14 @@ | ||||
| namespace Sanhe.Abp.MenuManagement | ||||
| { | ||||
|     public class MenuMeta | ||||
|     { | ||||
|         /// <summary> | ||||
|         /// 标题 | ||||
|         /// </summary> | ||||
|         public string Title { get; set; } | ||||
|         /// <summary> | ||||
|         /// 图标 | ||||
|         /// </summary> | ||||
|         public string Icon { get; set; } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,14 @@ | ||||
| namespace Sanhe.Abp.MenuManagement | ||||
| { | ||||
|     public class MenuRequestDto | ||||
|     { | ||||
|         /// <summary> | ||||
|         /// 名称 | ||||
|         /// </summary> | ||||
|         public string Name { get; set; } | ||||
|         /// <summary> | ||||
|         /// 类型 | ||||
|         /// </summary> | ||||
|         public MenuEnumType? Type { get; set; } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,24 @@ | ||||
| using Sanhe.Abp.MenuManagement.Localization; | ||||
| using Volo.Abp.Authorization.Permissions; | ||||
| using Volo.Abp.Localization; | ||||
| using Volo.Abp.MultiTenancy; | ||||
| 
 | ||||
| namespace Sanhe.Abp.MenuManagement.Permissions; | ||||
| 
 | ||||
| public class MenuManagementPermissionDefinitionProvider : PermissionDefinitionProvider | ||||
| { | ||||
|     public override void Define(IPermissionDefinitionContext context) | ||||
|     { | ||||
|         var moduleGroup = context.AddGroup(MenuManagementPermissions.GroupName, L("MenuManagement:MenuManagement")); | ||||
|         var permission = moduleGroup.AddPermission(MenuManagementPermissions.Menus.Default, L("MenuManagement:MenuManagement"), multiTenancySide: MultiTenancySides.Host); | ||||
|         permission.AddChild(MenuManagementPermissions.Menus.Create, L("MenuManagement:Create"), multiTenancySide: MultiTenancySides.Host); | ||||
|         permission.AddChild(MenuManagementPermissions.Menus.Update, L("MenuManagement:Update"), multiTenancySide: MultiTenancySides.Host); | ||||
|         permission.AddChild(MenuManagementPermissions.Menus.Delete, L("MenuManagement:Delete"), multiTenancySide: MultiTenancySides.Host); | ||||
|         permission.AddChild(MenuManagementPermissions.Menus.CreatePermission, L("MenuManagement:CreatePermission"), multiTenancySide: MultiTenancySides.Host); | ||||
|     } | ||||
| 
 | ||||
|     private static LocalizableString L(string name) | ||||
|     { | ||||
|         return LocalizableString.Create<MenuManagementResource>(name); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,22 @@ | ||||
| using Volo.Abp.Reflection; | ||||
| 
 | ||||
| namespace Sanhe.Abp.MenuManagement.Permissions; | ||||
| 
 | ||||
| public class MenuManagementPermissions | ||||
| { | ||||
|     public const string GroupName = "MenuManagement"; | ||||
| 
 | ||||
|     public static class Menus | ||||
|     { | ||||
|         public const string Default = GroupName + ".Menus"; | ||||
|         public const string Create = Default + ".Create"; | ||||
|         public const string Update = Default + ".Update"; | ||||
|         public const string Delete = Default + ".Delete"; | ||||
|         public const string CreatePermission = Default + ".CreatePermission"; | ||||
|     } | ||||
| 
 | ||||
|     public static string[] GetAll() | ||||
|     { | ||||
|         return ReflectionHelper.GetPublicConstantsRecursively(typeof(MenuManagementPermissions)); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,20 @@ | ||||
| using System; | ||||
| 
 | ||||
| namespace Sanhe.Abp.MenuManagement | ||||
| { | ||||
|     public class UpdateMenuGrantDto | ||||
|     { | ||||
|         /// <summary> | ||||
|         /// 菜单Id | ||||
|         /// </summary> | ||||
|         public Guid Id { get; set; } | ||||
|         /// <summary> | ||||
|         /// 权限key | ||||
|         /// </summary> | ||||
|         public string PermissionKey { get; set; } | ||||
|         /// <summary> | ||||
|         /// 是否分配 | ||||
|         /// </summary> | ||||
|         public bool IsGranted { get; set; } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,7 @@ | ||||
| namespace Sanhe.Abp.MenuManagement | ||||
| { | ||||
|     public class UpdateMenuGrantsDto | ||||
|     { | ||||
|         public UpdateMenuGrantDto[] Menus { get; set; } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,3 @@ | ||||
| <Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> | ||||
|   <ConfigureAwait ContinueOnCapturedContext="false" /> | ||||
| </Weavers> | ||||
| @ -0,0 +1,22 @@ | ||||
| <Project Sdk="Microsoft.NET.Sdk"> | ||||
| 
 | ||||
| 	<Import Project="..\..\..\configureawait.props" /> | ||||
| 	<Import Project="..\..\..\common.props" /> | ||||
| 
 | ||||
| 	<PropertyGroup> | ||||
| 		<TargetFramework>netstandard2.0</TargetFramework> | ||||
| 		<RootNamespace /> | ||||
| 	</PropertyGroup> | ||||
| 
 | ||||
| 	<ItemGroup> | ||||
| 		<PackageReference Include="Volo.Abp.AutoMapper" Version="$(VoloAbpVersion)" /> | ||||
| 		<PackageReference Include="Volo.Abp.Ddd.Application" Version="$(VoloAbpVersion)" /> | ||||
| 		<PackageReference Include="Volo.Abp.PermissionManagement.Application" Version="$(VoloAbpVersion)" /> | ||||
| 	</ItemGroup> | ||||
| 
 | ||||
| 	<ItemGroup> | ||||
| 	  <ProjectReference Include="..\Sanhe.Abp.MenuManagement.Application.Contracts\Sanhe.Abp.MenuManagement.Application.Contracts.csproj" /> | ||||
| 	  <ProjectReference Include="..\Sanhe.Abp.MenuManagement.Domain\Sanhe.Abp.MenuManagement.Domain.csproj" /> | ||||
| 	</ItemGroup> | ||||
| 	 | ||||
| </Project> | ||||
| @ -0,0 +1,182 @@ | ||||
| using Microsoft.AspNetCore.Authorization; | ||||
| using Microsoft.Extensions.Localization; | ||||
| using Sanhe.Abp.MenuManagement.Permissions; | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| using System.Threading.Tasks; | ||||
| using Volo.Abp; | ||||
| using Volo.Abp.Application.Dtos; | ||||
| using Volo.Abp.Application.Services; | ||||
| using Volo.Abp.Authorization.Permissions; | ||||
| using Volo.Abp.Domain.Repositories; | ||||
| using Volo.Abp.MultiTenancy; | ||||
| 
 | ||||
| namespace Sanhe.Abp.MenuManagement | ||||
| { | ||||
|     [Authorize] | ||||
|     public class MenuAppService : CrudAppService<Menu, MenuDto, Guid, MenuRequestDto, CreateOrUpdateMenuDto, CreateOrUpdateMenuDto>, | ||||
|         IMenuAppService | ||||
|     { | ||||
|         private readonly IPermissionDefinitionManager _permissionDefinitionManager; | ||||
|         private readonly IMenuManager _menuManager; | ||||
|         private readonly IStringLocalizerFactory _stringLocalizerFactory; | ||||
| 
 | ||||
|         public MenuAppService( | ||||
|             IRepository<Menu, Guid> repository, | ||||
|             IPermissionDefinitionManager permissionDefinitionManager, | ||||
|             IMenuManager menuManager, | ||||
|             IStringLocalizerFactory stringLocalizerFactory | ||||
|         ) : base(repository) | ||||
|         { | ||||
|             _permissionDefinitionManager = permissionDefinitionManager; | ||||
|             _menuManager = menuManager; | ||||
|             _stringLocalizerFactory = stringLocalizerFactory; | ||||
|         } | ||||
| 
 | ||||
|         protected override string GetListPolicyName => MenuManagementPermissions.Menus.Default; | ||||
|         protected override string CreatePolicyName => MenuManagementPermissions.Menus.Create; | ||||
|         protected override string UpdatePolicyName => MenuManagementPermissions.Menus.Update; | ||||
|         protected override string DeletePolicyName => MenuManagementPermissions.Menus.Delete; | ||||
| 
 | ||||
|         public override async Task<MenuDto> UpdateAsync(Guid id, CreateOrUpdateMenuDto input) | ||||
|         { | ||||
|             await PermissionChecker(input.PermissionKey, id); | ||||
| 
 | ||||
|             // 当更新菜单权限时,同时刷 PermissionGrant | ||||
|             var menu = await GetEntityByIdAsync(id); | ||||
|             if (menu.PermissionKey != input.PermissionKey) | ||||
|             { | ||||
|                 await _menuManager.UpdatePermissionGrantAsync(id, menu.PermissionKey, input.PermissionKey); | ||||
|             } | ||||
| 
 | ||||
|             return await base.UpdateAsync(id, input); | ||||
|         } | ||||
| 
 | ||||
|         public virtual Task<List<AuthPolicyDto>> GetAuthPolicies() | ||||
|         { | ||||
|             var result = new List<AuthPolicyDto>(); | ||||
|             var groups = _permissionDefinitionManager.GetGroups(); | ||||
|             var multiTenancySide = CurrentTenant.GetMultiTenancySide(); | ||||
|             foreach (var group in groups) | ||||
|             { | ||||
|                 if (group.Permissions.Count == 0) | ||||
|                 { | ||||
|                     continue; | ||||
|                 } | ||||
| 
 | ||||
|                 foreach (var permission in group.Permissions) | ||||
|                 { | ||||
|                     if (multiTenancySide != MultiTenancySides.Host && | ||||
|                         !permission.MultiTenancySide.HasFlag(multiTenancySide)) | ||||
|                     { | ||||
|                         continue; | ||||
|                     } | ||||
| 
 | ||||
|                     var policy = new AuthPolicyDto() | ||||
|                     { | ||||
|                         Name = permission.Name, | ||||
|                         DisplayName = permission.DisplayName.Localize(_stringLocalizerFactory), | ||||
|                         Children = new List<AuthPolicyDto> | ||||
|                         { | ||||
|                             new AuthPolicyDto | ||||
|                             { | ||||
|                                 Name = permission.Name, | ||||
|                                 DisplayName = permission.DisplayName.Localize(_stringLocalizerFactory) | ||||
|                             } | ||||
|                         } | ||||
|                     }; | ||||
|                     result.Add(policy); | ||||
| 
 | ||||
|                     foreach (var c in permission.Children) | ||||
|                     { | ||||
|                         if (multiTenancySide != MultiTenancySides.Host && | ||||
|                             !c.MultiTenancySide.HasFlag(multiTenancySide)) | ||||
|                         { | ||||
|                             continue; | ||||
|                         } | ||||
| 
 | ||||
|                         policy.Children.Add(new AuthPolicyDto | ||||
|                         { | ||||
|                             Name = c.Name, | ||||
|                             DisplayName = c.DisplayName.Localize(_stringLocalizerFactory) | ||||
|                         }); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             return Task.FromResult(result); | ||||
|         } | ||||
| 
 | ||||
|         public override async Task<MenuDto> CreateAsync(CreateOrUpdateMenuDto input) | ||||
|         { | ||||
|             await PermissionChecker(input.PermissionKey); | ||||
|             return await base.CreateAsync(input); | ||||
|         } | ||||
| 
 | ||||
|         public override async Task<PagedResultDto<MenuDto>> GetListAsync(MenuRequestDto input) | ||||
|         { | ||||
|             //await CheckGetListPolicyAsync(); | ||||
|             var queryable = await Repository.GetQueryableAsync(); | ||||
|             var allMenus = queryable | ||||
|                 .WhereIf(input.Type.HasValue, m => m.MenuType == input.Type) | ||||
|                 .WhereIf(!input.Name.IsNullOrWhiteSpace(), m => m.DisplayName.Contains(input.Name)) | ||||
|                 .ToList(); | ||||
| 
 | ||||
|             var root = allMenus | ||||
|                 .Where(x => !x.ParentId.HasValue) // 没有parentId | ||||
|                 .Union( | ||||
|                     // 有parentId,但是“allMenus"中不存在他的Parent。 | ||||
|                     allMenus.Where(x => x.ParentId.HasValue).Where(x => allMenus.All(y => x.ParentId != y.Id)) | ||||
|                 ) | ||||
|                 .OrderBy(x => x.Sort); | ||||
| 
 | ||||
|             var menuDtos = new List<MenuDto>(); | ||||
|             foreach (var menu in root) | ||||
|             { | ||||
|                 var dto = ObjectMapper.Map<Menu, MenuDto>(menu); | ||||
|                 menuDtos.Add(dto); | ||||
|                 // AddChildrenMenuRecursively(dto, allMenus); | ||||
| 
 | ||||
|                 SortChildrenMenu(dto); | ||||
|             } | ||||
| 
 | ||||
|             return new PagedResultDto<MenuDto>(allMenus.Count, menuDtos); | ||||
|         } | ||||
| 
 | ||||
|         private async Task PermissionChecker(string permissionName, Guid? menuId = null) | ||||
|         { | ||||
|             if (!permissionName.IsNullOrWhiteSpace()) | ||||
|             { | ||||
|                 var permission = _permissionDefinitionManager.GetOrNull(permissionName); | ||||
|                 if (permission == null) | ||||
|                 { | ||||
|                     throw new UserFriendlyException($"未知的权限:“{permissionName}”。"); | ||||
|                 } | ||||
| 
 | ||||
|                 var menu = await Repository.FirstOrDefaultAsync(x => x.PermissionKey == permissionName && x.Id != menuId); | ||||
|                 if (menu != null) | ||||
|                 { | ||||
|                     throw new UserFriendlyException($"权限已经被菜单“{menu.DisplayName}”绑定。"); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         private void SortChildrenMenu(MenuDto dto) | ||||
|         { | ||||
|             dto.Children.Sort((a, b) => string.Compare(a.Sort, b.Sort, StringComparison.Ordinal)); | ||||
|             dto.Children.ForEach(SortChildrenMenu); | ||||
|         } | ||||
| 
 | ||||
|         private void AddChildrenMenuRecursively(MenuDto parent, List<Menu> allMenus) | ||||
|         { | ||||
|             foreach (var menu in allMenus.Where(x => x.ParentId == parent.Id).OrderBy(x => x.Sort)) | ||||
|             { | ||||
|                 var dto = ObjectMapper.Map<Menu, MenuDto>(menu); | ||||
|                 parent.Children.Add(dto); | ||||
| 
 | ||||
|                 AddChildrenMenuRecursively(dto, allMenus); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,215 @@ | ||||
| using Microsoft.AspNetCore.Authorization; | ||||
| using Microsoft.Extensions.Options; | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| using System.Threading.Tasks; | ||||
| using Volo.Abp; | ||||
| using Volo.Abp.Application.Services; | ||||
| using Volo.Abp.Authorization; | ||||
| using Volo.Abp.MultiTenancy; | ||||
| using Volo.Abp.PermissionManagement; | ||||
| using Volo.Abp.Security.Claims; | ||||
| 
 | ||||
| namespace Sanhe.Abp.MenuManagement | ||||
| { | ||||
|     [Authorize] | ||||
|     public class MenuGrantAppService : ApplicationService, IMenuGrantAppService | ||||
|     { | ||||
|         private readonly IAbpAuthorizationPolicyProvider _abpAuthorizationPolicyProvider; | ||||
|         private readonly IUserMenuGrantChecker _userMenuGrantChecker; | ||||
|         private readonly ICurrentPrincipalAccessor _principalAccessor; | ||||
|         private readonly IAuthorizationService _authorizationService; | ||||
|         private readonly IMenuManager _menuManager; | ||||
|         private readonly IMenuAppService _menuAppService; | ||||
|         private readonly IMenuRepository _menuRepository; | ||||
|         private readonly PermissionManagementOptions _options; | ||||
|         private readonly IPermissionAppService _permissionAppService; | ||||
| 
 | ||||
|         public MenuGrantAppService( | ||||
|             IMenuRepository menuRepository, | ||||
|             IMenuManager menuManager, | ||||
|             IMenuAppService menuAppService, | ||||
|             IPermissionAppService permissionAppService, | ||||
|             IOptions<PermissionManagementOptions> options, | ||||
|             IAuthorizationService authorizationService, | ||||
|             IAbpAuthorizationPolicyProvider abpAuthorizationPolicyProvider, | ||||
|             IUserMenuGrantChecker userMenuGrantChecker, | ||||
|             ICurrentPrincipalAccessor principalAccessor | ||||
|         ) | ||||
|         { | ||||
|             _menuRepository = menuRepository; | ||||
|             _menuManager = menuManager; | ||||
|             _menuAppService = menuAppService; | ||||
|             _permissionAppService = permissionAppService; | ||||
|             _authorizationService = authorizationService; | ||||
|             _abpAuthorizationPolicyProvider = abpAuthorizationPolicyProvider; | ||||
|             _userMenuGrantChecker = userMenuGrantChecker; | ||||
|             _principalAccessor = principalAccessor; | ||||
|             _options = options.Value; | ||||
|         } | ||||
| 
 | ||||
|         public virtual async Task<GetMenuResultDto> GetListAsync() | ||||
|         { | ||||
|             var queryable = await _menuRepository.GetQueryableAsync(); | ||||
|             var rootMenus = queryable.Where( | ||||
|                     x => x.MenuType == MenuEnumType.Menu | ||||
|                 ) | ||||
|                 .OrderBy(x => x.Sort) | ||||
|                 .ToList() | ||||
|                 .Where(x => !x.ParentId.HasValue).ToList(); // 根节点 | ||||
| 
 | ||||
|             var menuDtos = new List<FrontMenu>(); | ||||
|             foreach (var menu in rootMenus) | ||||
|             { | ||||
|                 var isGranted = await _userMenuGrantChecker.CheckAsync(_principalAccessor.Principal, menu); | ||||
|                 if (isGranted) | ||||
|                 { | ||||
|                     var dto = ObjectMapper.Map<Menu, FrontMenu>(menu); | ||||
|                     menuDtos.Add(dto); | ||||
|                     await FilterChildrenMenuRecursivelyAsync(menu, dto); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             var permissionGrants = new List<string>(); | ||||
|             var policyNames = await _abpAuthorizationPolicyProvider.GetPoliciesNamesAsync(); | ||||
|             foreach (var policyName in policyNames) | ||||
|             { | ||||
|                 if (await _authorizationService.IsGrantedAsync(policyName)) | ||||
|                 { | ||||
|                     permissionGrants.Add(policyName); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             return new GetMenuResultDto | ||||
|             { | ||||
|                 Menus = menuDtos, | ||||
|                 PermissionGrants = permissionGrants | ||||
|             }; | ||||
|         } | ||||
| 
 | ||||
|         public virtual async Task<GetMenuGrantListResultDto> GetAsync(string providerName, string providerKey) | ||||
|         { | ||||
|             await CheckProviderPolicy(providerName); | ||||
|             return await InternalGetAsync(providerName, providerKey); | ||||
|         } | ||||
| 
 | ||||
|         public virtual async Task UpdateAsync(string providerName, string providerKey, UpdateMenuGrantsDto input) | ||||
|         { | ||||
|             await CheckProviderPolicy(providerName); | ||||
| 
 | ||||
|             foreach (var grantDto in input.Menus) | ||||
|             { | ||||
|                 await _menuManager.SetAsync(grantDto.Id, providerName, providerKey, grantDto.IsGranted); | ||||
|             } | ||||
| 
 | ||||
|             var permissions = _menuManager.GetPermissions(providerName); | ||||
|             var updatePermissionDtos = permissions | ||||
|                 .Select(x => | ||||
|                 { | ||||
|                     var menuDto = input.Menus.FirstOrDefault(y => y.PermissionKey == x.Name); | ||||
|                     var dto = new UpdatePermissionDto | ||||
|                     { | ||||
|                         Name = x.Name, | ||||
|                         IsGranted = menuDto?.IsGranted ?? true // 默认给予授权,如果菜单与权限绑定,则以菜单的授权为主。 | ||||
|                     }; | ||||
| 
 | ||||
|                     return dto; | ||||
|                 }) | ||||
|                 .ToArray(); | ||||
| 
 | ||||
|             await _permissionAppService.UpdateAsync( | ||||
|                 providerName, | ||||
|                 providerKey, | ||||
|                 new UpdatePermissionsDto | ||||
|                 { | ||||
|                     Permissions = updatePermissionDtos | ||||
|                 } | ||||
|             ); | ||||
|         } | ||||
| 
 | ||||
|         private async Task<GetMenuGrantListResultDto> InternalGetAsync(string providerName, string providerKey) | ||||
|         { | ||||
|             var allMenus = (await _menuRepository.GetQueryableAsync()).ToList(); | ||||
|             var multiTenancySide = CurrentTenant.GetMultiTenancySide(); | ||||
| 
 | ||||
|             var menuGrants = new List<MenuGrantInfoDto>(); | ||||
| 
 | ||||
|             foreach (var menu in allMenus) | ||||
|             { | ||||
|                 if (!menu.MultiTenancySide.HasFlag(multiTenancySide)) | ||||
|                 { | ||||
|                     continue; | ||||
|                 } | ||||
| 
 | ||||
|                 var grantInfo = await _menuManager.GetAsync(menu.Id, providerName, providerKey); | ||||
|                 menuGrants.Add(new MenuGrantInfoDto | ||||
|                 { | ||||
|                     Id = menu.Id, | ||||
|                     Name = menu.Name, | ||||
|                     DisplayName = menu.DisplayName, | ||||
|                     PermissionKey = menu.PermissionKey, | ||||
|                     IsGranted = grantInfo.IsGranted, | ||||
|                     GrantedProviders = grantInfo.Providers | ||||
|                 }); | ||||
|             } | ||||
| 
 | ||||
|             // _menuAppService.GetListAsync 没有做租户过滤,所以 这里要根据当前用户租户过滤。 | ||||
|             var menuDtos = new List<MenuDto>(); | ||||
|             foreach (var menu in (await _menuAppService.GetListAsync(new MenuRequestDto())).Items) | ||||
|             { | ||||
|                 if (menu.MultiTenancySide.HasFlag(multiTenancySide)) | ||||
|                 { | ||||
|                     menuDtos.Add(menu); | ||||
|                     FilterChildrenByTenancySide(menu, multiTenancySide); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             return new GetMenuGrantListResultDto | ||||
|             { | ||||
|                 MenuGrants = menuGrants, | ||||
|                 Menus = menuDtos | ||||
|             }; | ||||
|         } | ||||
| 
 | ||||
|         private void FilterChildrenByTenancySide(MenuDto menuDto, MultiTenancySides multiTenancySides) | ||||
|         { | ||||
|             if (menuDto.Children?.Count > 0) | ||||
|             { | ||||
|                 menuDto.Children = menuDto.Children.Where(x => x.MultiTenancySide.HasFlag(multiTenancySides)).ToList(); | ||||
|                 foreach (var child in menuDto.Children) | ||||
|                 { | ||||
|                     FilterChildrenByTenancySide(child, multiTenancySides); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         private async Task CheckProviderPolicy(string providerName) | ||||
|         { | ||||
|             var policyName = _options.ProviderPolicies.GetOrDefault(providerName); | ||||
|             if (policyName.IsNullOrEmpty()) | ||||
|             { | ||||
|                 throw new AbpException($"No policy defined to get/set permissions for the provider '{policyName}'. Use {nameof(PermissionManagementOptions)} to map the policy."); | ||||
|             } | ||||
| 
 | ||||
|             await AuthorizationService.CheckAsync(policyName); | ||||
|         } | ||||
| 
 | ||||
|         private async Task FilterChildrenMenuRecursivelyAsync(Menu parent, FrontMenu vueMenu) | ||||
|         { | ||||
|             if (parent.Children != null) | ||||
|             { | ||||
|                 vueMenu.Children = new List<FrontMenu>(); | ||||
|                 foreach (var menu in parent.Children) | ||||
|                 { | ||||
|                     if (await _userMenuGrantChecker.CheckAsync(_principalAccessor.Principal, menu)) | ||||
|                     { | ||||
|                         var dto = ObjectMapper.Map<Menu, FrontMenu>(menu); | ||||
|                         vueMenu.Children.Add(dto); | ||||
|                         await FilterChildrenMenuRecursivelyAsync(menu, dto); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,13 @@ | ||||
| using Sanhe.Abp.MenuManagement.Localization; | ||||
| using Volo.Abp.Application.Services; | ||||
| 
 | ||||
| namespace Sanhe.Abp.MenuManagement; | ||||
| 
 | ||||
| public abstract class MenuManagementAppService : ApplicationService | ||||
| { | ||||
|     protected MenuManagementAppService() | ||||
|     { | ||||
|         LocalizationResource = typeof(MenuManagementResource); | ||||
|         ObjectMapperContext = typeof(MenuManagementApplicationModule); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,24 @@ | ||||
| using AutoMapper; | ||||
| 
 | ||||
| namespace Sanhe.Abp.MenuManagement; | ||||
| 
 | ||||
| public class MenuManagementApplicationAutoMapperProfile : Profile | ||||
| { | ||||
|     public MenuManagementApplicationAutoMapperProfile() | ||||
|     { | ||||
|         CreateMap<Menu, MenuDto>(); | ||||
|         CreateMap<CreateOrUpdateMenuDto, Menu>(); | ||||
|         CreateMap<Menu, FrontMenu>() | ||||
|             .ForMember(d => d.Component, opt => { opt.MapFrom(s => s.ComponentPath); }) | ||||
|             .ForMember(d => d.Path, opt => { opt.MapFrom(s => s.RouterPath); }) | ||||
|             .ForMember(d => d.Meta, | ||||
|                 opt => | ||||
|                 { | ||||
|                     opt.MapFrom(s => new MenuMeta | ||||
|                     { | ||||
|                         Icon = s.Icon, | ||||
|                         Title = s.DisplayName | ||||
|                     }); | ||||
|                 }); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,26 @@ | ||||
| using Microsoft.Extensions.DependencyInjection; | ||||
| using Volo.Abp.Application; | ||||
| using Volo.Abp.AutoMapper; | ||||
| using Volo.Abp.Modularity; | ||||
| using Volo.Abp.PermissionManagement; | ||||
| 
 | ||||
| namespace Sanhe.Abp.MenuManagement; | ||||
| 
 | ||||
| [DependsOn( | ||||
|     typeof(AbpPermissionManagementApplicationModule), | ||||
|     typeof(MenuManagementDomainModule), | ||||
|     typeof(MenuManagementApplicationContractsModule), | ||||
|     typeof(AbpDddApplicationModule), | ||||
|     typeof(AbpAutoMapperModule) | ||||
|     )] | ||||
| public class MenuManagementApplicationModule : AbpModule | ||||
| { | ||||
|     public override void ConfigureServices(ServiceConfigurationContext context) | ||||
|     { | ||||
|         context.Services.AddAutoMapperObjectMapper<MenuManagementApplicationModule>(); | ||||
|         Configure<AbpAutoMapperOptions>(options => | ||||
|         { | ||||
|             options.AddMaps<MenuManagementApplicationModule>(); | ||||
|         }); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,3 @@ | ||||
| <Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> | ||||
|   <ConfigureAwait /> | ||||
| </Weavers> | ||||
| @ -0,0 +1,19 @@ | ||||
| <Project Sdk="Microsoft.NET.Sdk"> | ||||
| 
 | ||||
| 	<Import Project="..\..\..\configureawait.props" /> | ||||
| 	<Import Project="..\..\..\common.props" /> | ||||
| 
 | ||||
| 	<PropertyGroup> | ||||
| 		<TargetFramework>netstandard2.0</TargetFramework> | ||||
| 		<RootNamespace /> | ||||
| 	</PropertyGroup> | ||||
| 
 | ||||
| 	<ItemGroup> | ||||
| 		<PackageReference Include="Volo.Abp.Identity.Domain" Version="$(VoloAbpVersion)" /> | ||||
| 		<PackageReference Include="Volo.Abp.Identity.Domain.Shared" Version="$(VoloAbpVersion)" /> | ||||
| 	</ItemGroup> | ||||
| 
 | ||||
| 	<ItemGroup> | ||||
| 	  <ProjectReference Include="..\Sanhe.Abp.MenuManagement.Domain\Sanhe.Abp.MenuManagement.Domain.csproj" /> | ||||
| 	</ItemGroup> | ||||
| </Project> | ||||
| @ -0,0 +1,60 @@ | ||||
| using System; | ||||
| using System.Threading.Tasks; | ||||
| using Volo.Abp.Authorization.Permissions; | ||||
| using Volo.Abp.Guids; | ||||
| using Volo.Abp.Identity; | ||||
| using Volo.Abp.MultiTenancy; | ||||
| 
 | ||||
| namespace Sanhe.Abp.MenuManagement.Identity | ||||
| { | ||||
|     public class RoleMenuManagementProvider : MenuManagementProvider | ||||
|     { | ||||
|         private readonly IUserRoleFinder _userRoleFinder; | ||||
| 
 | ||||
|         public RoleMenuManagementProvider( | ||||
|             IMenuGrantRepository menuGrantRepository, | ||||
|             IPermissionDefinitionManager permissionDefinitionManager, | ||||
|             IMenuGrantChecker menuGrantChecker, | ||||
|             IGuidGenerator guidGenerator, | ||||
|             ICurrentTenant currentTenant, | ||||
|             IUserRoleFinder userRoleFinder) : | ||||
|             base(menuGrantRepository, | ||||
|                 permissionDefinitionManager, | ||||
|                 menuGrantChecker, | ||||
|                 guidGenerator, | ||||
|                 currentTenant) | ||||
|         { | ||||
|             _userRoleFinder = userRoleFinder; | ||||
|         } | ||||
| 
 | ||||
|         public override string Name => RolePermissionValueProvider.ProviderName; | ||||
| 
 | ||||
|         public override async Task<MenuGrantInfo> CheckAsync(Guid menuId, string providerName, string providerKey) | ||||
|         { | ||||
|             if (providerName == Name) | ||||
|             { | ||||
|                 return new MenuGrantInfo( | ||||
|                     (await MenuGrantChecker.CheckAsync(menuId, providerName, providerKey)).IsGranted, | ||||
|                     providerKey | ||||
|                 ); | ||||
|             } | ||||
| 
 | ||||
|             if (providerName == UserPermissionValueProvider.ProviderName) | ||||
|             { | ||||
|                 var userId = Guid.Parse(providerKey); | ||||
|                 var roleNames = await _userRoleFinder.GetRolesAsync(userId); | ||||
| 
 | ||||
|                 foreach (var roleName in roleNames) | ||||
|                 { | ||||
|                     var grantCache = await MenuGrantChecker.CheckAsync(menuId, Name, roleName); | ||||
|                     if (grantCache.IsGranted) | ||||
|                     { | ||||
|                         return new MenuGrantInfo(true, roleName); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             return MenuGrantInfo.NonGranted; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,22 @@ | ||||
| using Volo.Abp.Authorization.Permissions; | ||||
| using Volo.Abp.Guids; | ||||
| using Volo.Abp.MultiTenancy; | ||||
| 
 | ||||
| namespace Sanhe.Abp.MenuManagement.Identity | ||||
| { | ||||
|     public class UserMenuManagementProvider : MenuManagementProvider | ||||
|     { | ||||
|         public UserMenuManagementProvider( | ||||
|             IMenuGrantRepository menuGrantRepository, | ||||
|             IPermissionDefinitionManager permissionDefinitionManager, | ||||
|             IMenuGrantChecker menuGrantChecker, | ||||
|             IGuidGenerator guidGenerator, | ||||
|             ICurrentTenant currentTenant) : | ||||
|             base(menuGrantRepository, permissionDefinitionManager, menuGrantChecker, guidGenerator, currentTenant) | ||||
|         { | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
|         public override string Name => UserPermissionValueProvider.ProviderName; | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,24 @@ | ||||
| using Sanhe.Abp.MenuManagement.Identity; | ||||
| using Volo.Abp.Identity; | ||||
| using Volo.Abp.Modularity; | ||||
| using Volo.Abp.PermissionManagement; | ||||
| 
 | ||||
| namespace Sanhe.Abp.MenuManagement | ||||
| { | ||||
|     [DependsOn( | ||||
|         typeof(AbpIdentityDomainSharedModule), | ||||
|         typeof(AbpPermissionManagementDomainModule), | ||||
|         typeof(MenuManagementDomainModule) | ||||
|     )] | ||||
|     public class MenuManagementDomainIdentityModule : AbpModule | ||||
|     { | ||||
|         public override void ConfigureServices(ServiceConfigurationContext context) | ||||
|         { | ||||
|             Configure<MenuManagementOptions>(options => | ||||
|             { | ||||
|                 options.ManagementProviders.Add<UserMenuManagementProvider>(); | ||||
|                 options.ManagementProviders.Add<RoleMenuManagementProvider>(); | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,3 @@ | ||||
| <Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> | ||||
|   <ConfigureAwait ContinueOnCapturedContext="false" /> | ||||
| </Weavers> | ||||
| @ -0,0 +1,25 @@ | ||||
| <Project Sdk="Microsoft.NET.Sdk"> | ||||
| 
 | ||||
| 	<Import Project="..\..\..\configureawait.props" /> | ||||
| 	<Import Project="..\..\..\common.props" /> | ||||
| 
 | ||||
| 	<PropertyGroup> | ||||
| 		<TargetFramework>netstandard2.0</TargetFramework> | ||||
| 		<RootNamespace /> | ||||
| 	</PropertyGroup> | ||||
| 
 | ||||
| 	<ItemGroup> | ||||
| 	  <None Remove="Sanhe\Abp\MenuManagement\Localization\Resources\en.json" /> | ||||
| 	  <None Remove="Sanhe\Abp\MenuManagement\Localization\Resources\zh-Hans.json" /> | ||||
| 	</ItemGroup> | ||||
| 
 | ||||
| 	<ItemGroup> | ||||
| 	  <EmbeddedResource Include="Sanhe\Abp\MenuManagement\Localization\Resources\en.json" /> | ||||
| 	  <EmbeddedResource Include="Sanhe\Abp\MenuManagement\Localization\Resources\zh-Hans.json" /> | ||||
| 	</ItemGroup> | ||||
| 
 | ||||
| 	<ItemGroup> | ||||
| 		<PackageReference Include="Volo.Abp.PermissionManagement.Domain.Shared" Version="$(VoloAbpVersion)" /> | ||||
| 		<PackageReference Include="Volo.Abp.Validation" Version="$(VoloAbpVersion)" /> | ||||
| 	</ItemGroup> | ||||
| </Project> | ||||
| @ -0,0 +1,9 @@ | ||||
| using Volo.Abp.Localization; | ||||
| 
 | ||||
| namespace Sanhe.Abp.MenuManagement.Localization; | ||||
| 
 | ||||
| [LocalizationResourceName("MenuManagement")] | ||||
| public class MenuManagementResource | ||||
| { | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,25 @@ | ||||
| { | ||||
|   "culture": "en", | ||||
|   "texts": { | ||||
|     "ManageYourProfile": "Manage your profile", | ||||
|     "Menu:Menu": "MenuMenu", | ||||
|     "Menu": "Menu", | ||||
|     "MenuTenantId": "MenuTenantId", | ||||
|     "MenuName": "MenuName", | ||||
|     "MenuDisplayName": "MenuDisplayName", | ||||
|     "MenuMenuType": "MenuMenuType", | ||||
|     "MenuComponentPath": "MenuComponentPath", | ||||
|     "MenuRouterPath": "MenuRouterPath", | ||||
|     "MenuParentId": "MenuParentId", | ||||
|     "MenuParent": "MenuParent", | ||||
|     "MenuIcon": "MenuIcon", | ||||
|     "MenuSort": "MenuSort", | ||||
|     "MenuTargetUrl": "MenuTargetUrl", | ||||
|     "MenuPermissionKey": "MenuPermissionKey", | ||||
|     "MenuChildren": "MenuChildren", | ||||
|     "CreateMenu": "CreateMenu", | ||||
|     "EditMenu": "EditMenu", | ||||
|     "MenuDeletionConfirmationMessage": "Are you sure to delete the menu {0}?", | ||||
|     "SuccessfullyDeleted": "Successfully deleted" | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,30 @@ | ||||
| { | ||||
|   "culture": "zh-Hans", | ||||
|   "texts": { | ||||
|     "ManageYourProfile": "管理个人资料", | ||||
|     "Menu:Menu": "MenuMenu", | ||||
|     "Menu": "Menu", | ||||
|     "MenuTenantId": "MenuTenantId", | ||||
|     "MenuName": "MenuName", | ||||
|     "MenuDisplayName": "MenuDisplayName", | ||||
|     "MenuMenuType": "MenuMenuType", | ||||
|     "MenuComponentPath": "MenuComponentPath", | ||||
|     "MenuRouterPath": "MenuRouterPath", | ||||
|     "MenuParentId": "MenuParentId", | ||||
|     "MenuParent": "MenuParent", | ||||
|     "MenuIcon": "MenuIcon", | ||||
|     "MenuSort": "MenuSort", | ||||
|     "MenuTargetUrl": "MenuTargetUrl", | ||||
|     "MenuPermissionKey": "MenuPermissionKey", | ||||
|     "MenuChildren": "MenuChildren", | ||||
|     "CreateMenu": "CreateMenu", | ||||
|     "EditMenu": "EditMenu", | ||||
|     "MenuDeletionConfirmationMessage": "Are you sure to delete the menu {0}?", | ||||
|     "SuccessfullyDeleted": "Successfully deleted", | ||||
|     "MenuManagement:MenuManagement": "菜单管理", | ||||
|     "MenuManagement:Create": "创建", | ||||
|     "MenuManagement:Update": "编辑", | ||||
|     "MenuManagement:Delete": "删除", | ||||
|     "MenuManagement:CreatePermission": "创建权限" | ||||
|   } | ||||
| }  | ||||
| @ -0,0 +1,17 @@ | ||||
| namespace Sanhe.Abp.MenuManagement | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 菜单类型 | ||||
|     /// </summary> | ||||
|     public enum MenuEnumType | ||||
|     { | ||||
|         /// <summary> | ||||
|         ///  菜单 | ||||
|         /// </summary> | ||||
|         Menu, | ||||
|         /// <summary> | ||||
|         ///  权限 | ||||
|         /// </summary> | ||||
|         Permission | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,16 @@ | ||||
| namespace Sanhe.Abp.MenuManagement | ||||
| { | ||||
|     public static class MenuGrantConsts | ||||
|     { | ||||
|         public const int MaxProviderNameLength = 64; | ||||
| 
 | ||||
|         public const int MaxProviderKeyLength = 64; | ||||
|     } | ||||
| 
 | ||||
|     public static class MenuConsts | ||||
|     { | ||||
|         public const int MaxProviderNameLength = 64; | ||||
| 
 | ||||
|         public const int MaxProviderKeyLength = 64; | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,18 @@ | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| 
 | ||||
| namespace Sanhe.Abp.MenuManagement | ||||
| { | ||||
|     public class MenuGrantInfoDto | ||||
|     { | ||||
|         /// <summary> | ||||
|         /// Id | ||||
|         /// </summary> | ||||
|         public Guid Id { get; set; } | ||||
|         public string Name { get; set; } | ||||
|         public string DisplayName { get; set; } | ||||
|         public string PermissionKey { get; set; } | ||||
|         public bool IsGranted { get; set; } | ||||
|         public List<MenuProviderInfo> GrantedProviders { get; set; } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,38 @@ | ||||
| using Sanhe.Abp.MenuManagement.Localization; | ||||
| using Volo.Abp.Localization; | ||||
| using Volo.Abp.Localization.ExceptionHandling; | ||||
| using Volo.Abp.Modularity; | ||||
| using Volo.Abp.PermissionManagement; | ||||
| using Volo.Abp.Validation; | ||||
| using Volo.Abp.Validation.Localization; | ||||
| using Volo.Abp.VirtualFileSystem; | ||||
| 
 | ||||
| namespace Sanhe.Abp.MenuManagement; | ||||
| 
 | ||||
| [DependsOn( | ||||
|     typeof(AbpValidationModule), | ||||
|     typeof(AbpPermissionManagementDomainSharedModule) | ||||
| )] | ||||
| public class MenuManagementDomainSharedModule : AbpModule | ||||
| { | ||||
|     public override void ConfigureServices(ServiceConfigurationContext context) | ||||
|     { | ||||
|         Configure<AbpVirtualFileSystemOptions>(options => | ||||
|         { | ||||
|             options.FileSets.AddEmbedded<MenuManagementDomainSharedModule>(); | ||||
|         }); | ||||
| 
 | ||||
|         Configure<AbpLocalizationOptions>(options => | ||||
|         { | ||||
|             options.Resources | ||||
|                 .Add<MenuManagementResource>("en") | ||||
|                 .AddBaseTypes(typeof(AbpValidationResource)) | ||||
|                 .AddVirtualJson("/Sanhe/Abp/MenuManagement/Localization/Resources"); | ||||
|         }); | ||||
| 
 | ||||
|         Configure<AbpExceptionLocalizationOptions>(options => | ||||
|         { | ||||
|             options.MapCodeNamespace("MenuManagement", typeof(MenuManagementResource)); | ||||
|         }); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,21 @@ | ||||
| using JetBrains.Annotations; | ||||
| using Volo.Abp; | ||||
| 
 | ||||
| namespace Sanhe.Abp.MenuManagement | ||||
| { | ||||
|     public class MenuProviderInfo | ||||
|     { | ||||
|         public string Name { get; } | ||||
| 
 | ||||
|         public string Key { get; } | ||||
| 
 | ||||
|         public MenuProviderInfo([NotNull] string name, [NotNull] string key) | ||||
|         { | ||||
|             Check.NotNull(name, nameof(name)); | ||||
|             Check.NotNull(key, nameof(key)); | ||||
| 
 | ||||
|             Name = name; | ||||
|             Key = key; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,3 @@ | ||||
| <Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> | ||||
|   <ConfigureAwait ContinueOnCapturedContext="false" /> | ||||
| </Weavers> | ||||
| @ -0,0 +1,20 @@ | ||||
| <Project Sdk="Microsoft.NET.Sdk"> | ||||
| 
 | ||||
| 	<Import Project="..\..\..\configureawait.props" /> | ||||
| 	<Import Project="..\..\..\common.props" /> | ||||
| 	 | ||||
| 	<PropertyGroup> | ||||
| 		<TargetFramework>netstandard2.0</TargetFramework> | ||||
| 		<RootNamespace /> | ||||
| 	</PropertyGroup> | ||||
| 
 | ||||
| 	<ItemGroup> | ||||
| 		<PackageReference Include="Volo.Abp.Ddd.Domain" Version="$(VoloAbpVersion)" /> | ||||
| 		<PackageReference Include="Volo.Abp.PermissionManagement.Domain" Version="$(VoloAbpVersion)" /> | ||||
| 	</ItemGroup> | ||||
| 
 | ||||
| 	<ItemGroup> | ||||
| 	  <ProjectReference Include="..\Sanhe.Abp.MenuManagement.Domain.Shared\Sanhe.Abp.MenuManagement.Domain.Shared.csproj" /> | ||||
| 	</ItemGroup> | ||||
| 	 | ||||
| </Project> | ||||
| @ -0,0 +1,10 @@ | ||||
| using System; | ||||
| using System.Threading.Tasks; | ||||
| 
 | ||||
| namespace Sanhe.Abp.MenuManagement | ||||
| { | ||||
|     public interface IMenuGrantChecker | ||||
|     { | ||||
|         Task<MenuGrantCacheItem> CheckAsync(Guid menuId, string providerName, string providerKey); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,24 @@ | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
| using Volo.Abp.Domain.Repositories; | ||||
| 
 | ||||
| namespace Sanhe.Abp.MenuManagement | ||||
| { | ||||
|     public interface IMenuGrantRepository : IBasicRepository<MenuGrant, Guid> | ||||
|     { | ||||
|         Task<MenuGrant> FindAsync(Guid menuId, | ||||
|             string providerName, | ||||
|             string providerKey, | ||||
|             CancellationToken cancellationToken = default); | ||||
| 
 | ||||
|         Task<List<MenuGrant>> GetListAsync( | ||||
|             string providerName, | ||||
|             string providerKey, | ||||
|             CancellationToken cancellationToken = default | ||||
|         ); | ||||
| 
 | ||||
|         Task<List<MenuGrant>> GetGrantByMenuIdAsync(Guid menuId, bool noTracking = true); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,9 @@ | ||||
| using System.Threading.Tasks; | ||||
| 
 | ||||
| namespace Sanhe.Abp.MenuManagement | ||||
| { | ||||
|     public interface IMenuGrantRuntimeCheckerProvider | ||||
|     { | ||||
|         Task<MenuGrantResultEnum> CheckAsync(MenuGrantRuntimeCheckerContent context); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,25 @@ | ||||
| using JetBrains.Annotations; | ||||
| using System; | ||||
| using System.Threading.Tasks; | ||||
| using Volo.Abp.DependencyInjection; | ||||
| 
 | ||||
| namespace Sanhe.Abp.MenuManagement | ||||
| { | ||||
|     public interface IMenuManagementProvider : ITransientDependency | ||||
|     { | ||||
|         string Name { get; } | ||||
| 
 | ||||
|         Task<MenuGrantInfo> CheckAsync( | ||||
|             [NotNull] Guid menuId, | ||||
|             [NotNull] string providerName, | ||||
|             [NotNull] string providerKey | ||||
|         ); | ||||
| 
 | ||||
|         Task SetAsync( | ||||
|             [NotNull] Guid menuId, | ||||
|             [NotNull] string providerKey, | ||||
|             bool isGranted | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,15 @@ | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Threading.Tasks; | ||||
| using Volo.Abp.Authorization.Permissions; | ||||
| 
 | ||||
| namespace Sanhe.Abp.MenuManagement | ||||
| { | ||||
|     public interface IMenuManager | ||||
|     { | ||||
|         IReadOnlyList<PermissionDefinition> GetPermissions(string providerName); | ||||
|         Task<MenuWithGrantedProviders> GetAsync(Guid menuId, string providerName, string providerKey); | ||||
|         Task SetAsync(Guid menuId, string providerName, string providerKey, bool isGranted); | ||||
|         Task UpdatePermissionGrantAsync(Guid menuId, string oldPermission, string newPermission); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,10 @@ | ||||
| using System; | ||||
| using Volo.Abp.Domain.Repositories; | ||||
| 
 | ||||
| namespace Sanhe.Abp.MenuManagement | ||||
| { | ||||
|     public interface IMenuRepository : IRepository<Menu, Guid> | ||||
|     { | ||||
| 
 | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,65 @@ | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Security.Claims; | ||||
| using System.Security.Principal; | ||||
| using System.Threading.Tasks; | ||||
| using Volo.Abp.DependencyInjection; | ||||
| using Volo.Abp.MultiTenancy; | ||||
| 
 | ||||
| namespace Sanhe.Abp.MenuManagement | ||||
| { | ||||
|     public interface IUserMenuGrantChecker | ||||
|     { | ||||
|         Task<bool> CheckAsync(ClaimsPrincipal claimsPrincipal, Menu menu); | ||||
|     } | ||||
| 
 | ||||
|     public class UserMenuGrantChecker : IUserMenuGrantChecker, ITransientDependency | ||||
|     { | ||||
|         private readonly ICurrentTenant _currentTenant; | ||||
|         private readonly IEnumerable<IMenuGrantRuntimeCheckerProvider> _checkerProviders; | ||||
| 
 | ||||
|         public UserMenuGrantChecker( | ||||
|             ICurrentTenant currentTenant, | ||||
|             IEnumerable<IMenuGrantRuntimeCheckerProvider> checkerProviders | ||||
|         ) | ||||
|         { | ||||
|             _currentTenant = currentTenant; | ||||
|             _checkerProviders = checkerProviders; | ||||
|         } | ||||
| 
 | ||||
|         public async Task<bool> CheckAsync(ClaimsPrincipal claimsPrincipal, Menu menu) | ||||
|         { | ||||
|             var multiTenancySide = claimsPrincipal?.GetMultiTenancySide() | ||||
|                                    ?? _currentTenant.GetMultiTenancySide(); | ||||
| 
 | ||||
|             if (!menu.MultiTenancySide.HasFlag(multiTenancySide)) | ||||
|             { | ||||
|                 return false; | ||||
|             } | ||||
| 
 | ||||
|             if (menu.PermissionKey.IsNullOrWhiteSpace()) | ||||
|             { | ||||
|                 return true; | ||||
|             } | ||||
| 
 | ||||
|             var isGranted = false; | ||||
|             var content = new MenuGrantRuntimeCheckerContent | ||||
|             { | ||||
|                 Menu = menu, | ||||
|                 Principal = claimsPrincipal | ||||
|             }; | ||||
| 
 | ||||
|             foreach (var provider in _checkerProviders) | ||||
|             { | ||||
|                 var result = await provider.CheckAsync(content); | ||||
|                 if (result == MenuGrantResultEnum.Granted) | ||||
|                 { | ||||
|                     isGranted = true; | ||||
|                     break; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             return isGranted; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,100 @@ | ||||
| using System; | ||||
| using System.Collections.ObjectModel; | ||||
| using System.ComponentModel; | ||||
| using Volo.Abp.Domain.Entities.Auditing; | ||||
| using Volo.Abp.MultiTenancy; | ||||
| 
 | ||||
| namespace Sanhe.Abp.MenuManagement | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 菜单 | ||||
|     /// </summary> | ||||
|     [DisplayName("菜单")] | ||||
|     public class Menu : FullAuditedAggregateRoot<Guid> | ||||
|     { | ||||
|         /// <summary> | ||||
|         /// 名称 | ||||
|         /// </summary> | ||||
|         [DisplayName("名称")] | ||||
|         public virtual string Name { get; protected set; } | ||||
|         /// <summary> | ||||
|         /// 显示名称 | ||||
|         /// </summary> | ||||
|         [DisplayName("显示名称")] | ||||
|         public virtual string DisplayName { get; protected set; } | ||||
|         /// <summary> | ||||
|         /// 类型 | ||||
|         /// </summary> | ||||
|         [DisplayName("类型")] | ||||
|         public virtual MenuEnumType MenuType { get; protected set; } | ||||
|         /// <summary> | ||||
|         /// 组件路径 | ||||
|         /// </summary> | ||||
|         [DisplayName("组件路径")] | ||||
|         public virtual string ComponentPath { get; set; } | ||||
|         /// <summary> | ||||
|         /// 路由路径 | ||||
|         /// </summary> | ||||
|         [DisplayName("路由路径")] | ||||
|         public virtual string RouterPath { get; set; } | ||||
|         /// <summary> | ||||
|         /// 父Id | ||||
|         /// </summary> | ||||
|         [DisplayName("父Id")] | ||||
|         public virtual Guid? ParentId { get; set; } | ||||
|         /// <summary> | ||||
|         /// 图标 | ||||
|         /// </summary> | ||||
|         [DisplayName("图标")] | ||||
|         public virtual string Icon { get; set; } | ||||
|         /// <summary> | ||||
|         /// 排序 | ||||
|         /// </summary> | ||||
|         [DisplayName("排序")] | ||||
|         public virtual string Sort { get; set; } | ||||
|         /// <summary> | ||||
|         /// window.open _blank  | ||||
|         /// </summary> | ||||
|         [DisplayName("window.open _blank ")] | ||||
|         public virtual string TargetUrl { get; set; } | ||||
|         /// <summary> | ||||
|         /// 此菜单关联的权限key | ||||
|         /// </summary> | ||||
|         [DisplayName("此菜单关联的权限key")] | ||||
|         public virtual string PermissionKey { get; set; } | ||||
|         /// <summary> | ||||
|         /// 表示多租户应用程序中的所属方 | ||||
|         /// </summary> | ||||
|         [DisplayName("表示多租户应用程序中的所属方")] | ||||
|         public virtual MultiTenancySides MultiTenancySide { get; set; } | ||||
|         /// <summary> | ||||
|         /// 父菜单 | ||||
|         /// </summary> | ||||
|         public virtual Menu Parent { get; set; } | ||||
|         /// <summary> | ||||
|         /// 子菜单 | ||||
|         /// </summary> | ||||
|         public virtual Collection<Menu> Children { get; set; } | ||||
| 
 | ||||
|         protected Menu() | ||||
|         { | ||||
|         } | ||||
| 
 | ||||
|         public Menu( | ||||
|             Guid id, | ||||
|             string name, | ||||
|             string displayName, | ||||
|             MenuEnumType menuType, | ||||
|             MultiTenancySides multiTenancySide = MultiTenancySides.Both | ||||
|         ) | ||||
|         { | ||||
|             Id = id; | ||||
|             Name = name; | ||||
|             DisplayName = displayName; | ||||
|             MenuType = menuType; | ||||
|             MultiTenancySide = multiTenancySide; | ||||
| 
 | ||||
|             Children = new Collection<Menu>(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,60 @@ | ||||
| using JetBrains.Annotations; | ||||
| using System; | ||||
| using System.ComponentModel; | ||||
| using Volo.Abp; | ||||
| using Volo.Abp.Domain.Entities; | ||||
| using Volo.Abp.MultiTenancy; | ||||
| 
 | ||||
| namespace Sanhe.Abp.MenuManagement | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 菜单分配 | ||||
|     /// </summary> | ||||
|     [DisplayName("菜单分配")] | ||||
|     public class MenuGrant : Entity<Guid>, IMultiTenant | ||||
|     { | ||||
|         /// <summary> | ||||
|         /// 租户Id | ||||
|         /// </summary> | ||||
|         [DisplayName("租户Id")] | ||||
|         public virtual Guid? TenantId { get; protected set; } | ||||
|         /// <summary> | ||||
|         /// 菜单Id | ||||
|         /// </summary> | ||||
|         [DisplayName("菜单Id")] | ||||
|         [NotNull] | ||||
|         public virtual Guid MenuId { get; protected set; } | ||||
|         /// <summary> | ||||
|         /// 提供器名称 | ||||
|         /// </summary> | ||||
|         [DisplayName("提供器名称")] | ||||
|         [NotNull] | ||||
|         public virtual string ProviderName { get; protected set; } | ||||
|         /// <summary> | ||||
|         /// 提供器Key | ||||
|         /// </summary> | ||||
|         [DisplayName("提供器Key")] | ||||
|         [CanBeNull] | ||||
|         public virtual string ProviderKey { get; protected internal set; } | ||||
| 
 | ||||
|         protected MenuGrant() | ||||
|         { | ||||
|         } | ||||
| 
 | ||||
|         public MenuGrant( | ||||
|             Guid id, | ||||
|             [NotNull] Guid menuId, | ||||
|             [NotNull] string providerName, | ||||
|             [CanBeNull] string providerKey, | ||||
|             Guid? tenantId = null) | ||||
|         { | ||||
|             Check.NotNull(menuId, nameof(menuId)); | ||||
| 
 | ||||
|             Id = id; | ||||
|             MenuId = menuId; | ||||
|             ProviderName = Check.NotNullOrWhiteSpace(providerName, nameof(providerName)); | ||||
|             ProviderKey = providerKey; | ||||
|             TenantId = tenantId; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,27 @@ | ||||
| using System; | ||||
| 
 | ||||
| namespace Sanhe.Abp.MenuManagement | ||||
| { | ||||
|     [Serializable] | ||||
|     public class MenuGrantCacheItem | ||||
|     { | ||||
|         public Guid MenuId { get; set; } | ||||
|         public bool IsGranted { get; set; } | ||||
| 
 | ||||
|         public MenuGrantCacheItem() | ||||
|         { | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
|         public MenuGrantCacheItem(Guid menuId, bool isGranted) | ||||
|         { | ||||
|             MenuId = menuId; | ||||
|             IsGranted = isGranted; | ||||
|         } | ||||
| 
 | ||||
|         public static string CalculateCacheKey(Guid menuId, string providerName, string providerKey) | ||||
|         { | ||||
|             return "pn:" + providerName + ",pk:" + providerKey + ",m:" + menuId; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,65 @@ | ||||
| using System; | ||||
| using System.Threading.Tasks; | ||||
| using Volo.Abp.Caching; | ||||
| using Volo.Abp.DependencyInjection; | ||||
| using Volo.Abp.Domain.Entities.Events; | ||||
| using Volo.Abp.EventBus; | ||||
| using Volo.Abp.MultiTenancy; | ||||
| 
 | ||||
| namespace Sanhe.Abp.MenuManagement | ||||
| { | ||||
|     public class MenuGrantCacheItemInvalidator : | ||||
|         ILocalEventHandler<EntityCreatedEventData<MenuGrant>>, | ||||
|         ILocalEventHandler<EntityDeletedEventData<MenuGrant>>, | ||||
|         ITransientDependency | ||||
|     { | ||||
|         public MenuGrantCacheItemInvalidator( | ||||
|             ICurrentTenant currentTenant, | ||||
|             IDistributedCache<MenuGrantCacheItem> cache | ||||
|         ) | ||||
|         { | ||||
|             CurrentTenant = currentTenant; | ||||
|             Cache = cache; | ||||
|         } | ||||
| 
 | ||||
|         protected ICurrentTenant CurrentTenant { get; } | ||||
| 
 | ||||
|         protected IDistributedCache<MenuGrantCacheItem> Cache { get; } | ||||
| 
 | ||||
|         public async Task HandleEventAsync(EntityCreatedEventData<MenuGrant> eventData) | ||||
|         { | ||||
|             var cacheKey = CalculateCacheKey( | ||||
|                 eventData.Entity.MenuId, | ||||
|                 eventData.Entity.ProviderName, | ||||
|                 eventData.Entity.ProviderKey | ||||
|             ); | ||||
| 
 | ||||
|             using (CurrentTenant.Change(eventData.Entity.TenantId)) | ||||
|             { | ||||
|                 await Cache.SetAsync(cacheKey, new MenuGrantCacheItem(eventData.Entity.MenuId, true)); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         public async Task HandleEventAsync(EntityDeletedEventData<MenuGrant> eventData) | ||||
|         { | ||||
|             var cacheKey = CalculateCacheKey( | ||||
|                 eventData.Entity.MenuId, | ||||
|                 eventData.Entity.ProviderName, | ||||
|                 eventData.Entity.ProviderKey | ||||
|             ); | ||||
| 
 | ||||
|             using (CurrentTenant.Change(eventData.Entity.TenantId)) | ||||
|             { | ||||
|                 await Cache.SetAsync(cacheKey, new MenuGrantCacheItem(eventData.Entity.MenuId, false)); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         protected virtual string CalculateCacheKey( | ||||
|             Guid menuId, | ||||
|             string providerName, | ||||
|             string providerKey) | ||||
|         { | ||||
|             return MenuGrantCacheItem.CalculateCacheKey(menuId, providerName, providerKey); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,59 @@ | ||||
| using Microsoft.Extensions.Logging; | ||||
| using System; | ||||
| using System.Threading.Tasks; | ||||
| using Volo.Abp.Caching; | ||||
| using Volo.Abp.DependencyInjection; | ||||
| 
 | ||||
| namespace Sanhe.Abp.MenuManagement | ||||
| { | ||||
|     public class MenuGrantChecker : IMenuGrantChecker, ITransientDependency | ||||
|     { | ||||
|         private readonly IDistributedCache<MenuGrantCacheItem> _cache; | ||||
|         private readonly ILogger<MenuGrantChecker> _logger; | ||||
|         private readonly IMenuGrantRepository _menuGrantRepository; | ||||
| 
 | ||||
|         public MenuGrantChecker( | ||||
|             IDistributedCache<MenuGrantCacheItem> cache, | ||||
|             ILogger<MenuGrantChecker> logger, | ||||
|             IMenuGrantRepository menuGrantRepository | ||||
|         ) | ||||
|         { | ||||
|             _cache = cache; | ||||
|             _logger = logger; | ||||
|             _menuGrantRepository = menuGrantRepository; | ||||
|         } | ||||
| 
 | ||||
|         public async Task<MenuGrantCacheItem> CheckAsync(Guid menuId, string providerName, string providerKey) | ||||
|         { | ||||
|             var cacheKey = MenuGrantCacheItem.CalculateCacheKey(menuId, providerName, providerKey); | ||||
| 
 | ||||
|             _logger.LogDebug("MenuGrantCheckerCache.CheckAsync: {cacheKey}", cacheKey); | ||||
| 
 | ||||
|             var cacheItem = await _cache.GetAsync(cacheKey); | ||||
| 
 | ||||
|             if (cacheItem != null) | ||||
|             { | ||||
|                 _logger.LogDebug("Found in the cache: {cacheKey}", cacheKey); | ||||
|                 return cacheItem; | ||||
|             } | ||||
| 
 | ||||
|             _logger.LogDebug("Not found in the cache, getting from the repository: {cacheKey}", cacheKey); | ||||
| 
 | ||||
|             cacheItem = new MenuGrantCacheItem( | ||||
|                 menuId, | ||||
|                 await _menuGrantRepository.FindAsync(menuId, providerName, providerKey) != null | ||||
|             ); | ||||
| 
 | ||||
|             _logger.LogDebug("Setting the cache item: {cacheKey}", cacheKey); | ||||
| 
 | ||||
|             await _cache.SetAsync( | ||||
|                 cacheKey, | ||||
|                 cacheItem | ||||
|             ); | ||||
| 
 | ||||
|             _logger.LogDebug("Finished setting the cache item: {cacheKey}", cacheKey); | ||||
| 
 | ||||
|             return cacheItem; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,19 @@ | ||||
| using JetBrains.Annotations; | ||||
| 
 | ||||
| namespace Sanhe.Abp.MenuManagement | ||||
| { | ||||
|     public class MenuGrantInfo | ||||
|     { | ||||
|         public static MenuGrantInfo NonGranted { get; } = new MenuGrantInfo(false); | ||||
| 
 | ||||
|         public virtual bool IsGranted { get; } | ||||
| 
 | ||||
|         public virtual string ProviderKey { get; } | ||||
| 
 | ||||
|         public MenuGrantInfo(bool isGranted, [CanBeNull] string providerKey = null) | ||||
|         { | ||||
|             IsGranted = isGranted; | ||||
|             ProviderKey = providerKey; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,8 @@ | ||||
| namespace Sanhe.Abp.MenuManagement | ||||
| { | ||||
|     public enum MenuGrantResultEnum | ||||
|     { | ||||
|         Undefined, | ||||
|         Granted | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,10 @@ | ||||
| using System.Security.Claims; | ||||
| 
 | ||||
| namespace Sanhe.Abp.MenuManagement | ||||
| { | ||||
|     public class MenuGrantRuntimeCheckerContent | ||||
|     { | ||||
|         public Menu Menu { get; set; } | ||||
|         public ClaimsPrincipal Principal { get; set; } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,61 @@ | ||||
| using System.Linq; | ||||
| using System.Threading.Tasks; | ||||
| using Volo.Abp.DependencyInjection; | ||||
| using Volo.Abp.Security.Claims; | ||||
| 
 | ||||
| namespace Sanhe.Abp.MenuManagement | ||||
| { | ||||
|     public abstract class MenuGrantRuntimeCheckerProvider : IMenuGrantRuntimeCheckerProvider | ||||
|     { | ||||
|         protected IMenuGrantChecker MenuGrantChecker { get; } | ||||
| 
 | ||||
|         protected MenuGrantRuntimeCheckerProvider(IMenuGrantChecker menuGrantChecker) | ||||
|         { | ||||
|             MenuGrantChecker = menuGrantChecker; | ||||
|         } | ||||
| 
 | ||||
|         public abstract Task<MenuGrantResultEnum> CheckAsync(MenuGrantRuntimeCheckerContent context); | ||||
|     } | ||||
| 
 | ||||
|     public class RoleMenuGrantRuntimeCheckerProvider : MenuGrantRuntimeCheckerProvider, ITransientDependency | ||||
|     { | ||||
|         public RoleMenuGrantRuntimeCheckerProvider(IMenuGrantChecker menuGrantChecker) : base(menuGrantChecker) | ||||
|         { | ||||
|         } | ||||
| 
 | ||||
|         public override async Task<MenuGrantResultEnum> CheckAsync(MenuGrantRuntimeCheckerContent context) | ||||
|         { | ||||
|             var roles = context.Principal?.FindAll(AbpClaimTypes.Role).Select(c => c.Value).ToArray(); | ||||
| 
 | ||||
|             if (roles == null || !roles.Any()) | ||||
|             { | ||||
|                 return MenuGrantResultEnum.Undefined; | ||||
|             } | ||||
| 
 | ||||
|             foreach (var role in roles) | ||||
|             { | ||||
|                 var result = await MenuGrantChecker.CheckAsync(context.Menu.Id, "R", role); | ||||
|                 if (result.IsGranted) | ||||
|                 { | ||||
|                     return MenuGrantResultEnum.Granted; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             return MenuGrantResultEnum.Undefined; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public class UserMenuGrantRuntimeCheckerProvider : MenuGrantRuntimeCheckerProvider, ITransientDependency | ||||
|     { | ||||
|         public UserMenuGrantRuntimeCheckerProvider(IMenuGrantChecker menuGrantChecker) : base(menuGrantChecker) | ||||
|         { | ||||
|         } | ||||
| 
 | ||||
|         public override async Task<MenuGrantResultEnum> CheckAsync(MenuGrantRuntimeCheckerContent context) | ||||
|         { | ||||
|             var userId = context.Principal?.FindFirst(AbpClaimTypes.UserId)?.Value; | ||||
|             var result = await MenuGrantChecker.CheckAsync(context.Menu.Id, "U", userId); | ||||
|             return result.IsGranted ? MenuGrantResultEnum.Granted : MenuGrantResultEnum.Undefined; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,10 @@ | ||||
| namespace Sanhe.Abp.MenuManagement; | ||||
| 
 | ||||
| public static class MenuManagementDbProperties | ||||
| { | ||||
|     public static string DbTablePrefix { get; set; } = "MenuManagement"; | ||||
| 
 | ||||
|     public static string DbSchema { get; set; } = null; | ||||
| 
 | ||||
|     public const string ConnectionStringName = "MenuManagement"; | ||||
| } | ||||
| @ -0,0 +1,13 @@ | ||||
| using Volo.Abp.Domain; | ||||
| using Volo.Abp.Modularity; | ||||
| 
 | ||||
| namespace Sanhe.Abp.MenuManagement; | ||||
| 
 | ||||
| [DependsOn( | ||||
|     typeof(AbpDddDomainModule), | ||||
|     typeof(MenuManagementDomainSharedModule) | ||||
| )] | ||||
| public class MenuManagementDomainModule : AbpModule | ||||
| { | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,14 @@ | ||||
| using Volo.Abp.Collections; | ||||
| 
 | ||||
| namespace Sanhe.Abp.MenuManagement | ||||
| { | ||||
|     public class MenuManagementOptions | ||||
|     { | ||||
|         public ITypeList<IMenuManagementProvider> ManagementProviders { get; } | ||||
| 
 | ||||
|         public MenuManagementOptions() | ||||
|         { | ||||
|             ManagementProviders = new TypeList<IMenuManagementProvider>(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,81 @@ | ||||
| using System; | ||||
| using System.Threading.Tasks; | ||||
| using Volo.Abp.Authorization.Permissions; | ||||
| using Volo.Abp.Guids; | ||||
| using Volo.Abp.MultiTenancy; | ||||
| 
 | ||||
| namespace Sanhe.Abp.MenuManagement | ||||
| { | ||||
|     public abstract class MenuManagementProvider : IMenuManagementProvider | ||||
|     { | ||||
|         protected IMenuGrantRepository MenuGrantRepository { get; } | ||||
|         protected IPermissionDefinitionManager PermissionDefinitionManager { get; } | ||||
|         protected IMenuGrantChecker MenuGrantChecker { get; } | ||||
|         protected IGuidGenerator GuidGenerator { get; } | ||||
|         protected ICurrentTenant CurrentTenant { get; } | ||||
|         public abstract string Name { get; } | ||||
| 
 | ||||
|         protected MenuManagementProvider(IMenuGrantRepository menuGrantRepository, | ||||
|             IPermissionDefinitionManager permissionDefinitionManager, | ||||
|             IMenuGrantChecker menuGrantChecker, | ||||
|             IGuidGenerator guidGenerator, | ||||
|             ICurrentTenant currentTenant) | ||||
|         { | ||||
|             MenuGrantRepository = menuGrantRepository; | ||||
|             PermissionDefinitionManager = permissionDefinitionManager; | ||||
|             MenuGrantChecker = menuGrantChecker; | ||||
|             GuidGenerator = guidGenerator; | ||||
|             CurrentTenant = currentTenant; | ||||
|         } | ||||
| 
 | ||||
|         public virtual async Task<MenuGrantInfo> CheckAsync(Guid menuId, string providerName, string providerKey) | ||||
|         { | ||||
|             if (providerName != Name) | ||||
|             { | ||||
|                 return MenuGrantInfo.NonGranted; | ||||
|             } | ||||
| 
 | ||||
|             return new MenuGrantInfo( | ||||
|                 (await MenuGrantChecker.CheckAsync(menuId, providerName, providerKey)).IsGranted, | ||||
|                 providerKey | ||||
|             ); | ||||
|         } | ||||
| 
 | ||||
|         public virtual Task SetAsync(Guid menuId, string providerKey, bool isGranted) | ||||
|         { | ||||
|             return isGranted | ||||
|                 ? GrantAsync(menuId, providerKey) | ||||
|                 : RevokeAsync(menuId, providerKey); | ||||
|         } | ||||
| 
 | ||||
|         protected virtual async Task GrantAsync(Guid menuId, string providerKey) | ||||
|         { | ||||
|             var menuGrant = await MenuGrantRepository.FindAsync(menuId, Name, providerKey); | ||||
|             if (menuGrant != null) | ||||
|             { | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|             await MenuGrantRepository.InsertAsync( | ||||
|                 new MenuGrant( | ||||
|                     GuidGenerator.Create(), | ||||
|                     menuId, | ||||
|                     Name, | ||||
|                     providerKey, | ||||
|                     CurrentTenant.Id | ||||
|                 ) | ||||
|             ); | ||||
|         } | ||||
| 
 | ||||
|         protected virtual async Task RevokeAsync(Guid menuId, string providerKey) | ||||
|         { | ||||
|             var permissionGrant = await MenuGrantRepository.FindAsync(menuId, Name, providerKey); | ||||
|             if (permissionGrant == null) | ||||
|             { | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|             await MenuGrantRepository.DeleteAsync(permissionGrant); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,116 @@ | ||||
| using Microsoft.Extensions.DependencyInjection; | ||||
| using Microsoft.Extensions.Options; | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Collections.Immutable; | ||||
| using System.Linq; | ||||
| using System.Threading.Tasks; | ||||
| using Volo.Abp; | ||||
| using Volo.Abp.Authorization.Permissions; | ||||
| using Volo.Abp.DependencyInjection; | ||||
| using Volo.Abp.MultiTenancy; | ||||
| using Volo.Abp.PermissionManagement; | ||||
| 
 | ||||
| namespace Sanhe.Abp.MenuManagement | ||||
| { | ||||
|     public class MenuManager : IMenuManager, ITransientDependency | ||||
|     { | ||||
|         private readonly IPermissionDefinitionManager _permissionDefinitionManager; | ||||
|         private readonly IMenuGrantRepository _menuGrantRepository; | ||||
|         private readonly IPermissionManager _permissionManager; | ||||
|         private readonly ICurrentTenant _currentTenant; | ||||
|         private readonly IReadOnlyList<IMenuManagementProvider> _managementProviders; | ||||
|         private readonly MenuManagementOptions _options; | ||||
| 
 | ||||
|         public MenuManager( | ||||
|             IPermissionDefinitionManager permissionDefinitionManager, | ||||
|             IMenuGrantRepository menuGrantRepository, | ||||
|             IPermissionManager permissionManager, | ||||
|             IOptions<MenuManagementOptions> options, | ||||
|             IServiceProvider serviceProvider, | ||||
|             ICurrentTenant currentTenant) | ||||
|         { | ||||
|             _permissionDefinitionManager = permissionDefinitionManager; | ||||
|             _menuGrantRepository = menuGrantRepository; | ||||
|             _permissionManager = permissionManager; | ||||
|             _currentTenant = currentTenant; | ||||
|             _options = options.Value; | ||||
| 
 | ||||
|             _managementProviders = _options.ManagementProviders | ||||
|                 .Select(c => serviceProvider.GetRequiredService(c) as IMenuManagementProvider) | ||||
|                 .ToList(); | ||||
|         } | ||||
| 
 | ||||
|         public virtual IReadOnlyList<PermissionDefinition> GetPermissions(string providerName) | ||||
|         { | ||||
|             var multiTenancySide = _currentTenant.GetMultiTenancySide(); | ||||
| 
 | ||||
|             var permissions = _permissionDefinitionManager.GetPermissions(); | ||||
|             return permissions.Where(x => | ||||
|             { | ||||
|                 if (x.Providers.Any() && !x.Providers.Contains(providerName)) | ||||
|                 { | ||||
|                     return false; | ||||
|                 } | ||||
| 
 | ||||
|                 if (!x.MultiTenancySide.HasFlag(multiTenancySide)) | ||||
|                 { | ||||
|                     return false; | ||||
|                 } | ||||
| 
 | ||||
|                 return true; | ||||
|             }).ToImmutableList(); | ||||
|         } | ||||
| 
 | ||||
|         public virtual async Task<MenuWithGrantedProviders> GetAsync(Guid menuId, string providerName, string providerKey) | ||||
|         { | ||||
|             var result = new MenuWithGrantedProviders(menuId, false); | ||||
| 
 | ||||
|             foreach (var provider in _managementProviders) | ||||
|             { | ||||
|                 var providerResult = await provider.CheckAsync(menuId, providerName, providerKey); | ||||
|                 if (providerResult.IsGranted) | ||||
|                 { | ||||
|                     result.IsGranted = true; | ||||
|                     result.Providers.Add(new MenuProviderInfo(provider.Name, providerResult.ProviderKey)); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             return result; | ||||
|         } | ||||
| 
 | ||||
|         public virtual async Task SetAsync(Guid menuId, string providerName, string providerKey, bool isGranted) | ||||
|         { | ||||
|             var currentGrantInfo = await GetAsync(menuId, providerName, providerKey); | ||||
|             if (currentGrantInfo.IsGranted == isGranted) | ||||
|             { | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|             var provider = _managementProviders.FirstOrDefault(m => m.Name == providerName); | ||||
|             if (provider == null) | ||||
|             { | ||||
|                 throw new UserFriendlyException("Unknown menu management provider: " + providerName); | ||||
|             } | ||||
| 
 | ||||
|             await provider.SetAsync(menuId, providerKey, isGranted); | ||||
|         } | ||||
| 
 | ||||
|         public virtual async Task UpdatePermissionGrantAsync(Guid menuId, string oldPermission, string newPermission) | ||||
|         { | ||||
|             var menuGrants = await _menuGrantRepository.GetGrantByMenuIdAsync(menuId); | ||||
|             foreach (var g in menuGrants) | ||||
|             { | ||||
|                 if (!oldPermission.IsNullOrEmpty() && !newPermission.IsNullOrEmpty()) // 菜单原本有权限控制,修改为无权限控制:这种情况不清除授权,视为开放权限。 | ||||
|                 { | ||||
|                     await _permissionManager.SetAsync(oldPermission, g.ProviderName, g.ProviderKey, false); | ||||
|                 } | ||||
| 
 | ||||
|                 if (!newPermission.IsNullOrEmpty()) | ||||
|                 { | ||||
|                     await _permissionManager.SetAsync(newPermission, g.ProviderName, g.ProviderKey, true); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,26 @@ | ||||
| using JetBrains.Annotations; | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using Volo.Abp; | ||||
| 
 | ||||
| namespace Sanhe.Abp.MenuManagement | ||||
| { | ||||
|     public class MenuWithGrantedProviders | ||||
|     { | ||||
|         public Guid MenuId { get; } | ||||
| 
 | ||||
|         public bool IsGranted { get; set; } | ||||
| 
 | ||||
|         public List<MenuProviderInfo> Providers { get; set; } | ||||
| 
 | ||||
|         public MenuWithGrantedProviders([NotNull] Guid menuId, bool isGranted) | ||||
|         { | ||||
|             Check.NotNull(menuId, nameof(menuId)); | ||||
| 
 | ||||
|             MenuId = menuId; | ||||
|             IsGranted = isGranted; | ||||
| 
 | ||||
|             Providers = new List<MenuProviderInfo>(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,48 @@ | ||||
| using System; | ||||
| using System.Collections.ObjectModel; | ||||
| using System.Linq; | ||||
| using Volo.Abp.Domain.Entities.Auditing; | ||||
| using Volo.Abp.MultiTenancy; | ||||
| 
 | ||||
| namespace Sanhe.Abp.MenuManagement | ||||
| { | ||||
|     public class Permission : AuditedEntity<Guid>, IMultiTenant | ||||
|     { | ||||
|         public virtual Guid? TenantId { get; protected set; } | ||||
|         public virtual string Key { get; protected set; } | ||||
|         public virtual string Name { get; protected set; } | ||||
|         public virtual Guid GroupId { get; protected set; } | ||||
|         public virtual PermissionGroup Group { get; protected set; } | ||||
|         public virtual Guid? ParentId { get; set; } | ||||
|         public virtual Permission Parent { get; set; } | ||||
|         public virtual Collection<Permission> Children { get; set; } | ||||
| 
 | ||||
|         protected Permission() | ||||
|         { | ||||
|         } | ||||
| 
 | ||||
|         public Permission(Guid id, string key, string name, Guid groupId) | ||||
|         { | ||||
|             Id = id; | ||||
|             Key = key; | ||||
|             Name = name; | ||||
|             GroupId = groupId; | ||||
|             Children = new Collection<Permission>(); | ||||
|         } | ||||
| 
 | ||||
|         public virtual Permission AddChildren(Guid id, string key, string name) | ||||
|         { | ||||
|             if (Children.Any(x => x.Key == key)) | ||||
|             { | ||||
|                 throw new Volo.Abp.UserFriendlyException("权限名重复"); | ||||
|             } | ||||
| 
 | ||||
|             var page = new Permission(id, key, name, GroupId); | ||||
|             page.Parent = this; | ||||
|             page.ParentId = Id; | ||||
|             Children.Add(page); | ||||
| 
 | ||||
|             return page; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,63 @@ | ||||
| using System; | ||||
| using System.Collections.ObjectModel; | ||||
| using System.Linq; | ||||
| using Volo.Abp.Domain.Entities.Auditing; | ||||
| using Volo.Abp.MultiTenancy; | ||||
| 
 | ||||
| namespace Sanhe.Abp.MenuManagement | ||||
| { | ||||
|     public class PermissionGroup : AuditedEntity<Guid>, IMultiTenant | ||||
|     { | ||||
|         public virtual Guid? TenantId { get; protected set; } | ||||
|         public virtual string Key { get; protected set; } | ||||
|         public virtual string Name { get; protected set; } | ||||
|         public virtual Guid? ParentId { get; set; } | ||||
|         public virtual PermissionGroup Parent { get; set; } | ||||
|         public virtual Collection<PermissionGroup> Children { get; set; } | ||||
|         public virtual Collection<Permission> Permissions { get; set; } | ||||
| 
 | ||||
|         protected PermissionGroup() | ||||
|         { | ||||
|         } | ||||
| 
 | ||||
|         public PermissionGroup(Guid id, string key, string name, Guid? tenantId) | ||||
|         { | ||||
|             Id = id; | ||||
|             Key = key; | ||||
|             Name = name; | ||||
|             TenantId = tenantId; | ||||
| 
 | ||||
|             Children = new Collection<PermissionGroup>(); | ||||
|             Permissions = new Collection<Permission>(); | ||||
|         } | ||||
| 
 | ||||
|         public virtual Permission AddPermission(Guid id, string key, string name) | ||||
|         { | ||||
|             if (Permissions.Any(x => x.Key == key)) | ||||
|             { | ||||
|                 throw new Volo.Abp.UserFriendlyException("权限名重复"); | ||||
|             } | ||||
| 
 | ||||
|             var permission = new Permission(id, key, name, Id); | ||||
| 
 | ||||
|             Permissions.Add(permission); | ||||
| 
 | ||||
|             return permission; | ||||
|         } | ||||
| 
 | ||||
|         public virtual PermissionGroup AddChildren(Guid id, string key, string name, Guid? tenantId = null) | ||||
|         { | ||||
|             if (Children.Any(x => x.Key == key)) | ||||
|             { | ||||
|                 throw new Volo.Abp.UserFriendlyException("分组名重复"); | ||||
|             } | ||||
| 
 | ||||
|             var group = new PermissionGroup(id, key, name, tenantId); | ||||
|             group.Parent = this; | ||||
|             group.ParentId = Id; | ||||
|             Children.Add(group); | ||||
| 
 | ||||
|             return group; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,13 @@ | ||||
| using Volo.Abp.Settings; | ||||
| 
 | ||||
| namespace Sanhe.Abp.MenuManagement.Settings; | ||||
| 
 | ||||
| public class MenuManagementSettingDefinitionProvider : SettingDefinitionProvider | ||||
| { | ||||
|     public override void Define(ISettingDefinitionContext context) | ||||
|     { | ||||
|         /* Define module settings here. | ||||
|          * Use names from MenuManagementSettings class. | ||||
|          */ | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,10 @@ | ||||
| namespace Sanhe.Abp.MenuManagement.Settings; | ||||
| 
 | ||||
| public static class MenuManagementSettings | ||||
| { | ||||
|     public const string GroupName = "MenuManagement"; | ||||
| 
 | ||||
|     /* Add constants for setting names. Example: | ||||
|      * public const string MySettingName = GroupName + ".MySettingName"; | ||||
|      */ | ||||
| } | ||||
| @ -0,0 +1,3 @@ | ||||
| <Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> | ||||
|   <ConfigureAwait ContinueOnCapturedContext="false" /> | ||||
| </Weavers> | ||||
| @ -0,0 +1,20 @@ | ||||
| <Project Sdk="Microsoft.NET.Sdk"> | ||||
| 
 | ||||
| 	<Import Project="..\..\..\configureawait.props" /> | ||||
| 	<Import Project="..\..\..\common.props" /> | ||||
| 
 | ||||
| 	<PropertyGroup> | ||||
| 		<TargetFramework>net6.0</TargetFramework> | ||||
| 		<RootNamespace /> | ||||
| 	</PropertyGroup> | ||||
| 
 | ||||
| 	<ItemGroup> | ||||
| 		<PackageReference Include="Volo.Abp.EntityFrameworkCore" Version="$(VoloAbpVersion)" /> | ||||
| 		<PackageReference Include="Volo.Abp.PermissionManagement.EntityFrameworkCore" Version="$(VoloAbpVersion)" /> | ||||
| 	</ItemGroup> | ||||
| 
 | ||||
| 	<ItemGroup> | ||||
| 	  <ProjectReference Include="..\Sanhe.Abp.MenuManagement.Domain\Sanhe.Abp.MenuManagement.Domain.csproj" /> | ||||
| 	</ItemGroup> | ||||
| 
 | ||||
| </Project> | ||||
| @ -0,0 +1,12 @@ | ||||
| using Microsoft.EntityFrameworkCore; | ||||
| using Volo.Abp.Data; | ||||
| using Volo.Abp.EntityFrameworkCore; | ||||
| 
 | ||||
| namespace Sanhe.Abp.MenuManagement.EntityFrameworkCore; | ||||
| 
 | ||||
| [ConnectionStringName(MenuManagementDbProperties.ConnectionStringName)] | ||||
| public interface IMenuManagementDbContext : IEfCoreDbContext | ||||
| { | ||||
|     DbSet<Menu> Menus { get; set; } | ||||
|     DbSet<MenuGrant> MenuGrants { get; set; } | ||||
| } | ||||
| @ -0,0 +1,25 @@ | ||||
| using Microsoft.EntityFrameworkCore; | ||||
| using Volo.Abp.Data; | ||||
| using Volo.Abp.EntityFrameworkCore; | ||||
| 
 | ||||
| namespace Sanhe.Abp.MenuManagement.EntityFrameworkCore; | ||||
| 
 | ||||
| [ConnectionStringName(MenuManagementDbProperties.ConnectionStringName)] | ||||
| public class MenuManagementDbContext : AbpDbContext<MenuManagementDbContext>, IMenuManagementDbContext | ||||
| { | ||||
|     public DbSet<Menu> Menus { get; set; } | ||||
|     public DbSet<MenuGrant> MenuGrants { get; set; } | ||||
| 
 | ||||
|     public MenuManagementDbContext(DbContextOptions<MenuManagementDbContext> options) | ||||
|         : base(options) | ||||
|     { | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     protected override void OnModelCreating(ModelBuilder builder) | ||||
|     { | ||||
|         base.OnModelCreating(builder); | ||||
| 
 | ||||
|         builder.ConfigureMenuManagement(); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,43 @@ | ||||
| using Microsoft.EntityFrameworkCore; | ||||
| using Volo.Abp; | ||||
| using Volo.Abp.EntityFrameworkCore.Modeling; | ||||
| 
 | ||||
| namespace Sanhe.Abp.MenuManagement.EntityFrameworkCore; | ||||
| 
 | ||||
| public static class MenuManagementDbContextModelCreatingExtensions | ||||
| { | ||||
|     public static void ConfigureMenuManagement( | ||||
|         this ModelBuilder builder) | ||||
|     { | ||||
|         Check.NotNull(builder, nameof(builder)); | ||||
| 
 | ||||
|         builder.Entity<Menu>(b => | ||||
|         { | ||||
|             b.ToTable(MenuManagementDbProperties.DbTablePrefix + "Menus", MenuManagementDbProperties.DbSchema); | ||||
|             b.ConfigureByConvention(); | ||||
| 
 | ||||
|             b.Property(x => x.Name).HasMaxLength(50); | ||||
|             b.Property(x => x.DisplayName).HasMaxLength(50); | ||||
|             b.Property(x => x.ComponentPath).HasMaxLength(100); | ||||
|             b.Property(x => x.RouterPath).HasMaxLength(100); | ||||
|             b.Property(x => x.Icon).HasMaxLength(50); | ||||
|             b.Property(x => x.Sort).HasMaxLength(50); | ||||
|             b.Property(x => x.TargetUrl).HasMaxLength(500); | ||||
|             b.Property(x => x.PermissionKey).HasMaxLength(100); | ||||
| 
 | ||||
|             b.HasIndex(x => new { x.PermissionKey }); | ||||
|         }); | ||||
| 
 | ||||
|         builder.Entity<MenuGrant>(b => | ||||
|         { | ||||
|             b.ToTable(MenuManagementDbProperties.DbTablePrefix + "MenuGrants", MenuManagementDbProperties.DbSchema); | ||||
|             b.ConfigureByConvention(); | ||||
| 
 | ||||
|             b.Property(x => x.MenuId).IsRequired(); | ||||
|             b.Property(x => x.ProviderName).HasMaxLength(MenuGrantConsts.MaxProviderNameLength).IsRequired(); | ||||
|             b.Property(x => x.ProviderKey).HasMaxLength(MenuGrantConsts.MaxProviderKeyLength).IsRequired(); | ||||
| 
 | ||||
|             b.HasIndex(x => new { x.MenuId, x.ProviderName, x.ProviderKey }); | ||||
|         }); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,22 @@ | ||||
| using Microsoft.Extensions.DependencyInjection; | ||||
| using Volo.Abp.EntityFrameworkCore; | ||||
| using Volo.Abp.Modularity; | ||||
| using Volo.Abp.PermissionManagement.EntityFrameworkCore; | ||||
| 
 | ||||
| namespace Sanhe.Abp.MenuManagement.EntityFrameworkCore; | ||||
| 
 | ||||
| [DependsOn( | ||||
|     typeof(AbpPermissionManagementEntityFrameworkCoreModule), | ||||
|     typeof(MenuManagementDomainModule), | ||||
|     typeof(AbpEntityFrameworkCoreModule) | ||||
| )] | ||||
| public class MenuManagementEntityFrameworkCoreModule : AbpModule | ||||
| { | ||||
|     public override void ConfigureServices(ServiceConfigurationContext context) | ||||
|     { | ||||
|         context.Services.AddAbpDbContext<MenuManagementDbContext>(options => | ||||
|         { | ||||
|             options.AddRepository<Menu, MenuRepository>(); | ||||
|         }); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,66 @@ | ||||
| using Microsoft.EntityFrameworkCore; | ||||
| using Sanhe.Abp.MenuManagement.EntityFrameworkCore; | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
| using Volo.Abp.Domain.Repositories.EntityFrameworkCore; | ||||
| using Volo.Abp.EntityFrameworkCore; | ||||
| 
 | ||||
| namespace Sanhe.Abp.MenuManagement | ||||
| { | ||||
|     public class MenuGrantRepository : EfCoreRepository<IMenuManagementDbContext, MenuGrant, Guid>, IMenuGrantRepository | ||||
|     { | ||||
|         public MenuGrantRepository(IDbContextProvider<IMenuManagementDbContext> dbContextProvider) : base(dbContextProvider) | ||||
|         { | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
|         public virtual async Task<MenuGrant> FindAsync(Guid menuId, string providerName, string providerKey, | ||||
|             CancellationToken cancellationToken = default) | ||||
|         { | ||||
|             return await (await GetDbSetAsync()) | ||||
|                 .FirstOrDefaultAsync(s => | ||||
|                         s.MenuId == menuId && | ||||
|                         s.ProviderName == providerName && | ||||
|                         s.ProviderKey == providerKey, | ||||
|                     GetCancellationToken(cancellationToken) | ||||
|                 ); | ||||
|         } | ||||
| 
 | ||||
|         public virtual async Task<List<MenuGrant>> GetListAsync(string providerName, string providerKey, | ||||
|             CancellationToken cancellationToken = default) | ||||
|         { | ||||
|             return await (await GetDbSetAsync()) | ||||
|                 .Where(x => x.ProviderKey == providerKey && x.ProviderName == providerName) | ||||
|                 .ToListAsync(GetCancellationToken(cancellationToken)); | ||||
| 
 | ||||
|             //return await ( | ||||
|             //    from g in DbSet | ||||
|             //    join m in DbContext.Set<Menu>() on g.MenuId equals m.Id | ||||
|             //    where g.ProviderKey == providerKey && g.ProviderName == providerName | ||||
|             //    select new MenuGrantInfoDto | ||||
|             //    { | ||||
|             //        Id = g.MenuId, | ||||
|             //        Name = m.Name, | ||||
|             //        DisplayName = m.DisplayName, | ||||
|             //        PermissionKey = m.PermissionKey, | ||||
|             //        IsGranted = true | ||||
|             //    } | ||||
|             //).ToListAsync(GetCancellationToken(cancellationToken)); | ||||
|         } | ||||
| 
 | ||||
|         public virtual async Task<List<MenuGrant>> GetGrantByMenuIdAsync(Guid menuId, bool noTracking = true) | ||||
|         { | ||||
|             var query = (await GetDbSetAsync()).Where(x => x.MenuId == menuId); | ||||
| 
 | ||||
|             if (noTracking) | ||||
|             { | ||||
|                 query = query.AsNoTracking(); | ||||
|             } | ||||
| 
 | ||||
|             return await query.ToListAsync(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,25 @@ | ||||
| using Microsoft.EntityFrameworkCore; | ||||
| using Sanhe.Abp.MenuManagement.EntityFrameworkCore; | ||||
| using System; | ||||
| using System.Linq; | ||||
| using System.Threading.Tasks; | ||||
| using Volo.Abp.Domain.Repositories.EntityFrameworkCore; | ||||
| using Volo.Abp.EntityFrameworkCore; | ||||
| 
 | ||||
| namespace Sanhe.Abp.MenuManagement | ||||
| { | ||||
|     public class MenuRepository : EfCoreRepository<IMenuManagementDbContext, Menu, Guid>, IMenuRepository | ||||
|     { | ||||
|         public MenuRepository(IDbContextProvider<IMenuManagementDbContext> dbContextProvider) : base(dbContextProvider) | ||||
|         { | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
|         public override async Task<IQueryable<Menu>> WithDetailsAsync() | ||||
|         { | ||||
|             var queryable = await GetQueryableAsync(); | ||||
| 
 | ||||
|             return queryable.Include(x => x.Parent); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,3 @@ | ||||
| <Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> | ||||
|   <ConfigureAwait ContinueOnCapturedContext="false" /> | ||||
| </Weavers> | ||||
| @ -0,0 +1,16 @@ | ||||
| <Project Sdk="Microsoft.NET.Sdk"> | ||||
| 
 | ||||
| 	<Import Project="..\..\..\configureawait.props" /> | ||||
| 	<Import Project="..\..\..\common.props" /> | ||||
| 
 | ||||
| 	<PropertyGroup> | ||||
| 		<TargetFramework>net6.0</TargetFramework> | ||||
| 		<RootNamespace /> | ||||
| 	</PropertyGroup> | ||||
| 	 | ||||
| 	<ItemGroup> | ||||
| 		<PackageReference Include="Volo.Abp.AspNetCore.Mvc" Version="$(VoloAbpVersion)" /> | ||||
| 		<PackageReference Include="Volo.Abp.PermissionManagement.HttpApi" Version="$(VoloAbpVersion)" /> | ||||
| 		<ProjectReference Include="..\Sanhe.Abp.MenuManagement.Application.Contracts\Sanhe.Abp.MenuManagement.Application.Contracts.csproj" /> | ||||
| 	</ItemGroup> | ||||
| </Project> | ||||
| @ -0,0 +1,57 @@ | ||||
| using Microsoft.AspNetCore.Mvc; | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Threading.Tasks; | ||||
| using Volo.Abp; | ||||
| using Volo.Abp.Application.Dtos; | ||||
| 
 | ||||
| namespace Sanhe.Abp.MenuManagement | ||||
| { | ||||
|     [RemoteService] | ||||
|     [Route("api/menu-management/menus")] | ||||
|     public class MenuController : MenuManagementController, IMenuAppService | ||||
|     { | ||||
|         private readonly IMenuAppService _menuAppService; | ||||
| 
 | ||||
|         public MenuController(IMenuAppService menuAppService) | ||||
|         { | ||||
|             _menuAppService = menuAppService; | ||||
|         } | ||||
| 
 | ||||
|         [HttpGet("{id}")] | ||||
|         public virtual Task<MenuDto> GetAsync(Guid id) | ||||
|         { | ||||
|             return _menuAppService.GetAsync(id); | ||||
|         } | ||||
| 
 | ||||
|         [HttpGet] | ||||
|         public virtual Task<PagedResultDto<MenuDto>> GetListAsync(MenuRequestDto input) | ||||
|         { | ||||
|             return _menuAppService.GetListAsync(input); | ||||
|         } | ||||
| 
 | ||||
|         [HttpPost] | ||||
|         public virtual Task<MenuDto> CreateAsync(CreateOrUpdateMenuDto input) | ||||
|         { | ||||
|             return _menuAppService.CreateAsync(input); | ||||
|         } | ||||
| 
 | ||||
|         [HttpPut("{id}")] | ||||
|         public virtual Task<MenuDto> UpdateAsync(Guid id, CreateOrUpdateMenuDto input) | ||||
|         { | ||||
|             return _menuAppService.UpdateAsync(id, input); | ||||
|         } | ||||
| 
 | ||||
|         [HttpDelete("{id}")] | ||||
|         public virtual Task DeleteAsync(Guid id) | ||||
|         { | ||||
|             return _menuAppService.DeleteAsync(id); | ||||
|         } | ||||
| 
 | ||||
|         [HttpGet("auth-policies")] | ||||
|         public virtual Task<List<AuthPolicyDto>> GetAuthPolicies() | ||||
|         { | ||||
|             return _menuAppService.GetAuthPolicies(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,39 @@ | ||||
| using Microsoft.AspNetCore.Mvc; | ||||
| using System.Threading.Tasks; | ||||
| using Volo.Abp; | ||||
| 
 | ||||
| namespace Sanhe.Abp.MenuManagement | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 菜单分配 | ||||
|     /// </summary> | ||||
|     [RemoteService] | ||||
|     [Route("api/menu-management/menu-grant")] | ||||
|     public class MenuGrantController : MenuManagementController, IMenuGrantAppService | ||||
|     { | ||||
|         private readonly IMenuGrantAppService _menuGrantAppService; | ||||
| 
 | ||||
|         public MenuGrantController(IMenuGrantAppService menuGrantAppService) | ||||
|         { | ||||
|             _menuGrantAppService = menuGrantAppService; | ||||
|         } | ||||
| 
 | ||||
|         [HttpGet("list")] | ||||
|         public virtual Task<GetMenuResultDto> GetListAsync() | ||||
|         { | ||||
|             return _menuGrantAppService.GetListAsync(); | ||||
|         } | ||||
| 
 | ||||
|         [HttpGet] | ||||
|         public virtual Task<GetMenuGrantListResultDto> GetAsync(string providerName, string providerKey) | ||||
|         { | ||||
|             return _menuGrantAppService.GetAsync(providerName, providerKey); | ||||
|         } | ||||
| 
 | ||||
|         [HttpPut] | ||||
|         public virtual Task UpdateAsync(string providerName, string providerKey, UpdateMenuGrantsDto input) | ||||
|         { | ||||
|             return _menuGrantAppService.UpdateAsync(providerName, providerKey, input); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,12 @@ | ||||
| using Sanhe.Abp.MenuManagement.Localization; | ||||
| using Volo.Abp.AspNetCore.Mvc; | ||||
| 
 | ||||
| namespace Sanhe.Abp.MenuManagement; | ||||
| 
 | ||||
| public abstract class MenuManagementController : AbpControllerBase | ||||
| { | ||||
|     protected MenuManagementController() | ||||
|     { | ||||
|         LocalizationResource = typeof(MenuManagementResource); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,34 @@ | ||||
| using Localization.Resources.AbpUi; | ||||
| using Microsoft.Extensions.DependencyInjection; | ||||
| using Sanhe.Abp.MenuManagement.Localization; | ||||
| using Volo.Abp.AspNetCore.Mvc; | ||||
| using Volo.Abp.Localization; | ||||
| using Volo.Abp.Modularity; | ||||
| using Volo.Abp.PermissionManagement.HttpApi; | ||||
| 
 | ||||
| namespace Sanhe.Abp.MenuManagement; | ||||
| 
 | ||||
| [DependsOn( | ||||
|     typeof(AbpPermissionManagementHttpApiModule), | ||||
|     typeof(MenuManagementApplicationContractsModule), | ||||
|     typeof(AbpAspNetCoreMvcModule))] | ||||
| public class MenuManagementHttpApiModule : AbpModule | ||||
| { | ||||
|     public override void PreConfigureServices(ServiceConfigurationContext context) | ||||
|     { | ||||
|         PreConfigure<IMvcBuilder>(mvcBuilder => | ||||
|         { | ||||
|             mvcBuilder.AddApplicationPartIfNotExists(typeof(MenuManagementHttpApiModule).Assembly); | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     public override void ConfigureServices(ServiceConfigurationContext context) | ||||
|     { | ||||
|         Configure<AbpLocalizationOptions>(options => | ||||
|         { | ||||
|             options.Resources | ||||
|                 .Get<MenuManagementResource>() | ||||
|                 .AddBaseTypes(typeof(AbpUiResource)); | ||||
|         }); | ||||
|     } | ||||
| } | ||||
					Loading…
					
					
				
		Reference in new issue