Browse Source

Add resource-based permissions for BookStore and update authorization checks

pull/24679/head
maliming 8 months ago
parent
commit
e3f1bc4b47
No known key found for this signature in database GPG Key ID: A646B9CB645ECEA4
  1. 185
      docs/en/framework/fundamentals/authorization/resource-based-authorization.md

185
docs/en/framework/fundamentals/authorization/resource-based-authorization.md

@ -41,6 +41,29 @@ Implementing resource-based authorization involves three main steps:
Define resource permissions in your `PermissionDefinitionProvider` class using the `AddResourcePermission` method: Define resource permissions in your `PermissionDefinitionProvider` class using the `AddResourcePermission` method:
```csharp
namespace Acme.BookStore.Permissions;
public static class BookStorePermissions
{
public const string GroupName = "BookStore";
public static class Books
{
public const string Default = GroupName + ".Books";
public const string ManagePermissions = Default + ".ManagePermissions";
public static class Resources
{
public const string Name = "Acme.BookStore.Books.Book";
public const string View = Name + ".View";
public const string Edit = Name + ".Edit";
public const string Delete = Name + ".Delete";
}
}
}
```
```csharp ```csharp
using Volo.Abp.Authorization.Permissions; using Volo.Abp.Authorization.Permissions;
using Volo.Abp.Localization; using Volo.Abp.Localization;
@ -54,49 +77,54 @@ namespace Acme.BookStore.Permissions
var myGroup = context.AddGroup("BookStore"); var myGroup = context.AddGroup("BookStore");
// Standard permissions // Standard permissions
myGroup.AddPermission("BookStore_Document_Create"); myGroup.AddPermission(BookStorePermissions.Books.Default, L("Permission:Books"));
// Permission to manage resource permissions (required) // Permission to manage resource permissions (required)
myGroup.AddPermission("BookStore_Document_ManagePermissions"); myGroup.AddPermission(BookStorePermissions.Books.ManagePermissions, L("Permission:Books:ManagePermissions"));
// Resource-based permissions // Resource-based permissions
context.AddResourcePermission( context.AddResourcePermission(
name: "BookStore_Document_View", name: BookStorePermissions.Books.Resources.View,
resourceName: "BookStore.Document", resourceName: BookStorePermissions.Books.Resources.Name,
managementPermissionName: "BookStore_Document_ManagePermissions", managementPermissionName: BookStorePermissions.Books.ManagePermissions,
displayName: LocalizableString.Create<BookStoreResource>("Permission:Document:View"), displayName: L("Permission:Books:View")
multiTenancySide: MultiTenancySides.Tenant
); );
context.AddResourcePermission( context.AddResourcePermission(
name: "BookStore_Document_Edit", name: BookStorePermissions.Books.Resources.Edit,
resourceName: "BookStore.Document", resourceName: BookStorePermissions.Books.Resources.Name,
managementPermissionName: "BookStore_Document_ManagePermissions", managementPermissionName: BookStorePermissions.Books.ManagePermissions,
displayName: LocalizableString.Create<BookStoreResource>("Permission:Document:Edit") displayName: L("Permission:Books:Edit")
); );
context.AddResourcePermission( context.AddResourcePermission(
name: "BookStore_Document_Delete", name: BookStorePermissions.Books.Resources.Delete,
resourceName: "BookStore.Document", resourceName: BookStorePermissions.Books.Resources.Name,
managementPermissionName: "BookStore_Document_ManagePermissions", managementPermissionName: BookStorePermissions.Books.ManagePermissions,
displayName: LocalizableString.Create<BookStoreResource>("Permission:Document:Delete") displayName: L("Permission:Books:Delete"),
multiTenancySide: MultiTenancySides.Host
); );
} }
} }
private static LocalizableString L(string name)
{
return LocalizableString.Create<BookStoreResource>(name);
}
} }
``` ```
The `AddResourcePermission` method requires the following parameters: The `AddResourcePermission` method requires the following parameters:
* `name`: A unique name for the resource permission. * `name`: A unique name for the resource permission.
* `resourceName`: An identifier for the resource type. This is typically the full name of the entity class (e.g., `BookStore.Document`). * `resourceName`: An identifier for the resource type. This is typically the full name of the entity class (e.g., `Acme.BookStore.Books.Book`).
* `managementPermissionName`: A standard permission that controls who can manage resource permissions. Users with this permission can grant/revoke resource permissions for specific resources. * `managementPermissionName`: A standard permission that controls who can manage resource permissions. Users with this permission can grant/revoke resource permissions for specific resources.
* `displayName`: (Optional) A localized display name shown in the UI. * `displayName`: (Optional) A localized display name shown in the UI.
* `multiTenancySide`: (Optional) Specifies on which side of a multi-tenant application this permission can be used. Accepts `MultiTenancySides.Host` (only for the host side), `MultiTenancySides.Tenant` (only for tenants), or `MultiTenancySides.Both` (default, available on both sides). * `multiTenancySide`: (Optional) Specifies on which side of a multi-tenant application this permission can be used. Accepts `MultiTenancySides.Host` (only for the host side), `MultiTenancySides.Tenant` (only for tenants), or `MultiTenancySides.Both` (default, available on both sides).
### Checking Resource Permissions ### Checking Resource Permissions
Use the `IResourcePermissionChecker` service to check if a user/role/client has a specific permission for a resource: Use the `IAuthorizationService` service to check if a user/role/client has a specific permission for a resource:
```csharp ```csharp
using System; using System;
@ -104,101 +132,96 @@ using System.Threading.Tasks;
using Volo.Abp.Application.Services; using Volo.Abp.Application.Services;
using Volo.Abp.Authorization.Permissions.Resources; using Volo.Abp.Authorization.Permissions.Resources;
namespace Acme.BookStore.Documents namespace Acme.BookStore.Books
{ {
public class DocumentAppService : ApplicationService, IDocumentAppService public class BookAppService : ApplicationService, IBookAppService
{ {
private readonly IResourcePermissionChecker _resourcePermissionChecker; private readonly IBookRepository _bookRepository;
private readonly IDocumentRepository _documentRepository;
public DocumentAppService( public BookAppService(IBookRepository bookRepository)
IResourcePermissionChecker resourcePermissionChecker,
IDocumentRepository documentRepository)
{ {
_resourcePermissionChecker = resourcePermissionChecker; _bookRepository = bookRepository;
_documentRepository = documentRepository;
} }
public async Task<DocumentDto> GetAsync(Guid id) public virtual async Task<BookDto> GetAsync(Guid id)
{ {
// Check if the current user can view this specific document var book = await _bookRepository.GetAsync(id);
if (!await _resourcePermissionChecker.IsGrantedAsync(
"BookStore_Document_View", // Check if the current user can view this specific book
"BookStore.Document", var isGranted = await AuthorizationService.IsGrantedAsync(book, BookStorePermissions.Books.Resources.View); // AuthorizationService is a property of the ApplicationService class and will be automatically injected.
id.ToString())) if (!isGranted)
{ {
throw new AbpAuthorizationException( throw new AbpAuthorizationException("You don't have permission to view this book.");
"You don't have permission to view this document.");
} }
var document = await _documentRepository.GetAsync(id); return ObjectMapper.Map<Book, BookDto>(book);
return ObjectMapper.Map<Document, DocumentDto>(document);
} }
public async Task UpdateAsync(Guid id, UpdateDocumentDto input) public virtual async Task UpdateAsync(Guid id, UpdateBookDto input)
{ {
// Check if the current user can edit this specific document var book = await _bookRepository.GetAsync(id);
if (!await _resourcePermissionChecker.IsGrantedAsync(
"BookStore_Document_Edit", // Check if the current user can edit this specific book
"BookStore.Document", var isGranted = await AuthorizationService.IsGrantedAsync(book, BookStorePermissions.Books.Resources.Edit); // AuthorizationService is a property of the ApplicationService class and will be automatically injected.
id.ToString())) if (!isGranted)
{ {
throw new AbpAuthorizationException( throw new AbpAuthorizationException("You don't have permission to edit this book.");
"You don't have permission to edit this document.");
} }
var document = await _documentRepository.GetAsync(id); book.Title = input.Title;
document.Title = input.Title; book.Content = input.Content;
document.Content = input.Content; await _bookRepository.UpdateAsync(book);
await _documentRepository.UpdateAsync(document);
} }
} }
} }
``` ```
In this example, the `DocumentAppService` injects `IResourcePermissionChecker` and uses its `IsGrantedAsync` method to verify if the current user has the required permission for a specific document before performing the operation. The method takes the permission name, resource name, and the resource key (document ID) as parameters. In this example, the `BookAppService` uses `IAuthorizationService` to check if the current user has the required permission for a specific book before performing the operation. The method takes the `Book` entity object and resource permission name as parameters.
#### Using with Entities (IKeyedObject) #### IKeyedObject
ABP entities implement the `IKeyedObject` interface, which provides a `GetObjectKey()` method. For entities with a primary key, `GetObjectKey()` returns the key as a string. This enables convenient extension methods on `IResourcePermissionChecker`: The `IAuthorizationService` internally uses `IResourcePermissionChecker` to check resource permissions, and gets the resource key by calling the `GetObjectKey()` method of the `IKeyedObject` interface. All ABP entities implement the `IKeyedObject` interface, so you can directly pass entity objects to the `IsGrantedAsync` method.
```csharp
// Instead of this:
await _resourcePermissionChecker.IsGrantedAsync(
"BookStore_Document_View",
"BookStore.Document",
document.Id.ToString());
// You can write:
await _resourcePermissionChecker.IsGrantedAsync(
"BookStore_Document_View",
document);
```
> See the [Entities documentation](../../architecture/domain-driven-design/entities.md) for more information about the `IKeyedObject` interface. > See the [Entities documentation](../../architecture/domain-driven-design/entities.md) for more information about the `IKeyedObject` interface.
#### Checking Multiple Permissions #### IResourcePermissionChecker
You can also directly use the `IResourcePermissionChecker` service to check resource permissions which provides more advanced features, such as checking multiple permissions at once:
You can check multiple permissions at once using the overload that accepts an array of permission names: > You have to pass the resource key (obtained via `GetObjectKey()`) explicitly when using `IResourcePermissionChecker`.
```csharp ```csharp
public async Task<DocumentPermissionsDto> GetPermissionsAsync(Guid id) public class BookAppService : ApplicationService, IBookAppService
{ {
var result = await _resourcePermissionChecker.IsGrantedAsync( private readonly IBookRepository _bookRepository;
new[] { private readonly IResourcePermissionChecker _resourcePermissionChecker;
"BookStore_Document_View",
"BookStore_Document_Edit", public BookAppService(IBookRepository bookRepository, IResourcePermissionChecker resourcePermissionChecker)
"BookStore_Document_Delete"
},
"BookStore.Document",
id.ToString());
return new DocumentPermissionsDto
{ {
CanView = result.Result["BookStore_Document_View"] == PermissionGrantResult.Granted, _bookRepository = bookRepository;
CanEdit = result.Result["BookStore_Document_Edit"] == PermissionGrantResult.Granted, _resourcePermissionChecker = resourcePermissionChecker;
CanDelete = result.Result["BookStore_Document_Delete"] == PermissionGrantResult.Granted }
};
public async Task<BookPermissionsDto> GetPermissionsAsync(Guid id)
{
var book = await _bookRepository.GetAsync(id);
var result = await _resourcePermissionChecker.IsGrantedAsync(new[]
{
BookStorePermissions.Books.Resources.View,
BookStorePermissions.Books.Resources.Edit,
BookStorePermissions.Books.Resources.Delete
},
BookStorePermissions.Books.Resources.Name,
book.GetObjectKey()!);
return new BookPermissionsDto
{
CanView = result.Result[BookStorePermissions.Books.Resources.View] == PermissionGrantResult.Granted,
CanEdit = result.Result[BookStorePermissions.Books.Resources.Edit] == PermissionGrantResult.Granted,
CanDelete = result.Result[BookStorePermissions.Books.Resources.Delete] == PermissionGrantResult.Granted
};
}
} }
``` ```

Loading…
Cancel
Save