Almost all enterprise applications use authorization in some level. Authorization is used to check if a user is allowed to perform some specific operation in the application. ASP.NET Boilerplate defines a permission based infrastructure to implement authorization.
Authorization system uses IPermissionChecker to check permissions. While you can implement it in your own way, it's fully implemented in module-zero project. If it's not implemented, NullPermissionChecker is used which grants all permissions to everyone.
A unique permission is defined for each operation needed to be authorized. We should define a permission before use it. ASP.NET Boilerplate is designed to be modular. So, different modules can have different permissions. A module should create a class derived from AuthorizationProvider in order to define it's permissions. An example authorization provider is shown below:
public class MyAuthorizationProvider : AuthorizationProvider
{
public override void SetPermissions(IPermissionDefinitionContext context)
{
var administration = context.CreatePermission("Administration");
var userManagement = administration.CreateChildPermission("Administration.UserManagement");
userManagement.CreateChildPermission("Administration.UserManagement.CreateUser");
var roleManagement = administration.CreateChildPermission("Administration.RoleManagement");
}
}
IPermissionDefinitionContext has methods to get and create permissions.
A permission have some properties to define it:
A permission can have a parent and child permissions. While this does not effect permission checking, it may help to group permissions in UI.
After creating an authorization provider, we should register it in PreInitialize method of our module:
Configuration.Authorization.Providers.Add<MyAuthorizationProvider>();
Authorization providers are registered to dependency injection automatically. So, an authorization provider can inject any dependency (like a repository) to build permission definitions using some other sources.
AbpAuthorize (AbpMvcAuthorize for MVC Controllers and AbpApiAuthorize for Web API Controllers) attribute is the easiest and most common way of checking permissions. Consider the application service method shown below:
[AbpAuthorize("Administration.UserManagement.CreateUser")]
public void CreateUser(CreateUserInput input)
{
//A user can not execute this method if he is not granted for "Administration.UserManagement.CreateUser" permission.
}
CreateUser method can not be called by a user who is not granted for permission "Administration.UserManagement.CreateUser".
AbpAuthorize attribute also checks if current user is logged in (using IAbpSession.UserId). So, if we declare an AbpAuthorize for a method, it at least checks for login:
[AbpAuthorize]
public void SomeMethod(SomeMethodInput input)
{
//A user can not execute this method if he did not login.
}
ASP.NET Boilerplate uses power of dynamic method interception for authorization. So, there is some restrictions for the methods use AbpAuthorize attribute.
Also,
Notice ; There are three AbpAuthorize attributes: In an application service (application layer), we use Abp.Authorization.AbpAuthorize class. In an MVC controller (web layer), we use Abp.Web.Mvc.Authorization.AbpMvcAuthorize class and in ASP.NET Web API, we use Abp.WebApi.Authorization.AbpApiAuthorize attribute. This difference comes from inheritance. In MVC side, it's derived from MVC's own Authorize class. In Web API side it's derived from Web API's Authorize class. Thus, it's better integrated to MVC and Web API. But, in application layer it's completely ASP.NET Boilerplate's implementation and does not extend any class.
While AbpAuthorize attribute pretty enough for most cases, there must be situations we should check for a permission in a method body. We can inject and use IPermissionChecker for that as shown in the example below:
public void CreateUser(CreateOrUpdateUserInput input)
{
if (!PermissionChecker.IsGranted("Administration.UserManagement.CreateUser"))
{
throw new AbpAuthorizationException("You are not authorized to create user!");
}
//A user can not reach this point if he is not granted for "Administration.UserManagement.CreateUser" permission.
}
Surely, you can code any logic since IsGranted simply returns true or false (It has Async version also). If you simply check a permission and throw an exception as shown above, you can use the Authorize method:
public void CreateUser(CreateOrUpdateUserInput input)
{
PermissionChecker.Authorize("Administration.UserManagement.CreateUser");
//A user can not reach this point if he is not granted for "Administration.UserManagement.CreateUser" permission.
}
Since authorization generally implemented in application layer, ApplicationService base class injects and defines PermissionChecker property. Thus, permission checker can be used without injecting in application service classes.
Base view class defines IsGranted method to check if current user has a permission. Thus, we can conditionally render the view. Example:
@if (IsGranted("Administration.UserManagement.CreateUser"))
{
<button id="CreateNewUserButton" class="btn btn-primary"><i class="fa fa-plus"></i> @L("CreateNewUser")</button>
}
In the client side, we can use API defined in abp.auth namespace. In most case, we need to check if current user has a specific permission (with permission name). Example:
abp.auth.isGranted('Administration.UserManagement.CreateUser');
You can also use abp.auth.grantedPermissions to get all granted permissions or abp.auth.allPermissions to get all available permission names in the application. Check abp.auth namespace on runtime for others.
We may need to definitions of permission. IPermissionManager can be injected and used in that case.