ONE MORE CORRECTION, SORRY! This time I tested it ;)
using Abp.Authorization;
using Abp.Localization;
using Abp.MultiTenancy;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace MyApp.Authorization
{
public class MyAppAuthorizationProvider : AuthorizationProvider
{
public override void SetPermissions(IPermissionDefinitionContext context)
{
List<PermissionInfo> permissionInfos = new List<PermissionInfo>();
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
if (!assembly.IsDynamic)
{
foreach (TypeInfo typeInfo in assembly.GetExportedTypes())
{
// Attributes on Fields (Constants)
foreach (FieldInfo fieldInfo in typeInfo.GetFields())
{
var permissionAttribute = fieldInfo.GetCustomAttribute<PermissionAttribute>(false);
if (permissionAttribute != null)
{
permissionInfos.Add(
new PermissionInfo()
{
Name = (string)fieldInfo.GetRawConstantValue(),
DisplayName = permissionAttribute.DisplayName,
IsGrantedByDefault = permissionAttribute.IsGrantedByDefault,
MultiTenancySides = permissionAttribute.MultiTenancySides
});
}
}
}
}
}
var parentNameBuilder = new StringBuilder();
foreach (PermissionInfo permissionInfo in permissionInfos.OrderBy(p => p.Name))
{
if (!permissionInfo.Name.Contains("."))
{
// Create top level permission
var newPermission = context.CreatePermission(
name: permissionInfo.Name,
displayName: F(permissionInfo.DisplayName),
isGrantedByDefault: permissionInfo.IsGrantedByDefault,
description: F(permissionInfo.Description),
multiTenancySides: permissionInfo.MultiTenancySides);
}
else
{
// Create child permission under appropriate parent
var nameParts = permissionInfo.Name.Split('.');
parentNameBuilder.Clear();
parentNameBuilder.Append(nameParts[0]);
Permission parentPermission = context.GetPermissionOrNull(parentNameBuilder.ToString());
for (int i = 1; i < nameParts.Length - 1; i++)
{
parentNameBuilder.Append(".");
parentNameBuilder.Append(nameParts[i]);
parentPermission = parentPermission.Children.Where(p => string.Compare(p.Name, parentNameBuilder.ToString(), true) == 0).FirstOrDefault();
}
if (parentPermission == null)
{
throw new Exception("Parent permission not defined: '" + parentNameBuilder.ToString() + "'");
}
var newPermission = parentPermission.CreateChildPermission(
name: permissionInfo.Name,
displayName: F(permissionInfo.DisplayName),
isGrantedByDefault: permissionInfo.IsGrantedByDefault,
description: F(permissionInfo.Description),
multiTenancySides: permissionInfo.MultiTenancySides);
}
}
}
public class PermissionInfo
{
public string Name;
public string DisplayName;
public string Description;
public MultiTenancySides MultiTenancySides;
public bool IsGrantedByDefault;
}
/// <summary>
/// Creates a fixed localizable string (avoids using localization - just pass it the text directly)
/// </summary>
private static ILocalizableString F(string value)
{
return new FixedLocalizableString(value);
}
private static ILocalizableString L(string name)
{
return new LocalizableString(name, MyAppConstants.LocalizationSourceName);
}
}
}
You can just comment out the model builder stuff that references constants in my other projects.
This assumes you have configured your projects' build output folder to "bin" instead of "bin\debug" and "bin\release". The reason for that is you don't want it referencing a debug dll if you are building in release to deploy to Azure or something. So I recommend you change your build output paths to "bin".
But actually it works just fine if you don't, you just need to change the paths to the DLLs at the top.
I wrote:
// Note: The Name (not display name) of the property is defined by the name of the constant the attribute is applied to.
But it should actually say this:
// Note: The Name (not display name) of the property is defined by the value of the constant the attribute is applied to.
Here you go. Adapt this to your needs:
Core project - MyAppAuthorizationProvider.cs:
using Abp.Authorization;
using Abp.Localization;
using Abp.MultiTenancy;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace MyApp.Authorization
{
public class MyAppAuthorizationProvider : AuthorizationProvider
{
public override void SetPermissions(IPermissionDefinitionContext context)
{
List<PermissionInfo> permissionInfos = new List<PermissionInfo>();
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
if (!assembly.IsDynamic)
{
foreach (TypeInfo typeInfo in assembly.GetExportedTypes())
{
// Attributes on Fields (Constants)
foreach (FieldInfo fieldInfo in typeInfo.GetFields())
{
var permissionAttribute = fieldInfo.GetCustomAttribute<PermissionAttribute>(false);
if (permissionAttribute != null)
{
permissionInfos.Add(
new PermissionInfo()
{
Name = (string)fieldInfo.GetRawConstantValue(),
DisplayName = permissionAttribute.DisplayName,
IsGrantedByDefault = permissionAttribute.IsGrantedByDefault,
MultiTenancySides = permissionAttribute.MultiTenancySides
});
}
}
}
}
}
foreach (PermissionInfo permissionInfo in permissionInfos.OrderBy(p => p.Name))
{
Permission parentPermission = null;
Permission newPermission = null;
if (permissionInfo.Name.Contains("."))
{
string parentName = permissionInfo.Name.Substring(0, permissionInfo.Name.LastIndexOf('.'));
parentPermission = context.GetPermissionOrNull(parentName);
if (parentPermission == null)
{
throw new Exception("Permission not defined: '" + parentName + "'");
}
newPermission = parentPermission.CreateChildPermission(
name: permissionInfo.Name,
displayName: F(permissionInfo.DisplayName),
isGrantedByDefault: permissionInfo.IsGrantedByDefault,
description: F(permissionInfo.Description),
multiTenancySides: permissionInfo.MultiTenancySides);
}
else
{
newPermission = context.CreatePermission(
name: permissionInfo.Name,
displayName: F(permissionInfo.DisplayName),
isGrantedByDefault: permissionInfo.IsGrantedByDefault,
description: F(permissionInfo.Description),
multiTenancySides: permissionInfo.MultiTenancySides);
}
}
}
public class PermissionInfo
{
public string Name;
public string DisplayName;
public string Description;
public MultiTenancySides MultiTenancySides;
public bool IsGrantedByDefault;
}
/// <summary>
/// Creates a fixed localizable string (avoids using localization - just pass it the text directly)
/// </summary>
private static ILocalizableString F(string value)
{
return new FixedLocalizableString(value);
}
private static ILocalizableString L(string name)
{
return new LocalizableString(name, MyAppConstants.LocalizationSourceName);
}
}
}
Core project - PermissionAttribute.cs
using Abp.MultiTenancy;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MyApp.Authorization
{
public partial class PermissionAttribute : Attribute
{
// Note: The Name (not display name) of the property is defined by the name of the constant the attribute is applied to.
public string DisplayName { get; set; }
public string Description { get; set; }
private MultiTenancySides _multiTenancySides = MultiTenancySides.Host | MultiTenancySides.Tenant;
public MultiTenancySides MultiTenancySides
{
get { return _multiTenancySides; }
set { _multiTenancySides = value; }
}
public bool IsGrantedByDefault { get; set; }
}
}
Then, ANYWHERE in your solution, in ANY PROJECT, you can define classes like this:
using MyApp.Authorization;
using System.ComponentModel;
namespace MyApp.Authorization
{
public static partial class PermissionNames
{
[Permission(DisplayName = "Administration")] // MultiTenancySides defaults to Host & Tenant if not specified. This way, the host AND customers (tenants) can have admin sections.
public const string Administration = "Administration";
[Permission(DisplayName = "Customer Management", MultiTenancySides = Abp.MultiTenancy.MultiTenancySides.Host)]
public const string CustomerManagement = "Administration.CustomerManagement"; // Permissions will automatically be arranged into the proper hierarchy (parent/child) based on the Parent.Child.Grandchild dot-based naming pattern.
[Permission(DisplayName = "Customer Portal", MultiTenancySides = Abp.MultiTenancy.MultiTenancySides.Tenant)]
public const string CustomerPortal = "CustomerPortal"; // Only customers (tenants) can be assigned this permission
}
}
And the core auth code will find the attributes on the constants and automagically create your permissions with the correct hierarchical structure