Merge branch 'net7' into json

pull/13357/head
maliming 3 years ago
commit 047aeb861f
No known key found for this signature in database
GPG Key ID: 096224957E51C89E

@ -38,5 +38,6 @@
<s:Boolean x:Key="/Default/Environment/TypeNameHintsOptions/ShowTypeNameHintsForLambdaExpressionParameters/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/Environment/TypeNameHintsOptions/ShowTypeNameHintsForLinqQueryRangeVariables/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/Environment/TypeNameHintsOptions/ShowTypeNameHintsForPatternMatchingExpressions/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Dapr/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Volo/@EntryIndexedValue">True</s:Boolean>
</wpf:ResourceDictionary>

@ -1,425 +0,0 @@
# Deploy Abp Webapp to Azure App Service
> In this document, you'll learn how to create and deploy your first abp web app to [Azure App Service](https://docs.microsoft.com/en-us/azure/app-service/overview). The App Service supports various versions of .NET apps, and provides a highly scalable, self-patching web hosting service. Abp web apps are cross-platform and can be hosted on Linux and Windows.
****Prerequisites****
- An Azure account with an active subscription. [Create an account for free](https://azure.microsoft.com/free/dotnet).
- A GitHub account [Create an account for free](http://github.com/).
## Create a new ABP Framework application
Create a repository on [GitHub.com](https://github.com/) (keep all the default settings)
Open the command prompt and clone the repository into a folder on your computer
```bash
git clone https://github.com/your-username/your-repository-name.git
```
Check your dotnet version. It should be at least 3.1.x
```bash
dotnet --version
```
Install or update the *ABP CLI* using a command line window
```bash
dotnet tool install -g Volo.Abp.Cli || dotnet tool update -g Volo.Abp.Cli
```
Open the command prompt in the *GitHub repository folder* and create a *new abp Blazor solution* with the command below
```bash
abp new YourAppName -u blazor
```
Open the command prompt in the *[YourAppName].DbMigrator* project and enter the command below to apply the database migrations
```bash
dotnet run
```
Open the command prompt in the *[YourAppName].HttpApi.Host* project to run the API project
```bash
dotnet run
```
Navigate to the *applicationUrl* specified in *the launchSettings.json* file of the *[YourAppName].HttpApi.Host project*. You should get the *Swagger window*
Open the command prompt in the *[YourAppName].Blazor* folder and enter the command below to run the Blazor project
```bash
dotnet run
```
Navigate to the *applicationUrl* specified in the *launchSettings.json* file of the *[YourAppName].Blazor* project. You should get the *ABP Framework Welcome window*
Stop both the *API* and the *Blazor* project by pressing **CTRL+C**
Before the github commit, you have to delete "**/wwwroot/libs/*" at *.gitignore* file.
![azdevops-23](../images/azdevops-23.png)
Open the command prompt in the root folder of your project and *add, commit and push* all your changes to your GitHub repository
```bash
git add .
git commit -m initialcommit
git push
```
## Create a SQL Database on Azure and change the connection string in the appsettings.json files
* Login into [Azure Portal](https://portal.azure.com/)
* Click **Create a resource**
* Search for *SQL Database*
* Click the **Create** button in the *SQL Database window*
* Create a new resource group. Name it *rg[YourAppName]*
* Enter *[YourAppName]Db* as database name
* Create a new Server and name it *[yourappname]server*
* Enter a serveradmin login and passwords. Click the **OK** button
* Select your *Location*
* Check *Allow Azure services to access server*
* Click **Configure database**. Go to the *Basic* version and click the **Apply** button
* Click the **Review + create** button. Click **Create**
* Click **Go to resource** and click **SQL server** when the SQL Database is created
* Click **Networking** under Security left side menu
* Select **Selected networks** and click **Add your client IP$ address** at the Firewall rules
* Select **Allow Azure and resources to access this seerver** and save
* Go to your **SQL database**, click **Connection strings** and copy the connection string
* Copy/paste the appsettings.json files of the [YourAppName].HttpApi.Host and the [YourAppName].DbMigrator project
* Do not forget to replace {your_password} with the correct server password you entered in Azure SQL Database
Open the command prompt in the [YourAppName].DbMigrator project again and enter the command below to apply the database migrations
```bash
dotnet run
```
Open the command prompt in the [YourAppName].HttpApi.Host project and enter the command below to check your API is working
```bash
dotnet run
```
Stop the [YourAppName].HttpApi.Host by entering CTRL+C
Open the command prompt in the root folder of your project and add, commit and push all your changes to your GitHub repository
```bash
git add .
git commit -m initialcommit
git push
```
## Set up the Build pipeline in AzureDevops and publish the Build Artifacts
* Sign in into Azure DevOps
* Click **New organization** and follow the steps to create a new organisation. Name it [YourAppName]org
* Enter [YourAppName]Proj as project name in the ***Create a project to get started*** window
* Select **Public visibility** and click the **Create project** button
* Click the **Pipelines** button to continue
* Click the **Create Pipeline** button
Select GitHub in the Select your repository window
![azdevops-1](../images/azdevops-1.png)
* Enter the Connection name. [YourAppName]GitHubConnection and click **Authorize using OAuth**
* Select your **GitHub** [YourAppName]repo and click Continue
* Search for **ASP.NET** in the ***Select a template*** window
![azdevops-2](../images/azdevops-2.png)
* Select the ASP.NET Core template and click the **Apply** button
* Add the below commands block as a first step in the pipeline
```
- task: UseDotNet@2
inputs:
packageType: 'sdk'
version: '6.0.106'
```
![azdevops-18](../images/azdevops-18.png)
* Select **Settings** on the second task(Nugetcommand@2) in the pipeline
* Select **Feeds in my Nuget.config** and type **Nuget.config** in the text box
![azdevops-3](../images/azdevops-3.png)
* Add the below commands block to the end of the pipeline
```
- task: PublishBuildArtifacts@1
displayName: 'Publish Artifact'
inputs:
PathtoPublish: '$(build.artifactstagingdirectory)'
ArtifactName: '$(Parameters.ArtifactName)'
condition: succeededOrFailed()
```
![azdevops-4](../images/azdevops-4.png)
```
# ASP.NET
# Build and test ASP.NET projects.
# Add steps that publish symbols, save build artifacts, deploy, and more:
# https://docs.microsoft.com/azure/devops/pipelines/apps/aspnet/build-aspnet-4
trigger:
- main
pool:
vmImage: 'windows-latest'
variables:
solution: '**/*.sln'
buildPlatform: 'Any CPU'
buildConfiguration: 'Release'
steps:
- task: UseDotNet@2
inputs:
packageType: 'sdk'
version: '6.0.106'
- task: NuGetToolInstaller@1
- task: NuGetCommand@2
inputs:
command: 'restore'
restoreSolution: '$(solution)'
feedsToUse: 'config'
nugetConfigPath: 'NuGet.config'
- task: VSBuild@1
inputs:
solution: '$(solution)'
msbuildArgs: '/p:DeployOnBuild=true /p:WebPublishMethod=Package /p:PackageAsSingleFile=true /p:SkipInvalidConfigurations=true /p:PackageLocation="$(build.artifactStagingDirectory)"'
platform: '$(buildPlatform)'
configuration: '$(buildConfiguration)'
- task: VSTest@2
inputs:
platform: '$(buildPlatform)'
configuration: '$(buildConfiguration)'
- task: PublishBuildArtifacts@1
displayName: 'Publish Artifact'
inputs:
PathtoPublish: '$(build.artifactstagingdirectory)'
ArtifactName: '$(Parameters.ArtifactName)'
publishLocation: 'Container'
condition: succeededOrFailed()
```
* Click **Save & queue** in the top menu. Click **Save & queue** again and click **Save and run** to run the Build pipeline
* When the Build pipeline has finished. Click **1 published; 1 consumed**
## Create a Web App in the Azure Portal to deploy [YourAppName].HttpApi.Host project
* Search for Web App in the *Search the Marketplace* field
* Click the **Create** button in the Web App window
* Select rg[YourAppName] in the *Resource Group* dropdown
* Enter [YourAppName]API in the *Name input* field
* Select code, .NET Core 3.1 (LTS) and windows as *Operating System*
* Enter [YourAppName]API in the *Name input* field
* Select .NET Core 3.1 (LTS) in the *Runtime stack* dropdown
* Select Windows as *Operating System*
* Select the same *Region* as in the SQL server you created in Part 3
![azdevops-5](../images/azdevops-5.png)
* Click **Create new** in the Windows Plan. Name it [YourAppName]ApiWinPlan
* Click **Change size** in Sku and size. Go to the Dev/Test Free F1 version and click the **Apply** button
![azdevops-6](../images/azdevops-6.png)
* Click the **Review + create** button. Click the **Create** button
* Click **Go to resource** when the Web App has been created
## Create a Release pipeline in the AzureDevops and deploy [YourAppName].HttpApi.Host project
* Sign in into [Azure DevOps](https://azure.microsoft.com/en-us/services/devops/)
* Click [YourAppName]Proj and click **Releases** in the *Pipelines* menu
* Click the **New pipeline** button in the *No release pipelines found* window
* Select *Azure App Service deployment* and click the **Apply** button
![azdevops-7](../images/azdevops-7.png)
* Enter *[YourAppName]staging* in the *Stage name* field in the *Stage* window. And close the window
* Click **+ Add an artifact** in the *Pipeline* tab
* Select the **Build** icon as *Source type* in the *Add an artifact* window
* Select Build pipeline in the *Source (build pipeline)* dropdown and click the **Add** button
![azdevops-8](../images/azdevops-8.png)
* Click the **Continuous deployment trigger (thunderbolt icon)**
* Set the toggle to **Enabled** in the the *Continuous deployment trigger* window
* Click **+ Add** in *No filters added*. Select **Include** in the *Type* dropdown. Select your branch in the *Build branch* dropdown and close the window
![azdevops-9](../images/azdevops-9.png)
* Click **the little red circle with the exclamation mark** in the *Tasks* tab menu
* Select your subscription in the *Azure subscription* dropdown.
![azdevops-10](../images/azdevops-10.png)
* Click **Authorize** and enter your credentials in the next screens
* After Authorization, select the **[YourAppName]API** in the *App service name* dropdown
* Click the **Deploy Azure App Service** task
* Select **[YourAppName].HttpApi.Host.zip** in the *Package or folder* input field
![azdevops-11](../images/azdevops-11.png)
* Click the **Save** icon in the top menu and click **OK**
* Click **Create release** in the top menu. Click **Create** to create a release
* Click the *Pipeline* tab and wait until the Deployment succeeds
![azdevops-12](../images/azdevops-12.png)
* Open a browser and navigate to the URL of your Web App
```
https://[YourAppName]api.azurewebsites.net
```
![azdevops-13](../images/azdevops-13.png)
## Create a Web App in Azure Portal to deploy [YourAppName].Blazor project
* Login into [Azure Portal](https://portal.azure.com/)
* Click **Create a resource**
* Search for *Web App* in the *Search the Marketplace* field
* Click the **Create** button in the *Web App* window
* Select *rg[YourAppName]* in the *Resource Group* dropdown
* Enter *[YourAppName]Blazor* in the *Name* input field
* Select *.NET Core 3.1 (LTS)* in the *Runtime stack* dropdown
* Select *Windows* as *Operating System*
* Select the same region as the SQL server you created in Part 3
* Select the [YourAppName]ApiWinPlan in the *Windows Plan* dropdown
![azdevops-14](../images/azdevops-14.png)
* Click the **Review + create** button. Click **Create** button
* Click **Go to resource** when the Web App has been created
* Copy the URL of the Blazor Web App for later use
```
https://[YourAppName]blazor.azurewebsites.net
```
## Change the Web App configuration for the Azure App Service
Copy the URL of the Api Host and Blazor Web App. Change appsettings.json files in the Web App as follows images.
![azdevops-19](../images/azdevops-19.png)
![azdevops-20](../images/azdevops-20.png)
![azdevops-21](../images/azdevops-21.png)
## Add an extra Stage in the Release pipeline in the AzureDevops to deploy [YourAppName].Blazor project
* Go to the *Release* pipeline in [Azure DevOps](https://azure.microsoft.com/en-us/services/devops/) and click **Edit**
* Click the **+ Add** link and add a **New Stage**
![azdevops-15](../images/azdevops-15.png)
* Select *Azure App Service deployment* and click the **Apply** button
* Enter *BlazorDeployment* in the *Stage name* input field and close the *Stage* window
* Click the **little red circle with the exclamation mark** in the BlazorDeployment stage
* Select your subscription in the *Azure subscription* dropdown
* Select your Blazor Web App in the *App service name* dropdown
* Click the **Deploy Azure App Service task**
* Select *[YourAppName].Blazor.zip* in the *Package or folder* input field
![azdevops-16](../images/azdevops-16.png)
* Click **Save** in the top menu and click the **OK** button after
* Click **Create release** in the top menu and click the **Create** button
![azdevops-17](../images/azdevops-17.png)
![azdevops-22](../images/azdevops-22.png)

@ -7,6 +7,3 @@ However, there are some topics that you should care about when you are deploying
## Guides
* [Deploying to a clustered environment](Clustered-Environment.md): Explains how to configure your application when you want to run multiple instances of your application concurrently.
* [Deploy Abp Webapp to Azure App Service](Deploy-Azure-App-Service.md): Explains how to create and deploy your first abp web app to [Azure App Service](https://docs.microsoft.com/en-us/azure/app-service/overview).

@ -1,102 +1,268 @@
## ABP OpenIddict Modules
## ABP OpenIddict Module
## User Interface
This module implements the domain logic and database integrations, but not provides any UI. Management UI is useful if you need to add applications and scopes on the fly. In this case, you may build the management UI yourself or consider to purchase the [ABP Commercial](https://commercial.abp.io/) which provides the management UI for this module.
OpenIddict module provides an integration with the [OpenIddict](https://github.com/openiddict/openiddict-core) which provides advanced authentication features like single sign-on, single log-out, and API access control. This module persists applications, scopes, and other OpenIddict-related objects to the database.
## How to Install
This module comes as pre-installed (as NuGet/NPM packages) when you [create a new solution](https://abp.io/get-started) with the ABP Framework. You can continue to use it as package and get updates easily, or you can include its source code into your solution (see `get-source` [CLI](../CLI.md) command) to develop your custom module.
This module comes as pre-installed (as NuGet/NPM packages) when you [create a new solution](https://abp.io/get-started) with the ABP Framework. You can continue to use it as a package and get updates easily, or you can include its source code into your solution (see `get-source` [CLI](../CLI.md) command) to develop your custom module.
### The Source Code
The source code of this module can be accessed [here](https://github.com/abpframework/abp/tree/dev/modules/openiddict). The source code is licensed with [MIT](https://choosealicense.com/licenses/mit/), so you can freely use and customize it.
The source code of this module can be accessed [here](https://github.com/abpframework/abp/tree/dev/modules/openiddict). The source code is licensed by [MIT](https://choosealicense.com/licenses/mit/), so you can freely use and customize it.
## Relations to Other Modules
## User Interface
This module is based on the [Identity Module](Identity.md) and have an [integration package](https://www.nuget.org/packages/Volo.Abp.Account.Web.OpenIddict) with the [Account Module](Account.md).
This module implements the domain logic and database integrations but does not provide any UI. Management UI is useful if you need to add applications and scopes on the fly. In this case, you may build the management UI yourself or consider purchasing the [ABP Commercial](https://commercial.abp.io/) which provides the management UI for this module.
## The module
## Relations to Other Modules
### Demo projects
This module is based on the [Identity Module](Identity.md) and has an [integration package](https://www.nuget.org/packages/Volo.Abp.Account.Web.OpenIddict) with the [Account Module](Account.md).
In the module's `app` directory there are six projects(including `angular`)
## Options
* `OpenIddict.Demo.Server`: An abp application with integrated modules (has two `clients` and a `scope`).
* `OpenIddict.Demo.API`: ASP NET Core API application using JwtBearer authentication
* `OpenIddict.Demo.Client.Mvc`: ASP NET Core MVC application using `OpenIdConnect` for authentication
* `OpenIddict.Demo.Client.Console`: Use `IdentityModel` to test OpenIddict's various endpoints, and call the api of `OpenIddict.Demo.API`
* `OpenIddict.Demo.Client.BlazorWASM:` ASP NET Core Blazor application using `OidcAuthentication` for authentication
* `angular`: An angular application that integrates the abp ng modules and uses oauth for authentication
### OpenIddictBuilder
#### How to run?
`OpenIddictBuilder` can be configured in the `PreConfigureServices` method of your OpenIddict [module](https://docs.abp.io/en/abp/latest/Module-Development-Basics).
Confirm the connection string of `appsettings.json` in the `OpenIddict.Demo.Server` project. Running the project will automatically create the database and initialize the data.
After running the `OpenIddict.Demo.API` project, then you can run the rest of the projects to test.
Example:
### Domain module
```csharp
public override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<OpenIddictBuilder>(builder =>
{
//Set options here...
});
}
```
There are four main entities included in this module.
`OpenIddictBuilder` contains various extension methods to configure the OpenIddict services:
* OpenIddictApplication: **Represents applications(client)**
* OpenIddictScope: **Represents scopes**
* OpenIddictAuthorization: **Represents authorizations, Track of logical chains of tokens and user consent..**
* OpenIddictToken: **Represents various tokens.**
- `AddServer()` registers the OpenIddict token server services in the DI container. Contains `OpenIddictServerBuilder` configurations.
- `AddCore()` registers the OpenIddict core services in the DI container. Contains `OpenIddictCoreBuilder` configurations.
- `AddValidation()` registers the OpenIddict token validation services in the DI container. Contains `OpenIddictValidationBuilder` configurations.
Domain also implements four store interfaces in OpenIddict, OpenIddict uses store to manage entities, corresponding to the above four entities, Custom entity repository is used in the store.
### OpenIddictCoreBuilder
`OpenIddictCoreBuilder` contains extension methods to configure the OpenIddict core services.
```cs
//Manager
OpenIddictApplicationManager
OpenIddictScopeManager
OpenIddictAuthorizationManager
OpenIddictTokenManager
//Store
IOpenIddictApplicationStore
IOpenIddictScopeStore
IOpenIddictAuthorizationStore
IOpenIddictTokenStore
//Repository
IOpenIddictApplicationRepository
IOpenIddictScopeRepository
IOpenIddictAuthorizationRepository
IOpenIddictTokenRepository
Example:
```csharp
public override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<OpenIddictCoreBuilder>(builder =>
{
//Set options here...
});
}
```
We enabled most of OpenIddict's features in the `AddOpenIddict` method, You can change OpenIddict's related builder options via `PreConfigure`.
These services contain:
```cs
PreConfigure<OpenIddictBuilder>(builder =>
{
//builder
});
- Adding `ApplicationStore`, `AuthorizationStore`, `ScopeStore`, `TokenStore`.
- Replacing `ApplicationManager`, `AuthorizationManager`, `ScopeManager`, `TokenManager`.
- Replacing `ApplicationStoreResolver`, `AuthorizationStoreResolver`, `ScopeStoreResolver`, `TokenStoreResolver`.
- Setting `DefaultApplicationEntity`, `DefaultAuthorizationEntity`, `DefaultScopeEntity`, `DefaultTokenEntity`.
### OpenIddictServerBuilder
PreConfigure<OpenIddictCoreBuilder>(builder =>
`OpenIddictServerBuilder` contains extension methods to configure OpenIddict server services.
Example:
```csharp
public override void PreConfigureServices(ServiceConfigurationContext context)
{
//builder
});
PreConfigure<OpenIddictServerBuilder>(builder =>
{
//Set options here...
});
}
```
These services contain:
- Registering claims, scopes.
- Setting the `Issuer` URI that is used as the base address for the endpoint URIs returned from the discovery endpoint.
- Adding development signing keys, encryption/signing keys, credentials, and certificates.
- Adding/removing event handlers.
- Enabling/disabling grant types.
- Setting authentication server endpoint URIs.
### OpenIddictValidationBuilder
PreConfigure<OpenIddictServerBuilder>(builder =>
`OpenIddictValidationBuilder` contains extension methods to configure OpenIddict validation services.
Example:
```csharp
public override void PreConfigureServices(ServiceConfigurationContext context)
{
//builder
});
PreConfigure<OpenIddictValidationBuilder>(builder =>
{
//Set options here...
});
}
```
#### AbpOpenIddictAspNetCoreOptions
These services contain:
- `AddAudiances()` for resource servers.
- `SetIssuer()` URI that is used to determine the actual location of the OAuth 2.0/OpenID Connect configuration document when using provider discovery.
- `SetConfiguration()` to configure `OpenIdConnectConfiguration`.
- `UseIntrospection()` to use introspection instead of local/direct validation.
- Adding encryption key, credentials, and certificates.
- Adding/removing event handlers.
- `SetClientId() ` to set the client identifier `client_id ` when communicating with the remote authorization server (e.g for introspection).
- `SetClientSecret()` to set the identifier `client_secret` when communicating with the remote authorization server (e.g for introspection).
- `EnableAuthorizationEntryValidation()` to enable authorization validation to ensure the `access token` is still valid by making a database call for each API request. *Note:* This may have a negative impact on performance and can only be used with an OpenIddict-based authorization server.
- `EnableTokenEntryValidation()` to enable authorization validation to ensure the `access token` is still valid by making a database call for each API request. *Note:* This may have a negative impact on performance and it is required when the OpenIddict server is configured to use reference tokens.
- `UseLocalServer()` to register the OpenIddict validation/server integration services.
- `UseAspNetCore()` to register the OpenIddict validation services for ASP.NET Core in the DI container.
## Internals
### Domain Layer
#### Aggregates
##### OpenIddictApplication
OpenIddictApplications represent the applications that can request tokens from your OpenIddict Server.
- `OpenIddictApplications` (aggregate root): Represents an OpenIddict application.
- `ClientId` (string): The client identifier associated with the current application.
- `ClientSecret` (string): The client secret associated with the current application. Maybe hashed or encrypted for security reasons.
- `ConsentType` (string): The consent type associated with the current application.
- `DisplayName` (string): The display name associated with the current application.
- `DisplayNames` (string): The localized display names associated with the current application serialized as a JSON object.
- `Permissions` (string): The permissions associated with the current application, serialized as a JSON array.
- `PostLogoutRedirectUris` (string): The logout callback URLs associated with the current application, serialized as a JSON array.
- `Properties` (string): The additional properties associated with the current application serialized as a JSON object or null.
- `RedirectUris` (string): The callback URLs associated with the current application, serialized as a JSON array.
- `Requirements` (string): The requirements associated with the current application
- `Type` (string): The application type associated with the current application.
- `ClientUri` (string): URI to further information about client.
- `LogoUri` (string): URI to client logo.
##### OpenIddictAuthorization
OpenIddictAuthorizations are used to keep the allowed scopes, authorization flow types.
- `OpenIddictAuthorization` (aggregate root): Represents an OpenIddict authorization.
- `ApplicationId` (Guid?): The application associated with the current authorization.
- `Properties` (string): The additional properties associated with the current authorization serialized as a JSON object or null.
- `Scopes` (string): The scopes associated with the current authorization, serialized as a JSON array.
- `Status` (string): The status of the current authorization.
- `Subject` (string): The subject associated with the current authorization.
- `Type` (string): The type of the current authorization.
##### OpenIddictScope
OpenIddictScopes are used to keep the scopes of resources.
- `OpenIddictScope` (aggregate root): Represents an OpenIddict scope.
- `Description` (string): The public description associated with the current scope.
- `Descriptions` (string): The localized public descriptions associated with the current scope, serialized as a JSON object.
- `DisplayName` (string): The display name associated with the current scope.
- `DisplayNames` (string): The localized display names associated with the current scope serialized as a JSON object.
- `Name` (string): The unique name associated with the current scope.
- `Properties` (string): The additional properties associated with the current scope serialized as a JSON object or null.
- `Resources` (string): The resources associated with the current scope, serialized as a JSON array.
##### OpenIddictToken
OpenIddictTokens are used to persist the application tokens.
- `OpenIddictToken` (aggregate root): Represents an OpenIddict token.
- `ApplicationId` (Guid?): The application associated with the current token.
- `AuthorizationId` (Guid?): The application associated with the current token.
- `CreationDate` (DateTime?): The UTC creation date of the current token.
- `ExpirationDate` (DateTime?): The UTC expiration date of the current token.
- `Payload` (string): The payload of the current token, if applicable. Only used for reference tokens and may be encrypted for security reasons.
- `Properties` (string): The additional properties associated with the current token serialized as a JSON object or null.
- `RedemptionDate` (DateTime?): The UTC redemption date of the current token.
- `Status` (string): The status of the current authorization.
- `ReferenceId` (string): The reference identifier associated with the current token, if applicable. Only used for reference tokens and may be hashed or encrypted for security reasons.
- `Status` (string): The status of the current token.
- `Subject` (string): The subject associated with the current token.
- `Type` (string): The type of the current token.
`UpdateAbpClaimTypes(default: true)`: Updates AbpClaimTypes to be compatible with identity server claims.
`AddDevelopmentEncryptionAndSigningCertificate(default: true)`: Registers (and generates if necessary) a user-specific development encryption/development signing certificate.
#### Stores
You can also change this options via `PreConfigure`.
This module implements OpenIddict stores:
#### Automatically removing orphaned tokens/authorizations
- `IAbpOpenIdApplicationStore`
- `IOpenIddictAuthorizationStore`
- `IOpenIddictScopeStore`
- `IOpenIddictTokenStore`
There is a background task in the `Domain` module (`enabled by default`) that automatically removes orphaned tokens/authorizations, you can configure `TokenCleanupOptions` to manage it.
##### Repositories
### ASP NET Core module
The following custom repositories are defined in this module:
- `IOpenIddictApplicationRepository`
- `IOpenIddictAuthorizationRepository`
- `IOpenIddictScopeRepository`
- `IOpenIddictTokenRepository`
##### Domain Services
This module doesn't contain any domain service but overrides the service below:
- `AbpApplicationManager` used to populate/get `AbpApplicationDescriptor` information that contains `ClientUri` and `LogoUri`.
### Database Providers
#### Common
##### Table/Collection Prefix & Schema
All tables/collections use the `OpenIddict` prefix by default. Set static properties on the `AbpOpenIddictDbProperties` class if you need to change the table prefix or set a schema name (if supported by your database provider).
##### Connection String
This module uses `AbpOpenIddict` for the connection string name. If you don't define a connection string with this name, it fallbacks to the `Default` connection string.
See the [connection strings](https://docs.abp.io/en/abp/latest/Connection-Strings) documentation for details.
#### Entity Framework Core
##### Tables
- **OpenIddictApplications**
- **OpenIddictAuthorizations**
- **OpenIddictScopes**
- **OpenIddictTokens**
#### MongoDB
##### Collections
- **OpenIddictApplications**
- **OpenIddictAuthorizations**
- **OpenIddictScopes**
- **OpenIddictTokens**
## ASP.NET Core Module
This module integrates ASP NET Core, with built-in MVC controllers for four protocols. It uses OpenIddict's [Pass-through mode](https://documentation.openiddict.com/guides/index.html#pass-through-mode).
@ -107,15 +273,57 @@ LogoutController -> connect/logout
UserInfoController -> connect/userinfo
```
> We will implement the related functions of **device flow** in the PRO module..
> **Device flow** implementation will be done in the commercial module.
#### AbpOpenIddictAspNetCoreOptions
`AbpOpenIddictAspNetCoreOptions` can be configured in the `PreConfigureServices` method of your OpenIddict [module](https://docs.abp.io/en/abp/latest/Module-Development-Basics).
Example:
```csharp
PreConfigure<AbpOpenIddictAspNetCoreOptions>(options =>
{
//Set options here...
});
```
`AbpOpenIddictAspNetCoreOptions` properties:
- `UpdateAbpClaimTypes(default: true)`: Updates `AbpClaimTypes` to be compatible with the Openiddict claims.
- `AddDevelopmentEncryptionAndSigningCertificate(default: true)`: Registers (and generates if necessary) a user-specific development encryption/development signing certificate.
#### Automatically Removing Orphaned Tokens/Authorizations
The background task that automatically removes orphaned tokens/authorizations. This can be configured by `TokenCleanupOptions` to manage it.
`TokenCleanupOptions` can be configured in the `PreConfigureServices` method of your OpenIddict [module](https://docs.abp.io/en/abp/latest/Module-Development-Basics).
Example:
```csharp
PreConfigure<TokenCleanupOptions>(options =>
{
//Set options here...
});
```
`TokenCleanupOptions` properties:
- `IsCleanupEnabled` (default: true): Enable/disable token clean up.
- `CleanupPeriod` (default: 3,600,000 ms): Setting clean up period.
- `DisableAuthorizationPruning`: Setting a boolean indicating whether authorizations pruning should be disabled.
- `DisableTokenPruning`: Setting a boolean indicating whether token pruning should be disabled.
- `MinimumAuthorizationLifespan` (default: 14 days): Setting the minimum lifespan authorizations must have to be pruned. Cannot be less than 10 minutes.
- `MinimumTokenLifespan` (default: 14 days): Setting the minimum lifespan tokens must have to be pruned. Cannot be less than 10 minutes.
#### How to control claims in access_token and id_token
#### Updating Claims In Access_token and Id_token
You can use the [Claims Principal Factory](https://docs.abp.io/en/abp/latest/Authorization#claims-principal-factory) to add/remove claims to the `ClaimsPrincipal`.
[Claims Principal Factory](https://docs.abp.io/en/abp/latest/Authorization#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`.
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`.
You can create a service that inherits from `IAbpOpenIddictClaimDestinationsProvider` and add it to DI to fully control the destinations of claims
Create a service that inherits from `IAbpOpenIddictClaimDestinationsProvider` and add it to DI to fully control the destinations of claims.
```cs
public class MyClaimDestinationsProvider : IAbpOpenIddictClaimDestinationsProvider, ITransientDependency
@ -133,30 +341,11 @@ Configure<AbpOpenIddictClaimDestinationsOptions>(options =>
});
```
For detailed information, please refer to: [OpenIddict claim destinations](https://documentation.openiddict.com/configuration/claim-destinations.html)
### EF Core module
Implements the above four repository interfaces.
### MongoDB module
Implements the above four repository interfaces.
## OpenIddict
For detailed information, please refer to: [OpenIddict claim destinations](https://documentation.openiddict.com/configuration/claim-destinations.html)
### Documentation
#### Disable AccessToken Encryption
For more details about OpenIddict, please refer to its official documentation and Github.
https://documentation.openiddict.com
https://github.com/openiddict/openiddict-core#resources
### Disable AccessToken Encryption
ABP disables the `access token encryption` by default for compatibility, you can manually enable it if needed.
ABP disables the `access token encryption` by default for compatibility, it can be enabled manually if needed.
```cs
public override void PreConfigureServices(ServiceConfigurationContext context)
@ -170,22 +359,15 @@ public override void PreConfigureServices(ServiceConfigurationContext context)
https://documentation.openiddict.com/configuration/token-formats.html#disabling-jwt-access-token-encryption
### PKCE
https://documentation.openiddict.com/configuration/proof-key-for-code-exchange.html
### Request/Response process
I will briefly introduce the principle of OpenIddict so that everyone can quickly understand it.
### Request/Response Process
The `OpenIddict.Server.AspNetCore` adds an authentication scheme(`Name: OpenIddict.Server.AspNetCore, handler: OpenIddictServerAspNetCoreHandler`) and implements the `IAuthenticationRequestHandler` interface.
It will be executed first in `AuthenticationMiddleware` and can short-circuit the current request. Otherwise, `DefaultAuthenticateScheme` will be called and continue to execute the pipeline.
`OpenIddictServerAspNetCoreHandler` will call various built-in handlers(Handling requests and responses), And the handler will process according to the context or skip logic that has nothing to do with it.
`OpenIddictServerAspNetCoreHandler` will call various built-in handlers (handling requests and responses), And the handler will process according to the context or skip logic that has nothing to do with it.
Example a token request:
Example of a token request:
```
POST /connect/token HTTP/1.1
@ -199,11 +381,11 @@ Content-Type: application/x-www-form-urlencoded
scope=AbpAPI offline_access
```
This request will be processed by various handlers. They will confirm the endpoint type of the request, check `http/https`, verify that the request parameters (`client. scope etc`) are valid and exist in the database, etc. Various protocol checks. And build a `OpenIddictRequest` object, If there are any errors, the response content may be set and directly short-circuit the current request.
This request will be processed by various handlers. They will confirm the endpoint type of the request, check `HTTP/HTTPS`, verify that the request parameters (`client. scope, etc`) are valid and exist in the database, etc. Various protocol checks. And build a `OpenIddictRequest` object, If there are any errors, the response content may be set and directly short-circuit the current request.
If everything is ok, the request will go to our processing controller(eg `TokenController`), we can get an `OpenIddictRequest` from the http request at this time. The rest of our work will be based on this object.
If everything is ok, the request will go to our processing controller(eg `TokenController`), we can get an `OpenIddictRequest` from the HTTP request at this time. The rest will be based on this object.
We may check the `username` and `password` in the request. If it is correct we create a `ClaimsPrincipal` object and return a `SignInResult`, which uses the `OpenIddict.Validation.AspNetCore` authentication scheme name, will calls `OpenIddictServerAspNetCoreHandler` for processing.
Check the `username` and `password` in the request. If it is correct create a `ClaimsPrincipal` object and return a `SignInResult`, which uses the `OpenIddict.Validation.AspNetCore` authentication scheme name, will calls `OpenIddictServerAspNetCoreHandler` for processing.
`OpenIddictServerAspNetCoreHandler` do some checks to generate json and replace the http response content.
@ -214,10 +396,26 @@ If you need to customize OpenIddict, you need to replace/delete/add new handlers
Please refer to:
https://documentation.openiddict.com/guides/index.html#events-model
## Migrating Guide
### PKCE
[Migrating from IdentityServer to OpenIddict Step by Step Guide ](../Migration-Guides/OpenIddict-Step-by-Step.md)
https://documentation.openiddict.com/configuration/proof-key-for-code-exchange.html
## Sponsor
## Demo projects
In the module's `app` directory there are six projects(including `angular`)
* `OpenIddict.Demo.Server`: An abp application with integrated modules (has two `clients` and a `scope`).
* `OpenIddict.Demo.API`: ASP NET Core API application using JwtBearer authentication.
* `OpenIddict.Demo.Client.Mvc`: ASP NET Core MVC application using `OpenIdConnect` for authentication.
* `OpenIddict.Demo.Client.Console`: Use `IdentityModel` to test OpenIddict's various endpoints, and call the api of `OpenIddict.Demo.API`.
* `OpenIddict.Demo.Client.BlazorWASM:` ASP NET Core Blazor application using `OidcAuthentication` for authentication.
* `angular`: An angular application that integrates the abp ng modules and uses oauth for authentication.
Please consider sponsoring this project: https://github.com/sponsors/kevinchalet
#### How to run?
Confirm the connection string of `appsettings.json` in the `OpenIddict.Demo.Server` project. Running the project will automatically create the database and initialize the data.
After running the `OpenIddict.Demo.API` project, then you can run the rest of the projects to test.
## Migrating Guide
[Migrating from IdentityServer to OpenIddict Step by Step Guide ](../Migration-Guides/OpenIddict-Step-by-Step.md)

@ -0,0 +1,20 @@
# Angular UI: Current User
The current user information stored in Config State.
### How to Get a Current User Information Configuration
You can use the `getOne` or `getOne$` method of `ConfigStateService` to get a specific configuration property. For that, the property name should be passed to the method as parameter.
```js
// this.config is an instance of ConfigStateService
const currentUser = this.config.getOne("currentUser");
// or
this.config.getOne$("currentUser").subscribe(currentUser => {
// use currentUser here
})
```
> See the [ConfigStateService](./Config-State-Service) for more information.

@ -981,6 +981,10 @@
"text": "Authorization",
"path": "UI/Angular/Authorization.md"
},
{
"text": "Current User",
"path": "UI/Angular/Current-User.md"
},
{
"text": "HTTP Requests",
"path": "UI/Angular/HTTP-Requests.md"
@ -1282,10 +1286,6 @@
{
"text": "Deploying to a Clustered Environment",
"path": "Deployment/Clustered-Environment.md"
},
{
"text": "Deploy Abp Webapp to Azure App Service",
"path": "Deployment/Deploy-Azure-App-Service.md"
}
]
},

Binary file not shown.

Before

Width:  |  Height:  |  Size: 252 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 220 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 487 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 139 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 368 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 281 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 418 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 291 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 491 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 205 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 539 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 415 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 237 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 352 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 400 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 295 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 219 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 288 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 309 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 304 KiB

@ -417,6 +417,18 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Json.Abstractions"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.AspNetCore.Mvc.NewtonsoftJson", "src\Volo.Abp.AspNetCore.Mvc.NewtonsoftJson\Volo.Abp.AspNetCore.Mvc.NewtonsoftJson.csproj", "{0CFC9D4F-F12F-4B44-ABCF-AB4A0E9E85B2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Dapr", "src\Volo.Abp.Dapr\Volo.Abp.Dapr.csproj", "{192A829F-D608-4E41-8DE0-058E943E453F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.EventBus.Dapr", "src\Volo.Abp.EventBus.Dapr\Volo.Abp.EventBus.Dapr.csproj", "{DCC41E99-EBC7-4F19-BA0D-A6F770D8E431}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Http.Client.Dapr", "src\Volo.Abp.Http.Client.Dapr\Volo.Abp.Http.Client.Dapr.csproj", "{18B796D2-D45D-41AE-9A42-75C9B14B20DF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.AspNetCore.Mvc.Dapr", "src\Volo.Abp.AspNetCore.Mvc.Dapr\Volo.Abp.AspNetCore.Mvc.Dapr.csproj", "{5EED625D-8D86-492A-BCB8-F6C8CD8D4AA1}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.AspNetCore.Mvc.Dapr.EventBus", "src\Volo.Abp.AspNetCore.Mvc.Dapr.EventBus\Volo.Abp.AspNetCore.Mvc.Dapr.EventBus.csproj", "{B02EF042-C39E-45C4-A92D-BF7554E1889D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.DistributedLocking.Dapr", "src\Volo.Abp.DistributedLocking.Dapr\Volo.Abp.DistributedLocking.Dapr.csproj", "{CAE48068-233C-47A9-BEAB-DDF521730E7A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -1243,6 +1255,30 @@ Global
{0CFC9D4F-F12F-4B44-ABCF-AB4A0E9E85B2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0CFC9D4F-F12F-4B44-ABCF-AB4A0E9E85B2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0CFC9D4F-F12F-4B44-ABCF-AB4A0E9E85B2}.Release|Any CPU.Build.0 = Release|Any CPU
{192A829F-D608-4E41-8DE0-058E943E453F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{192A829F-D608-4E41-8DE0-058E943E453F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{192A829F-D608-4E41-8DE0-058E943E453F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{192A829F-D608-4E41-8DE0-058E943E453F}.Release|Any CPU.Build.0 = Release|Any CPU
{DCC41E99-EBC7-4F19-BA0D-A6F770D8E431}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DCC41E99-EBC7-4F19-BA0D-A6F770D8E431}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DCC41E99-EBC7-4F19-BA0D-A6F770D8E431}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DCC41E99-EBC7-4F19-BA0D-A6F770D8E431}.Release|Any CPU.Build.0 = Release|Any CPU
{18B796D2-D45D-41AE-9A42-75C9B14B20DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{18B796D2-D45D-41AE-9A42-75C9B14B20DF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{18B796D2-D45D-41AE-9A42-75C9B14B20DF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{18B796D2-D45D-41AE-9A42-75C9B14B20DF}.Release|Any CPU.Build.0 = Release|Any CPU
{5EED625D-8D86-492A-BCB8-F6C8CD8D4AA1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5EED625D-8D86-492A-BCB8-F6C8CD8D4AA1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5EED625D-8D86-492A-BCB8-F6C8CD8D4AA1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5EED625D-8D86-492A-BCB8-F6C8CD8D4AA1}.Release|Any CPU.Build.0 = Release|Any CPU
{B02EF042-C39E-45C4-A92D-BF7554E1889D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B02EF042-C39E-45C4-A92D-BF7554E1889D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B02EF042-C39E-45C4-A92D-BF7554E1889D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B02EF042-C39E-45C4-A92D-BF7554E1889D}.Release|Any CPU.Build.0 = Release|Any CPU
{CAE48068-233C-47A9-BEAB-DDF521730E7A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CAE48068-233C-47A9-BEAB-DDF521730E7A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CAE48068-233C-47A9-BEAB-DDF521730E7A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CAE48068-233C-47A9-BEAB-DDF521730E7A}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -1453,6 +1489,12 @@ Global
{0AD06E14-CBFE-4551-8D18-9E921D8F2A87} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{08531C5D-0436-4721-986D-96446CF54316} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{0CFC9D4F-F12F-4B44-ABCF-AB4A0E9E85B2} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{192A829F-D608-4E41-8DE0-058E943E453F} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{DCC41E99-EBC7-4F19-BA0D-A6F770D8E431} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{18B796D2-D45D-41AE-9A42-75C9B14B20DF} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{5EED625D-8D86-492A-BCB8-F6C8CD8D4AA1} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{B02EF042-C39E-45C4-A92D-BF7554E1889D} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{CAE48068-233C-47A9-BEAB-DDF521730E7A} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5}

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" />
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Volo.Abp.AspNetCore.Mvc.Dapr\Volo.Abp.AspNetCore.Mvc.Dapr.csproj" />
<ProjectReference Include="..\Volo.Abp.EventBus.Dapr\Volo.Abp.EventBus.Dapr.csproj" />
</ItemGroup>
</Project>

@ -0,0 +1,29 @@
using Microsoft.AspNetCore.Http.Json;
using Volo.Abp.AspNetCore.Mvc.Dapr.EventBus.SystemTextJson;
using Volo.Abp.EventBus.Dapr;
using Volo.Abp.Json.SystemTextJson;
using Volo.Abp.Modularity;
namespace Volo.Abp.AspNetCore.Mvc.Dapr.EventBus;
[DependsOn(
typeof(AbpAspNetCoreMvcDaprModule),
typeof(AbpEventBusDaprModule)
)]
public class AbpAspNetCoreMvcDaprEventBusModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
// TODO: Add NewtonsoftJson json converter.
Configure<JsonOptions>(options =>
{
options.SerializerOptions.Converters.Add(new AbpAspNetCoreMvcDaprSubscriptionDefinitionConverter());
});
Configure<AbpSystemTextJsonSerializerOptions>(options =>
{
options.JsonSerializerOptions.Converters.Add(new AbpAspNetCoreMvcDaprSubscriptionDefinitionConverter());
});
}
}

@ -0,0 +1,11 @@
namespace Volo.Abp.AspNetCore.Mvc.Dapr.EventBus;
public class AbpAspNetCoreMvcDaprEventBusOptions
{
public List<IAbpAspNetCoreMvcDaprPubSubProviderContributor> Contributors { get; }
public AbpAspNetCoreMvcDaprEventBusOptions()
{
Contributors = new List<IAbpAspNetCoreMvcDaprPubSubProviderContributor>();
}
}

@ -0,0 +1,8 @@
namespace Volo.Abp.AspNetCore.Mvc.Dapr.EventBus;
public class AbpAspNetCoreMvcDaprPubSubConsts
{
public const string DaprSubscribeUrl = "dapr/subscribe";
public const string DaprEventCallbackUrl = "api/abp/dapr/event";
}

@ -0,0 +1,63 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.Abp.AspNetCore.Mvc.Dapr.EventBus.Models;
using Volo.Abp.DependencyInjection;
using Volo.Abp.EventBus;
using Volo.Abp.EventBus.Dapr;
using Volo.Abp.EventBus.Distributed;
namespace Volo.Abp.AspNetCore.Mvc.Dapr.EventBus;
public class AbpAspNetCoreMvcDaprPubSubProvider : ITransientDependency
{
protected IServiceProvider ServiceProvider { get; }
protected AbpAspNetCoreMvcDaprEventBusOptions AspNetCoreMvcDaprEventBusOptions { get; }
protected AbpDaprEventBusOptions DaprEventBusOptions { get; }
protected AbpDistributedEventBusOptions DistributedEventBusOptions { get; }
public AbpAspNetCoreMvcDaprPubSubProvider(
IServiceProvider serviceProvider,
IOptions<AbpAspNetCoreMvcDaprEventBusOptions> aspNetCoreDaprEventBusOptions,
IOptions<AbpDaprEventBusOptions> daprEventBusOptions,
IOptions<AbpDistributedEventBusOptions> distributedEventBusOptions)
{
ServiceProvider = serviceProvider;
AspNetCoreMvcDaprEventBusOptions = aspNetCoreDaprEventBusOptions.Value;
DaprEventBusOptions = daprEventBusOptions.Value;
DistributedEventBusOptions = distributedEventBusOptions.Value;
}
public virtual async Task<List<AbpAspNetCoreMvcDaprSubscriptionDefinition>> GetSubscriptionsAsync()
{
var subscriptions = new List<AbpAspNetCoreMvcDaprSubscriptionDefinition>();
foreach (var handler in DistributedEventBusOptions.Handlers)
{
foreach (var @interface in handler.GetInterfaces().Where(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IDistributedEventHandler<>)))
{
var eventType = @interface.GetGenericArguments()[0];
var eventName = EventNameAttribute.GetNameOrDefault(eventType);
subscriptions.Add(new AbpAspNetCoreMvcDaprSubscriptionDefinition()
{
PubSubName = DaprEventBusOptions.PubSubName,
Topic = eventName,
Route = AbpAspNetCoreMvcDaprPubSubConsts.DaprEventCallbackUrl
});
}
}
if (AspNetCoreMvcDaprEventBusOptions.Contributors.Any())
{
using (var scope = ServiceProvider.CreateScope())
{
var context = new AbpAspNetCoreMvcDaprPubSubProviderContributorContext(scope.ServiceProvider, subscriptions);
foreach (var contributor in AspNetCoreMvcDaprEventBusOptions.Contributors)
{
await contributor.ContributeAsync(context);
}
}
}
return subscriptions;
}
}

@ -0,0 +1,16 @@
using Volo.Abp.AspNetCore.Mvc.Dapr.EventBus.Models;
namespace Volo.Abp.AspNetCore.Mvc.Dapr.EventBus;
public class AbpAspNetCoreMvcDaprPubSubProviderContributorContext
{
public IServiceProvider ServiceProvider { get; }
public List<AbpAspNetCoreMvcDaprSubscriptionDefinition> Subscriptions { get; }
public AbpAspNetCoreMvcDaprPubSubProviderContributorContext(IServiceProvider serviceProvider, List<AbpAspNetCoreMvcDaprSubscriptionDefinition> daprSubscriptionModels)
{
ServiceProvider = serviceProvider;
Subscriptions = daprSubscriptionModels;
}
}

@ -0,0 +1,36 @@
using System.Text.Json;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.Abp.AspNetCore.Mvc.Dapr.EventBus.Models;
using Volo.Abp.Dapr;
using Volo.Abp.EventBus.Dapr;
namespace Volo.Abp.AspNetCore.Mvc.Dapr.EventBus.Controllers;
[Area("abp")]
[RemoteService(Name = "abp")]
public class AbpAspNetCoreMvcDaprPubSubController : AbpController
{
[HttpGet(AbpAspNetCoreMvcDaprPubSubConsts.DaprSubscribeUrl)]
public virtual async Task<List<AbpAspNetCoreMvcDaprSubscriptionDefinition>> SubscribeAsync()
{
return await HttpContext.RequestServices.GetRequiredService<AbpAspNetCoreMvcDaprPubSubProvider>().GetSubscriptionsAsync();
}
[HttpPost(AbpAspNetCoreMvcDaprPubSubConsts.DaprEventCallbackUrl)]
public virtual async Task<IActionResult> EventsAsync()
{
var bodyJsonDocument = await JsonDocument.ParseAsync(HttpContext.Request.Body);
var request = JsonSerializer.Deserialize<AbpAspNetCoreMvcDaprSubscriptionRequest>(bodyJsonDocument.RootElement.GetRawText(),
HttpContext.RequestServices.GetRequiredService<IOptions<JsonOptions>>().Value.JsonSerializerOptions);
var distributedEventBus = HttpContext.RequestServices.GetRequiredService<DaprDistributedEventBus>();
var daprSerializer = HttpContext.RequestServices.GetRequiredService<IDaprSerializer>();
var eventData = daprSerializer.Deserialize(bodyJsonDocument.RootElement.GetProperty("data").GetRawText(), distributedEventBus.GetEventType(request.Topic));
await distributedEventBus.TriggerHandlersAsync(distributedEventBus.GetEventType(request.Topic), eventData);
return Ok();
}
}

@ -0,0 +1,6 @@
namespace Volo.Abp.AspNetCore.Mvc.Dapr.EventBus;
public interface IAbpAspNetCoreMvcDaprPubSubProviderContributor
{
Task ContributeAsync(AbpAspNetCoreMvcDaprPubSubProviderContributorContext context);
}

@ -0,0 +1,10 @@
namespace Volo.Abp.AspNetCore.Mvc.Dapr.EventBus.Models;
public class AbpAspNetCoreMvcDaprSubscriptionDefinition
{
public string PubSubName { get; set; }
public string Topic { get; set; }
public string Route { get; set; }
}

@ -0,0 +1,8 @@
namespace Volo.Abp.AspNetCore.Mvc.Dapr.EventBus.Models;
public class AbpAspNetCoreMvcDaprSubscriptionRequest
{
public string PubSubName { get; set; }
public string Topic { get; set; }
}

@ -0,0 +1,11 @@
using System.Text.Json;
namespace Volo.Abp.AspNetCore.Mvc.Dapr.EventBus.SystemTextJson;
public class AbpAspNetCoreMvcDaprPubSubJsonNamingPolicy : JsonNamingPolicy
{
public override string ConvertName(string name)
{
return name.ToLower();
}
}

@ -0,0 +1,25 @@
using System.Text.Json;
using System.Text.Json.Serialization;
using Volo.Abp.AspNetCore.Mvc.Dapr.EventBus.Models;
namespace Volo.Abp.AspNetCore.Mvc.Dapr.EventBus.SystemTextJson;
public class AbpAspNetCoreMvcDaprSubscriptionDefinitionConverter : JsonConverter<AbpAspNetCoreMvcDaprSubscriptionDefinition>
{
private JsonSerializerOptions _writeJsonSerializerOptions;
public override AbpAspNetCoreMvcDaprSubscriptionDefinition Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
throw new NotSupportedException();
}
public override void Write(Utf8JsonWriter writer, AbpAspNetCoreMvcDaprSubscriptionDefinition value, JsonSerializerOptions options)
{
_writeJsonSerializerOptions ??= JsonSerializerOptionsHelper.Create(new JsonSerializerOptions(options)
{
PropertyNamingPolicy = new AbpAspNetCoreMvcDaprPubSubJsonNamingPolicy()
}, x => x == this);
JsonSerializer.Serialize(writer, value, _writeJsonSerializerOptions);
}
}

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" />
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Volo.Abp.AspNetCore.Mvc\Volo.Abp.AspNetCore.Mvc.csproj" />
<ProjectReference Include="..\Volo.Abp.Dapr\Volo.Abp.Dapr.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Dapr.AspNetCore" Version="1.8.0" />
</ItemGroup>
</Project>

@ -0,0 +1,13 @@
using Volo.Abp.Dapr;
using Volo.Abp.Modularity;
namespace Volo.Abp.AspNetCore.Mvc.Dapr;
[DependsOn(
typeof(AbpAspNetCoreMvcModule),
typeof(AbpDaprModule)
)]
public class AbpAspNetCoreMvcDaprModule : AbpModule
{
}

@ -171,7 +171,8 @@ public class AbpApplicationConfigurationAppService : ApplicationService, IAbpApp
foreach (var policyName in policyNames)
{
if (await _defaultAuthorizationPolicyProvider.GetPolicyAsync(policyName) == null && _permissionDefinitionManager.GetOrNull(policyName) != null)
if (await _defaultAuthorizationPolicyProvider.GetPolicyAsync(policyName) == null &&
await _permissionDefinitionManager.GetOrNullAsync(policyName) != null)
{
abpPolicyNames.Add(policyName);
}
@ -214,9 +215,9 @@ public class AbpApplicationConfigurationAppService : ApplicationService, IAbpApp
{
var dictionary = new Dictionary<string, string>();
var localizer = _serviceProvider.GetRequiredService(
var localizer = (IStringLocalizer) _serviceProvider.GetRequiredService(
typeof(IStringLocalizer<>).MakeGenericType(resource.ResourceType)
) as IStringLocalizer;
);
foreach (var localizedString in localizer.GetAllStrings())
{

@ -1,4 +1,6 @@
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Data;
using Volo.Abp.Json;
using Volo.Abp.Modularity;
@ -24,4 +26,17 @@ public class AbpAuditingModule : AbpModule
{
context.Services.OnRegistred(AuditingInterceptorRegistrar.RegisterIfNeeded);
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
var applicationName = context.Services.GetApplicationName();
if (!applicationName.IsNullOrEmpty())
{
Configure<AbpAuditingOptions>(options =>
{
options.ApplicationName = applicationName;
});
}
}
}

@ -0,0 +1,14 @@
using JetBrains.Annotations;
using Volo.Abp.Localization;
using Volo.Abp.MultiTenancy;
namespace Volo.Abp.Authorization.Permissions;
public interface ICanAddChildPermission
{
PermissionDefinition AddPermission(
[NotNull] string name,
ILocalizableString displayName = null,
MultiTenancySides multiTenancySide = MultiTenancySides.Both,
bool isEnabled = true);
}

@ -37,8 +37,7 @@ public interface IPermissionDefinitionContext
/// </summary>
PermissionGroupDefinition AddGroup(
[NotNull] string name,
ILocalizableString displayName = null,
MultiTenancySides multiTenancySide = MultiTenancySides.Both);
ILocalizableString displayName = null);
/// <summary>
/// Tries to remove a permission group.

@ -1,17 +1,18 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using JetBrains.Annotations;
namespace Volo.Abp.Authorization.Permissions;
public interface IPermissionDefinitionManager
{
[NotNull]
PermissionDefinition Get([NotNull] string name);
[ItemNotNull]
Task<PermissionDefinition> GetAsync([NotNull] string name);
[CanBeNull]
PermissionDefinition GetOrNull([NotNull] string name);
[ItemCanBeNull]
Task<PermissionDefinition> GetOrNullAsync([NotNull] string name);
IReadOnlyList<PermissionDefinition> GetPermissions();
Task<IReadOnlyList<PermissionDefinition>> GetPermissionsAsync();
IReadOnlyList<PermissionGroupDefinition> GetGroups();
Task<IReadOnlyList<PermissionGroupDefinition>> GetGroupsAsync();
}

@ -7,7 +7,9 @@ using Volo.Abp.SimpleStateChecking;
namespace Volo.Abp.Authorization.Permissions;
public class PermissionDefinition : IHasSimpleStateCheckers<PermissionDefinition>
public class PermissionDefinition :
IHasSimpleStateCheckers<PermissionDefinition>,
ICanAddChildPermission
{
/// <summary>
/// Unique name of the permission.
@ -110,6 +112,16 @@ public class PermissionDefinition : IHasSimpleStateCheckers<PermissionDefinition
return child;
}
PermissionDefinition ICanAddChildPermission.AddPermission(
string name,
ILocalizableString displayName = null,
MultiTenancySides multiTenancySide = MultiTenancySides.Both,
bool isEnabled = true)
{
return this.AddChild(name, displayName, multiTenancySide, isEnabled);
}
/// <summary>
/// Sets a property in the <see cref="Properties"/> dictionary.

@ -20,8 +20,7 @@ public class PermissionDefinitionContext : IPermissionDefinitionContext
public virtual PermissionGroupDefinition AddGroup(
string name,
ILocalizableString displayName = null,
MultiTenancySides multiTenancySide = MultiTenancySides.Both)
ILocalizableString displayName = null)
{
Check.NotNull(name, nameof(name));
@ -30,7 +29,7 @@ public class PermissionDefinitionContext : IPermissionDefinitionContext
throw new AbpException($"There is already an existing permission group with name: {name}");
}
return Groups[name] = new PermissionGroupDefinition(name, displayName, multiTenancySide);
return Groups[name] = new PermissionGroupDefinition(name, displayName);
}
[NotNull]

@ -6,7 +6,7 @@ using Volo.Abp.MultiTenancy;
namespace Volo.Abp.Authorization.Permissions;
public class PermissionGroupDefinition //TODO: Consider to make possible a group have sub groups
public class PermissionGroupDefinition : ICanAddChildPermission
{
/// <summary>
/// Unique name of the group.
@ -21,12 +21,6 @@ public class PermissionGroupDefinition //TODO: Consider to make possible a group
}
private ILocalizableString _displayName;
/// <summary>
/// MultiTenancy side.
/// Default: <see cref="MultiTenancySides.Both"/>
/// </summary>
public MultiTenancySides MultiTenancySide { get; set; }
public IReadOnlyList<PermissionDefinition> Permissions => _permissions.ToImmutableList();
private readonly List<PermissionDefinition> _permissions;
@ -45,19 +39,17 @@ public class PermissionGroupDefinition //TODO: Consider to make possible a group
protected internal PermissionGroupDefinition(
string name,
ILocalizableString displayName = null,
MultiTenancySides multiTenancySide = MultiTenancySides.Both)
ILocalizableString displayName = null)
{
Name = name;
DisplayName = displayName ?? new FixedLocalizableString(Name);
MultiTenancySide = multiTenancySide;
Properties = new Dictionary<string, object>();
_permissions = new List<PermissionDefinition>();
}
public virtual PermissionDefinition AddPermission(
string name,
[NotNull] string name,
ILocalizableString displayName = null,
MultiTenancySides multiTenancySide = MultiTenancySides.Both,
bool isEnabled = true)

@ -31,7 +31,7 @@ public class AbpAuthorizationPolicyProvider : DefaultAuthorizationPolicyProvider
return policy;
}
var permission = _permissionDefinitionManager.GetOrNull(policyName);
var permission = await _permissionDefinitionManager.GetOrNullAsync(policyName);
if (permission != null)
{
//TODO: Optimize & Cache!
@ -43,16 +43,14 @@ public class AbpAuthorizationPolicyProvider : DefaultAuthorizationPolicyProvider
return null;
}
public Task<List<string>> GetPoliciesNamesAsync()
public async Task<List<string>> GetPoliciesNamesAsync()
{
return Task.FromResult(
_options.GetPoliciesNames()
.Union(
_permissionDefinitionManager
.GetPermissions()
.Select(p => p.Name)
)
.ToList()
);
return _options.GetPoliciesNames()
.Union(
(await _permissionDefinitionManager
.GetPermissionsAsync())
.Select(p => p.Name)
)
.ToList();
}
}

@ -0,0 +1,38 @@
using System.Text.Json.Nodes;
using Volo.Abp.DependencyInjection;
using Volo.Abp.SimpleStateChecking;
namespace Volo.Abp.Authorization.Permissions;
public class AuthenticatedSimpleStateCheckerSerializerContributor :
ISimpleStateCheckerSerializerContributor,
ISingletonDependency
{
public const string CheckerShortName = "A";
public string SerializeToJson<TState>(ISimpleStateChecker<TState> checker)
where TState : IHasSimpleStateCheckers<TState>
{
if (checker is not RequireAuthenticatedSimpleStateChecker<TState>)
{
return null;
}
var jsonObject = new JsonObject {
["T"] = CheckerShortName
};
return jsonObject.ToJsonString();
}
public ISimpleStateChecker<TState> Deserialize<TState>(JsonObject jsonObject, TState state)
where TState : IHasSimpleStateCheckers<TState>
{
if (jsonObject["T"]?.ToString() != CheckerShortName)
{
return null;
}
return new RequireAuthenticatedSimpleStateChecker<TState>();
}
}

@ -0,0 +1,13 @@
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Volo.Abp.Authorization.Permissions;
public interface IDynamicPermissionDefinitionStore
{
Task<PermissionDefinition> GetOrNullAsync(string name);
Task<IReadOnlyList<PermissionDefinition>> GetPermissionsAsync();
Task<IReadOnlyList<PermissionGroupDefinition>> GetGroupsAsync();
}

@ -0,0 +1,13 @@
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Volo.Abp.Authorization.Permissions;
public interface IStaticPermissionDefinitionStore
{
Task<PermissionDefinition> GetOrNullAsync(string name);
Task<IReadOnlyList<PermissionDefinition>> GetPermissionsAsync();
Task<IReadOnlyList<PermissionGroupDefinition>> GetGroupsAsync();
}

@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Authorization.Permissions;
public class NullDynamicPermissionDefinitionStore : IDynamicPermissionDefinitionStore, ISingletonDependency
{
private readonly static Task<PermissionDefinition> CachedPermissionResult = Task.FromResult((PermissionDefinition)null);
private readonly static Task<IReadOnlyList<PermissionDefinition>> CachedPermissionsResult =
Task.FromResult((IReadOnlyList<PermissionDefinition>)Array.Empty<PermissionDefinition>().ToImmutableList());
private readonly static Task<IReadOnlyList<PermissionGroupDefinition>> CachedGroupsResult =
Task.FromResult((IReadOnlyList<PermissionGroupDefinition>)Array.Empty<PermissionGroupDefinition>().ToImmutableList());
public Task<PermissionDefinition> GetOrNullAsync(string name)
{
return CachedPermissionResult;
}
public Task<IReadOnlyList<PermissionDefinition>> GetPermissionsAsync()
{
return CachedPermissionsResult;
}
public Task<IReadOnlyList<PermissionGroupDefinition>> GetGroupsAsync()
{
return CachedGroupsResult;
}
}

@ -43,8 +43,12 @@ public class PermissionChecker : IPermissionChecker, ITransientDependency
{
Check.NotNull(name, nameof(name));
var permission = PermissionDefinitionManager.Get(name);
var permission = await PermissionDefinitionManager.GetOrNullAsync(name);
if (permission == null)
{
return false;
}
if (!permission.IsEnabled)
{
return false;
@ -97,18 +101,24 @@ public class PermissionChecker : IPermissionChecker, ITransientDependency
{
Check.NotNull(names, nameof(names));
var multiTenancySide = claimsPrincipal?.GetMultiTenancySide() ?? CurrentTenant.GetMultiTenancySide();
var result = new MultiplePermissionGrantResult();
if (!names.Any())
{
return result;
}
var multiTenancySide = claimsPrincipal?.GetMultiTenancySide() ??
CurrentTenant.GetMultiTenancySide();
var permissionDefinitions = new List<PermissionDefinition>();
foreach (var name in names)
{
var permission = PermissionDefinitionManager.Get(name);
var permission = await PermissionDefinitionManager.GetOrNullAsync(name);
if (permission == null)
{
result.Result.Add(name, PermissionGrantResult.Prohibited);
continue;
}
result.Result.Add(name, PermissionGrantResult.Undefined);

@ -1,47 +1,27 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Authorization.Permissions;
public class PermissionDefinitionManager : IPermissionDefinitionManager, ISingletonDependency
public class PermissionDefinitionManager : IPermissionDefinitionManager, ITransientDependency
{
protected IDictionary<string, PermissionGroupDefinition> PermissionGroupDefinitions => _lazyPermissionGroupDefinitions.Value;
private readonly Lazy<Dictionary<string, PermissionGroupDefinition>> _lazyPermissionGroupDefinitions;
protected IDictionary<string, PermissionDefinition> PermissionDefinitions => _lazyPermissionDefinitions.Value;
private readonly Lazy<Dictionary<string, PermissionDefinition>> _lazyPermissionDefinitions;
protected AbpPermissionOptions Options { get; }
private readonly IServiceProvider _serviceProvider;
private readonly IStaticPermissionDefinitionStore _staticStore;
private readonly IDynamicPermissionDefinitionStore _dynamicStore;
public PermissionDefinitionManager(
IOptions<AbpPermissionOptions> options,
IServiceProvider serviceProvider)
IStaticPermissionDefinitionStore staticStore,
IDynamicPermissionDefinitionStore dynamicStore)
{
_serviceProvider = serviceProvider;
Options = options.Value;
_lazyPermissionDefinitions = new Lazy<Dictionary<string, PermissionDefinition>>(
CreatePermissionDefinitions,
isThreadSafe: true
);
_lazyPermissionGroupDefinitions = new Lazy<Dictionary<string, PermissionGroupDefinition>>(
CreatePermissionGroupDefinitions,
isThreadSafe: true
);
_staticStore = staticStore;
_dynamicStore = dynamicStore;
}
public virtual PermissionDefinition Get(string name)
public virtual async Task<PermissionDefinition> GetAsync(string name)
{
var permission = GetOrNull(name);
var permission = await GetOrNullAsync(name);
if (permission == null)
{
throw new AbpException("Undefined permission: " + name);
@ -50,82 +30,41 @@ public class PermissionDefinitionManager : IPermissionDefinitionManager, ISingle
return permission;
}
public virtual PermissionDefinition GetOrNull(string name)
public virtual async Task<PermissionDefinition> GetOrNullAsync(string name)
{
Check.NotNull(name, nameof(name));
return PermissionDefinitions.GetOrDefault(name);
}
public virtual IReadOnlyList<PermissionDefinition> GetPermissions()
{
return PermissionDefinitions.Values.ToImmutableList();
}
public IReadOnlyList<PermissionGroupDefinition> GetGroups()
{
return PermissionGroupDefinitions.Values.ToImmutableList();
}
protected virtual Dictionary<string, PermissionDefinition> CreatePermissionDefinitions()
{
var permissions = new Dictionary<string, PermissionDefinition>();
foreach (var groupDefinition in PermissionGroupDefinitions.Values)
{
foreach (var permission in groupDefinition.Permissions)
{
AddPermissionToDictionaryRecursively(permissions, permission);
}
}
return permissions;
return await _staticStore.GetOrNullAsync(name) ??
await _dynamicStore.GetOrNullAsync(name);
}
protected virtual void AddPermissionToDictionaryRecursively(
Dictionary<string, PermissionDefinition> permissions,
PermissionDefinition permission)
public virtual async Task<IReadOnlyList<PermissionDefinition>> GetPermissionsAsync()
{
if (permissions.ContainsKey(permission.Name))
{
throw new AbpException("Duplicate permission name: " + permission.Name);
}
permissions[permission.Name] = permission;
foreach (var child in permission.Children)
{
AddPermissionToDictionaryRecursively(permissions, child);
}
var staticPermissions = await _staticStore.GetPermissionsAsync();
var staticPermissionNames = staticPermissions
.Select(p => p.Name)
.ToImmutableHashSet();
var dynamicPermissions = await _dynamicStore.GetPermissionsAsync();
/* We prefer static permissions over dynamics */
return staticPermissions.Concat(
dynamicPermissions.Where(d => !staticPermissionNames.Contains(d.Name))
).ToImmutableList();
}
protected virtual Dictionary<string, PermissionGroupDefinition> CreatePermissionGroupDefinitions()
public async Task<IReadOnlyList<PermissionGroupDefinition>> GetGroupsAsync()
{
using (var scope = _serviceProvider.CreateScope())
{
var context = new PermissionDefinitionContext(scope.ServiceProvider);
var providers = Options
.DefinitionProviders
.Select(p => scope.ServiceProvider.GetRequiredService(p) as IPermissionDefinitionProvider)
.ToList();
foreach (var provider in providers)
{
provider.PreDefine(context);
}
foreach (var provider in providers)
{
provider.Define(context);
}
foreach (var provider in providers)
{
provider.PostDefine(context);
}
return context.Groups;
}
var staticGroups = await _staticStore.GetGroupsAsync();
var staticGroupNames = staticGroups
.Select(p => p.Name)
.ToImmutableHashSet();
var dynamicGroups = await _dynamicStore.GetGroupsAsync();
/* We prefer static groups over dynamics */
return staticGroups.Concat(
dynamicGroups.Where(d => !staticGroupNames.Contains(d.Name))
).ToImmutableList();
}
}
}

@ -0,0 +1,62 @@
using System.Linq;
using System.Text.Json.Nodes;
using Volo.Abp.Authorization.Permissions;
using Volo.Abp.DependencyInjection;
using Volo.Abp.SimpleStateChecking;
namespace Volo.Abp.GlobalFeatures;
public class PermissionsSimpleStateCheckerSerializerContributor :
ISimpleStateCheckerSerializerContributor,
ISingletonDependency
{
public const string CheckerShortName = "P";
public string SerializeToJson<TState>(ISimpleStateChecker<TState> checker)
where TState : IHasSimpleStateCheckers<TState>
{
if (checker is not RequirePermissionsSimpleStateChecker<TState> permissionsSimpleStateChecker)
{
return null;
}
var jsonObject = new JsonObject {
["T"] = CheckerShortName,
["A"] = permissionsSimpleStateChecker.RequiresAll
};
var nameArray = new JsonArray();
foreach (var permissionName in permissionsSimpleStateChecker.PermissionNames)
{
nameArray.Add(permissionName);
}
jsonObject["N"] = nameArray;
return jsonObject.ToJsonString();
}
public ISimpleStateChecker<TState> Deserialize<TState>(
JsonObject jsonObject,
TState state)
where TState : IHasSimpleStateCheckers<TState>
{
if (jsonObject["T"]?.ToString() != CheckerShortName)
{
return null;
}
var nameArray = jsonObject["N"] as JsonArray;
if (nameArray == null)
{
throw new AbpException("'N' is not an array in the serialized state checker! JsonObject: " + jsonObject.ToJsonString());
}
return new RequirePermissionsSimpleStateChecker<TState>(
new RequirePermissionsSimpleBatchStateCheckerModel<TState>(
state,
nameArray.Select(x => x.ToString()).ToArray(),
(bool?)jsonObject["A"] ?? false
)
);
}
}

@ -8,6 +8,10 @@ namespace Volo.Abp.Authorization.Permissions;
public class RequirePermissionsSimpleStateChecker<TState> : ISimpleStateChecker<TState>
where TState : IHasSimpleStateCheckers<TState>
{
public bool RequiresAll => _model.RequiresAll;
public string[] PermissionNames => _model.Permissions;
private readonly RequirePermissionsSimpleBatchStateCheckerModel<TState> _model;
public RequirePermissionsSimpleStateChecker(RequirePermissionsSimpleBatchStateCheckerModel<TState> model)

@ -0,0 +1,122 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Authorization.Permissions;
public class StaticPermissionDefinitionStore : IStaticPermissionDefinitionStore, ISingletonDependency
{
protected IDictionary<string, PermissionGroupDefinition> PermissionGroupDefinitions => _lazyPermissionGroupDefinitions.Value;
private readonly Lazy<Dictionary<string, PermissionGroupDefinition>> _lazyPermissionGroupDefinitions;
protected IDictionary<string, PermissionDefinition> PermissionDefinitions => _lazyPermissionDefinitions.Value;
private readonly Lazy<Dictionary<string, PermissionDefinition>> _lazyPermissionDefinitions;
protected AbpPermissionOptions Options { get; }
private readonly IServiceProvider _serviceProvider;
public StaticPermissionDefinitionStore(
IServiceProvider serviceProvider,
IOptions<AbpPermissionOptions> options)
{
_serviceProvider = serviceProvider;
Options = options.Value;
_lazyPermissionDefinitions = new Lazy<Dictionary<string, PermissionDefinition>>(
CreatePermissionDefinitions,
isThreadSafe: true
);
_lazyPermissionGroupDefinitions = new Lazy<Dictionary<string, PermissionGroupDefinition>>(
CreatePermissionGroupDefinitions,
isThreadSafe: true
);
}
protected virtual Dictionary<string, PermissionDefinition> CreatePermissionDefinitions()
{
var permissions = new Dictionary<string, PermissionDefinition>();
foreach (var groupDefinition in PermissionGroupDefinitions.Values)
{
foreach (var permission in groupDefinition.Permissions)
{
AddPermissionToDictionaryRecursively(permissions, permission);
}
}
return permissions;
}
protected virtual void AddPermissionToDictionaryRecursively(
Dictionary<string, PermissionDefinition> permissions,
PermissionDefinition permission)
{
if (permissions.ContainsKey(permission.Name))
{
throw new AbpException("Duplicate permission name: " + permission.Name);
}
permissions[permission.Name] = permission;
foreach (var child in permission.Children)
{
AddPermissionToDictionaryRecursively(permissions, child);
}
}
protected virtual Dictionary<string, PermissionGroupDefinition> CreatePermissionGroupDefinitions()
{
using (var scope = _serviceProvider.CreateScope())
{
var context = new PermissionDefinitionContext(scope.ServiceProvider);
var providers = Options
.DefinitionProviders
.Select(p => scope.ServiceProvider.GetRequiredService(p) as IPermissionDefinitionProvider)
.ToList();
foreach (var provider in providers)
{
provider.PreDefine(context);
}
foreach (var provider in providers)
{
provider.Define(context);
}
foreach (var provider in providers)
{
provider.PostDefine(context);
}
return context.Groups;
}
}
public Task<PermissionDefinition> GetOrNullAsync(string name)
{
return Task.FromResult(PermissionDefinitions.GetOrDefault(name));
}
public virtual Task<IReadOnlyList<PermissionDefinition>> GetPermissionsAsync()
{
return Task.FromResult<IReadOnlyList<PermissionDefinition>>(
PermissionDefinitions.Values.ToImmutableList()
);
}
public Task<IReadOnlyList<PermissionGroupDefinition>> GetGroupsAsync()
{
return Task.FromResult<IReadOnlyList<PermissionGroupDefinition>>(
PermissionGroupDefinitions.Values.ToImmutableList()
);
}
}

@ -113,8 +113,11 @@ public class BundlingService : IBundlingService, ITransientDependency
var contributor = CreateContributorInstance(bundleDefinition.BundleContributorType);
contributor.AddScripts(scriptContext);
}
scriptContext.BundleDefinitions.AddIfNotContains(
x => x.Source == "_framework/blazor.webassembly.js",
() => new BundleDefinition { Source = "_framework/blazor.webassembly.js" });
scriptContext.Add("_framework/blazor.webassembly.js");
return scriptContext;
}
@ -206,7 +209,7 @@ public class BundlingService : IBundlingService, ITransientDependency
builder.Append($" <script src=\"{script.Source}\"");
foreach (var additionalProperty in script.AdditionalProperties)
{
builder.Append($"{additionalProperty.Key}={additionalProperty.Value} ");
builder.Append($" {additionalProperty.Key}={additionalProperty.Value} ");
}
builder.AppendLine("></script>");

@ -41,10 +41,7 @@ public abstract class AppNoLayersTemplateBase : AppTemplateBase
break;
}
if (context.BuildArgs.DatabaseManagementSystem == DatabaseManagementSystem.PostgreSQL)
{
context.Symbols.Add("dbms:PostgreSQL");
}
context.Symbols.Add($"dbms:{context.BuildArgs.DatabaseManagementSystem}");
switch (context.BuildArgs.UiFramework)
{

@ -109,10 +109,7 @@ public abstract class AppTemplateBase : TemplateInfo
steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.MongoDB.Tests", projectFolderPath: "/aspnet-core/test/MyCompanyName.MyProjectName.MongoDB.Tests"));
}
if (context.BuildArgs.DatabaseManagementSystem == DatabaseManagementSystem.PostgreSQL)
{
context.Symbols.Add("dbms:PostgreSQL");
}
context.Symbols.Add($"dbms:{context.BuildArgs.DatabaseManagementSystem}");
}
protected void DeleteUnrelatedProjects(ProjectBuildContext context, List<ProjectBuildPipelineStep> steps)

@ -39,4 +39,10 @@ public static class ServiceCollectionApplicationExtensions
{
return await AbpApplicationFactory.CreateAsync(startupModuleType, services, optionsAction);
}
[CanBeNull]
public static string GetApplicationName(this IServiceCollection services)
{
return services.GetSingletonInstance<IApplicationNameAccessor>().ApplicationName;
}
}

@ -23,6 +23,8 @@ public abstract class AbpApplicationBase : IAbpApplication
public IServiceCollection Services { get; }
public IReadOnlyList<IAbpModuleDescriptor> Modules { get; }
public string ApplicationName { get; }
private bool _configuredServices;
@ -41,8 +43,11 @@ public abstract class AbpApplicationBase : IAbpApplication
var options = new AbpApplicationCreationOptions(services);
optionsAction?.Invoke(options);
ApplicationName = options.ApplicationName;
services.AddSingleton<IAbpApplication>(this);
services.AddSingleton<IApplicationNameAccessor>(this);
services.AddSingleton<IModuleContainer>(this);
services.AddCoreServices();

@ -20,6 +20,9 @@ public class AbpApplicationCreationOptions
public AbpConfigurationBuilderOptions Configuration { get; }
public bool SkipConfigureServices { get; set; }
[CanBeNull]
public string ApplicationName { get; set; }
public AbpApplicationCreationOptions([NotNull] IServiceCollection services)
{

@ -60,6 +60,6 @@ internal class AbpApplicationWithInternalServiceProvider : AbpApplicationBase, I
public override void Dispose()
{
base.Dispose();
ServiceScope.Dispose();
ServiceScope?.Dispose();
}
}

@ -0,0 +1,14 @@
using System;
namespace Volo.Abp.DependencyInjection;
/// <summary>
/// The root service provider of the application.
/// Be careful to use the root service provider since there is no way
/// to release/dispose objects resolved from the root service provider.
/// So, always create a new scope if you need to resolve any service.
/// </summary>
public interface IRootServiceProvider : IServiceProvider
{
}

@ -0,0 +1,19 @@
using System;
namespace Volo.Abp.DependencyInjection;
[ExposeServices(typeof(IRootServiceProvider))]
public class RootServiceProvider : IRootServiceProvider, ISingletonDependency
{
protected IServiceProvider ServiceProvider { get; }
public RootServiceProvider(IObjectAccessor<IServiceProvider> objectAccessor)
{
ServiceProvider = objectAccessor.Value;
}
public virtual object GetService(Type serviceType)
{
return ServiceProvider.GetService(serviceType);
}
}

@ -5,7 +5,10 @@ using Volo.Abp.Modularity;
namespace Volo.Abp;
public interface IAbpApplication : IModuleContainer, IDisposable
public interface IAbpApplication :
IModuleContainer,
IApplicationNameAccessor,
IDisposable
{
/// <summary>
/// Type of the startup (entrance) module of the application.
@ -23,7 +26,7 @@ public interface IAbpApplication : IModuleContainer, IDisposable
/// This can not be used before initialize the application.
/// </summary>
IServiceProvider ServiceProvider { get; }
/// <summary>
/// Calls the Pre/Post/ConfigureServicesAsync methods of the modules.
/// If you use this method, you must have set the <see cref="AbpApplicationCreationOptions.SkipConfigureServices"/>
@ -40,4 +43,4 @@ public interface IAbpApplication : IModuleContainer, IDisposable
/// Used to gracefully shutdown the application and all modules.
/// </summary>
void Shutdown();
}
}

@ -0,0 +1,11 @@
namespace Volo.Abp;
public interface IApplicationNameAccessor
{
/// <summary>
/// Name of the application.
/// This is useful for systems with multiple applications, to distinguish
/// resources of the applications located together.
/// </summary>
string ApplicationName { get; }
}

@ -6,4 +6,4 @@ public interface ISimpleStateChecker<TState>
where TState : IHasSimpleStateCheckers<TState>
{
Task<bool> IsEnabledAsync(SimpleStateCheckerContext<TState> context);
}
}

@ -0,0 +1,12 @@
using System.Text.Json.Nodes;
namespace Volo.Abp.SimpleStateChecking;
public interface ISimpleStateCheckerSerializer
{
public string Serialize<TState>(ISimpleStateChecker<TState> checker)
where TState : IHasSimpleStateCheckers<TState>;
public ISimpleStateChecker<TState> Deserialize<TState>(JsonObject jsonObject, TState state)
where TState : IHasSimpleStateCheckers<TState>;
}

@ -0,0 +1,15 @@
using System.Text.Json.Nodes;
using JetBrains.Annotations;
namespace Volo.Abp.SimpleStateChecking;
public interface ISimpleStateCheckerSerializerContributor
{
[CanBeNull]
public string SerializeToJson<TState>(ISimpleStateChecker<TState> checker)
where TState : IHasSimpleStateCheckers<TState>;
[CanBeNull]
public ISimpleStateChecker<TState> Deserialize<TState>(JsonObject jsonObject, TState state)
where TState : IHasSimpleStateCheckers<TState>;
}

@ -0,0 +1,50 @@
using System.Collections.Generic;
using System.Text.Json.Nodes;
using JetBrains.Annotations;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.SimpleStateChecking;
public class SimpleStateCheckerSerializer :
ISimpleStateCheckerSerializer,
ISingletonDependency
{
private readonly IEnumerable<ISimpleStateCheckerSerializerContributor> _contributors;
public SimpleStateCheckerSerializer(IEnumerable<ISimpleStateCheckerSerializerContributor> contributors)
{
_contributors = contributors;
}
[CanBeNull]
public string Serialize<TState>(ISimpleStateChecker<TState> checker)
where TState : IHasSimpleStateCheckers<TState>
{
foreach (var contributor in _contributors)
{
var result = contributor.SerializeToJson(checker);
if (result != null)
{
return result;
}
}
return null;
}
[CanBeNull]
public ISimpleStateChecker<TState> Deserialize<TState>(JsonObject jsonObject, TState state)
where TState : IHasSimpleStateCheckers<TState>
{
foreach (var contributor in _contributors)
{
var result = contributor.Deserialize(jsonObject, state);
if (result != null)
{
return result;
}
}
return null;
}
}

@ -0,0 +1,90 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json.Nodes;
namespace Volo.Abp.SimpleStateChecking;
public static class SimpleStateCheckerSerializerExtensions
{
public static string Serialize<TState>(
this ISimpleStateCheckerSerializer serializer,
IList<ISimpleStateChecker<TState>> stateCheckers)
where TState : IHasSimpleStateCheckers<TState>
{
switch (stateCheckers.Count)
{
case 0:
return null;
case 1:
var serializedChecker = serializer.Serialize(stateCheckers.Single());
return serializedChecker != null
? $"[{serializedChecker}]"
: null;
default:
var serializedCheckers = new List<string>(stateCheckers.Count);
foreach (var stateChecker in stateCheckers)
{
var serialized = serializer.Serialize(stateChecker);
if (serialized != null)
{
serializedCheckers.Add(serialized);
}
}
return serializedCheckers.Any()
? $"[{serializedCheckers.JoinAsString(",")}]"
: null;
}
}
public static ISimpleStateChecker<TState>[] DeserializeArray<TState>(
this ISimpleStateCheckerSerializer serializer,
string value,
TState state)
where TState : IHasSimpleStateCheckers<TState>
{
if (value.IsNullOrWhiteSpace())
{
return Array.Empty<ISimpleStateChecker<TState>>();
}
var array = JsonNode.Parse(value) as JsonArray;
if (array == null || array.Count == 0)
{
return Array.Empty<ISimpleStateChecker<TState>>();
}
if (array.Count == 1)
{
var jsonObject = array[0] as JsonObject;
if (jsonObject == null)
{
throw new AbpException("JSON value is not an array of objects: " + value);
}
var checker = serializer.Deserialize(jsonObject, state);
if (checker == null)
{
return Array.Empty<ISimpleStateChecker<TState>>();
}
return new[] { checker };
}
var checkers = new List<ISimpleStateChecker<TState>>();
for (var i = 0; i < array.Count; i++)
{
if (array[i] is not JsonObject jsonObject)
{
throw new AbpException("JSON value is not an array of objects: " + value);
}
checkers.Add(serializer.Deserialize(jsonObject, state));
}
return checkers.Where(x => x != null).ToArray();
}
}

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" />
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Volo.Abp.Json\Volo.Abp.Json.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Dapr.Client" Version="1.8.0" />
</ItemGroup>
</Project>

@ -0,0 +1,48 @@
using System.Collections.Concurrent;
using System.Text.Json;
using Dapr.Client;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Json.SystemTextJson;
namespace Volo.Abp.Dapr;
public class AbpDaprClientFactory : ITransientDependency
{
protected AbpDaprOptions Options { get; }
protected AbpSystemTextJsonSerializerOptions SystemTextJsonSerializerOptions { get; }
public AbpDaprClientFactory(
IOptions<AbpDaprOptions> options,
IOptions<AbpSystemTextJsonSerializerOptions> systemTextJsonSerializerOptions)
{
Options = options.Value;
SystemTextJsonSerializerOptions = systemTextJsonSerializerOptions.Value;
}
public virtual async Task<DaprClient> CreateAsync()
{
var builder = new DaprClientBuilder()
.UseJsonSerializationOptions(await CreateJsonSerializerOptions());
if (!Options.HttpEndpoint.IsNullOrWhiteSpace())
{
builder.UseHttpEndpoint(Options.HttpEndpoint);
}
if (!Options.GrpcEndpoint.IsNullOrWhiteSpace())
{
builder.UseGrpcEndpoint(Options.GrpcEndpoint);
}
return builder.Build();
}
private readonly static ConcurrentDictionary<string, JsonSerializerOptions> JsonSerializerOptionsCache = new ConcurrentDictionary<string, JsonSerializerOptions>();
protected virtual Task<JsonSerializerOptions> CreateJsonSerializerOptions()
{
return Task.FromResult(JsonSerializerOptionsCache.GetOrAdd(nameof(AbpDaprClientFactory),
_ => new JsonSerializerOptions(SystemTextJsonSerializerOptions.JsonSerializerOptions)));
}
}

@ -0,0 +1,15 @@
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Json;
using Volo.Abp.Modularity;
namespace Volo.Abp.Dapr;
[DependsOn(typeof(AbpJsonModule))]
public class AbpDaprModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
var configuration = context.Services.GetConfiguration();
Configure<AbpDaprOptions>(configuration.GetSection("Dapr"));
}
}

@ -0,0 +1,10 @@
namespace Volo.Abp.Dapr;
public class AbpDaprOptions
{
public string AppId { get; set; }
public string HttpEndpoint { get; set; }
public string GrpcEndpoint { get; set; }
}

@ -0,0 +1,16 @@
namespace Volo.Abp.Dapr;
public interface IDaprSerializer
{
byte[] Serialize(object obj);
object Deserialize(byte[] value, Type type);
T Deserialize<T>(byte[] value);
string SerializeToString(object obj);
object Deserialize(string value, Type type);
T Deserialize<T>(string value);
}

@ -0,0 +1,45 @@
using System.Text;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Json;
namespace Volo.Abp.Dapr;
public class Utf8JsonDaprSerializer : IDaprSerializer, ITransientDependency
{
private readonly IJsonSerializer _jsonSerializer;
public Utf8JsonDaprSerializer(IJsonSerializer jsonSerializer)
{
_jsonSerializer = jsonSerializer;
}
public byte[] Serialize(object obj)
{
return Encoding.UTF8.GetBytes(_jsonSerializer.Serialize(obj));
}
public object Deserialize(byte[] value, Type type)
{
return _jsonSerializer.Deserialize(type, Encoding.UTF8.GetString(value));
}
public T Deserialize<T>(byte[] value)
{
return _jsonSerializer.Deserialize<T>(Encoding.UTF8.GetString(value));
}
public string SerializeToString(object obj)
{
return _jsonSerializer.Serialize(obj);
}
public object Deserialize(string value, Type type)
{
return _jsonSerializer.Deserialize(type, value);
}
public T Deserialize<T>(string value)
{
return _jsonSerializer.Deserialize<T>(value);
}
}

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" />
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Volo.Abp.Dapr\Volo.Abp.Dapr.csproj" />
<ProjectReference Include="..\Volo.Abp.DistributedLocking.Abstractions\Volo.Abp.DistributedLocking.Abstractions.csproj" />
</ItemGroup>
</Project>

@ -0,0 +1,13 @@
namespace Volo.Abp.DistributedLocking.Dapr;
public class AbpDistributedLockDaprOptions
{
public string StoreName { get; set; }
public TimeSpan DefaultTimeout { get; set; }
public AbpDistributedLockDaprOptions()
{
DefaultTimeout = TimeSpan.FromSeconds(30);
}
}

@ -0,0 +1,11 @@
using Volo.Abp.Dapr;
using Volo.Abp.Modularity;
namespace Volo.Abp.DistributedLocking.Dapr;
[DependsOn(
typeof(AbpDistributedLockingAbstractionsModule),
typeof(AbpDaprModule))]
public class AbpDistributedLockingDaprModule : AbpModule
{
}

@ -0,0 +1,50 @@
using Microsoft.Extensions.Options;
using Volo.Abp.Dapr;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.DistributedLocking.Dapr;
[Dependency(ReplaceServices = true)]
public class DaprAbpDistributedLock : IAbpDistributedLock, ITransientDependency
{
protected AbpDaprClientFactory DaprClientFactory { get; }
protected AbpDistributedLockDaprOptions DistributedLockDaprOptions { get; }
protected AbpDaprOptions DaprOptions { get; }
public DaprAbpDistributedLock(
AbpDaprClientFactory daprClientFactory,
IOptions<AbpDistributedLockDaprOptions> distributedLockDaprOptions,
IOptions<AbpDaprOptions> daprOptions)
{
DaprClientFactory = daprClientFactory;
DaprOptions = daprOptions.Value;
DistributedLockDaprOptions = distributedLockDaprOptions.Value;
}
public async Task<IAbpDistributedLockHandle> TryAcquireAsync(
string name,
TimeSpan timeout = default,
CancellationToken cancellationToken = default)
{
if (timeout == default)
{
timeout = DistributedLockDaprOptions.DefaultTimeout;
}
var daprClient = await DaprClientFactory.CreateAsync();
var lockResponse = await daprClient.Lock(
DistributedLockDaprOptions.StoreName,
name,
DaprOptions.AppId,
(int)timeout.TotalSeconds,
cancellationToken);
if (lockResponse == null || !lockResponse.Success)
{
return null;
}
return new DaprAbpDistributedLockHandle(lockResponse);
}
}

@ -0,0 +1,18 @@
using Dapr.Client;
namespace Volo.Abp.DistributedLocking.Dapr;
public class DaprAbpDistributedLockHandle : IAbpDistributedLockHandle
{
protected TryLockResponse LockResponse { get; }
public DaprAbpDistributedLockHandle(TryLockResponse lockResponse)
{
LockResponse = lockResponse;
}
public async ValueTask DisposeAsync()
{
await LockResponse.DisposeAsync();
}
}

@ -16,6 +16,7 @@
<ItemGroup>
<ProjectReference Include="..\Volo.Abp.DistributedLocking.Abstractions\Volo.Abp.DistributedLocking.Abstractions.csproj" />
<ProjectReference Include="..\Volo.Abp.Threading\Volo.Abp.Threading.csproj" />
</ItemGroup>
<ItemGroup>

@ -1,11 +1,12 @@
using Volo.Abp.Modularity;
using Volo.Abp.Threading;
namespace Volo.Abp.DistributedLocking;
[DependsOn(
typeof(AbpDistributedLockingAbstractionsModule)
typeof(AbpDistributedLockingAbstractionsModule),
typeof(AbpThreadingModule)
)]
public class AbpDistributedLockingModule : AbpModule
{
}

@ -3,6 +3,7 @@ using System.Threading;
using System.Threading.Tasks;
using Medallion.Threading;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Threading;
namespace Volo.Abp.DistributedLocking;
@ -10,10 +11,14 @@ namespace Volo.Abp.DistributedLocking;
public class MedallionAbpDistributedLock : IAbpDistributedLock, ITransientDependency
{
protected IDistributedLockProvider DistributedLockProvider { get; }
protected ICancellationTokenProvider CancellationTokenProvider { get; }
public MedallionAbpDistributedLock(IDistributedLockProvider distributedLockProvider)
public MedallionAbpDistributedLock(
IDistributedLockProvider distributedLockProvider,
ICancellationTokenProvider cancellationTokenProvider)
{
DistributedLockProvider = distributedLockProvider;
CancellationTokenProvider = cancellationTokenProvider;
}
public async Task<IAbpDistributedLockHandle> TryAcquireAsync(
@ -23,7 +28,14 @@ public class MedallionAbpDistributedLock : IAbpDistributedLock, ITransientDepend
{
Check.NotNullOrWhiteSpace(name, nameof(name));
var handle = await DistributedLockProvider.TryAcquireLockAsync(name, timeout, cancellationToken);
CancellationTokenProvider.FallbackToProvider(cancellationToken);
var handle = await DistributedLockProvider.TryAcquireLockAsync(
name,
timeout,
CancellationTokenProvider.FallbackToProvider(cancellationToken)
);
if (handle == null)
{
return null;

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save