diff --git a/docs/en/deployment/configuring-production.md b/docs/en/deployment/configuring-production.md index 557b671614..8b3430046d 100644 --- a/docs/en/deployment/configuring-production.md +++ b/docs/en/deployment/configuring-production.md @@ -113,6 +113,6 @@ ABP uses .NET's standard [Logging services](../framework/fundamentals/logging.md ABP's startup solution templates come with [Swagger UI](https://swagger.io/) pre-installed. Swagger is a pretty standard and useful tool to discover and test your HTTP APIs on a built-in UI that is embedded into your application or service. It is typically used in development environment, but you may want to enable it on staging or production environments too. -While you will always secure your HTTP APIs with other techniques (like the [Authorization](../framework/fundamentals/authorization.md) system), allowing malicious software and people to easily discover your HTTP API endpoint details can be considered as a security problem for some systems. So, be careful while taking the decision of enabling or disabling Swagger for the production environment. +While you will always secure your HTTP APIs with other techniques (like the [Authorization](../framework/fundamentals/authorization/index.md) system), allowing malicious software and people to easily discover your HTTP API endpoint details can be considered as a security problem for some systems. So, be careful while taking the decision of enabling or disabling Swagger for the production environment. > You may also want to see the [ABP Swagger integration](../framework/api-development/swagger.md) document. diff --git a/docs/en/docs-nav.json b/docs/en/docs-nav.json index d630b3e9f5..fb94db85b2 100644 --- a/docs/en/docs-nav.json +++ b/docs/en/docs-nav.json @@ -462,12 +462,16 @@ "items": [ { "text": "Overview", - "path": "framework/fundamentals/authorization.md", + "path": "framework/fundamentals/authorization/index.md", "isIndex": true }, { "text": "Dynamic Claims", "path": "framework/fundamentals/dynamic-claims.md" + }, + { + "text": "Resource Based Authorization", + "path": "framework/fundamentals/authorization/resource-based-authorization.md" } ] }, diff --git a/docs/en/framework/api-development/standard-apis/configuration.md b/docs/en/framework/api-development/standard-apis/configuration.md index 3fcd22f6d9..53a666546c 100644 --- a/docs/en/framework/api-development/standard-apis/configuration.md +++ b/docs/en/framework/api-development/standard-apis/configuration.md @@ -9,7 +9,7 @@ ABP provides a pre-built and standard endpoint that contains some useful information about the application/service. Here, is the list of some fundamental information at this endpoint: -* Granted [policies](../../fundamentals/authorization.md) (permissions) for the current user. +* Granted [policies](../../fundamentals/authorization/index.md) (permissions) for the current user. * [Setting](../../infrastructure/settings.md) values for the current user. * Info about the [current user](../../infrastructure/current-user.md) (like id and user name). * Info about the current [tenant](../../architecture/multi-tenancy) (like id and name). diff --git a/docs/en/framework/architecture/domain-driven-design/application-services.md b/docs/en/framework/architecture/domain-driven-design/application-services.md index a0d0c2d5fc..8683241534 100644 --- a/docs/en/framework/architecture/domain-driven-design/application-services.md +++ b/docs/en/framework/architecture/domain-driven-design/application-services.md @@ -218,7 +218,7 @@ See the [validation document](../../fundamentals/validation.md) for more. It's possible to use declarative and imperative authorization for application service methods. -See the [authorization document](../../fundamentals/authorization.md) for more. +See the [authorization document](../../fundamentals/authorization/index.md) for more. ## CRUD Application Services diff --git a/docs/en/framework/architecture/domain-driven-design/entities.md b/docs/en/framework/architecture/domain-driven-design/entities.md index df727a61ba..c0b0869461 100644 --- a/docs/en/framework/architecture/domain-driven-design/entities.md +++ b/docs/en/framework/architecture/domain-driven-design/entities.md @@ -135,6 +135,29 @@ if (book1.EntityEquals(book2)) //Check equality } ``` +### `IKeyedObject` Interface + +ABP entities implement the `IKeyedObject` interface, which provides a way to get the entity's primary key as a string: + +```csharp +public interface IKeyedObject +{ + string? GetObjectKey(); +} +``` + +The `GetObjectKey()` method returns a string representation of the entity's primary key. For entities with a single key (like `Entity` or `Entity`), it returns the `Id` property converted to a string. For entities with composite keys, it returns the keys combined with a comma separator. + +This interface is particularly useful for scenarios where you need to identify an entity by its key in a type-agnostic way, such as: + +* **Resource-based authorization**: When checking or granting permissions for specific entity instances +* **Caching**: When creating cache keys based on entity identifiers +* **Logging and auditing**: When recording entity identifiers in a consistent format + +Since all ABP entities implement this interface through the `IEntity` interface, you can use `GetObjectKey()` on any entity without additional implementation. + +> See the [Resource-Based Authorization](../../fundamentals/authorization/resource-based-authorization.md) documentation for a practical example of using `IKeyedObject` with the permission system. + ## AggregateRoot Class "*Aggregate is a pattern in Domain-Driven Design. A DDD aggregate is a cluster of domain objects that can be treated as a single unit. An example may be an order and its line-items, these will be separate objects, but it's useful to treat the order (together with its line items) as a single aggregate.*" (see the [full description](http://martinfowler.com/bliki/DDD_Aggregate.html)) diff --git a/docs/en/framework/architecture/modularity/extending/customizing-application-modules-guide.md b/docs/en/framework/architecture/modularity/extending/customizing-application-modules-guide.md index 1d6c5ffe99..77f4a70d90 100644 --- a/docs/en/framework/architecture/modularity/extending/customizing-application-modules-guide.md +++ b/docs/en/framework/architecture/modularity/extending/customizing-application-modules-guide.md @@ -112,4 +112,4 @@ Also, see the following documents: * See [the localization document](../../../fundamentals/localization.md) to learn how to extend existing localization resources. * See [the settings document](../../../infrastructure/settings.md) to learn how to change setting definitions of a depended module. -* See [the authorization document](../../../fundamentals/authorization.md) to learn how to change permission definitions of a depended module. +* See [the authorization document](../../../fundamentals/authorization/index.md) to learn how to change permission definitions of a depended module. diff --git a/docs/en/framework/fundamentals/authorization.md b/docs/en/framework/fundamentals/authorization/index.md similarity index 74% rename from docs/en/framework/fundamentals/authorization.md rename to docs/en/framework/fundamentals/authorization/index.md index 1a104ef420..6cbd1a7a6a 100644 --- a/docs/en/framework/fundamentals/authorization.md +++ b/docs/en/framework/fundamentals/authorization/index.md @@ -9,13 +9,15 @@ Authorization is used to check if a user is allowed to perform some specific operations in the application. -ABP extends [ASP.NET Core Authorization](https://docs.microsoft.com/en-us/aspnet/core/security/authorization/introduction) by adding **permissions** as auto [policies](https://docs.microsoft.com/en-us/aspnet/core/security/authorization/policies) and allowing authorization system to be usable in the **[application services](../architecture/domain-driven-design/application-services.md)** too. +ABP extends [ASP.NET Core Authorization](https://docs.microsoft.com/en-us/aspnet/core/security/authorization/introduction) by adding **permissions** as auto [policies](https://docs.microsoft.com/en-us/aspnet/core/security/authorization/policies) and allowing authorization system to be usable in the **[application services](../../architecture/domain-driven-design/application-services.md)** too. So, all the ASP.NET Core authorization features and the documentation are valid in an ABP based application. This document focuses on the features that are added on top of ASP.NET Core authorization features. +ABP supports two types of permissions: **Standard permissions** apply globally (e.g., "can create documents"), while **resource-based permissions** target specific instances (e.g., "can edit Document #123"). This document covers standard permissions; see [Resource-Based Authorization](./resource-based-authorization.md) for fine-grained, per-resource access control. + ## Authorize Attribute -ASP.NET Core defines the [**Authorize**](https://docs.microsoft.com/en-us/aspnet/core/security/authorization/simple) attribute that can be used for an action, a controller or a page. ABP allows you to use the same attribute for an [application service](../architecture/domain-driven-design/application-services.md) too. +ASP.NET Core defines the [**Authorize**](https://docs.microsoft.com/en-us/aspnet/core/security/authorization/simple) attribute that can be used for an action, a controller or a page. ABP allows you to use the same attribute for an [application service](../../architecture/domain-driven-design/application-services.md) too. Example: @@ -87,9 +89,11 @@ namespace Acme.BookStore.Permissions > ABP automatically discovers this class. No additional configuration required! -> You typically define this class inside the `Application.Contracts` project of your [application](../../solution-templates/layered-web-application). The startup template already comes with an empty class named *YourProjectNamePermissionDefinitionProvider* that you can start with. +> You typically define this class inside the `Application.Contracts` project of your [application](../../../solution-templates/layered-web-application/index.md). The startup template already comes with an empty class named *YourProjectNamePermissionDefinitionProvider* that you can start with. + +In the `Define` method, you first need to add a **permission group** (or get an existing group), then add **permissions** to this group using the `AddPermission` method. -In the `Define` method, you first need to add a **permission group** or get an existing group then add **permissions** to this group. +> For resource-specific fine-grained permissions, use the `AddResourcePermission` method instead. See [Resource-Based Authorization](./resource-based-authorization.md) for details. When you define a permission, it becomes usable in the ASP.NET Core authorization system as a **policy** name. It also becomes visible in the UI. See permissions dialog for a role: @@ -100,6 +104,8 @@ When you define a permission, it becomes usable in the ASP.NET Core authorizatio When you save the dialog, it is saved to the database and used in the authorization system. +> **Note:** Only standard (global) permissions are shown in this dialog. Resource-based permissions are managed through the [Resource Permission Management Dialog](../../../modules/permission-management.md#resource-permission-management-dialog) on individual resource instances. + > The screen above is available when you have installed the identity module, which is basically used for user and role management. Startup templates come with the identity module pre-installed. #### Localizing the Permission Name @@ -125,15 +131,15 @@ Then you can define texts for "BookStore" and "Permission:BookStore_Author_Creat "Permission:BookStore_Author_Create": "Creating a new author" ``` -> For more information, see the [localization document](./localization.md) on the localization system. +> For more information, see the [localization document](../localization.md) on the localization system. The localized UI will be as seen below: -![authorization-new-permission-ui-localized](../../images/authorization-new-permission-ui-localized.png) +![authorization-new-permission-ui-localized](../../../images/authorization-new-permission-ui-localized.png) #### Multi-Tenancy -ABP supports [multi-tenancy](../architecture/multi-tenancy) as a first class citizen. You can define multi-tenancy side option while defining a new permission. It gets one of the three values defined below: +ABP supports [multi-tenancy](../../architecture/multi-tenancy/index.md) as a first class citizen. You can define multi-tenancy side option while defining a new permission. It gets one of the three values defined below: - **Host**: The permission is available only for the host side. - **Tenant**: The permission is available only for the tenant side. @@ -180,7 +186,7 @@ authorManagement.AddChild("Author_Management_Delete_Books"); The result on the UI is shown below (you probably want to localize permissions for your application): -![authorization-new-permission-ui-hierarcy](../../images/authorization-new-permission-ui-hierarcy.png) +![authorization-new-permission-ui-hierarcy](../../../images/authorization-new-permission-ui-hierarcy.png) For the example code, it is assumed that a role/user with "Author_Management" permission granted may have additional permissions. Then a typical application service that checks permissions can be defined as shown below: @@ -229,7 +235,7 @@ See [policy based authorization](https://docs.microsoft.com/en-us/aspnet/core/se ### Changing Permission Definitions of a Depended Module -A class deriving from the `PermissionDefinitionProvider` (just like the example above) can also get existing permission definitions (defined by the depended [modules](../architecture/modularity/basics.md)) and change their definitions. +A class deriving from the `PermissionDefinitionProvider` (just like the example above) can also get existing permission definitions (defined by the depended [modules](../../architecture/modularity/basics.md)) and change their definitions. Example: @@ -247,12 +253,12 @@ When you write this code inside your permission definition provider, it finds th You may want to disable a permission based on a condition. Disabled permissions are not visible on the UI and always returns `prohibited` when you check them. There are two built-in conditional dependencies for a permission definition; -* A permission can be automatically disabled if a [Feature](../infrastructure/features.md) was disabled. -* A permission can be automatically disabled if a [Global Feature](../infrastructure/global-features.md) was disabled. +* A permission can be automatically disabled if a [Feature](../../infrastructure/features.md) was disabled. +* A permission can be automatically disabled if a [Global Feature](../../infrastructure/global-features.md) was disabled. In addition, you can create your custom extensions. -#### Depending on a Features +#### Depending on Features Use the `RequireFeatures` extension method on your permission definition to make the permission available only if a given feature is enabled: @@ -261,7 +267,7 @@ myGroup.AddPermission("Book_Creation") .RequireFeatures("BookManagement"); ```` -#### Depending on a Global Feature +#### Depending on Global Features Use the `RequireGlobalFeatures` extension method on your permission definition to make the permission available only if a given feature is enabled: @@ -272,13 +278,13 @@ myGroup.AddPermission("Book_Creation") #### Creating a Custom Permission Dependency -`PermissionDefinition` supports state check, Please refer to [Simple State Checker's documentation](../infrastructure/simple-state-checker.md) +`PermissionDefinition` supports state check, please refer to [Simple State Checker's documentation](../../infrastructure/simple-state-checker.md) ## IAuthorizationService -ASP.NET Core provides the `IAuthorizationService` that can be used to check for authorization. Once you inject, you can use it in your code to conditionally control the authorization. +ASP.NET Core provides the `IAuthorizationService` that can be used to check for authorization. Once you inject it, you can use it in your code to conditionally control the authorization. -Example: +**Example:** ```csharp public async Task CreateAsync(CreateAuthorDto input) @@ -295,7 +301,7 @@ public async Task CreateAsync(CreateAuthorDto input) } ``` -> `AuthorizationService` is available as a property when you derive from ABP's `ApplicationService` base class. Since it is widely used in application services, `ApplicationService` pre-injects it for you. Otherwise, you can directly [inject](./dependency-injection.md) it into your class. +> `AuthorizationService` is available as a property when you derive from ABP's `ApplicationService` base class. Since it is widely used in application services, `ApplicationService` pre-injects it for you. Otherwise, you can directly [inject](../dependency-injection.md) it into your class. Since this is a typical code block, ABP provides extension methods to simplify it. @@ -320,15 +326,15 @@ public async Task CreateAsync(CreateAuthorDto input) See the following documents to learn how to re-use the authorization system on the client side: -* [ASP.NET Core MVC / Razor Pages UI: Authorization](../ui/mvc-razor-pages/javascript-api/auth.md) -* [Angular UI Authorization](../ui/angular/authorization.md) -* [Blazor UI Authorization](../ui/blazor/authorization.md) +* [ASP.NET Core MVC / Razor Pages UI: Authorization](../../ui/mvc-razor-pages/javascript-api/auth.md) +* [Angular UI Authorization](../../ui/angular/authorization.md) +* [Blazor UI Authorization](../../ui/blazor/authorization.md) ## Permission Management Permission management is normally done by an admin user using the permission management modal: -![authorization-new-permission-ui-localized](../../images/authorization-new-permission-ui-localized.png) +![authorization-new-permission-ui-localized](../../../images/authorization-new-permission-ui-localized.png) If you need to manage permissions by code, inject the `IPermissionManager` and use as shown below: @@ -356,13 +362,13 @@ public class MyService : ITransientDependency `SetForUserAsync` sets the value (true/false) for a permission of a user. There are more extension methods like `SetForRoleAsync` and `SetForClientAsync`. -`IPermissionManager` is defined by the permission management module. See the [permission management module documentation](../../modules/permission-management.md) for more information. +`IPermissionManager` is defined by the Permission Management module. For resource-based permissions, use `IResourcePermissionManager` instead. See the [Permission Management Module documentation](../../../modules/permission-management.md) for more information. ## Advanced Topics ### Permission Value Providers -Permission checking system is extensible. Any class derived from `PermissionValueProvider` (or implements `IPermissionValueProvider`) can contribute to the permission check. There are three pre-defined value providers: +The permission checking system is extensible. Any class derived from `PermissionValueProvider` (or implements `IPermissionValueProvider`) can contribute to the permission check. There are three pre-defined value providers: - `UserPermissionValueProvider` checks if the current user is granted for the given permission. It gets user id from the current claims. User claim name is defined with the `AbpClaimTypes.UserId` static property. - `RolePermissionValueProvider` checks if any of the roles of the current user is granted for the given permission. It gets role names from the current claims. Role claims name is defined with the `AbpClaimTypes.Role` static property. @@ -412,15 +418,35 @@ Configure(options => }); ``` +### Resource Permission Value Providers + +Similar to standard permission value providers, you can extend the resource permission checking system by creating custom **resource permission value providers**. ABP provides two built-in resource permission value providers: + +* `UserResourcePermissionValueProvider`: Checks permissions granted directly to users for a specific resource. +* `RoleResourcePermissionValueProvider`: Checks permissions granted to roles for a specific resource. + +You can create custom providers by implementing `IResourcePermissionValueProvider` or inheriting from `ResourcePermissionValueProvider`. Register them using: + +```csharp +Configure(options => +{ + options.ResourceValueProviders.Add(); +}); +``` + +> See the [Permission Management Module](../../../modules/permission-management.md#resource-permission-value-providers) documentation for detailed examples. + ### Permission Store -`IPermissionStore` is the only interface that needs to be implemented to read the value of permissions from a persistence source, generally a database system. The Permission Management module implements it and pre-installed in the application startup template. See the [permission management module documentation](../../modules/permission-management.md) for more information +`IPermissionStore` is the interface that needs to be implemented to read the value of permissions from a persistence source, generally a database system. The Permission Management module implements it and is pre-installed in the application startup template. See the [Permission Management Module documentation](../../../modules/permission-management.md) for more information. + +For resource-based permissions, `IResourcePermissionStore` serves the same purpose, storing and retrieving permissions for specific resource instances. ### AlwaysAllowAuthorizationService `AlwaysAllowAuthorizationService` is a class that is used to bypass the authorization service. It is generally used in integration tests where you may want to disable the authorization system. -Use `IServiceCollection.AddAlwaysAllowAuthorization()` extension method to register the `AlwaysAllowAuthorizationService` to the [dependency injection](./dependency-injection.md) system: +Use `IServiceCollection.AddAlwaysAllowAuthorization()` extension method to register the `AlwaysAllowAuthorizationService` to the [dependency injection](../../dependency-injection.md) system: ```csharp public override void ConfigureServices(ServiceConfigurationContext context) @@ -466,11 +492,24 @@ public static class CurrentUserExtensions } ``` -> If you use OpenIddict please see [Updating Claims in Access Token and ID Token](../../modules/openiddict#updating-claims-in-access_token-and-id_token). +> If you use OpenIddict please see [Updating Claims in Access Token and ID Token](../../../modules/openiddict#updating-claims-in-access_token-and-id_token). + +## Resource-Based Authorization + +While this document covers standard (global) permissions, ABP also supports **resource-based authorization** for fine-grained access control on specific resource instances. Resource-based authorization allows you to grant permissions for a specific document, project, or any other entity rather than granting a permission for all resources of that type. + +**Example scenarios:** + +* Allow users to edit **only their own** blog posts or documents +* Grant access to **specific projects** based on team membership +* Implement document sharing where **different users have different access levels** to the same document + +> See the [Resource-Based Authorization](./resource-based-authorization.md) document for implementation details. ## See Also -* [Permission Management Module](../../modules/permission-management.md) -* [ASP.NET Core MVC / Razor Pages JavaScript Auth API](../ui/mvc-razor-pages/javascript-api/auth.md) -* [Permission Management in Angular UI](../ui/angular/Permission-Management.md) +* [Resource-Based Authorization](./resource-based-authorization.md) +* [Permission Management Module](../../../modules/permission-management.md) +* [ASP.NET Core MVC / Razor Pages JavaScript Auth API](../../ui/mvc-razor-pages/javascript-api/auth.md) +* [Permission Management in Angular UI](../../ui/angular/Permission-Management.md) * [Video tutorial](https://abp.io/video-courses/essentials/authorization) \ No newline at end of file diff --git a/docs/en/framework/fundamentals/authorization/resource-based-authorization.md b/docs/en/framework/fundamentals/authorization/resource-based-authorization.md new file mode 100644 index 0000000000..310f2d1b65 --- /dev/null +++ b/docs/en/framework/fundamentals/authorization/resource-based-authorization.md @@ -0,0 +1,241 @@ +```json +//[doc-seo] +{ + "Description": "Learn how to implement resource-based authorization in ABP Framework for fine-grained access control on specific resource instances like documents, projects, or any entity." +} +``` + +# Resource-Based Authorization + +**Resource-Based Authorization** is a powerful feature that enables fine-grained access control based on specific resource instances. While the standard [authorization system](./index.md) grants permissions at a general level (e.g., "can edit documents"), resource-based authorization allows you to grant permissions for a **specific** document, project, or any other entity rather than granting a permission for all of them. + +## When to Use Resource-Based Authorization? + +Consider resource-based authorization when you need to: + +* Allow users to edit **only their own blog posts or documents** +* Grant access to **specific projects** based on team membership +* Implement document sharing **where different users have different access levels to the same document** +* Control access to resources based on ownership or custom sharing rules + +**Example Scenarios:** + +Imagine a document management system where: + +- User A can view and edit Document 1 +- User B can only view Document 1 +- User A has no access to Document 2 +- User C can manage permissions for Document 2 + +This level of granular control is what resource-based authorization provides. + +## Usage + +Implementing resource-based authorization involves three main steps: + +1. **Define** resource permissions in your `PermissionDefinitionProvider` +2. **Check** permissions using `IResourcePermissionChecker` +3. **Manage** permissions via UI or using `IResourcePermissionManager` for programmatic usages + +### Defining Resource Permissions + +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 +using Volo.Abp.Authorization.Permissions; +using Volo.Abp.Localization; + +namespace Acme.BookStore.Permissions +{ + public class BookStorePermissionDefinitionProvider : PermissionDefinitionProvider + { + public override void Define(IPermissionDefinitionContext context) + { + var myGroup = context.AddGroup("BookStore"); + + // Standard permissions + myGroup.AddPermission(BookStorePermissions.Books.Default, L("Permission:Books")); + + // Permission to manage resource permissions (required) + myGroup.AddPermission(BookStorePermissions.Books.ManagePermissions, L("Permission:Books:ManagePermissions")); + + // Resource-based permissions + context.AddResourcePermission( + name: BookStorePermissions.Books.Resources.View, + resourceName: BookStorePermissions.Books.Resources.Name, + managementPermissionName: BookStorePermissions.Books.ManagePermissions, + displayName: L("Permission:Books:View") + ); + + context.AddResourcePermission( + name: BookStorePermissions.Books.Resources.Edit, + resourceName: BookStorePermissions.Books.Resources.Name, + managementPermissionName: BookStorePermissions.Books.ManagePermissions, + displayName: L("Permission:Books:Edit") + ); + + context.AddResourcePermission( + name: BookStorePermissions.Books.Resources.Delete, + resourceName: BookStorePermissions.Books.Resources.Name, + managementPermissionName: BookStorePermissions.Books.ManagePermissions, + displayName: L("Permission:Books:Delete"), + multiTenancySide: MultiTenancySides.Host + ); + } + } + + private static LocalizableString L(string name) + { + return LocalizableString.Create(name); + } +} +``` + +The `AddResourcePermission` method requires the following parameters: + +* `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., `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. +* `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). + +### Checking Resource Permissions + +Use the `IAuthorizationService` service to check if a user/role/client has a specific permission for a resource: + +```csharp +using System; +using System.Threading.Tasks; +using Volo.Abp.Application.Services; +using Volo.Abp.Authorization.Permissions.Resources; + +namespace Acme.BookStore.Books +{ + public class BookAppService : ApplicationService, IBookAppService + { + private readonly IBookRepository _bookRepository; + + public BookAppService(IBookRepository bookRepository) + { + _bookRepository = bookRepository; + } + + public virtual async Task GetAsync(Guid id) + { + var book = await _bookRepository.GetAsync(id); + + // Check if the current user can view this specific book + var isGranted = await AuthorizationService.IsGrantedAsync(book, BookStorePermissions.Books.Resources.View); // AuthorizationService is a property of the ApplicationService class and will be automatically injected. + if (!isGranted) + { + throw new AbpAuthorizationException("You don't have permission to view this book."); + } + + return ObjectMapper.Map(book); + } + + public virtual async Task UpdateAsync(Guid id, UpdateBookDto input) + { + var book = await _bookRepository.GetAsync(id); + + // Check if the current user can edit this specific book + var isGranted = await AuthorizationService.IsGrantedAsync(book, BookStorePermissions.Books.Resources.Edit); // AuthorizationService is a property of the ApplicationService class and will be automatically injected. + if (!isGranted) + { + throw new AbpAuthorizationException("You don't have permission to edit this book."); + } + + book.Title = input.Title; + book.Content = input.Content; + await _bookRepository.UpdateAsync(book); + } + } +} +``` + +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. + +#### IKeyedObject + +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. + +> See the [Entities documentation](../../architecture/domain-driven-design/entities.md) for more information about the `IKeyedObject` interface. + +#### 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 have to pass the resource key (obtained via `GetObjectKey()`) explicitly when using `IResourcePermissionChecker`. + +```csharp +public class BookAppService : ApplicationService, IBookAppService +{ + private readonly IBookRepository _bookRepository; + private readonly IResourcePermissionChecker _resourcePermissionChecker; + + public BookAppService(IBookRepository bookRepository, IResourcePermissionChecker resourcePermissionChecker) + { + _bookRepository = bookRepository; + _resourcePermissionChecker = resourcePermissionChecker; + } + + public async Task 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 + }; + } +} +``` + +### Managing Resource Permissions + +Once you have defined resource permissions, you need a way to grant or revoke them for specific users, roles, or clients. The [Permission Management Module](../../../modules/permission-management.md) provides the infrastructure for managing resource permissions: + +- **UI Components**: Built-in modal dialogs for managing resource permissions on all supported UI frameworks (MVC/Razor Pages, Blazor, and Angular). These components allow administrators to grant or revoke permissions for users and roles on specific resource instances through a user-friendly interface. +- **`IResourcePermissionManager` Service**: A service for programmatically granting, revoking, and querying resource permissions at runtime. This is useful for scenarios like automatically granting permissions when a resource is created, implementing sharing functionality, or integrating with external systems. + +> See the [Permission Management Module](../../../modules/permission-management.md#resource-permission-management-dialog) documentation for detailed information on using the UI components and the `IResourcePermissionManager` service. + +## See Also + +* [Authorization](./index.md) +* [Permission Management Module](../../../modules/permission-management.md) +* [Entities](../../architecture/domain-driven-design/entities.md) diff --git a/docs/en/framework/fundamentals/dynamic-claims.md b/docs/en/framework/fundamentals/dynamic-claims.md index 03ddc701cd..24d95755c5 100644 --- a/docs/en/framework/fundamentals/dynamic-claims.md +++ b/docs/en/framework/fundamentals/dynamic-claims.md @@ -94,6 +94,6 @@ If you want to add your own dynamic claims contributor, you can create a class t ## See Also -* [Authorization](./authorization.md) +* [Authorization](./authorization/index.md) * [Claims-based authorization in ASP.NET Core](https://learn.microsoft.com/en-us/aspnet/core/security/authorization/claims) * [Mapping, customizing, and transforming claims in ASP.NET Core](https://learn.microsoft.com/en-us/aspnet/core/security/authentication/claims) diff --git a/docs/en/framework/fundamentals/exception-handling.md b/docs/en/framework/fundamentals/exception-handling.md index 85b1c3b9c4..5ccfcf124a 100644 --- a/docs/en/framework/fundamentals/exception-handling.md +++ b/docs/en/framework/fundamentals/exception-handling.md @@ -322,7 +322,7 @@ The `context` object contains necessary information about the exception occurred Some exception types are automatically thrown by the framework: -- `AbpAuthorizationException` is thrown if the current user has no permission to perform the requested operation. See [authorization](./authorization.md) for more. +- `AbpAuthorizationException` is thrown if the current user has no permission to perform the requested operation. See [authorization](./authorization/index.md) for more. - `AbpValidationException` is thrown if the input of the current request is not valid. See [validation](./validation.md) for more. - `EntityNotFoundException` is thrown if the requested entity is not available. This is mostly thrown by [repositories](../architecture/domain-driven-design/repositories.md). diff --git a/docs/en/framework/fundamentals/index.md b/docs/en/framework/fundamentals/index.md index 6aef484c41..7ffa99fd89 100644 --- a/docs/en/framework/fundamentals/index.md +++ b/docs/en/framework/fundamentals/index.md @@ -10,7 +10,7 @@ The following documents explains the fundamental building blocks to create ABP solutions: * [Application Startup](./application-startup.md) -* [Authorization](./authorization.md) +* [Authorization](./authorization/index.md) * [Caching](./caching.md) * [Configuration](./configuration.md) * [Connection Strings](./connection-strings.md) diff --git a/docs/en/framework/infrastructure/background-jobs/hangfire.md b/docs/en/framework/infrastructure/background-jobs/hangfire.md index 61408f303d..05cd214016 100644 --- a/docs/en/framework/infrastructure/background-jobs/hangfire.md +++ b/docs/en/framework/infrastructure/background-jobs/hangfire.md @@ -149,7 +149,7 @@ namespace MyProject Hangfire Dashboard provides information about your background jobs, including method names and serialized arguments as well as gives you an opportunity to manage them by performing different actions – retry, delete, trigger, etc. So it is important to restrict access to the Dashboard. To make it secure by default, only local requests are allowed, however you can change this by following the [official documentation](http://docs.hangfire.io/en/latest/configuration/using-dashboard.html) of Hangfire. -You can integrate the Hangfire dashboard to [ABP authorization system](../../fundamentals/authorization.md) using the **AbpHangfireAuthorizationFilter** +You can integrate the Hangfire dashboard to [ABP authorization system](../../fundamentals/authorization/index.md) using the **AbpHangfireAuthorizationFilter** class. This class is defined in the `Volo.Abp.Hangfire` package. The following example, checks if the current user is logged in to the application: ```csharp diff --git a/docs/en/framework/infrastructure/interceptors.md b/docs/en/framework/infrastructure/interceptors.md index d50de3ad84..9005c3d612 100644 --- a/docs/en/framework/infrastructure/interceptors.md +++ b/docs/en/framework/infrastructure/interceptors.md @@ -42,7 +42,7 @@ Automatically begins and commits/rolls back a database transaction when entering Input DTOs are automatically validated against data annotation attributes and custom validation rules before executing the service logic, providing consistent validation behavior across all services. -### [Authorization](../fundamentals/authorization.md) +### [Authorization](../fundamentals/authorization/index.md) Checks user permissions before allowing the execution of application service methods, ensuring security policies are enforced consistently. diff --git a/docs/en/framework/ui/angular/permission-management.md b/docs/en/framework/ui/angular/permission-management.md index abba8843de..8d30d925bb 100644 --- a/docs/en/framework/ui/angular/permission-management.md +++ b/docs/en/framework/ui/angular/permission-management.md @@ -7,7 +7,7 @@ # Permission Management -A permission is a simple policy that is granted or prohibited for a particular user, role or client. You can read more about [authorization in ABP](../../fundamentals/authorization.md) document. +A permission is a simple policy that is granted or prohibited for a particular user, role or client. You can read more about [authorization in ABP](../../fundamentals/authorization/index.md) document. You can get permission of authenticated user using `getGrantedPolicy` or `getGrantedPolicy$` method of `PermissionService`. diff --git a/docs/en/framework/ui/blazor/authorization.md b/docs/en/framework/ui/blazor/authorization.md index fc006136b4..453f37963b 100644 --- a/docs/en/framework/ui/blazor/authorization.md +++ b/docs/en/framework/ui/blazor/authorization.md @@ -9,7 +9,7 @@ Blazor applications can use the same authorization system and permissions defined in the server side. -> This document is only for authorizing on the Blazor UI. See the [Server Side Authorization](../../fundamentals/authorization.md) to learn how to define permissions and control the authorization system. +> This document is only for authorizing on the Blazor UI. See the [Server Side Authorization](../../fundamentals/authorization/index.md) to learn how to define permissions and control the authorization system. ## Basic Usage @@ -76,7 +76,7 @@ There are some useful extension methods for the `IAuthorizationService`: ## See Also -* [Authorization](../../fundamentals/authorization.md) (server side) +* [Authorization](../../fundamentals/authorization/index.md) (server side) * [Blazor Security](https://docs.microsoft.com/en-us/aspnet/core/blazor/security/) (Microsoft documentation) * [ICurrentUser Service](../../infrastructure/current-user.md) * [Video tutorial](https://abp.io/video-courses/essentials/authorization) diff --git a/docs/en/framework/ui/blazor/page-toolbar-extensions.md b/docs/en/framework/ui/blazor/page-toolbar-extensions.md index 7e341c50fe..9c96bf90f7 100644 --- a/docs/en/framework/ui/blazor/page-toolbar-extensions.md +++ b/docs/en/framework/ui/blazor/page-toolbar-extensions.md @@ -102,7 +102,7 @@ protected override async ValueTask SetToolbarItemsAsync() #### Permissions -If your button/component should be available based on a [permission/policy](../../fundamentals/authorization.md), you can pass the permission/policy name as the `RequiredPolicyName` parameter to the `AddButton` and `AddComponent` methods. +If your button/component should be available based on a [permission/policy](../../fundamentals/authorization/index.md), you can pass the permission/policy name as the `RequiredPolicyName` parameter to the `AddButton` and `AddComponent` methods. ### Add a Page Toolbar Contributor diff --git a/docs/en/framework/ui/mvc-razor-pages/auto-complete-select.md b/docs/en/framework/ui/mvc-razor-pages/auto-complete-select.md index 69f92be3f2..7157c3ab95 100644 --- a/docs/en/framework/ui/mvc-razor-pages/auto-complete-select.md +++ b/docs/en/framework/ui/mvc-razor-pages/auto-complete-select.md @@ -78,4 +78,4 @@ It'll be automatically bound to a collection of defined value type. ## Notices If the authenticated user doesn't have permission on the given URL, the user will get an authorization error. Be careful while designing this kind of UIs. -You can create a specific, [unauthorized](../../fundamentals/authorization.md) endpoint/method to get the list of items, so the page can retrieve lookup data of dependent entity without giving the entire read permission to users. \ No newline at end of file +You can create a specific, [unauthorized](../../fundamentals/authorization/index.md) endpoint/method to get the list of items, so the page can retrieve lookup data of dependent entity without giving the entire read permission to users. \ No newline at end of file diff --git a/docs/en/framework/ui/mvc-razor-pages/javascript-api/ajax.md b/docs/en/framework/ui/mvc-razor-pages/javascript-api/ajax.md index 3ee58ca458..c710cb4c45 100644 --- a/docs/en/framework/ui/mvc-razor-pages/javascript-api/ajax.md +++ b/docs/en/framework/ui/mvc-razor-pages/javascript-api/ajax.md @@ -32,7 +32,7 @@ abp.ajax({ }); ```` -This command logs the list of users to the console, if you've **logged in** to the application and have [permission](../../../fundamentals/authorization.md) for the user management page of the [Identity Module](../../../../modules/identity.md). +This command logs the list of users to the console, if you've **logged in** to the application and have [permission](../../../fundamentals/authorization/index.md) for the user management page of the [Identity Module](../../../../modules/identity.md). ## Error Handling diff --git a/docs/en/framework/ui/mvc-razor-pages/javascript-api/auth.md b/docs/en/framework/ui/mvc-razor-pages/javascript-api/auth.md index 5f31c184ec..83677eec57 100644 --- a/docs/en/framework/ui/mvc-razor-pages/javascript-api/auth.md +++ b/docs/en/framework/ui/mvc-razor-pages/javascript-api/auth.md @@ -9,7 +9,7 @@ Auth API allows you to check permissions (policies) for the current user in the client side. In this way, you can conditionally show/hide UI parts or perform your client side logic based on the current permissions. -> This document only explains the JavaScript API. See the [authorization document](../../../fundamentals/authorization.md) to understand the ABP authorization & permission system. +> This document only explains the JavaScript API. See the [authorization document](../../../fundamentals/authorization/index.md) to understand the ABP authorization & permission system. ## Basic Usage diff --git a/docs/en/framework/ui/mvc-razor-pages/navigation-menu.md b/docs/en/framework/ui/mvc-razor-pages/navigation-menu.md index 41cc31f77f..f26d18ebbd 100644 --- a/docs/en/framework/ui/mvc-razor-pages/navigation-menu.md +++ b/docs/en/framework/ui/mvc-razor-pages/navigation-menu.md @@ -117,7 +117,7 @@ There are more options of a menu item (the constructor of the `ApplicationMenuIt As seen above, a menu contributor contributes to the menu dynamically. So, you can perform any custom logic or get menu items from any source. -One use case is the [authorization](../../fundamentals/authorization.md). You typically want to add menu items by checking a permission. +One use case is the [authorization](../../fundamentals/authorization/index.md). You typically want to add menu items by checking a permission. **Example: Check if the current user has a permission** diff --git a/docs/en/framework/ui/mvc-razor-pages/page-toolbar-extensions.md b/docs/en/framework/ui/mvc-razor-pages/page-toolbar-extensions.md index 54ad4e182c..00ac30a49d 100644 --- a/docs/en/framework/ui/mvc-razor-pages/page-toolbar-extensions.md +++ b/docs/en/framework/ui/mvc-razor-pages/page-toolbar-extensions.md @@ -134,7 +134,7 @@ Configure(options => #### Permissions -If your button/component should be available based on a [permission/policy](../../fundamentals/authorization.md), you can pass the permission/policy name as the `requiredPolicyName` parameter to the `AddButton` and `AddComponent` methods. +If your button/component should be available based on a [permission/policy](../../fundamentals/authorization/index.md), you can pass the permission/policy name as the `requiredPolicyName` parameter to the `AddButton` and `AddComponent` methods. ### Add a Page Toolbar Contributor diff --git a/docs/en/framework/ui/mvc-razor-pages/toolbars.md b/docs/en/framework/ui/mvc-razor-pages/toolbars.md index fd7160417d..2876242ff0 100644 --- a/docs/en/framework/ui/mvc-razor-pages/toolbars.md +++ b/docs/en/framework/ui/mvc-razor-pages/toolbars.md @@ -79,7 +79,7 @@ public class MyToolbarContributor : IToolbarContributor } ```` -You can use the [authorization](../../fundamentals/authorization.md) to decide whether to add a `ToolbarItem`. +You can use the [authorization](../../fundamentals/authorization/index.md) to decide whether to add a `ToolbarItem`. ````csharp if (await context.IsGrantedAsync("MyPermissionName")) diff --git a/docs/en/framework/ui/mvc-razor-pages/widgets.md b/docs/en/framework/ui/mvc-razor-pages/widgets.md index 7e894027f6..0e7d708551 100644 --- a/docs/en/framework/ui/mvc-razor-pages/widgets.md +++ b/docs/en/framework/ui/mvc-razor-pages/widgets.md @@ -12,7 +12,7 @@ ABP provides a model and infrastructure to create **reusable widgets**. Widget s * Have **scripts & styles** dependencies for your widget. * Create **dashboards** with widgets used inside. * Define widgets in reusable **[modules](../../architecture/modularity/basics.md)**. -* Co-operate widgets with **[authorization](../../fundamentals/authorization.md)** and **[bundling](bundling-minification.md)** systems. +* Co-operate widgets with **[authorization](../../fundamentals/authorization/index.md)** and **[bundling](bundling-minification.md)** systems. ## Basic Widget Definition @@ -482,7 +482,7 @@ Used to refresh the widget when needed. It has a filter argument that can be use Some widgets may need to be available only for authenticated or authorized users. In this case, use the following properties of the `Widget` attribute: * `RequiresAuthentication` (`bool`): Set to true to make this widget usable only for authentication users (user have logged in to the application). -* `RequiredPolicies` (`List`): A list of policy names to authorize the user. See [the authorization document](../../fundamentals/authorization.md) for more info about policies. +* `RequiredPolicies` (`List`): A list of policy names to authorize the user. See [the authorization document](../../fundamentals/authorization/index.md) for more info about policies. Example: diff --git a/docs/en/images/resource-based-permission.gif b/docs/en/images/resource-based-permission.gif new file mode 100644 index 0000000000..fbf86ad7a6 Binary files /dev/null and b/docs/en/images/resource-based-permission.gif differ diff --git a/docs/en/index.md b/docs/en/index.md index 46b9923b57..86b7104b06 100644 --- a/docs/en/index.md +++ b/docs/en/index.md @@ -68,7 +68,7 @@ There are a lot of features provided by ABP to achieve real world scenarios easi #### Cross Cutting Concerns -ABP also simplifies (and even automates wherever possible) cross cutting concerns and common non-functional requirements like [Exception Handling](./framework/fundamentals/exception-handling.md), [Validation](./framework/fundamentals/validation.md), [Authorization](./framework/fundamentals/authorization.md), [Localization](./framework/fundamentals/localization.md), [Caching](./framework/fundamentals/caching.md), [Dependency Injection](./framework/fundamentals/dependency-injection.md), [Setting Management](./framework/infrastructure/settings.md), etc. +ABP also simplifies (and even automates wherever possible) cross cutting concerns and common non-functional requirements like [Exception Handling](./framework/fundamentals/exception-handling.md), [Validation](./framework/fundamentals/validation.md), [Authorization](./framework/fundamentals/authorization/index.md), [Localization](./framework/fundamentals/localization.md), [Caching](./framework/fundamentals/caching.md), [Dependency Injection](./framework/fundamentals/dependency-injection.md), [Setting Management](./framework/infrastructure/settings.md), etc. ### Tooling diff --git a/docs/en/modules/identity-pro.md b/docs/en/modules/identity-pro.md index 0d26154932..7e66e5514c 100644 --- a/docs/en/modules/identity-pro.md +++ b/docs/en/modules/identity-pro.md @@ -75,7 +75,7 @@ You can manage permissions of a role: * A permission is an **action of the application** granted to roles and users. * A user with a role will **inherit** all the permissions granted for the role. -* Any module can **[define permissions](../framework/fundamentals/authorization.md#permission-system)**. Once you define a new permission, it will be available in this page. +* Any module can **[define permissions](../framework/fundamentals/authorization/index.md#permission-system)**. Once you define a new permission, it will be available in this page. * Left side is the **list of modules**. Once you click to a module name, you can check/uncheck permissions related to that module. ##### Role claims diff --git a/docs/en/modules/identity.md b/docs/en/modules/identity.md index 8d800fe131..b302f74428 100644 --- a/docs/en/modules/identity.md +++ b/docs/en/modules/identity.md @@ -31,7 +31,7 @@ The menu items and the related pages are authorized. That means the current user ![identity-module-permissions](../images/identity-module-permissions.png) -See the [Authorization document](../framework/fundamentals/authorization.md) to understand the permission system. +See the [Authorization document](../framework/fundamentals/authorization/index.md) to understand the permission system. ### Pages diff --git a/docs/en/modules/openiddict-pro.md b/docs/en/modules/openiddict-pro.md index f531f65531..94d8deb631 100644 --- a/docs/en/modules/openiddict-pro.md +++ b/docs/en/modules/openiddict-pro.md @@ -384,7 +384,7 @@ PreConfigure(options => #### Updating Claims In Access_token and Id_token -[Claims Principal Factory](../framework/fundamentals/authorization.md#claims-principal-factory) can be used to add/remove claims to the `ClaimsPrincipal`. +[Claims Principal Factory](../framework/fundamentals/authorization/index.md#claims-principal-factory) can be used to add/remove claims to the `ClaimsPrincipal`. The `AbpDefaultOpenIddictClaimDestinationsProvider` service will add `Name`, `Email,` and `Role` types of Claims to `access_token` and `id_token`, other claims are only added to `access_token` by default, and remove the `SecurityStampClaimType` secret claim of `Identity`. diff --git a/docs/en/modules/openiddict.md b/docs/en/modules/openiddict.md index c17b8200d2..c1ed40ff9f 100644 --- a/docs/en/modules/openiddict.md +++ b/docs/en/modules/openiddict.md @@ -332,7 +332,7 @@ Configure(options => #### Updating Claims In Access_token and Id_token -[Claims Principal Factory](../framework/fundamentals/authorization.md#claims-principal-factory) can be used to add/remove claims to the `ClaimsPrincipal`. +[Claims Principal Factory](../framework/fundamentals/authorization/index.md#claims-principal-factory) can be used to add/remove claims to the `ClaimsPrincipal`. The `AbpDefaultOpenIddictClaimsPrincipalHandler` service will add `Name`, `Email,` and `Role` types of Claims to `access_token` and `id_token`, other claims are only added to `access_token` by default, and remove the `SecurityStampClaimType` secret claim of `Identity`. diff --git a/docs/en/modules/permission-management.md b/docs/en/modules/permission-management.md index 85cefffe7d..2e5f997423 100644 --- a/docs/en/modules/permission-management.md +++ b/docs/en/modules/permission-management.md @@ -7,9 +7,9 @@ # Permission Management Module -This module implements the `IPermissionStore` to store and manage permissions values in a database. +This module implements the `IPermissionStore` and `IResourcePermissionStore` to store and manage permission values in a database. -> This document covers only the permission management module which persists permission values to a database. See the [Authorization document](../framework/fundamentals/authorization.md) to understand the authorization and permission systems. +> This document covers only the permission management module which persists permission values to a database. See the [Authorization document](../framework/fundamentals/authorization/index.md) to understand the authorization and permission systems. ## How to Install @@ -33,11 +33,87 @@ When you click *Actions* -> *Permissions* for a role, the permission management In this dialog, you can grant permissions for the selected role. The tabs in the left side represents main permission groups and the right side contains the permissions defined in the selected group. +### Resource Permission Management Dialog + +In addition to standard permissions, this module provides a reusable dialog for managing **resource-based permissions** on specific resource instances. This allows administrators to grant or revoke permissions for users, roles and clients on individual resources (e.g., a specific document, project, or any entity). + +![resource-permissions-module-dialog](../images/resource-based-permission.gif) + +The dialog displays all resource permissions defined for the resource type and allows granting them to specific users, roles or clients for the selected resource instance. + +You can integrate this dialog into your own application to manage permissions for your custom entities and resources. See the following sections to learn how to use the component in each UI framework. + +#### MVC / Razor Pages + +Use the `abp.ModalManager` to open the resource permission management dialog: + +````javascript +var _resourcePermissionsModal = new abp.ModalManager( + abp.appPath + 'AbpPermissionManagement/ResourcePermissionManagementModal' +); + +// Open the modal for a specific resource +_resourcePermissionsModal.open({ + resourceName: 'MyApp.Document', + resourceKey: documentId, + resourceDisplayName: documentTitle +}); +```` + +#### Blazor + +Use the `ResourcePermissionManagementModal` component's `OpenAsync` method to open the dialog: + +````razor +@using Volo.Abp.PermissionManagement.Blazor.Components + + + +@code { + private ResourcePermissionManagementModal ResourcePermissionModal { get; set; } + + private async Task OpenPermissionsModal() + { + await ResourcePermissionModal.OpenAsync( + resourceName: "MyApp.Document", + resourceKey: Document.Id.ToString(), + resourceDisplayName: Document.Title + ); + } +} +```` + +#### Angular + +Use the `ResourcePermissionManagementComponent`: + +````typescript +import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { ResourcePermissionManagementComponent } from '@abp/ng.permission-management'; + +@Component({ + // ... +}) +export class DocumentListComponent { + constructor(private modalService: NgbModal) {} + + openPermissionsModal(document: DocumentDto) { + const modalRef = this.modalService.open( + ResourcePermissionManagementComponent, + { size: 'lg' } + ); + modalRef.componentInstance.resourceName = 'MyApp.Document'; + modalRef.componentInstance.resourceKey = document.id; + modalRef.componentInstance.resourceDisplayName = document.title; + } +} +```` + ## IPermissionManager -`IPermissionManager` is the main service provided by this module. It is used to read and change the permission values. `IPermissionManager` is typically used by the *Permission Management Dialog*. However, you can inject it if you need to set a permission value. +`IPermissionManager` is the main service provided by this module. It is used to read and change the global permission values. `IPermissionManager` is typically used by the *Permission Management Dialog*. However, you can inject it if you need to set a permission value. -> If you just want to read/check permission values for the current user, use the `IAuthorizationService` or the `[Authorize]` attribute as explained in the [Authorization document](../framework/fundamentals/authorization.md). +> If you just want to read/check permission values for the current user, use the `IAuthorizationService` or the `[Authorize]` attribute as explained in the [Authorization document](../framework/fundamentals/authorization/index.md). **Example: Grant permissions to roles and users using the `IPermissionManager` service** @@ -67,9 +143,125 @@ public class MyService : ITransientDependency } ```` +## IResourcePermissionManager + +`IResourcePermissionManager` is the service for programmatically managing resource-based permissions. It is typically used by the *Resource Permission Management Dialog*. However, you can inject it when you need to grant, revoke, or query permissions for specific resource instances. + +> If you just want to check resource permission values for the current user, use the `IResourcePermissionChecker` service as explained in the [Resource-Based Authorization](../framework/fundamentals/authorization/resource-based-authorization.md) document. + +**Example: Grant and revoke resource permissions programmatically** + +````csharp +public class DocumentPermissionService : ITransientDependency +{ + private readonly IResourcePermissionManager _resourcePermissionManager; + + public DocumentPermissionService( + IResourcePermissionManager resourcePermissionManager) + { + _resourcePermissionManager = resourcePermissionManager; + } + + public async Task GrantViewPermissionToUserAsync( + Guid documentId, + Guid userId) + { + await _resourcePermissionManager.SetAsync( + permissionName: "MyApp_Document_View", + resourceName: "MyApp.Document", + resourceKey: documentId.ToString(), + providerName: "U", // User + providerKey: userId.ToString(), + isGranted: true + ); + } + + public async Task GrantEditPermissionToRoleAsync( + Guid documentId, + string roleName) + { + await _resourcePermissionManager.SetAsync( + permissionName: "MyApp_Document_Edit", + resourceName: "MyApp.Document", + resourceKey: documentId.ToString(), + providerName: "R", // Role + providerKey: roleName, + isGranted: true + ); + } + + public async Task RevokeUserPermissionsAsync( + Guid documentId, + Guid userId) + { + await _resourcePermissionManager.DeleteAsync( + resourceName: "MyApp.Document", + resourceKey: documentId.ToString(), + providerName: "U", + providerKey: userId.ToString() + ); + } +} +```` + +## IResourcePermissionStore + +The `IResourcePermissionStore` interface is responsible for retrieving resource permission values from the database. This module provides the default implementation that stores permissions in the database. + +You can query the store directly if needed: + +````csharp +public class MyService : ITransientDependency +{ + private readonly IResourcePermissionStore _resourcePermissionStore; + + public MyService(IResourcePermissionStore resourcePermissionStore) + { + _resourcePermissionStore = resourcePermissionStore; + } + + public async Task GetGrantedResourceKeysAsync(string permissionName) + { + // Get all resource keys where the permission is granted + return await _resourcePermissionStore.GetGrantedResourceKeysAsync( + "MyApp.Document", + permissionName); + } +} +```` + +## Cleaning Up Resource Permissions + +When a resource is deleted, you should clean up its associated permissions to avoid orphaned permission records in the database. You can do this directly in your delete logic or handle it asynchronously through event handlers: + +````csharp +public async Task DeleteDocumentAsync(Guid id) +{ + // Delete the document + await _documentRepository.DeleteAsync(id); + + // Clean up all permissions for this resource + await _resourcePermissionManager.DeleteAsync( + resourceName: "MyApp.Document", + resourceKey: id.ToString(), + providerName: "U", + providerKey: null // Deletes for all users + ); + + await _resourcePermissionManager.DeleteAsync( + resourceName: "MyApp.Document", + resourceKey: id.ToString(), + providerName: "R", + providerKey: null // Deletes for all roles + ); +} +```` + +> ABP modules automatically handle permission cleanup for their own entities. For your custom entities, you are responsible for cleaning up resource permissions when resources are deleted. + ## Permission Management Providers -Permission Management Module is extensible, just like the [permission system](../framework/fundamentals/authorization.md). You can extend it by defining permission management providers. +Permission Management Module is extensible, just like the [permission system](../framework/fundamentals/authorization/index.md). You can extend it by defining permission management providers. [Identity Module](identity.md) defines the following permission management providers: @@ -111,6 +303,109 @@ Configure(options => The order of the providers are important. Providers are executed in the reverse order. That means the `CustomPermissionManagementProvider` is executed first for this example. You can insert your provider in any order in the `Providers` list. +### Resource Permission Management Providers + +Similar to standard permission management providers, you can create custom providers for resource permissions by implementing `IResourcePermissionManagementProvider` or inheriting from `ResourcePermissionManagementProvider`: + +````csharp +public class CustomResourcePermissionManagementProvider + : ResourcePermissionManagementProvider +{ + public override string Name => "Custom"; + + public CustomResourcePermissionManagementProvider( + IResourcePermissionGrantRepository resourcePermissionGrantRepository, + IGuidGenerator guidGenerator, + ICurrentTenant currentTenant) + : base( + resourcePermissionGrantRepository, + guidGenerator, + currentTenant) + { + } +} +```` + +After creating the custom provider, you need to register your provider using the `PermissionManagementOptions` in your module class: + +````csharp +Configure(options => +{ + options.ResourceManagementProviders.Add(); +}); +```` + +## Permission Value Providers + +Permission value providers are used to determine if a permission is granted. They are different from management providers: **value providers** are used when *checking* permissions, while **management providers** are used when *setting* permissions. + +> For standard permissions, see the [Authorization document](../framework/fundamentals/authorization/index.md) for details on permission value providers. + +### Resource Permission Value Providers + +Similar to the standard permission system, you can create custom value providers for resource permissions. ABP comes with two built-in resource permission value providers: + +* `UserResourcePermissionValueProvider` (`U`): Checks permissions granted directly to users +* `RoleResourcePermissionValueProvider` (`R`): Checks permissions granted to roles + +You can create your own custom value provider by implementing the `IResourcePermissionValueProvider` interface or inheriting from the `ResourcePermissionValueProvider` base class: + +````csharp +using System.Threading.Tasks; +using Volo.Abp.Authorization.Permissions.Resources; + +public class OwnerResourcePermissionValueProvider : ResourcePermissionValueProvider +{ + public override string Name => "Owner"; + + public OwnerResourcePermissionValueProvider( + IResourcePermissionStore permissionStore) + : base(permissionStore) + { + } + + public override async Task CheckAsync( + ResourcePermissionValueCheckContext context) + { + // Check if the current user is the owner of the resource + var currentUserId = context.Principal?.FindUserId(); + if (currentUserId == null) + { + return PermissionGrantResult.Undefined; + } + + // Your logic to determine if user is the owner + var isOwner = await CheckIfUserIsOwnerAsync( + currentUserId.Value, + context.ResourceName, + context.ResourceKey); + + return isOwner + ? PermissionGrantResult.Granted + : PermissionGrantResult.Undefined; + } + + private Task CheckIfUserIsOwnerAsync( + Guid userId, + string resourceName, + string resourceKey) + { + // Implement your ownership check logic + throw new NotImplementedException(); + } +} +```` + +Register your custom provider in your module's `ConfigureServices` method: + +````csharp +Configure(options => +{ + options.ResourceValueProviders.Add(); +}); +```` + ## See Also -* [Authorization](../framework/fundamentals/authorization.md) \ No newline at end of file +* [Authorization](../framework/fundamentals/authorization/index.md) +* [Resource-Based Authorization](../framework/fundamentals/authorization/resource-based-authorization.md) diff --git a/docs/en/solution-templates/layered-web-application/overview.md b/docs/en/solution-templates/layered-web-application/overview.md index 4c9678fcb2..1a091649cc 100644 --- a/docs/en/solution-templates/layered-web-application/overview.md +++ b/docs/en/solution-templates/layered-web-application/overview.md @@ -41,7 +41,7 @@ The following **libraries and services** come **pre-installed** and **configured The following features are built and pre-configured for you in the solution. * **Authentication** is fully configured based on best practices. -* **[Permission](../../framework/fundamentals/authorization.md)** (authorization), **[setting](../../framework/infrastructure/settings.md)**, **[feature](../../framework/infrastructure/features.md)** and the **[localization](../../framework/fundamentals/localization.md)** management systems are pre-configured and ready to use. +* **[Permission](../../framework/fundamentals/authorization/index.md)** (authorization), **[setting](../../framework/infrastructure/settings.md)**, **[feature](../../framework/infrastructure/features.md)** and the **[localization](../../framework/fundamentals/localization.md)** management systems are pre-configured and ready to use. * **[Background job system](../../framework/infrastructure/background-jobs/index.md)**. * **[BLOB storge](../../framework/infrastructure/blob-storing/index.md)** system is installed with the [database provider](../../framework/infrastructure/blob-storing/database.md). * **On-the-fly database migration** system (services automatically migrated their database schema when you deploy a new version). **\*** diff --git a/docs/en/solution-templates/microservice/overview.md b/docs/en/solution-templates/microservice/overview.md index 6f0e2e9399..fc322565c3 100644 --- a/docs/en/solution-templates/microservice/overview.md +++ b/docs/en/solution-templates/microservice/overview.md @@ -49,7 +49,7 @@ The following features are built and pre-configured for you in the solution. * **OpenId Connect Authentication**, if you have selected the MVC UI. * **Authorization code flow** is implemented, if you have selected a SPA UI (Angular or Blazor WASM). * Other flows (resource owner password, client credentials...) are easy to use when you need them. -* **[Permission](../../framework/fundamentals/authorization.md)** (authorization), **[setting](../../framework/infrastructure/settings.md)**, **[feature](../../framework/infrastructure/features.md)** and the **[localization](../../framework/fundamentals/localization.md)** management systems are pre-configured and ready to use. +* **[Permission](../../framework/fundamentals/authorization/index.md)** (authorization), **[setting](../../framework/infrastructure/settings.md)**, **[feature](../../framework/infrastructure/features.md)** and the **[localization](../../framework/fundamentals/localization.md)** management systems are pre-configured and ready to use. * **[Background job system](../../framework/infrastructure/background-jobs/index.md)** with [RabbitMQ integrated](../../framework/infrastructure/background-jobs/rabbitmq.md). * **[BLOB storge](../../framework/infrastructure/blob-storing/index.md)** system is installed with the [database provider](../../framework/infrastructure/blob-storing/database.md) and a separate database. * **On-the-fly database migration** system (services automatically migrated their database schema when you deploy a new version) diff --git a/docs/en/solution-templates/microservice/permission-management.md b/docs/en/solution-templates/microservice/permission-management.md index e4e721a2cd..87aa0f8757 100644 --- a/docs/en/solution-templates/microservice/permission-management.md +++ b/docs/en/solution-templates/microservice/permission-management.md @@ -27,7 +27,7 @@ Since [Permission Management](../../modules/permission-management.md) is a funda ## Permission Management -The *Administration* microservice provides a set of APIs to manage permissions. Every microservice [defines](../../framework/fundamentals/authorization.md) its own permissions. When a microservice starts, it registers its permissions to the related permission definition tables if `SaveStaticPermissionsToDatabase` option is true for `PermissionManagementOptions`. Since the default value is true, this behavior is ensured. After that, you can see the permissions from the [Permission Management Dialog](../../modules/permission-management.md#permission-management-dialog) for related provider such as *User*, *Role* or *Client (OpenIddict Applications)*. +The *Administration* microservice provides a set of APIs to manage permissions. Every microservice [defines](../../framework/fundamentals/authorization/index.md) its own permissions. When a microservice starts, it registers its permissions to the related permission definition tables if `SaveStaticPermissionsToDatabase` option is true for `PermissionManagementOptions`. Since the default value is true, this behavior is ensured. After that, you can see the permissions from the [Permission Management Dialog](../../modules/permission-management.md#permission-management-dialog) for related provider such as *User*, *Role* or *Client (OpenIddict Applications)*. ![user-permissions](images/user-permissions.png) diff --git a/docs/en/solution-templates/single-layer-web-application/overview.md b/docs/en/solution-templates/single-layer-web-application/overview.md index a0e1e50093..c7e16090a7 100644 --- a/docs/en/solution-templates/single-layer-web-application/overview.md +++ b/docs/en/solution-templates/single-layer-web-application/overview.md @@ -39,7 +39,7 @@ The following **libraries and services** come **pre-installed** and **configured The solution comes with the following built-in and pre-configured features: * **Authentication** is fully configured based on best practices. -* **[Permission](../../framework/fundamentals/authorization.md)** (authorization), **[setting](../../framework/infrastructure/settings.md)**, **[feature](../../framework/infrastructure/features.md)** and the **[localization](../../framework/fundamentals/localization.md)** management systems are pre-configured and ready to use. +* **[Permission](../../framework/fundamentals/authorization/index.md)** (authorization), **[setting](../../framework/infrastructure/settings.md)**, **[feature](../../framework/infrastructure/features.md)** and the **[localization](../../framework/fundamentals/localization.md)** management systems are pre-configured and ready to use. * **[Background job system](../../framework/infrastructure/background-jobs/index.md)**. * **[BLOB storge](../../framework/infrastructure/blob-storing/index.md)** system is installed with the [database provider](../../framework/infrastructure/blob-storing/database.md). * **On-the-fly database migration** system (services automatically migrated their database schema when you deploy a new version). **\*** diff --git a/docs/en/solution-templates/single-layer-web-application/solution-structure.md b/docs/en/solution-templates/single-layer-web-application/solution-structure.md index ffb085855f..dd24b91f09 100644 --- a/docs/en/solution-templates/single-layer-web-application/solution-structure.md +++ b/docs/en/solution-templates/single-layer-web-application/solution-structure.md @@ -56,7 +56,7 @@ This template uses a single-project structure, with concerns separated into fold * **Migrations**: Contains the database migration files. It is created automatically by EF Core. * **ObjectMapping**: Define your [object-to-object mapping](../../framework/infrastructure/object-to-object-mapping.md) classes in this folder. * **Pages**: Define your UI pages (Razor Pages) in this folder (create `Controllers` and `Views` folders yourself if you prefer the MVC pattern). -* **Permissions**: Define your [permissions](../../framework/fundamentals/authorization.md) in this folder. +* **Permissions**: Define your [permissions](../../framework/fundamentals/authorization/index.md) in this folder. * **Services**: Define your [application services](../../framework/architecture/domain-driven-design/application-services.md) in this folder. ### How to Run? diff --git a/docs/en/tutorials/book-store/part-05.md b/docs/en/tutorials/book-store/part-05.md index 3db00ea423..7449b4b65d 100644 --- a/docs/en/tutorials/book-store/part-05.md +++ b/docs/en/tutorials/book-store/part-05.md @@ -30,7 +30,7 @@ ## Permissions -ABP provides an [authorization system](../../framework/fundamentals/authorization.md) based on the ASP.NET Core's [authorization infrastructure](https://docs.microsoft.com/en-us/aspnet/core/security/authorization/introduction). One major feature added on top of the standard authorization infrastructure is the **permission system** which allows to define permissions and enable/disable per role, user or client. +ABP provides an [authorization system](../../framework/fundamentals/authorization/index.md) based on the ASP.NET Core's [authorization infrastructure](https://docs.microsoft.com/en-us/aspnet/core/security/authorization/introduction). One major feature added on top of the standard authorization infrastructure is the **permission system** which allows to define permissions and enable/disable per role, user or client. ### Permission Names diff --git a/docs/en/tutorials/book-store/part-08.md b/docs/en/tutorials/book-store/part-08.md index 68796b99eb..02a0182fe5 100644 --- a/docs/en/tutorials/book-store/part-08.md +++ b/docs/en/tutorials/book-store/part-08.md @@ -183,7 +183,7 @@ public class AuthorAppService : BookStoreAppService, IAuthorAppService } ```` -* `[Authorize(BookStorePermissions.Authors.Default)]` is a declarative way to check a permission (policy) to authorize the current user. See the [authorization document](../../framework/fundamentals/authorization.md) for more. `BookStorePermissions` class will be updated below, don't worry for the compile error for now. +* `[Authorize(BookStorePermissions.Authors.Default)]` is a declarative way to check a permission (policy) to authorize the current user. See the [authorization document](../../framework/fundamentals/authorization/index.md) for more. `BookStorePermissions` class will be updated below, don't worry for the compile error for now. * Derived from the `BookStoreAppService`, which is a simple base class comes with the startup template. It is derived from the standard `ApplicationService` class. * Implemented the `IAuthorAppService` which was defined above. * Injected the `IAuthorRepository` and `AuthorManager` to use in the service methods.