diff --git a/.github/workflows/auto-pr.yml b/.github/workflows/auto-pr.yml index f916789293..4cb89fd21d 100644 --- a/.github/workflows/auto-pr.yml +++ b/.github/workflows/auto-pr.yml @@ -1,13 +1,13 @@ -name: Merge branch dev with rel-10.0 +name: Merge branch dev with rel-10.1 on: push: branches: - - rel-10.0 + - rel-10.1 permissions: contents: read jobs: - merge-dev-with-rel-10-0: + merge-dev-with-rel-10-1: permissions: contents: write # for peter-evans/create-pull-request to create branch pull-requests: write # for peter-evans/create-pull-request to create a PR @@ -18,14 +18,14 @@ jobs: ref: dev - name: Reset promotion branch run: | - git fetch origin rel-10.0:rel-10.0 - git reset --hard rel-10.0 + git fetch origin rel-10.1:rel-10.1 + git reset --hard rel-10.1 - name: Create Pull Request uses: peter-evans/create-pull-request@v3 with: - branch: auto-merge/rel-10-0/${{github.run_number}} - title: Merge branch dev with rel-10.0 - body: This PR generated automatically to merge dev with rel-10.0. Please review the changed files before merging to prevent any errors that may occur. + branch: auto-merge/rel-10-1/${{github.run_number}} + title: Merge branch dev with rel-10.1 + body: This PR generated automatically to merge dev with rel-10.1. Please review the changed files before merging to prevent any errors that may occur. reviewers: maliming draft: true token: ${{ github.token }} @@ -34,5 +34,5 @@ jobs: GH_TOKEN: ${{ secrets.BOT_SECRET }} run: | gh pr ready - gh pr review auto-merge/rel-10-0/${{github.run_number}} --approve - gh pr merge auto-merge/rel-10-0/${{github.run_number}} --merge --auto --delete-branch + gh pr review auto-merge/rel-10-1/${{github.run_number}} --approve + gh pr merge auto-merge/rel-10-1/${{github.run_number}} --merge --auto --delete-branch diff --git a/Directory.Packages.props b/Directory.Packages.props index 67953c873c..fa951f8d7d 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -7,7 +7,6 @@ - @@ -20,10 +19,10 @@ - - - - + + + + @@ -137,11 +136,11 @@ - + - + @@ -149,7 +148,7 @@ - + diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json index 7f9b928749..bb6c1aeef4 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json @@ -783,7 +783,7 @@ "Enum:SourceChannel:1": "Studio", "Enum:SourceChannel:2": "Support Site", "Enum:SourceChannel:3": "Suite", - "Menu:OrganizationTokenUsage": "Organization Token Usage", + "Menu:AITokens": "AI Tokens", "Permission:OrganizationTokenUsage": "Organization Token Usage" } } diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/en.json index 622f30420c..16b93b9cc5 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/en.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/en.json @@ -269,7 +269,7 @@ "Referral.CannotDeleteUsedLink": "You cannot delete a referral link that has already been used.", "Referral.CannotReferYourself": "You cannot create a referral link for your own email address.", "Referral:TargetEmail": "Target Email", - "Referral.CannotReferSameOrganizationMember": "You cannot create a referral link for a user who is already a member of your organization.", + "Referral.CannotReferSameOrganizationMember": "Referral links cannot be used for existing organization members.", "LinkCopiedToClipboard": "Link copied to clipboard", "AreYouSureToDeleteReferralLink": "Are you sure you want to delete this referral link?", "DefaultErrorMessage": "An error occurred." diff --git a/docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/POST.md b/docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/POST.md new file mode 100644 index 0000000000..a56dd5a464 --- /dev/null +++ b/docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/POST.md @@ -0,0 +1,173 @@ +# Announcing .NET Aspire Integration for ABP Microservice Template + +We are excited to announce the integration of **.NET Aspire** into the ABP microservice solution, available starting with **ABP Studio v2.0.0**. This integration brings a unified development experience for building, running, debugging, and deploying distributed applications. With Aspire, you can now orchestrate your entire microservice ecosystem with a single command, eliminating complex configurations and making local development effortless. + +## What is .NET Aspire? + +[Aspire](https://aspire.dev/get-started/what-is-aspire/) is a cloud-ready stack designed to streamline the development of distributed applications. It provides: + +- **Orchestration**: A code-first approach to defining and running distributed applications, managing dependencies, and launch order. +- **Integrations**: Pre-built components for common services (databases, caches, message brokers) with automatic configuration. +- **Tooling**: A developer dashboard for real-time monitoring of logs, traces, metrics, and resource health. +- **Service Discovery**: Automatic service-to-service communication without hardcoded endpoints. +- **Observability**: Built-in OpenTelemetry support for distributed tracing, metrics, and structured logging. + +## How Does It Work with ABP? + +When you enable .NET Aspire in an ABP microservice solution, you get a fully integrated development experience where: + +- All microservices, gateways, and applications are orchestrated through a single entry point (AppHost). +- Infrastructure containers (databases, Redis, RabbitMQ, Elasticsearch, etc.) are managed as code. +- OpenTelemetry, health checks, and service discovery are automatically configured for all projects via the shared ServiceDefaults project. + +## Enabling Aspire in Your Solution + +When creating a new microservice solution via ABP Studio: + +1. In the solution creation wizard, look for the **".NET Aspire Integration"** step. +2. Toggle the option to **enable .NET Aspire**. +3. Complete the wizard—Aspire projects will be generated along with your solution. + +![Enable Aspire in ABP Studio](aspire-configuration.png) + +## Solution Structure Changes + +When Aspire is enabled, two additional projects are added to your solution: + +![Aspire Solution Structure](aspire-solution-structure.png) + +### AppHost (Orchestrator) + +[`AppHost`](https://aspire.dev/get-started/app-host/) is the .NET Aspire orchestrator project that declares all resources (services, databases, containers, applications) and their dependencies in C# code. It provides: + +- **Centralized orchestration**: Start your entire microservice ecosystem with a single command. +- **Code-first infrastructure**: Databases, Redis, RabbitMQ, Elasticsearch, and observability tools are defined programmatically. +- **Dependency management**: Services start in the correct order using `WaitFor()` declarations. +- **Automatic configuration**: Connection strings, endpoints, and environment variables are injected automatically. + +### ServiceDefaults + +[`ServiceDefaults`](https://aspire.dev/fundamentals/service-defaults/) is a shared library that provides common cloud-native configuration for all projects in the solution. Every service uses the same observability, health check, and resilience patterns. + +| Feature | Description | +|---------|-------------| +| OpenTelemetry | Tracing, metrics, and structured logging with automatic instrumentation | +| Health Checks | `/health` and `/alive` endpoints for Kubernetes-style probes | +| Service Discovery | Automatic resolution of service endpoints | +| HTTP Resilience | Retry policies, timeouts, and circuit breakers for HTTP clients | + +## Running the Solution with Aspire + +Running your microservice solution has never been easier: + +1. Open **Solution Runner** in ABP Studio. +2. Select the **Aspire** profile. +3. Run `AppHost`. + +![Solution Runner with Aspire](solution-runner-aspire-profile.png) + +AppHost automatically: + +- Starts all infrastructure containers (database, Redis, RabbitMQ, Elasticsearch, etc.). +- Launches all microservices, gateways, and applications in dependency order. +- Injects connection strings and environment variables. +- Opens the Aspire Dashboard for monitoring. + +![Aspire AppHost Resource Topology](aspire-apphost-topology.png) + +## Aspire Dashboard + +The Aspire Dashboard provides real-time tracking of your application's state. It enables you to monitor logs, traces, metrics, and environment configurations in an intuitive UI. + +![Aspire Dashboard Resources](aspire-dashboard-resources.png) + +### Key Dashboard Features + +#### Console Logs + +Display console logs from all resources in real-time. Filter by resource and log level to quickly find relevant information during development and debugging. + +![Aspire Dashboard Console](aspire-dashboard-console.png) + +#### Structured Logs + +View structured logs from all resources with advanced filtering capabilities. Search and filter logs by resource, log level, timestamp, and custom properties. + +![Aspire Dashboard Structured Logs](aspire-dashboard-structured-logs.png) + +#### Distributed Traces + +Explore distributed traces across your microservices to understand request flows and identify performance bottlenecks. + +![Aspire Dashboard Traces](aspire-dashboard-traces.png) + +#### Metrics + +Monitor real-time metrics including HTTP requests, response times, garbage collection, memory usage, and custom metrics. + +![Aspire Dashboard Metrics](aspire-dashboard-metrics.png) + +## Pre-Configured Observability Tools + +AppHost comes with pre-configured observability and management tools: + +### Grafana + +Visualization and analytics platform for monitoring metrics with interactive dashboards. + +![Grafana Dashboard](aspire-grafana-dashboard.png) + +### Jaeger + +Distributed tracing system to monitor and troubleshoot problems across microservices. + +![Jaeger Traces](aspire-jaeger-traces.png) + +### Kibana + +Visualization tool for Elasticsearch data with search and data visualization capabilities for logs. + +![Kibana Dashboard](aspire-kibana-dashboard.png) + +### Prometheus + +Monitoring and alerting toolkit that collects and stores metrics as time series data. + +![Prometheus Dashboard](aspire-prometheus-dashboard.png) + +### RabbitMQ Management + +Web-based interface for managing and monitoring the RabbitMQ message broker. + +![RabbitMQ Management](aspire-rabbitmq-management.png) + +### Redis Insight + +Visual tool for Redis that allows you to browse data, run commands, and monitor performance. + +![Redis Insight](aspire-redis-insight.png) + +### Database Admin Tools + +The database management admin tool varies by database type: + +| Database | Tool | +|----------|------| +| SQL Server | DBeaver CloudBeaver | +| MySQL | phpMyAdmin | +| PostgreSQL | pgAdmin | +| MongoDB | Mongo Express | + +![pgAdmin Dashboard](aspire-database-postgre-pgadmin.png) + +## Get Started Today + +Ready to experience the power of .NET Aspire with ABP? Create a new microservice solution in ABP Studio and enable the .NET Aspire integration option. For detailed documentation, visit our [.NET Aspire Integration documentation](https://abp.io/docs/latest/solution-templates/microservice/aspire-integration). + +To learn more about .NET Aspire, visit: [https://aspire.dev](https://aspire.dev/get-started/what-is-aspire/) + +We are excited to bring this integration to you and can't wait to hear your feedback. If you have any questions or suggestions, please drop a comment below. + +Happy coding! + +**The Volosoft Team** diff --git a/docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-apphost-topology.png b/docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-apphost-topology.png new file mode 100644 index 0000000000..a7622cff63 Binary files /dev/null and b/docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-apphost-topology.png differ diff --git a/docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-configuration.png b/docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-configuration.png new file mode 100644 index 0000000000..74cd5066bb Binary files /dev/null and b/docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-configuration.png differ diff --git a/docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-dashboard-console.png b/docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-dashboard-console.png new file mode 100644 index 0000000000..fce1ca9355 Binary files /dev/null and b/docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-dashboard-console.png differ diff --git a/docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-dashboard-metrics.png b/docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-dashboard-metrics.png new file mode 100644 index 0000000000..690c3a910e Binary files /dev/null and b/docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-dashboard-metrics.png differ diff --git a/docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-dashboard-resources.png b/docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-dashboard-resources.png new file mode 100644 index 0000000000..7b6777a8b9 Binary files /dev/null and b/docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-dashboard-resources.png differ diff --git a/docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-dashboard-structured-logs.png b/docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-dashboard-structured-logs.png new file mode 100644 index 0000000000..4c26e1fe48 Binary files /dev/null and b/docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-dashboard-structured-logs.png differ diff --git a/docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-dashboard-traces.png b/docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-dashboard-traces.png new file mode 100644 index 0000000000..9e5d761582 Binary files /dev/null and b/docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-dashboard-traces.png differ diff --git a/docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-database-postgre-pgadmin.png b/docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-database-postgre-pgadmin.png new file mode 100644 index 0000000000..a11107ddd4 Binary files /dev/null and b/docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-database-postgre-pgadmin.png differ diff --git a/docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-grafana-dashboard.png b/docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-grafana-dashboard.png new file mode 100644 index 0000000000..6c1eb3999e Binary files /dev/null and b/docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-grafana-dashboard.png differ diff --git a/docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-jaeger-traces.png b/docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-jaeger-traces.png new file mode 100644 index 0000000000..e9409465b1 Binary files /dev/null and b/docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-jaeger-traces.png differ diff --git a/docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-kibana-dashboard.png b/docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-kibana-dashboard.png new file mode 100644 index 0000000000..90bcbf14b5 Binary files /dev/null and b/docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-kibana-dashboard.png differ diff --git a/docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-prometheus-dashboard.png b/docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-prometheus-dashboard.png new file mode 100644 index 0000000000..764f6cfb38 Binary files /dev/null and b/docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-prometheus-dashboard.png differ diff --git a/docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-rabbitmq-management.png b/docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-rabbitmq-management.png new file mode 100644 index 0000000000..362af7aac3 Binary files /dev/null and b/docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-rabbitmq-management.png differ diff --git a/docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-redis-insight.png b/docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-redis-insight.png new file mode 100644 index 0000000000..429cbefae7 Binary files /dev/null and b/docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-redis-insight.png differ diff --git a/docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-solution-structure.png b/docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-solution-structure.png new file mode 100644 index 0000000000..3379892b69 Binary files /dev/null and b/docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/aspire-solution-structure.png differ diff --git a/docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/solution-runner-aspire-profile.png b/docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/solution-runner-aspire-profile.png new file mode 100644 index 0000000000..d11edfe584 Binary files /dev/null and b/docs/en/Blog-Posts/2025-12-24-Announcing-Aspire-For-Microservice-Template/solution-runner-aspire-profile.png differ diff --git a/docs/en/Community-Articles/2025-12-13-Building-Dynamic-XML-Sitemaps-With-ABP-Framework/cover.png b/docs/en/Community-Articles/2025-12-13-Building-Dynamic-XML-Sitemaps-With-ABP-Framework/cover.png new file mode 100644 index 0000000000..69116d7961 Binary files /dev/null and b/docs/en/Community-Articles/2025-12-13-Building-Dynamic-XML-Sitemaps-With-ABP-Framework/cover.png differ diff --git a/docs/en/Community-Articles/2025-12-13-Building-Dynamic-XML-Sitemaps-With-ABP-Framework/images/sitemap-architecture.svg b/docs/en/Community-Articles/2025-12-13-Building-Dynamic-XML-Sitemaps-With-ABP-Framework/images/sitemap-architecture.svg new file mode 100644 index 0000000000..57721c4550 --- /dev/null +++ b/docs/en/Community-Articles/2025-12-13-Building-Dynamic-XML-Sitemaps-With-ABP-Framework/images/sitemap-architecture.svg @@ -0,0 +1,122 @@ + + + + + + + + + + Sitemap Module Architecture + + + 1. Discovery Layer + + RazorPageDiscoveryService + + • Scans assemblies + • Finds PageModel classes + • Extracts route metadata + + + + + + 2. Source Layer + + + + IStaticPageSitemapSource + + Processes attributes: + [IncludeSitemapXml] + Returns static page + sitemap items + + + + IGroupedSitemapSource + + Queries repositories: + Books, Articles, Products + Returns dynamic + sitemap items + + + + Custom Sources + + Implement + ISitemapItemSource + for custom logic + + + + + + + + 3. Collection Layer + + SitemapItemCollector + + • Aggregates items from all sources + • Groups by category (Main, Blog, Products) + • Removes duplicates + • Returns Dictionary<Group, Items> + + + + + + 4. Generation Layer + + SitemapXmlGenerator + + • Converts items to XML format + • Adds <loc>, <lastmod>, <priority> + • Validates against sitemap protocol + • Returns XML string + + + + + + 5. Management Layer + + + + SitemapFileGenerator + + • Orchestrates collection + • Calls XML generator + • Writes files to disk: + Sitemaps/ + + + + SitemapRegenerationWorker + + • Runs periodically (e.g., hourly) + • Triggers SitemapFileGenerator + • Non-blocking background execution + + + + + + 📄 sitemap.xml + 📄 sitemap-Blog.xml + 📄 sitemap-Products.xml + + + + diff --git a/docs/en/Community-Articles/2025-12-13-Building-Dynamic-XML-Sitemaps-With-ABP-Framework/post.md b/docs/en/Community-Articles/2025-12-13-Building-Dynamic-XML-Sitemaps-With-ABP-Framework/post.md new file mode 100644 index 0000000000..fd9d10756e --- /dev/null +++ b/docs/en/Community-Articles/2025-12-13-Building-Dynamic-XML-Sitemaps-With-ABP-Framework/post.md @@ -0,0 +1,475 @@ +# Building Dynamic XML Sitemaps with ABP Framework + +Search Engine Optimization (SEO) is crucial for any web application that wants to be discovered by users. One of the most fundamental SEO practices is providing a comprehensive XML sitemap that helps search engines crawl and index your website efficiently. In this article, we'll use a reusable ABP module that automatically generates dynamic XML sitemaps for both static Razor Pages and dynamic content from your database. + +By the end of this tutorial, you'll have a production-ready sitemap solution that discovers your pages automatically, includes dynamic content like blog posts or products, and regenerates sitemaps in the background without impacting performance. + +## What is an XML Sitemap? + +An XML sitemap is a file that lists all important pages of your website in a structured format that search engines can easily read. It acts as a roadmap for crawlers like Google, Bing, and others, telling them which pages exist, when they were last updated, and how they relate to each other. + +For modern web applications with dynamic content, manually maintaining sitemap files quickly becomes impractical. A dynamic sitemap solution that automatically discovers and updates URLs is essential for: + +- **Large content sites** with frequently changing blog posts, articles, or products +- **Multi-tenant applications** where each tenant may have different content +- **Enterprise applications** with complex page hierarchies +- **E-commerce platforms** with thousands of product pages + +## Why Build a Custom Sitemap Module? + +While there are general-purpose sitemap libraries available, building a custom module for ABP Framework provides several advantages: + +✅ **Deep ABP Integration**: Leverages ABP's dependency injection, background workers, and module system +✅ **Automatic Discovery**: Uses ASP.NET Core's Razor Page infrastructure to automatically find pages +✅ **Type-Safe Configuration**: Strongly-typed attributes and options for configuration +✅ **Multi-Group Support**: Organize sitemaps by logical groups (main, blog, products, etc.) +✅ **Background Generation**: Non-blocking sitemap regeneration using ABP's background worker system +✅ **Repository Integration**: Direct integration with ABP repositories for database entities + +## Project Architecture Overview + +Before using the module, let's understand its architecture: + +![Architecture Diagram](./images/sitemap-architecture.svg) + +The sitemap module consists of several key components: + +1. **Discovery Layer**: Discovers Razor Pages and their metadata using reflection +2. **Source Layer**: Defines contracts for providing sitemap items (static pages and dynamic content) +3. **Collection Layer**: Collects items from all registered sources +4. **Generation Layer**: Transforms collected items into XML format +5. **Management Layer**: Orchestrates file generation and background workers + +## Installation + +To get started, clone the demo repository which includes the sitemap module: + +```bash +git clone https://github.com/salihozkara/AbpSitemapDemo +cd AbpSitemapDemo +``` + +The repository contains the sitemap module in the `Modules/abp.sitemap/` directory. To use it in your own project, add a project reference: + +```xml + +``` + +## Module Configuration + +After installing the package, add the module to your ABP application's module class: + +```csharp +using Abp.Sitemap.Web; + +[DependsOn( + typeof(SitemapWebModule), // 👈 Add sitemap module + // ... other dependencies +)] +public class YourProjectWebModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + // Configure sitemap options + Configure(options => + { + options.BaseUrl = "https://yourdomain.com"; // 👈 Your website URL + options.FolderPath = "Sitemaps"; // 👈 Where XML files are stored + options.WorkerPeriod = 3600000; // 👈 Regenerate every hour (in milliseconds) + }); + } +} +``` + +> **Note:** In ABP applications, BaseUrl can be resolved from AppUrlOptions to stay consistent with environment configuration. + +That's it! The module is now integrated and will automatically: +- Discover your Razor Pages +- Generate sitemap XML files on application startup +- Regenerate sitemaps in the background every hour + +## Usage Examples + +Let's explore practical examples of using the sitemap module. You can see complete working examples in the [AbpSitemapDemo repository](https://github.com/salihozkara/AbpSitemapDemo). + +### Example 1: Mark Static Pages + +The simplest way to include pages in your sitemap is using attributes: + +```csharp +using Abp.Sitemap.Web.Sitemap.Sources.Page.Attributes; + +namespace YourProject.Pages; + +[IncludeSitemapXml] // 👈 Include in default "Main" group +public class IndexModel : PageModel +{ + public void OnGet() + { + // Your page logic + } +} + +[IncludeSitemapXml(Group = "Help")] +public class FaqModel : PageModel +{ + public void OnGet() + { + // Your page logic + } +} +``` + +These pages will be automatically discovered and included in the sitemap XML files. + +### Example 2: Add Dynamic Content from Database + +For dynamic content like blog posts, products, or articles, create a custom sitemap source. Here's a complete example using a Book entity: + +```csharp +using Abp.Sitemap.Web.Sitemap.Core; +using Abp.Sitemap.Web.Sitemap.Sources.Group; +using Volo.Abp.DependencyInjection; + +namespace YourProject.Sitemaps; + +public class BookSitemapSource : GroupedSitemapItemSource, ITransientDependency +{ + public BookSitemapSource( + IReadOnlyRepository repository, + IAsyncQueryableExecuter executer) + : base(repository, executer, group: "Books") // 👈 Creates sitemap-Books.xml + { + Filter = x => x.IsPublished; // 👈 Only published books + } + + protected override Expression> Selector => + book => new SitemapItem( + book.Id.ToString(), // 👈 Unique identifier + $"/Books/Detail/{book.Id}", // 👈 URL pattern matching your route + book.LastModificationTime ?? book.CreationTime // 👈 Last modified date + ) + { + ChangeFrequency = "weekly", + Priority = 0.7 + }; +} +``` + +Key points: +- Inherits from `GroupedSitemapItemSource` +- Specifies the entity type (`Book`) +- Defines a group name ("Books") which creates `sitemap-Books.xml` +- Uses `Filter` to include only published books +- Maps entity properties to sitemap URLs using `Selector` +- Automatically registered via `ITransientDependency` + +### Example 3: Category-Based Dynamic Content + +For content with categories, you can build more complex URL patterns: + +```csharp +using Abp.Sitemap.Web.Sitemap.Core; +using Abp.Sitemap.Web.Sitemap.Sources.Group; + +namespace YourProject.Sitemaps; + +public class ArticleSitemapSource : GroupedSitemapItemSource
, ITransientDependency +{ + public ArticleSitemapSource( + IReadOnlyRepository
repository, + IAsyncQueryableExecuter executer) + : base(repository, executer, "Articles") + { + // Multiple filter conditions + Filter = x => x.IsPublished && + !x.IsDeleted && + x.PublishDate <= DateTime.Now; + } + + protected override Expression> Selector => + article => new SitemapItem( + article.Id.ToString(), + $"/blog/{article.Category.Slug}/{article.Slug}", // 👈 Category-based URL + article.LastModificationTime ?? article.CreationTime + ); +} +``` + +This example demonstrates: +- Multiple filter conditions for complex business logic +- Building URLs with category slugs + +## Testing Your Sitemaps + +After configuring the module, test your sitemap generation: + +### 1. Run Your Application + +```bash +dotnet run +``` + +The sitemaps are automatically generated on application startup. + +### 2. Check Generated Files + +Navigate to `{WebProject}/Sitemaps/` directory (at the root of your web project): + +``` +{WebProject} +└── Sitemaps/ + ├── sitemap.xml # Main group (static pages) + ├── sitemap-Books.xml # Books from database + ├── sitemap-Articles.xml # Articles from database + └── sitemap-Help.xml # Help pages +``` + +### 3. Verify XML Content + +Open `sitemap-Books.xml` and verify the structure: + +```xml + + + + https://yourdomain.com/Books/Detail/3a071e39-12c9-48d7-8c1e-3b4f5c6d7e8f + 2025-12-13 + + + https://yourdomain.com/Books/Detail/7b8c9d0e-1f2a-3b4c-5d6e-7f8g9h0i1j2k + 2025-12-10 + + +``` + +### 4. Test in Browser + +Visit the sitemap URLs directly (the module serves them from the root path): +- Main sitemap: `https://localhost:5001/sitemap.xml` +- Books sitemap: `https://localhost:5001/sitemap-Books.xml` + +> **Note:** The sitemaps are stored in `{WebProject}/Sitemaps/` directory and served directly from the root URL. + +## Advanced Configuration + +### Custom Regeneration Schedule + +Control when sitemaps are regenerated using cron expressions: + +```csharp +public override void ConfigureServices(ServiceConfigurationContext context) +{ + Configure(options => + { + options.BaseUrl = "https://yourdomain.com"; + options.WorkerCronExpression = "0 0 2 * * ?"; // 👈 Every day at 2 AM + // Or use period in milliseconds: + // options.WorkerPeriod = 7200000; // 2 hours + }); +} +``` + +### Environment-Specific Configuration + +Use different settings for development and production: + +```csharp +public override void ConfigureServices(ServiceConfigurationContext context) +{ + var configuration = context.Services.GetConfiguration(); + var hostingEnvironment = context.Services.GetHostingEnvironment(); + + Configure(options => + { + if (hostingEnvironment.IsDevelopment()) + { + options.BaseUrl = "https://localhost:5001"; + options.WorkerPeriod = 300000; // 5 minutes for testing + } + else + { + options.BaseUrl = configuration["App:SelfUrl"]!; + options.WorkerPeriod = 3600000; // 1 hour in production + } + + options.FolderPath = "Sitemaps"; + }); +} +``` + +### Manual Sitemap Generation + +Trigger sitemap generation manually (useful for admin panels): + +```csharp +using Abp.Sitemap.Web.Sitemap.Management; + +public class SitemapManagementService : ITransientDependency +{ + private readonly SitemapFileGenerator _generator; + + public SitemapManagementService(SitemapFileGenerator generator) + { + _generator = generator; + } + + [Authorize("Admin")] + public async Task RegenerateSitemapsAsync() + { + await _generator.GenerateAsync(); // 👈 Manual regeneration + } +} +``` + +## Real-World Use Cases + +Here are practical scenarios where the sitemap module excels: + +### E-Commerce Platform +```csharp +// Products grouped by category +public class ProductSitemapSource : GroupedSitemapItemSource +{ + // Automatically includes all active products with stock +} + +// Separate sitemap for categories +public class CategorySitemapSource : GroupedSitemapItemSource +{ + // All browsable categories +} + +// Brand pages +public class BrandSitemapSource : GroupedSitemapItemSource +{ + // All active brands +} +``` + +Result: `sitemap-Products.xml`, `sitemap-Categories.xml`, `sitemap-Brands.xml` + +### Content Management System +```csharp +// Blog posts by date +public class BlogPostSitemapSource : GroupedSitemapItemSource +{ + // Filter by published date, priority based on view count +} + +// Static CMS pages +[IncludeSitemapXml] +public class AboutUsModel : PageModel { } +``` + +## Best Practices + +### 1. Group Related Content +Organize your sitemaps logically: +```csharp +// ✅ Good: Logical grouping +"Products", "Categories", "Brands", "Blog", "Help" + +// ❌ Bad: Everything in one group +"Main" // Contains 50,000 mixed URLs +``` + +### 2. Use Filters Wisely +```csharp +// ✅ Good: Only published, non-deleted content +Filter = x => x.IsPublished && + !x.IsDeleted && + x.PublishDate <= DateTime.Now + +// ❌ Bad: Including draft content +Filter = x => true // Everything included +``` + +### 3. Keep URLs Clean +```csharp +// ✅ Good: SEO-friendly URLs +$"/products/{product.Slug}" +$"/blog/{year}/{month}/{article.Slug}" + +// ❌ Bad: Technical IDs exposed +$"/product-detail?id={product.Id}" +``` + +## Troubleshooting + +### Sitemap Not Generated +**Problem:** No XML files in `{WebProject}/Sitemaps/` + +**Solutions:** +1. Check module is added to dependencies +2. Verify `SitemapOptions.BaseUrl` is configured +3. Check application logs for errors +4. Ensure the web project directory has write permissions + +### Pages Not Appearing +**Problem:** Some pages missing from sitemap + +**Solutions:** +1. Verify `[IncludeSitemapXml]` attribute is present +2. Check namespace imports: `using Abp.Sitemap.Web.Sitemap.Sources.Page.Attributes;` +3. Ensure PageModel classes are public +4. Check filter conditions in custom sources + +### Background Worker Not Running +**Problem:** Sitemaps not regenerating automatically + +**Solutions:** +1. Check `SitemapOptions.WorkerPeriod` is set +2. Verify background workers are enabled in ABP configuration +3. Check application logs for worker errors + +## Performance Considerations + +### Caching Strategy +Consider adding caching for frequently accessed sitemaps: + +```csharp +public class CachedSitemapFileGenerator : ITransientDependency +{ + private readonly SitemapFileGenerator _generator; + private readonly IDistributedCache _cache; + + public async Task GetOrGenerateAsync(string group) + { + var cacheKey = $"Sitemap:{group}"; + var cached = await _cache.GetStringAsync(cacheKey); + + if (cached != null) + return cached; + + await _generator.GenerateAsync(); + // Read and cache... + } +} +``` + +## Conclusion + +The ABP Sitemap module provides a production-ready solution for dynamic sitemap generation in ABP Framework applications. By leveraging ABP's architecture—dependency injection, repository pattern, and background workers—the module automatically discovers pages, includes dynamic content, and regenerates sitemaps without manual intervention. + +Key benefits: +✅ **Zero Configuration** for basic scenarios +✅ **Type-Safe** attribute-based configuration +✅ **Extensible** for complex business logic +✅ **Performance** optimized with background processing +✅ **SEO-Friendly** following XML sitemap standards + +Whether you're building a blog, e-commerce platform, or enterprise application, this module provides a solid foundation for search engine optimization. + +## Additional Resources + +### Documentation +- [ABP Framework Documentation](https://abp.io/docs/latest/) +- [ABP Background Workers](https://abp.io/docs/latest/framework/infrastructure/background-workers) +- [ABP Repository Pattern](https://abp.io/docs/latest/framework/architecture/domain-driven-design/repositories) +- [ABP Dependency Injection](https://abp.io/docs/latest/framework/fundamentals/dependency-injection) + +### Source Code +- [Complete Working Demo](https://github.com/salihozkara/AbpSitemapDemo) - Full implementation with examples + - [BookSitemapSource](https://github.com/salihozkara/AbpSitemapDemo/blob/master/AbpSitemapDemo/Pages/Books/Index.cshtml.cs#L23) - Entity-based source example + - [Index.cshtml](https://github.com/salihozkara/AbpSitemapDemo/blob/master/AbpSitemapDemo/Pages/Index.cshtml#L9) - Page attribute usage diff --git a/docs/en/Community-Articles/2025-12-13-Building-Dynamic-XML-Sitemaps-With-ABP-Framework/summary.md b/docs/en/Community-Articles/2025-12-13-Building-Dynamic-XML-Sitemaps-With-ABP-Framework/summary.md new file mode 100644 index 0000000000..0c20291c95 --- /dev/null +++ b/docs/en/Community-Articles/2025-12-13-Building-Dynamic-XML-Sitemaps-With-ABP-Framework/summary.md @@ -0,0 +1 @@ +Learn how to use the ABP Sitemap module for automatic XML sitemap generation in your ABP Framework applications. \ No newline at end of file diff --git a/docs/en/Community-Articles/2025-12-18-Announcement-AIMAnagement/cover.png b/docs/en/Community-Articles/2025-12-18-Announcement-AIMAnagement/cover.png new file mode 100644 index 0000000000..3739cbb97a Binary files /dev/null and b/docs/en/Community-Articles/2025-12-18-Announcement-AIMAnagement/cover.png differ diff --git a/docs/en/Community-Articles/2025-12-18-Announcement-AIMAnagement/images/abp-studio-ai-management.png b/docs/en/Community-Articles/2025-12-18-Announcement-AIMAnagement/images/abp-studio-ai-management.png new file mode 100644 index 0000000000..5577fb0b33 Binary files /dev/null and b/docs/en/Community-Articles/2025-12-18-Announcement-AIMAnagement/images/abp-studio-ai-management.png differ diff --git a/docs/en/Community-Articles/2025-12-18-Announcement-AIMAnagement/images/ai-management-workspace-playground.png b/docs/en/Community-Articles/2025-12-18-Announcement-AIMAnagement/images/ai-management-workspace-playground.png new file mode 100644 index 0000000000..7051772489 Binary files /dev/null and b/docs/en/Community-Articles/2025-12-18-Announcement-AIMAnagement/images/ai-management-workspace-playground.png differ diff --git a/docs/en/Community-Articles/2025-12-18-Announcement-AIMAnagement/images/ai-management-workspace-widget.png b/docs/en/Community-Articles/2025-12-18-Announcement-AIMAnagement/images/ai-management-workspace-widget.png new file mode 100644 index 0000000000..bb3a75d8cd Binary files /dev/null and b/docs/en/Community-Articles/2025-12-18-Announcement-AIMAnagement/images/ai-management-workspace-widget.png differ diff --git a/docs/en/Community-Articles/2025-12-18-Announcement-AIMAnagement/images/aimanagement-workspace-geminiasopenai.png b/docs/en/Community-Articles/2025-12-18-Announcement-AIMAnagement/images/aimanagement-workspace-geminiasopenai.png new file mode 100644 index 0000000000..a221b3ed7d Binary files /dev/null and b/docs/en/Community-Articles/2025-12-18-Announcement-AIMAnagement/images/aimanagement-workspace-geminiasopenai.png differ diff --git a/docs/en/Community-Articles/2025-12-18-Announcement-AIMAnagement/post.md b/docs/en/Community-Articles/2025-12-18-Announcement-AIMAnagement/post.md new file mode 100644 index 0000000000..80c4a8fbeb --- /dev/null +++ b/docs/en/Community-Articles/2025-12-18-Announcement-AIMAnagement/post.md @@ -0,0 +1,106 @@ +# Introducing the AI Management Module: Manage AI Integration Dynamically + +We are excited to announce the **AI Management Module**, a powerful new module to the ABP Platform that makes managing AI capabilities in your applications easier. No need to redeploy your application, now you can configure, test, and manage your AI integrations on the fly through an intuitive user interface! + +## What is the AI Management Module? + +Built on top of the [ABP Framework's AI infrastructure](https://abp.io/docs/latest/framework/infrastructure/artificial-intelligence), the AI Management Module allows you to manage AI workspaces dynamically without touching your code. Whether you're building a customer support chatbot, adding AI-powered search, or creating intelligent automation workflows, this module provides everything you need to manage AI integrations through a user-friendly interface. + +> **Note**: The AI Management Module is currently in **preview** and available to ABP Team or higher license holders. + +## What it offers? + +### Manage AI Without Redeployment + +Create, configure, and update AI workspaces directly from the UI. Switch between different AI providers (OpenAI, Azure OpenAI, Ollama, etc.), change models, adjust prompts, and test configurations, all without restarting your application or deploying new code. + +### Built-In Chat Interface + +Test your AI workspaces immediately with the included chat interface in playground pages. Verify your configurations work correctly before using them in production. Perfect for experimenting with different models, prompts, and settings. + + ![AI Management Playground](./images/ai-management-workspace-playground.png) + +### Flexible for Any Architecture + +Whether you're building a monolith, microservices, or something in between, the module adapts to your needs: +- Host AI management directly in your application with full UI and database +- Deploy a centralized AI service that multiple applications can consume +- Use it as an API gateway pattern for your microservices + +### Works with Any AI Provider + +Even AI Management module doesn't implement all the providers by default, it provides extensibility options with a good abstraction for other providers like Azure, Anthropic Claude, Google Gemini, and more. Or you can directly use the OpenAI adapter with LLMs that support OpenAI API. + +- Example of using Gemini as an OpenAI provider: + + ![Using Gemini as an OpenAI provider](./images/aimanagement-workspace-geminiasopenai.png) + + +You can even add your own custom AI providers: [learn how to implement a custom AI provider factory in the documentation](https://abp.io/docs/latest/modules/ai-management#implementing-custom-ai-provider-factories). + +### Ready to Use Chat Widget + +Drop a compact, pre-built chat widget into any page with minimal code. It includes streaming support, conversation history, and API integration for customization. + +- Simple to use with minimal code + ```cs + @await Component.InvokeAsync(typeof(ChatClientChatViewComponent), new ChatClientChatViewModel + { + WorkspaceName = "StoryTeller", + }) + ``` + +- And result is a working, pre-integrated widget + + ![AI Management Chat Widget](./images/ai-management-workspace-widget.png) + +- [See the widget documentation](https://abp.io/docs/latest/modules/ai-management#client-usage-mvc-ui) for details and all parameters for customization. + +### Security + +Control who can manage and use AI workspaces with permission-based access control. Isolate your AI configurations by using workspaces with different permissions. Also, resource based authorization on workspaces is on the way and will be available in the next versions. It'll allow you to manage access to specific workspaces by a user or role. + +## Getting Started + +Installation is straightforward using the [ABP Studio](https://abp.io/studio). You can just enable **AI Management** module while creating a new project with ABP Studio and configure your preferred AI provider and model in the solution creation wizard. + +![ABP Studio AI Management Solution Creation Wizard](./images/abp-studio-ai-management.png) + +## Roadmap + +### v10.0 ✅ +- Workspace Management +- MVC UI +- Playground + - Chat History _(Client-Side)_ +- Client Components +- Integration to Startup Templates + +### v10.1 +- Blazor UI +- Angular UI +- Resource based authorization on Workspaces +- Agent-Framework compatibility examples + +### Future Goals +- Microservice templates +- MCP Support +- RAG with file upload _(md, pdf, txt)_ +- Chat History _(Server-Side Conversations)_ +- OpenAI Compatible Endpoints +- Tenant-Based Configuration +- Extended RAG capabilities, _(ie. providing application data as tools)_ + + +## Ready to Get Started? + +The AI Management Module is available now for ABP Team and higher license holders. + +**Learn More:** +- [AI Management Module Documentation](https://abp.io/docs/latest/modules/ai-management) - All features, scenarios, and technical details. +- [AI Infrastructure Documentation](https://abp.io/docs/latest/framework/infrastructure/artificial-intelligence) - Understanding AI workspaces in the framework. +- [Usage Scenarios](https://abp.io/docs/latest/modules/ai-management#usage-scenarios) - Examples for different architectures. + +--- + +*The AI Management Module is currently in preview. We're excited to hear your feedback as we continue to improve and add new features!* \ No newline at end of file diff --git a/docs/en/Community-Articles/2025-12-23-Referral-Program-Announcement/post.md b/docs/en/Community-Articles/2025-12-23-Referral-Program-Announcement/post.md new file mode 100644 index 0000000000..77621a1be2 --- /dev/null +++ b/docs/en/Community-Articles/2025-12-23-Referral-Program-Announcement/post.md @@ -0,0 +1,38 @@ +We are happy to share some exciting news. We launched **ABP.IO Referral Program** as a way to thank our customers and community members who help introduce ABP.IO to new professionals and organizations\! + +If you already use ABP.IO and believe in it, you can now get benefits by recommending it to others. + +## **What is ABP.IO Referral Program?** +Referral_Program_-3 + +ABP.IO Referral Program rewards people who bring new customers to ABP.IO. + +When someone you refer purchases an ABP.IO license, you earn a commission as a thank-you for your contribution. + +*\*This referral program is available to users who have an organization.* + +## **What Benefits Do You Get?** + +* Earn **5% commission** on the total sales price of each new license you refer + +* Get rewarded for sharing a platform you already know and trust + +## **Who Can Join?** + +You can participate if: + +* You are an existing ABP.IO customer who has purchased a license before + +* You are a license owner or a developer + +* Your license is active or expired + +* The purchase is not made by your own company + +## **Start Referring Today** + +If you are interested in joining the program, you can get started right away. + +👉 [**Start Referring Now**](https://abp.io/my-referrals) + +Thank you for being part of our community. Your support helps us grow and now it pays back. diff --git a/docs/en/docs-nav.json b/docs/en/docs-nav.json index 08b4553d64..1cce28fa3f 100644 --- a/docs/en/docs-nav.json +++ b/docs/en/docs-nav.json @@ -563,7 +563,11 @@ { "text": "Microsoft.Extensions.AI", "path": "framework/infrastructure/artificial-intelligence/microsoft-extensions-ai.md" - }, + }, + { + "text": "Agent Framework", + "path": "framework/infrastructure/artificial-intelligence/microsoft-agent-framework.md" + }, { "text": "Semantic Kernel", "path": "framework/infrastructure/artificial-intelligence/microsoft-semantic-kernel.md" @@ -1561,6 +1565,10 @@ "text": "SSR Configuration", "path": "framework/ui/angular/ssr-configuration.md" }, + { + "text": "AI Tools Configuration", + "path": "framework/ui/angular/ai-config.md" + }, { "text": "PWA Configuration", "path": "framework/ui/angular/pwa-configuration.md" @@ -1861,6 +1869,10 @@ "text": "Overriding the User Interface", "path": "framework/architecture/modularity/extending/overriding-user-interface.md" }, + { + "text": "How to Override LeptonX CSS Variables", + "path": "framework/ui/common/leptonx-css-variables.md" + }, { "text": "Utilities", "items": [ @@ -2294,6 +2306,10 @@ "text": "Helm Charts and Kubernetes", "path": "solution-templates/microservice/helm-charts-and-kubernetes.md" }, + { + "text": ".NET Aspire Integration", + "path": "solution-templates/microservice/aspire-integration.md" + }, { "text": "Guides", "items": [ @@ -2355,13 +2371,17 @@ "path": "modules/account-pro.md", "isIndex": true }, + { + "text": "Idle Session Timeout", + "path": "modules/account/idle-session-timeout.md" + }, { "text": "Tenant impersonation & User impersonation", "path": "modules/account/impersonation.md" }, { - "text": "Idle Session Timeout", - "path": "modules/account/idle-session-timeout.md" + "text": "Web Authentication API (WebAuthn) passkeys", + "path": "modules/account/passkey.md" } ] }, diff --git a/docs/en/framework/architecture/multi-tenancy/index.md b/docs/en/framework/architecture/multi-tenancy/index.md index c58769e57e..11ac3f7af1 100644 --- a/docs/en/framework/architecture/multi-tenancy/index.md +++ b/docs/en/framework/architecture/multi-tenancy/index.md @@ -47,7 +47,7 @@ ABP supports all the following approaches to store the tenant data in the databa - **Database per Tenant**: Every tenant has a separate, dedicated database to store the data related to that tenant. - **Hybrid**: Some tenants share a single database while some tenants may have their own databases. -[Saas module (PRO)](../../../modules/saas.md) allows you to set a connection string for any tenant (as optional), so you can achieve any of the approaches. +[SaaS module (PRO)](../../../modules/saas.md) allows you to set a connection string for any tenant (as optional), so you can achieve any of the approaches. > You can see the community article *[Multi-Tenancy with Separate Databases in .NET and ABP Framework](https://abp.io/community/articles/multitenancy-with-separate-databases-in-dotnet-and-abp-51nvl4u9)* for more details about different database architectures with practical implementation details. @@ -466,7 +466,7 @@ The [Tenant Management module](../../../modules/tenant-management.md) provides a ### A note about separate database per tenant approach in open source version -While ABP fully supports this option, managing connection strings of tenants from the UI is not available in open source version. You need to have [Saas module (PRO)](../../../modules/saas.md). +While ABP fully supports this option, managing connection strings of tenants from the UI is not available in open source version. You need to have [SaaS module (PRO)](../../../modules/saas.md). Alternatively, you can implement this feature yourself by customizing the tenant management module and tenant application service to create and migrate the database on the fly. ## See Also diff --git a/docs/en/framework/fundamentals/dependency-injection.md b/docs/en/framework/fundamentals/dependency-injection.md index a1eef66761..2e2e3e377a 100644 --- a/docs/en/framework/fundamentals/dependency-injection.md +++ b/docs/en/framework/fundamentals/dependency-injection.md @@ -264,7 +264,7 @@ public class TaxAppService : ApplicationService ``TaxAppService`` gets ``ITaxCalculator`` in its constructor. The dependency injection system automatically provides the requested service at runtime. -Constructor injection is preffered way of injecting dependencies to a class. In that way, the class can not be constructed unless all constructor-injected dependencies are provided. Thus, the class explicitly declares it's required services. +Constructor injection is preferred way of injecting dependencies to a class. In that way, the class can not be constructed unless all constructor-injected dependencies are provided. Thus, the class explicitly declares it's required services. ### Property Injection diff --git a/docs/en/framework/fundamentals/exception-handling.md b/docs/en/framework/fundamentals/exception-handling.md index 73e5d055fe..85b1c3b9c4 100644 --- a/docs/en/framework/fundamentals/exception-handling.md +++ b/docs/en/framework/fundamentals/exception-handling.md @@ -88,7 +88,7 @@ Error **details** in an optional field of the JSON error message. Thrown `Except } ```` -`AbpValidationException` implements the `IHasValidationErrors` interface and it is automatically thrown by the framework when a request input is not valid. So, usually you don't need to deal with validation errors unless you have higly customised validation logic. +`AbpValidationException` implements the `IHasValidationErrors` interface and it is automatically thrown by the framework when a request input is not valid. So, usually you don't need to deal with validation errors unless you have highly customized validation logic. ### Logging @@ -289,7 +289,7 @@ The `IHttpExceptionStatusCodeFinder` is used to automatically determine the HTTP ### Custom Mappings -Automatic HTTP status code determination can be overrided by custom mappings. For example: +Automatic HTTP status code determination can be overridden by custom mappings. For example: ````C# services.Configure(options => diff --git a/docs/en/framework/fundamentals/validation.md b/docs/en/framework/fundamentals/validation.md index 50540b5625..78e93e59f5 100644 --- a/docs/en/framework/fundamentals/validation.md +++ b/docs/en/framework/fundamentals/validation.md @@ -121,7 +121,7 @@ namespace Acme.BookStore #### Enabling/Disabling Validation -You can use the `[DisableValidation]` to disable it for methods, classs and properties. +You can use the `[DisableValidation]` to disable it for methods, classes and properties. ````csharp [DisableValidation] diff --git a/docs/en/framework/infrastructure/artificial-intelligence/index.md b/docs/en/framework/infrastructure/artificial-intelligence/index.md index bdd1125e9d..ddf9ad4b07 100644 --- a/docs/en/framework/infrastructure/artificial-intelligence/index.md +++ b/docs/en/framework/infrastructure/artificial-intelligence/index.md @@ -27,12 +27,14 @@ abp add-package Volo.Abp.AI The `Volo.Abp.AI` package provides integration with the following libraries: * [Microsoft.Extensions.AI](https://learn.microsoft.com/en-us/dotnet/ai/microsoft-extensions-ai) +* [Microsoft.Agents.AI (Agent Framework)](https://learn.microsoft.com/en-us/agent-framework/overview/agent-framework-overview) * [Microsoft.SemanticKernel](https://learn.microsoft.com/en-us/semantic-kernel/overview/) -The Microsoft.Extensions.AI library is suggested for library developers to keep the library dependency minimum and simple (since it provides basic abstractions and fundamental AI provider integrations), while Semantic Kernel is suggested for applications that need rich and advanced AI integration features. +The **Microsoft.Extensions.AI** library is suggested for library developers to keep the library dependency minimum and simple (since it provides basic abstractions and fundamental AI provider integrations). For applications, **Microsoft Agent Framework** is the recommended choice as it combines the best of both AutoGen and Semantic Kernel (it's direct successor of these two frameworks), offering simple abstractions for single- and multi-agent patterns along with advanced features like thread-based state management, type safety, filters, and telemetry. **Semantic Kernel** can still be used if you need its specific AI integration features. Check the following documentation to learn how to use these libraries with the ABP integration: - [ABP Microsoft.Extensions.AI integration](./microsoft-extensions-ai.md) +- [ABP Microsoft.Agents.AI (Agent Framework) integration](./microsoft-agent-framework.md) - [ABP Microsoft.SemanticKernel integration](./microsoft-semantic-kernel.md) diff --git a/docs/en/framework/infrastructure/artificial-intelligence/microsoft-agent-framework.md b/docs/en/framework/infrastructure/artificial-intelligence/microsoft-agent-framework.md new file mode 100644 index 0000000000..d3e7d128fc --- /dev/null +++ b/docs/en/framework/infrastructure/artificial-intelligence/microsoft-agent-framework.md @@ -0,0 +1,218 @@ +# Microsoft.Agents.AI (Agent Framework) + +[Microsoft Agent Framework](https://learn.microsoft.com/en-us/agent-framework/overview/agent-framework-overview) is an open-source development kit for **building AI agents** and **multi-agent workflows**. It is the direct successor to both *AutoGen* and [*Semantic Kernel*](./microsoft-semantic-kernel.md), combining their strengths while adding new capabilities, and is the suggested framework for building AI agent applications. This documentation is about the usage of this library with ABP Framework. Make sure you have read the [Artificial Intelligence](./index.md) documentation before reading this documentation. + +## Usage + +**Microsoft Agent Framework** works on top of `IChatClient` from **Microsoft.Extensions.AI**. After obtaining an `IChatClient` instance, you can create an AI agent using the `CreateAIAgent` extension method: + +```csharp +using Microsoft.Agents.AI; +using Microsoft.Extensions.AI; + +public class MyService +{ + private readonly IChatClient _chatClient; + + public MyService(IChatClient chatClient) + { + _chatClient = chatClient; + } + + public async Task GetResponseAsync(string userMessage) + { + AIAgent agent = _chatClient.CreateAIAgent( + instructions: "You are a helpful assistant that provides concise answers." + ); + + AgentRunResponse response = await agent.RunAsync(userMessage); + + return response.Text; + } +} +``` + +You can also use `IChatClientAccessor` to access the `IChatClient` in scenarios where AI capabilities are **optional**, such as when developing a module or a service that may use AI capabilities optionally: + +```csharp +using Microsoft.Agents.AI; +using Microsoft.Extensions.AI; +using Volo.Abp.AI; + +public class MyService +{ + private readonly IChatClientAccessor _chatClientAccessor; + + public MyService(IChatClientAccessor chatClientAccessor) + { + _chatClientAccessor = chatClientAccessor; + } + + public async Task GetResponseAsync(string userMessage) + { + var chatClient = _chatClientAccessor.ChatClient; + if (chatClient is null) + { + return "No chat client configured"; + } + + AIAgent agent = chatClient.CreateAIAgent( + instructions: "You are a helpful assistant that provides concise answers." + ); + + var response = await agent.RunAsync(userMessage); + + return response.Text; + } +} +``` + +### Workspaces + +Workspaces are a way to configure isolated AI configurations for a named scope. You can define a workspace by decorating a class with the `WorkspaceNameAttribute` attribute that carries the workspace name. +- Workspace names must be unique. +- Workspace names cannot contain spaces _(use underscores or camelCase)_. +- Workspace names are case-sensitive. + +```csharp +using Volo.Abp.AI; + +[WorkspaceName("CommentSummarization")] +public class CommentSummarization +{ +} +``` + +> [!NOTE] +> If you don't specify the workspace name, the full name of the class will be used as the workspace name. + +You can resolve generic versions of `IChatClient` and `IChatClientAccessor` services for a specific workspace as generic arguments. If Chat Client is not configured for a workspace, you will get `null` from the accessor services. You should check the accessor before using it. This applies only for specified workspaces. Another workspace may have a configured Chat Client. + +`IChatClient` or `IChatClientAccessor` can be resolved to access a specific workspace's chat client. This is a typed chat client and can be configured separately from the default chat client. + +Example of resolving a typed chat client for a workspace: + +```csharp +using Microsoft.Agents.AI; +using Microsoft.Extensions.AI; +using Volo.Abp.AI; + +public class MyService +{ + private readonly IChatClient _chatClient; + + public MyService(IChatClient chatClient) + { + _chatClient = chatClient; + } + + public async Task GetResponseAsync(string userMessage) + { + AIAgent agent = _chatClient.CreateAIAgent( + instructions: "You are a customer support assistant. Be polite and helpful." + ); + + var response = await agent.RunAsync(userMessage); + return response.Text; + } +} +``` + +Example of resolving a typed chat client accessor for a workspace: + +```csharp +using Microsoft.Agents.AI; +using Microsoft.Extensions.AI; +using Volo.Abp.AI; + +public class MyService +{ + private readonly IChatClientAccessor _chatClientAccessor; + + public MyService(IChatClientAccessor chatClientAccessor) + { + _chatClientAccessor = chatClientAccessor; + } + + public async Task GetResponseAsync(string userMessage) + { + var chatClient = _chatClientAccessor.ChatClient; + if (chatClient is null) + { + return "No chat client configured"; + } + + AIAgent agent = chatClient.CreateAIAgent( + instructions: "You are a customer support assistant. Be polite and helpful." + ); + + var response = await agent.RunAsync(userMessage); + return response.Text; + } +} +``` + +## Configuration + +**Microsoft Agent Framework** uses `IChatClient` from **Microsoft.Extensions.AI** as its foundation. Therefore, the configuration process for workspaces is the same as described in the [Microsoft.Extensions.AI documentation](./microsoft-extensions-ai.md#configuration). + +You need to configure the Chat Client for your workspace using `AbpAIWorkspaceOptions`, and then you can use the `CreateAIAgent` extension method to create AI agents from the configured chat client. + +To configure a chat client, you'll need a LLM provider package such as [Microsoft.Extensions.AI.OpenAI](https://www.nuget.org/packages/Microsoft.Extensions.AI.OpenAI) or [OllamaSharp](https://www.nuget.org/packages/OllamaSharp/). + +_The following example requires [OllamaSharp](https://www.nuget.org/packages/OllamaSharp/) package to be installed._ + +Demonstration of the default workspace configuration: + +```csharp +[DependsOn(typeof(AbpAIModule))] +public class MyProjectModule : AbpModule +{ + public override void PreConfigureServices(ServiceConfigurationContext context) + { + PreConfigure(options => + { + options.Workspaces.ConfigureDefault(configuration => + { + configuration.ConfigureChatClient(chatClientConfiguration => + { + chatClientConfiguration.Builder = new ChatClientBuilder( + sp => new OllamaApiClient("http://localhost:11434", "mistral") + ); + }); + }); + }); + } +} +``` + +Demonstration of the isolated workspace configuration: + +```csharp +[DependsOn(typeof(AbpAIModule))] +public class MyProjectModule : AbpModule +{ + public override void PreConfigureServices(ServiceConfigurationContext context) + { + PreConfigure(options => + { + options.Workspaces.Configure(configuration => + { + configuration.ConfigureChatClient(chatClientConfiguration => + { + chatClientConfiguration.Builder = new ChatClientBuilder( + sp => new OllamaApiClient("http://localhost:11434", "mistral") + ); + }); + }); + }); + } +} +``` + +## See Also + +- [Usage of Microsoft.Extensions.AI](./microsoft-extensions-ai.md) +- [Usage of Semantic Kernel](./microsoft-semantic-kernel.md) +- [Microsoft Agent Framework Overview](https://learn.microsoft.com/en-us/agent-framework/overview/agent-framework-overview) +- [AI Samples for .NET](https://learn.microsoft.com/en-us/samples/dotnet/ai-samples/ai-samples/) \ No newline at end of file diff --git a/docs/en/framework/infrastructure/artificial-intelligence/microsoft-extensions-ai.md b/docs/en/framework/infrastructure/artificial-intelligence/microsoft-extensions-ai.md index 35b370585e..b44572838a 100644 --- a/docs/en/framework/infrastructure/artificial-intelligence/microsoft-extensions-ai.md +++ b/docs/en/framework/infrastructure/artificial-intelligence/microsoft-extensions-ai.md @@ -172,5 +172,6 @@ public class MyProjectModule : AbpModule ## See Also +- [Usage of Agent Framework](./microsoft-agent-framework.md) - [Usage of Semantic Kernel](./microsoft-semantic-kernel.md) - [AI Samples for .NET](https://learn.microsoft.com/en-us/samples/dotnet/ai-samples/ai-samples/) \ No newline at end of file diff --git a/docs/en/framework/infrastructure/audit-logging.md b/docs/en/framework/infrastructure/audit-logging.md index e559e5ed22..9811e542ab 100644 --- a/docs/en/framework/infrastructure/audit-logging.md +++ b/docs/en/framework/infrastructure/audit-logging.md @@ -215,7 +215,7 @@ public class MyUser : Entity public string Email { get; set; } - [DisableAuditing] //Ignore the Passoword on audit logging + [DisableAuditing] //Ignore the Password on audit logging public string Password { get; set; } } ```` diff --git a/docs/en/framework/infrastructure/background-workers/hangfire.md b/docs/en/framework/infrastructure/background-workers/hangfire.md index 832084de4c..9b65a488a9 100644 --- a/docs/en/framework/infrastructure/background-workers/hangfire.md +++ b/docs/en/framework/infrastructure/background-workers/hangfire.md @@ -61,7 +61,7 @@ After you have installed these NuGet packages, you need to configure your projec var configuration = context.Services.GetConfiguration(); var hostingEnvironment = context.Services.GetHostingEnvironment(); - //... other configarations. + //... other configurations. ConfigureHangfire(context, configuration); } diff --git a/docs/en/framework/infrastructure/background-workers/quartz.md b/docs/en/framework/infrastructure/background-workers/quartz.md index 7f591e0aeb..9693e4795f 100644 --- a/docs/en/framework/infrastructure/background-workers/quartz.md +++ b/docs/en/framework/infrastructure/background-workers/quartz.md @@ -43,7 +43,7 @@ public class YourModule : AbpModule } ```` -> Quartz background worker integration provided `QuartzPeriodicBackgroundWorkerAdapter` to adapt `PeriodicBackgroundWorkerBase` and `AsyncPeriodicBackgroundWorkerBase` derived class. So, you can still fllow the [background workers document](../background-workers) to define the background worker. +> Quartz background worker integration provided `QuartzPeriodicBackgroundWorkerAdapter` to adapt `PeriodicBackgroundWorkerBase` and `AsyncPeriodicBackgroundWorkerBase` derived class. So, you can still follow the [background workers document](../background-workers) to define the background worker. ## Configuration diff --git a/docs/en/framework/infrastructure/current-user.md b/docs/en/framework/infrastructure/current-user.md index a411d1f4d5..cb5954a778 100644 --- a/docs/en/framework/infrastructure/current-user.md +++ b/docs/en/framework/infrastructure/current-user.md @@ -101,7 +101,7 @@ For a web application, it gets the `User` property of the current `HttpContext`. ### Basic Usage -You can inject `ICurrentPrincipalAccessor` and use the `Principal` property to the the current principal: +You can inject `ICurrentPrincipalAccessor` and use the `Principal` property to get the current principal: ````csharp public class MyService : ITransientDependency diff --git a/docs/en/framework/infrastructure/emailing.md b/docs/en/framework/infrastructure/emailing.md index 7c240a9d75..a9304b2e57 100644 --- a/docs/en/framework/infrastructure/emailing.md +++ b/docs/en/framework/infrastructure/emailing.md @@ -235,7 +235,7 @@ The final rendered message was shown above. You typically want to replace the standard templates with your own ones, so you can prepare a branded email messages. To do that, you can use the power of the [virtual file system](../infrastructure/virtual-file-system.md) (VFS) or replace them in your own template definition provider. -Pathes of the templates in the virtual file system are shown below: +Paths of the templates in the virtual file system are shown below: * `/Volo/Abp/Emailing/Templates/Layout.tpl` * `/Volo/Abp/Emailing/Templates/Message.tpl` @@ -250,7 +250,7 @@ See the [text templating system](./text-templating) document for details. ## NullEmailSender -`NullEmailSender` is a built-in class that implements the `IEmailSender`, but writes email contents to the [standard log system](../fundamentals/logging.md), rathen than actually sending the emails. +`NullEmailSender` is a built-in class that implements the `IEmailSender`, but writes email contents to the [standard log system](../fundamentals/logging.md), rather than actually sending the emails. This class can be useful especially in development time where you generally don't want to send real emails. The [application startup template](../../solution-templates/layered-web-application) already uses this class in the **DEBUG mode** with the following configuration in the domain layer: diff --git a/docs/en/framework/infrastructure/features.md b/docs/en/framework/infrastructure/features.md index 2d135b822b..e450dc2068 100644 --- a/docs/en/framework/infrastructure/features.md +++ b/docs/en/framework/infrastructure/features.md @@ -395,8 +395,31 @@ There are three pre-defined value providers, executed by the given order: * `TenantFeatureValueProvider` tries to get if the feature value is explicitly set for the **current tenant**. * `EditionFeatureValueProvider` tries to get the feature value for the current edition. Edition Id is obtained from the current principal identity (`ICurrentPrincipalAccessor`) with the claim name `editionid` (a constant defined as`AbpClaimTypes.EditionId`). Editions are not implemented for the [tenant management](../../modules/tenant-management.md) module. You can implement it yourself or consider to use the [SaaS module](https://abp.io/modules/Volo.Saas) of the ABP Commercial. +* `ConfigurationFeatureValueProvider`: Gets the value from the [IConfiguration service](../fundamentals/configuration.md). * `DefaultValueFeatureValueProvider` gets the default value of the feature. +#### Feature Values in the Application Configuration + +The `ConfigurationFeatureValueProvider` reads the feature values from the `IConfiguration` service, which can read values from the `appsettings.json` by default. So, the easiest way to configure feature values is to define them in the `appsettings.json` file. + +For example, you can configure feature values as shown below: + +````json +{ + "Features": { + "MyApp.Reporting": "true", + "MyApp.PdfReporting": "true", + "MyApp.MaxProductCount": "50" + } +} +```` + +Feature values should be configured under the `Features` section as like in this example. + +> `IConfiguration` is an .NET Core service and it can read values not only from the `appsettings.json`, but also from the environment, user secrets... etc. See [Microsoft's documentation](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/) for more. + +#### Custom Feature Value Providers + You can write your own provider by inheriting the `FeatureValueProvider`. **Example: Enable all features for a user with "SystemAdmin" as a "User_Type" claim value** diff --git a/docs/en/framework/infrastructure/settings.md b/docs/en/framework/infrastructure/settings.md index b48b1c84dc..38c9528d86 100644 --- a/docs/en/framework/infrastructure/settings.md +++ b/docs/en/framework/infrastructure/settings.md @@ -43,7 +43,7 @@ ABP automatically discovers this class and registers the setting definitions. * **DefaultValue**: A setting may have a default value. * **DisplayName**: A localizable string that can be used to show the setting name on the UI. * **Description**: A localizable string that can be used to show the setting description on the UI. -* **IsVisibleToClients**: A boolean value indicates that whether this setting value is available in the client side or not. Default value is false to prevent accidently publishing an internal critical setting value. +* **IsVisibleToClients**: A boolean value indicates that whether this setting value is available in the client side or not. Default value is false to prevent accidentally publishing an internal critical setting value. * **IsInherited**: A boolean value indicates that whether this setting value is inherited from other providers or not. Default value is true and fallbacks to the next provider if the setting value was not set for the requested provider (see the setting value providers section for more). * **IsEncrypted**: A boolean value indicates that whether this setting value should be encrypted on save and decrypted on read. It makes possible to secure the setting value in the database. * **Providers**: Can be used to restrict providers available for a particular setting (see the setting value providers section for more). diff --git a/docs/en/framework/ui/angular/ai-config.md b/docs/en/framework/ui/angular/ai-config.md new file mode 100644 index 0000000000..06f690016a --- /dev/null +++ b/docs/en/framework/ui/angular/ai-config.md @@ -0,0 +1,262 @@ +```json +//[doc-seo] +{ + "Description": "Learn how to configure AI-powered development tools for ABP Framework Angular applications with automatic setup for Claude, Cursor, Copilot, Gemini, Junie, and Windsurf." +} +``` + +# AI Configuration + +ABP Framework provides an **AI Configuration Generator** that helps developers set up AI-powered coding assistants for their Angular applications. This schematic automatically generates configuration files for popular AI tools with pre-configured ABP best practices and guidelines. + +## Overview + +The AI Configuration Generator is an Angular schematic that creates standardized configuration files for various AI development tools. These configurations include: + +- ABP Framework coding standards and best practices +- Angular development guidelines +- Project-specific rules and conventions +- Full-stack development patterns (ABP .NET + Angular) + +## Supported AI Tools + +The generator supports the following AI coding assistants: + +- **Claude** - Creates `.claude/CLAUDE.md` configuration file +- **Copilot** - Creates `.github/copilot-instructions.md` configuration file +- **Cursor** - Creates `.cursor/rules/cursor.mdc` configuration file +- **Gemini** - Creates `.gemini/GEMINI.md` configuration file +- **Junie** - Creates `.junie/guidelines.md` configuration file +- **Windsurf** - Creates `.windsurf/rules/guidelines.md` configuration file + +## Usage + +### Basic Usage + +Generate AI configuration for a single tool: + +```bash +ng g @abp/ng.schematics:ai-config --tool=claude +``` + +### Multiple Tools + +Generate configurations for multiple AI tools at once: + +```bash +# Comma-separated +ng g @abp/ng.schematics:ai-config --tool=claude,cursor,copilot + +# Space-separated (with quotes) +ng g @abp/ng.schematics:ai-config --tool="claude cursor gemini" + +# Multiple --tool flags +ng g @abp/ng.schematics:ai-config --tool=claude --tool=cursor --tool=gemini +``` + +### Target Specific Project + +By default, configurations are generated at the workspace root. To target a specific project: + +```bash +ng g @abp/ng.schematics:ai-config --tool=claude --target-project=my-app +``` + +This creates the configuration files in the `my-app` project root directory. + +### Overwrite Existing Files + +If configuration files already exist, use the `--overwrite` flag to replace them: + +```bash +ng g @abp/ng.schematics:ai-config --tool=cursor --overwrite +``` + +## Schema Options + +The AI Configuration Generator accepts the following options: + +### tool + +- **Type:** `string` +- **Required:** Yes +- **Description:** Comma-separated list of AI tools to generate configurations for +- **Valid values:** `claude`, `copilot`, `cursor`, `gemini`, `junie`, `windsurf` +- **Example:** `"claude,cursor,copilot"` + +### targetProject + +- **Type:** `string` +- **Required:** No +- **Description:** The name of the target project in your workspace +- **Default:** Workspace root (`/`) +- **Example:** `"my-angular-app"` + +### overwrite + +- **Type:** `boolean` +- **Required:** No +- **Default:** `false` +- **Description:** Whether to overwrite existing configuration files + +## Configuration Content + +All generated configuration files include comprehensive guidelines for: + +### General Principles +- Clear separation between backend (ABP/.NET) and frontend (Angular) layers +- Modular architecture patterns +- Official ABP documentation references +- Readability, maintainability, and performance standards + +### ABP / .NET Development Rules +- Standard folder structure (`*.Application`, `*.Domain`, `*.EntityFrameworkCore`, `*.HttpApi`) +- C# coding conventions and naming patterns +- Modern C# features (records, pattern matching, null-coalescing) +- ABP module integration (Permissions, Settings, Audit Logging) +- Error handling and validation patterns + +### Angular Development Rules +- Angular coding style and best practices +- Component architecture patterns +- Reactive programming with RxJS +- ABP Angular package usage (`@abp/ng.core`, `@abp/ng.theme.shared`) +- State management and service patterns + +### Performance and Testing +- Performance optimization techniques +- Unit testing and integration testing guidelines +- Best practices for both backend and frontend + +## Examples + +### Example 1: Setup Claude for Development + +```bash +ng g @abp/ng.schematics:ai-config --tool=claude +``` + +Output: +``` +🚀 Generating AI configuration files... +📁 Target path: / +🤖 Selected tools: claude +✅ AI configuration files generated successfully! + +📝 Generated files: + - .claude/CLAUDE.md + +💡 Tip: Restart your IDE or AI tool to apply the new configurations. +``` + +### Example 2: Setup Multiple Tools for a Project + +```bash +ng g @abp/ng.schematics:ai-config --tool="cursor,copilot,gemini" --target-project=acme-app +``` + +Output: +``` +🚀 Generating AI configuration files... +📁 Target path: /acme-app +🤖 Selected tools: cursor, copilot, gemini +✅ AI configuration files generated successfully! + +📝 Generated files: + - /acme-app/.cursor/rules/cursor.mdc + - /acme-app/.github/copilot-instructions.md + - /acme-app/.gemini/GEMINI.md + +💡 Tip: Restart your IDE or AI tool to apply the new configurations. +``` + +### Example 3: Update Existing Configuration + +```bash +ng g @abp/ng.schematics:ai-config --tool=windsurf --overwrite +``` + +This will regenerate the Windsurf configuration file even if it already exists. + +## File Structure + +After running the generator, your project will have configuration files in their respective directories: + +``` +your-project/ +├── .claude/ +│ └── CLAUDE.md # Claude AI configuration +├── .cursor/ +│ └── rules/ +│ └── cursor.mdc # Cursor AI configuration +├── .github/ +│ └── copilot-instructions.md # GitHub Copilot configuration +├── .gemini/ +│ └── GEMINI.md # Gemini AI configuration +├── .junie/ +│ └── guidelines.md # Junie AI configuration +└── .windsurf/ + └── rules/ + └── guidelines.md # Windsurf AI configuration +``` + +## Best Practices + +1. **Generate Early**: Set up AI configurations at the beginning of your project to ensure consistent code quality from the start. + +2. **Multiple Tools**: If your team uses different AI assistants, generate configurations for all of them to maintain consistency across the team. + +3. **Version Control**: Commit the generated configuration files to your repository so all team members benefit from the same AI guidelines. + +4. **Keep Updated**: When ABP releases new best practices or your project evolves, regenerate configurations with the `--overwrite` flag. + +5. **Project-Specific**: For monorepos or multi-project workspaces, use `--target-project` to create project-specific configurations. + +## Troubleshooting + +### Configuration File Already Exists + +If you see a warning that a configuration file already exists: + +``` +⚠️ Configuration file already exists: .claude/CLAUDE.md + Use --overwrite flag to replace existing files. +``` + +Add the `--overwrite` flag to replace it: + +```bash +ng g @abp/ng.schematics:ai-config --tool=claude --overwrite +``` + +### Invalid Tool Name + +If you specify an invalid tool name: + +``` +Invalid AI tool(s): chatgpt. Valid options are: claude, copilot, cursor, gemini, junie, windsurf +``` + +Make sure to use only the supported tool names listed above. + +### No Tools Selected + +If you run the command without specifying any tools: + +```bash +ng g @abp/ng.schematics:ai-config +``` + +You'll see usage examples and available tools: + +``` +ℹ️ No AI tools selected. Skipping configuration generation. + +💡 Usage examples: + ng g @abp/ng.schematics:ai-config --tool=claude,cursor + ng g @abp/ng.schematics:ai-config --tool="claude, cursor" + ng g @abp/ng.schematics:ai-config --tool=gemini --tool=cursor + ng g @abp/ng.schematics:ai-config --tool=gemini --target-project=my-app + +Available tools: claude, copilot, cursor, gemini, junie, windsurf +``` \ No newline at end of file diff --git a/docs/en/framework/ui/angular/ssr-configuration.md b/docs/en/framework/ui/angular/ssr-configuration.md index 258fce99b4..e058beb861 100644 --- a/docs/en/framework/ui/angular/ssr-configuration.md +++ b/docs/en/framework/ui/angular/ssr-configuration.md @@ -1,13 +1,13 @@ ```json //[doc-seo] { - "Description": "Learn how to configure Server-Side Rendering (SSR) for your Angular application in the ABP Framework to improve performance and SEO." + "Description": "Learn how to configure Server-Side Rendering (SSR) for your Angular application in the ABP Framework to improve performance and SEO." } ``` # SSR Configuration -[Server-Side Rendering (SSR)](https://angular.io/guide/ssr) is a process that involves rendering pages on the server, resulting in initial HTML content that contains the page state. This allows the browser to show the page to the user immediately, before the JavaScript bundles are downloaded and executed. +[Server-Side Rendering (SSR)](https://angular.dev/guide/ssr) is a process that involves rendering pages on the server, resulting in initial HTML content that contains the page state. This allows the browser to show the page to the user immediately, before the JavaScript bundles are downloaded and executed. SSR improves the **performance** (First Contentful Paint) and **SEO** (Search Engine Optimization) of your application. @@ -105,10 +105,10 @@ If your project uses the **Webpack Builder** (`@angular-devkit/build-angular:bro ```typescript import { - AngularNodeAppEngine, - createNodeRequestHandler, - isMainModule, - writeResponseToNodeResponse, + AngularNodeAppEngine, + createNodeRequestHandler, + isMainModule, + writeResponseToNodeResponse, } from '@angular/ssr/node'; import express from 'express'; import { dirname, resolve } from 'node:path'; @@ -128,28 +128,28 @@ const angularApp = new AngularNodeAppEngine(); * Serve static files from /browser */ app.use( - express.static(browserDistFolder, { - maxAge: '1y', - index: false, - redirect: false, - }), + express.static(browserDistFolder, { + maxAge: '1y', + index: false, + redirect: false, + }), ); /** * Handle all other requests by rendering the Angular application. */ app.use((req, res, next) => { - angularApp - .handle(req) - .then(response => { - if (response) { - res.cookie('ssr-init', 'true', {...secureCookie, httpOnly: false}); - return writeResponseToNodeResponse(response, res); - } else { - return next() - } - }) - .catch(next); + angularApp + .handle(req) + .then(response => { + if (response) { + res.cookie('ssr-init', 'true', {...secureCookie, httpOnly: false}); + return writeResponseToNodeResponse(response, res); + } else { + return next() + } + }) + .catch(next); }); // ... (Start server logic) @@ -162,10 +162,10 @@ export const reqHandler = createNodeRequestHandler(app); import { RenderMode, ServerRoute } from '@angular/ssr'; export const serverRoutes: ServerRoute[] = [ - { - path: '**', - renderMode: RenderMode.Server - } + { + path: '**', + renderMode: RenderMode.Server + } ]; ``` @@ -180,16 +180,16 @@ import { serverRoutes } from './app.routes.server'; import { SSR_FLAG } from '@abp/ng.core'; const serverConfig: ApplicationConfig = { - providers: [ - provideAppInitializer(() => { - const platformId = inject(PLATFORM_ID); - const transferState = inject(TransferState); - if (isPlatformServer(platformId)) { - transferState.set(SSR_FLAG, true); - } - }), - provideServerRendering(withRoutes(serverRoutes)), - ], + providers: [ + provideAppInitializer(() => { + const platformId = inject(PLATFORM_ID); + const transferState = inject(TransferState); + if (isPlatformServer(platformId)) { + transferState.set(SSR_FLAG, true); + } + }), + provideServerRendering(withRoutes(serverRoutes)), + ], }; export const config = mergeApplicationConfig(appConfig, serverConfig); @@ -237,13 +237,177 @@ The schematic installs `openid-client` to handle authentication on the server si > Ensure your OpenID Connect configuration (in `environment.ts` or `app.config.ts`) is compatible with the server environment. -## 5. Deployment +## 5. Render Modes & Hybrid Rendering -To deploy your Angular SSR application to a production server, follow these steps: +Angular 20 provides different rendering modes that you can configure per route in the `app.routes.server.ts` file to optimize performance and SEO. -### 5.1. Build the Application +### 5.1. Available Render Modes + +```typescript +import { RenderMode, ServerRoute } from '@angular/ssr'; + +export const serverRoutes: ServerRoute[] = [ + // Server-Side Rendering - renders on every request + { + path: 'dashboard', + renderMode: RenderMode.Server + }, + + // Prerender (SSG) - renders at build time + { + path: 'about', + renderMode: RenderMode.Prerender + }, + + // Client-Side Rendering - renders only in browser + { + path: 'admin/**', + renderMode: RenderMode.Client + }, + + // Default fallback + { + path: '**', + renderMode: RenderMode.Server + } +]; +``` + +#### RenderMode.Server (SSR) +Renders HTML on every request. Best for dynamic content, personalized pages, and pages requiring authentication. + +#### RenderMode.Prerender (SSG) +Generates static HTML at build time. Best for marketing pages, blog posts, and content that doesn't change frequently. + +For dynamic routes, use `getPrerenderParams`: + +```typescript +{ + path: 'blog/:slug', + renderMode: RenderMode.Prerender, + getPrerenderParams: async () => { + const posts = await fetchBlogPosts(); + return posts.map(post => ({ slug: post.slug })); + } +} +``` + +#### RenderMode.Client (CSR) +Traditional client-side rendering. Best for highly interactive applications and admin panels that don't need SEO. + +### 5.2. Hybrid Rendering + +Combine different modes in one application for optimal results: + +```typescript +export const serverRoutes: ServerRoute[] = [ + // Static pages + { path: '', renderMode: RenderMode.Prerender }, + { path: 'about', renderMode: RenderMode.Prerender }, -Run the build command to generate the production artifacts: + // Dynamic pages + { path: 'account', renderMode: RenderMode.Server }, + { path: 'orders', renderMode: RenderMode.Server }, + + // Admin area + { path: 'admin/**', renderMode: RenderMode.Client }, +]; +``` + +## 6. Hydration + +Hydration is the process where Angular attaches to server-rendered HTML and makes it interactive. The ABP schematic automatically configures hydration for your application. + +### 6.1. Common Hydration Issues + +**Problem: Browser APIs on Server** + +```typescript +// ❌ Bad - will fail on server +const width = window.innerWidth; + +// ✅ Good - check platform +import { isPlatformBrowser } from '@angular/common'; +import { PLATFORM_ID, inject } from '@angular/core'; + +export class MyComponent { + platformId = inject(PLATFORM_ID); + + getWidth() { + if (isPlatformBrowser(this.platformId)) { + return window.innerWidth; + } + return 0; + } +} +``` + +**Problem: Random or Time-Based Values** + +```typescript +// ❌ Bad - generates different values on server and client +id = Math.random(); +currentTime = new Date(); + +// ✅ Good - use TransferState for consistent data +import { TransferState, makeStateKey } from '@angular/core'; + +const TIME_KEY = makeStateKey('time'); + +constructor(private transferState: TransferState) { + if (isPlatformServer(this.platformId)) { + this.transferState.set(TIME_KEY, new Date().toISOString()); + } else { + this.time = this.transferState.get(TIME_KEY, new Date().toISOString()); + } +} +``` + +**Enable Debug Tracing:** + +```typescript +// app.config.ts +import { provideClientHydration, withDebugTracing } from '@angular/platform-browser'; + +export const appConfig: ApplicationConfig = { + providers: [ + provideClientHydration(withDebugTracing()), + ] +}; +``` + +## 7. Environment Variables + +Configure your SSR application using environment variables in `server.ts`: + +```typescript +// server.ts +const PORT = process.env['PORT'] || 4000; +const HOST = process.env['HOST'] || 'localhost'; + +// Start the server +if (isMainModule(import.meta.url)) { + app.listen(PORT, () => { + console.log(`Server running on http://${HOST}:${PORT}`); + }); +} +``` + +For production, set environment variables: + +```bash +# .env file or environment configuration +NODE_ENV=production +PORT=4000 +HOST=0.0.0.0 +API_URL=https://api.yourdomain.com +``` + +## 8. Deployment + +To deploy your Angular SSR application to a production server: + +### 8.1. Build the Application ```shell yarn build @@ -251,30 +415,152 @@ yarn build yarn run build:ssr ``` -### 5.2. Prepare Artifacts +### 8.2. Prepare Artifacts -After the build is complete, you will find the output in the `dist` folder. -For the **Application Builder**, the output structure typically looks like this: +Copy the `dist/MyProjectName` folder to your server: ``` dist/MyProjectName/ ├── browser/ # Client-side bundles -└── server/ # Server-side bundles and entry point (server.mjs) +└── server/ # Server-side bundles (server.mjs) ``` -You need to copy the entire `dist/MyProjectName` folder to your server. +### 8.3. Install Production Dependencies + +On your server, install only the required dependencies (schematic already added them to package.json): + +```shell +npm install --production +``` -### 5.3. Run the Server +Required dependencies: +- `express`: Web server framework +- `openid-client`: Authentication support -On your server, navigate to the folder where you copied the artifacts and run the server using Node.js: +### 8.4. Run the Server +**Development/Testing:** ```shell node server/server.mjs ``` -> [!TIP] -> It is recommended to use a process manager like [PM2](https://pm2.keymetrics.io/) to keep your application alive and handle restarts. +**Production (with PM2):** + +Use [PM2](https://pm2.keymetrics.io/) to keep your application alive and manage restarts: ```shell +npm install -g pm2 pm2 start server/server.mjs --name "my-app" +pm2 startup # Configure PM2 to start on boot +pm2 save # Save current process list +``` + +## 9. Troubleshooting + +### 9.1. "Window/Document is not defined" + +Browser APIs don't exist on the server. Always check the platform: + +```typescript +import { isPlatformBrowser } from '@angular/common'; + +if (isPlatformBrowser(this.platformId)) { + // Safe to use window, document, localStorage, etc. +} +``` + +### 9.2. "LocalStorage is not defined" + +ABP Core provides `AbpLocalStorageService` that implements the `Storage` interface and works safely on both server and client: + +```typescript +import { AbpLocalStorageService } from '@abp/ng.core'; + +@Injectable({ providedIn: 'root' }) +export class MyService { + private storage = inject(AbpLocalStorageService); + + saveData(key: string, value: string): void { + // Safe on both server and client + this.storage.setItem(key, value); + } + + getData(key: string): string | null { + // Returns null on server, actual value on client + return this.storage.getItem(key); + } +} +``` + +`AbpLocalStorageService` implements all `Storage` methods: +- `getItem(key: string): string | null` +- `setItem(key: string, value: string): void` +- `removeItem(key: string): void` +- `clear(): void` +- `key(index: number): string | null` +- `length: number` + +### 9.3. Hydration Mismatch Errors + +If you see "NG0500" errors in the console: + +1. Enable debug tracing (see section 6.1) +2. Check for dynamic content (dates, random IDs) +3. Ensure server and client render the same HTML +4. Use `TransferState` for data consistency + +### 9.4. Avoiding Duplicate API Calls + +ABP Core provides a `transferStateInterceptor` that automatically prevents duplicate HTTP GET requests during hydration. When you use `provideAbpCore()`, this interceptor is already active. + +**How it works:** +- Server: Stores HTTP GET responses in `TransferState` +- Client: Reuses stored responses during hydration +- Automatically cleans up stored data after use + +```typescript +// app.config.ts +import { provideAbpCore } from '@abp/ng.core'; + +export const appConfig: ApplicationConfig = { + providers: [ + provideAbpCore(), + // transferStateInterceptor is automatically included + ] +}; ``` + +The interceptor works with all HTTP GET requests made through `HttpClient`: + +```typescript +// This service automatically benefits from the interceptor +@Injectable({ providedIn: 'root' }) +export class UserService { + private http = inject(HttpClient); + + getUsers() { + // On server: Response is cached in TransferState + // On client: Cached response is used (no duplicate request) + return this.http.get('/api/users'); + } +} +``` + +> [!NOTE] +> The interceptor only works with GET requests. POST, PUT, DELETE, and PATCH requests are not cached. + +## Additional Resources + +- [Angular SSR Official Guide](https://angular.dev/guide/ssr) +- [Angular Hydration Documentation](https://angular.dev/guide/hydration) +- [PM2 Process Manager](https://pm2.keymetrics.io/) + +## Summary + +The ABP Angular SSR schematic provides: +- ✅ Automatic SSR setup with necessary dependencies +- ✅ Server-side authentication with OpenID Connect +- ✅ Multiple render modes (Server, Prerender, Client, Hybrid) +- ✅ Hydration support for better performance + +Configure render modes based on your needs, handle platform differences properly, and use environment variables for deployment configuration. diff --git a/docs/en/framework/ui/common/leptonx-css-variables.md b/docs/en/framework/ui/common/leptonx-css-variables.md new file mode 100644 index 0000000000..938bc92304 --- /dev/null +++ b/docs/en/framework/ui/common/leptonx-css-variables.md @@ -0,0 +1,126 @@ +```json +//[doc-seo] +{ + "Description": "Explore LeptonX CSS Variables to customize theming with ease, controlling colors, spacing, and styles for a cohesive application design." +} +``` + +# LeptonX CSS Variables Documentation + +LeptonX uses CSS custom properties (variables) prefixed with `--lpx-*` to provide a flexible theming system. These variables control colors, spacing, shadows, and component-specific styles throughout the application. + +## Brand & Semantic Colors + +| Variable | Description | +|----------|-------------| +| `--lpx-brand` | Brand-specific accent color | +| `--lpx-brand-text` | Text color used on brand-colored backgrounds | + +## Base Colors + +| Variable | Description | +|----------|-------------| +| `--lpx-light` | Light shade for subtle backgrounds or text | +| `--lpx-dark` | Dark shade for contrasting elements | + +## Layout & Surface Colors + +| Variable | Description | +|----------|-------------| +| `--lpx-content-bg` | Main content area background color | +| `--lpx-content-text` | Default text color for content areas | +| `--lpx-card-bg` | Card component background color | +| `--lpx-card-title-text-color` | Card title text color | +| `--lpx-border-color` | Default border color for dividers and outlines | +| `--lpx-shadow` | Box shadow definition for elevated elements | + +## Navigation + +| Variable | Description | +|----------|-------------| +| `--lpx-navbar-color` | Navbar background color | +| `--lpx-navbar-text-color` | Navbar default text/icon color | +| `--lpx-navbar-active-text-color` | Navbar active/hover text color | +| `--lpx-navbar-active-bg-color` | Navbar active item background color | + +## Utility + +| Variable | Description | +|----------|-------------| +| `--lpx-radius` | Global border-radius value for rounded corners | + +## Global Override + +Applies to all themes and pages: + +```css +:root { + /* Brand & Semantic */ + --lpx-brand: #f72585; + + /* Base Colors */ + --lpx-light: #f5f7fb; + --lpx-dark: #0b0f19; + + /* Layout & Surface */ + --lpx-content-bg: #101018; + --lpx-content-text: #cfd6e4; + --lpx-card-bg: #151a2b; + --lpx-card-title-text-color: #ffffff; + --lpx-border-color: #242836; + --lpx-shadow: 0 10px 30px rgba(0, 0, 0, 0.25); + + /* Navigation */ + --lpx-navbar-color: #0d1020; + --lpx-navbar-text-color: #aab2c8; + --lpx-navbar-active-text-color: #ffffff; + --lpx-navbar-active-bg-color: rgba(247, 37, 133, 0.15); + + /* Utility */ + --lpx-radius: 10px; +} + +``` + +## Theme-Scoped Override + +Applies only when a specific theme class is active (e.g., `.lpx-theme-dark` on `` or ``): + +```css +:root .lpx-theme-dark { + /* Brand & Semantic */ + --lpx-brand: #4dd0e1; + + /* Base Colors */ + --lpx-light: #e0f7fa; + --lpx-dark: #020617; + + /* Layout & Surface */ + --lpx-content-bg: #0b1118; + --lpx-content-text: #c7d0e0; + --lpx-card-bg: #111a24; + --lpx-card-title-text-color: #e6f1ff; + --lpx-border-color: #1e2a3a; + --lpx-shadow: 0 12px 32px rgba(0, 0, 0, 0.45); + + /* Navigation */ + --lpx-navbar-color: #0f1a22; + --lpx-navbar-text-color: #9fb3c8; + --lpx-navbar-active-text-color: #ffffff; + --lpx-navbar-active-bg-color: rgba(77, 208, 225, 0.18); + + /* Utility */ + --lpx-radius: 12px; +} +``` + +## Component/Page-Specific Override + +For targeted customizations that should only affect a specific section: + +```css +.my-custom-page { + --lpx-brand: #e91e63; + --lpx-card-bg: #1a1a2e; +} +``` \ No newline at end of file diff --git a/docs/en/get-started/images/abp-studio-new-microservice-solution-dialog-optional-modules.png b/docs/en/get-started/images/abp-studio-new-microservice-solution-dialog-optional-modules.png index a268e4cde5..3b1a4abe68 100644 Binary files a/docs/en/get-started/images/abp-studio-new-microservice-solution-dialog-optional-modules.png and b/docs/en/get-started/images/abp-studio-new-microservice-solution-dialog-optional-modules.png differ diff --git a/docs/en/get-started/images/abp-studio-new-microservice-solution-dialog-properties.png b/docs/en/get-started/images/abp-studio-new-microservice-solution-dialog-properties.png index fd811125c7..5f2a9b7938 100644 Binary files a/docs/en/get-started/images/abp-studio-new-microservice-solution-dialog-properties.png and b/docs/en/get-started/images/abp-studio-new-microservice-solution-dialog-properties.png differ diff --git a/docs/en/get-started/images/abp-studio-new-solution-dialog-additional-options-microservice.png b/docs/en/get-started/images/abp-studio-new-solution-dialog-additional-options-microservice.png index da7a5f22ea..cb85c3a2b8 100644 Binary files a/docs/en/get-started/images/abp-studio-new-solution-dialog-additional-options-microservice.png and b/docs/en/get-started/images/abp-studio-new-solution-dialog-additional-options-microservice.png differ diff --git a/docs/en/get-started/images/abp-studio-new-solution-dialog-additional-services.png b/docs/en/get-started/images/abp-studio-new-solution-dialog-additional-services.png index f7f088804b..6bbf3fb22a 100644 Binary files a/docs/en/get-started/images/abp-studio-new-solution-dialog-additional-services.png and b/docs/en/get-started/images/abp-studio-new-solution-dialog-additional-services.png differ diff --git a/docs/en/get-started/images/abp-studio-new-solution-dialog-aspire-configuration-microservice.png b/docs/en/get-started/images/abp-studio-new-solution-dialog-aspire-configuration-microservice.png new file mode 100644 index 0000000000..74cd5066bb Binary files /dev/null and b/docs/en/get-started/images/abp-studio-new-solution-dialog-aspire-configuration-microservice.png differ diff --git a/docs/en/get-started/images/abp-studio-new-solution-dialog-database-configurations-microservice.png b/docs/en/get-started/images/abp-studio-new-solution-dialog-database-configurations-microservice.png index 910b522dd3..e41580ad1e 100644 Binary files a/docs/en/get-started/images/abp-studio-new-solution-dialog-database-configurations-microservice.png and b/docs/en/get-started/images/abp-studio-new-solution-dialog-database-configurations-microservice.png differ diff --git a/docs/en/get-started/images/abp-studio-new-solution-dialog-database-provider-microservice.png b/docs/en/get-started/images/abp-studio-new-solution-dialog-database-provider-microservice.png index 4b8965456c..ed52bac62c 100644 Binary files a/docs/en/get-started/images/abp-studio-new-solution-dialog-database-provider-microservice.png and b/docs/en/get-started/images/abp-studio-new-solution-dialog-database-provider-microservice.png differ diff --git a/docs/en/get-started/images/abp-studio-new-solution-dialog-dynamic-localization.png b/docs/en/get-started/images/abp-studio-new-solution-dialog-dynamic-localization.png index 3dedb0aba0..455a6bd574 100644 Binary files a/docs/en/get-started/images/abp-studio-new-solution-dialog-dynamic-localization.png and b/docs/en/get-started/images/abp-studio-new-solution-dialog-dynamic-localization.png differ diff --git a/docs/en/get-started/images/abp-studio-new-solution-dialog-languages-microservice.png b/docs/en/get-started/images/abp-studio-new-solution-dialog-languages-microservice.png new file mode 100644 index 0000000000..705fee9325 Binary files /dev/null and b/docs/en/get-started/images/abp-studio-new-solution-dialog-languages-microservice.png differ diff --git a/docs/en/get-started/images/abp-studio-new-solution-dialog-microservice.png b/docs/en/get-started/images/abp-studio-new-solution-dialog-microservice.png index e98c96ac07..9b9210b269 100644 Binary files a/docs/en/get-started/images/abp-studio-new-solution-dialog-microservice.png and b/docs/en/get-started/images/abp-studio-new-solution-dialog-microservice.png differ diff --git a/docs/en/get-started/images/abp-studio-new-solution-dialog-mobile-framework-microservice.png b/docs/en/get-started/images/abp-studio-new-solution-dialog-mobile-framework-microservice.png index f883555e4a..04e1af7ebe 100644 Binary files a/docs/en/get-started/images/abp-studio-new-solution-dialog-mobile-framework-microservice.png and b/docs/en/get-started/images/abp-studio-new-solution-dialog-mobile-framework-microservice.png differ diff --git a/docs/en/get-started/images/abp-studio-new-solution-dialog-multi-tenancy.png b/docs/en/get-started/images/abp-studio-new-solution-dialog-multi-tenancy.png index 2bc29805ee..3c06ae001d 100644 Binary files a/docs/en/get-started/images/abp-studio-new-solution-dialog-multi-tenancy.png and b/docs/en/get-started/images/abp-studio-new-solution-dialog-multi-tenancy.png differ diff --git a/docs/en/get-started/images/abp-studio-new-solution-dialog-public-web-site.png b/docs/en/get-started/images/abp-studio-new-solution-dialog-public-web-site.png index 3dff0567fd..40325a5358 100644 Binary files a/docs/en/get-started/images/abp-studio-new-solution-dialog-public-web-site.png and b/docs/en/get-started/images/abp-studio-new-solution-dialog-public-web-site.png differ diff --git a/docs/en/get-started/images/abp-studio-new-solution-dialog-ui-framework-microservice.png b/docs/en/get-started/images/abp-studio-new-solution-dialog-ui-framework-microservice.png index 4a6fbf2d40..27e39f3776 100644 Binary files a/docs/en/get-started/images/abp-studio-new-solution-dialog-ui-framework-microservice.png and b/docs/en/get-started/images/abp-studio-new-solution-dialog-ui-framework-microservice.png differ diff --git a/docs/en/get-started/images/abp-studio-new-solution-dialog-ui-theme-microservice.png b/docs/en/get-started/images/abp-studio-new-solution-dialog-ui-theme-microservice.png index 553f0ca395..53692d03de 100644 Binary files a/docs/en/get-started/images/abp-studio-new-solution-dialog-ui-theme-microservice.png and b/docs/en/get-started/images/abp-studio-new-solution-dialog-ui-theme-microservice.png differ diff --git a/docs/en/get-started/microservice.md b/docs/en/get-started/microservice.md index d57c652b8e..ee7e31bf85 100644 --- a/docs/en/get-started/microservice.md +++ b/docs/en/get-started/microservice.md @@ -94,6 +94,25 @@ Once you select the desired modules, click the *Next* button for the *UI Theme* LeptonX is the suggested UI theme that is proper for production usage. Select one of the themes and configure the additional options if you want. +Click the Next button to see *Language Selection* selection: + +![abp-studio-new-solution-dialog-languages](images/abp-studio-new-solution-dialog-languages-microservice.png) + +In this step, you can choose which languages your application will support. + +* Default Language: Select the main language for your app. + +* Localizable User Interface: Turn this on to support multiple languages. + +* Available Languages: Check the languages you want to include. + +* Click Add Custom Language if you want to add a language that is not listed. + +Click the Next button to see *.NET Aspire* configuration selection: + +![abp-studio-new-solution-dialog-aspire-configuration](images/abp-studio-new-solution-dialog-aspire-configuration-microservice.png) + +In this step, you can enable or disable the .NET Aspire integration for your solution. If you enable it, the solution will be pre-configured to work with .NET Aspire for easier microservice development and deployment. See the [Aspire Integration](../solution-templates/microservice/aspire-integration.md) document for more information about this feature. Click the Next button to see *Additional Options* selection: diff --git a/docs/en/images/add-passkey.png b/docs/en/images/add-passkey.png new file mode 100644 index 0000000000..345cdd644d Binary files /dev/null and b/docs/en/images/add-passkey.png differ diff --git a/docs/en/images/ai-management-widget.png b/docs/en/images/ai-management-widget.png new file mode 100644 index 0000000000..2f396f3e5a Binary files /dev/null and b/docs/en/images/ai-management-widget.png differ diff --git a/docs/en/images/my-passkey.png b/docs/en/images/my-passkey.png new file mode 100644 index 0000000000..2b40045165 Binary files /dev/null and b/docs/en/images/my-passkey.png differ diff --git a/docs/en/images/passkey-login.png b/docs/en/images/passkey-login.png new file mode 100644 index 0000000000..dabf324e7f Binary files /dev/null and b/docs/en/images/passkey-login.png differ diff --git a/docs/en/images/passkey-login2.png b/docs/en/images/passkey-login2.png new file mode 100644 index 0000000000..349d8a2dca Binary files /dev/null and b/docs/en/images/passkey-login2.png differ diff --git a/docs/en/images/passkey-setting.png b/docs/en/images/passkey-setting.png new file mode 100644 index 0000000000..6211e91d9c Binary files /dev/null and b/docs/en/images/passkey-setting.png differ diff --git a/docs/en/images/pen-test-alert-list-10.1.png b/docs/en/images/pen-test-alert-list-10.1.png new file mode 100644 index 0000000000..3a9d10feff Binary files /dev/null and b/docs/en/images/pen-test-alert-list-10.1.png differ diff --git a/docs/en/modules/account-pro.md b/docs/en/modules/account-pro.md index 81e1177e33..7944c7448a 100644 --- a/docs/en/modules/account-pro.md +++ b/docs/en/modules/account-pro.md @@ -424,3 +424,4 @@ This module doesn't define any additional distributed event. See the [standard d * [Linked Accounts](./account/linkedaccounts.md) * [Session Management](./account/session-management.md) * [Idle Session Timeout](./account/idle-session-timeout.md) +* [Web Authentication API (WebAuthn) passkeys](./account/passkey.md) diff --git a/docs/en/modules/account/impersonation.md b/docs/en/modules/account/impersonation.md index c6fdae0ebf..3335ea50e5 100644 --- a/docs/en/modules/account/impersonation.md +++ b/docs/en/modules/account/impersonation.md @@ -7,7 +7,7 @@ # Tenant impersonation & User impersonation -User impersonation allows you to temporarily sign in as a different user in your tenant's users. This article introduces how to enable impersonation in ABP. Impersonation is enabled by defautl in ABP v5.0 and above. +User impersonation allows you to temporarily sign in as a different user in your tenant's users. This article introduces how to enable impersonation in ABP. Impersonation is enabled by default in ABP v5.0 and above. ## Introduction diff --git a/docs/en/modules/account/passkey.md b/docs/en/modules/account/passkey.md new file mode 100644 index 0000000000..b8f00ee576 --- /dev/null +++ b/docs/en/modules/account/passkey.md @@ -0,0 +1,63 @@ +# Web Authentication API (WebAuthn) passkeys + +The `Web Authentication API (WebAuthn) passkeys` feature allows users to authenticate using passkeys, which are more secure and user-friendly alternatives to traditional passwords. Passkeys leverage public key cryptography to provide strong authentication without the need for users to remember complex passwords. + +## Enabling Passkeys + +You can enable/disable the `Web Authentication API (WebAuthn) passkeys` feature in the `Setting > Account > Passkeys` page. Also, there is an option to allow how many passkeys a user can register: + +![passkey-setting](../../images/passkey-setting.png) + +## Manage Passkeys + +You can add/rename/delete your passkeys in the `Account/Manage` page: + +![my-passkey](../../images/my-passkey.png) + +Click the `Add Passkey` button to register a new passkey. You will be prompted to use your device's built-in biometric authentication (such as fingerprint or facial recognition) or an external security key to complete the registration process: + +![add-passkey](../../images/add-passkey.png) + +## Using Passkey for Login + +Once you enable the passkey feature and register at least one passkey, you can use it to log in to your account. On the login page, select the `Passkey login` option and follow the prompts to authenticate using your registered passkey: + +![passkey-login](../../images/passkey-login.png) + +![passkey-login2](../../images/passkey-login2.png) + +## Configure passkey options + +ASP.NET Core Identity provides various options to configure passkey behavior through the `IdentityPasskeyOptions` class, which include: + +- **AuthenticatorTimeout**: Gets or sets the time that the browser should wait for the authenticator to provide a passkey as a TimeSpan. This option applies to both creating a new passkey and requesting an existing passkey. This option is treated as a hint to the browser, and the browser may ignore the option. The default value is 5 minutes. +- **ChallengeSize**: Gets or sets the size of the challenge in bytes sent to the client during attestation and assertion. This option applies to both creating a new passkey and requesting an existing passkey. The default value is 32 bytes. +- **ServerDomain**: Gets or sets the effective Relying Party ID (domain) of the server. This should be unique and will be used as the identity for the server. This option applies to both creating a new passkey and requesting an existing passkey. If null, which is the default value, the server's origin is used. For more information, see Relying Party Identifier RP ID. + +**Example configuration:** + +```csharp +builder.Services.Configure(options => +{ + options.ServerDomain = "abp.io"; + options.AuthenticatorTimeout = TimeSpan.FromMinutes(3); + options.ChallengeSize = 64; +}); +``` + +For a complete list of configuration options, see [IdentityPasskeyOptions](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.identity.identitypasskeyoptions). For the most up-to-date browser defaults, see the [W3C WebAuthn specification](https://www.w3.org/TR/webauthn-3/). + +## HTTPS requirement + +All passkey operations require HTTPS. The implementation stores authentication data in encrypted and signed cookies that could be intercepted over unencrypted connections. + +## Browser Support + +Passkeys are supported in most modern browsers, including: Chrome, Edge, Firefox, and Safari. Ensure that you are using the latest version of your browser to take advantage of passkey functionality. + +## Additional resources + +For more information on WebAuthn and passkeys, refer to the following resources: + +- [Enable Web Authentication API (WebAuthn) passkeys](https://learn.microsoft.com/en-us/aspnet/core/security/authentication/passkeys) +- [Web Authentication API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Authentication_API) diff --git a/docs/en/modules/ai-management/index.md b/docs/en/modules/ai-management/index.md index b9492d5a4f..e50253c22c 100644 --- a/docs/en/modules/ai-management/index.md +++ b/docs/en/modules/ai-management/index.md @@ -28,7 +28,6 @@ abp add-module Volo.AIManagement Open ABP Studio, navigate to your solution explorer, **Right Click** on the project and select **Import Module**. Choose `Volo.AIManagement` from `NuGet` tab and check the "Install this Module" checkbox. Click the "OK" button to install the module. - ## Packages This module follows the [module development best practices guide](../../framework/architecture/best-practices) and consists of several NuGet and NPM packages. See the guide if you want to understand the packages and relations between them. @@ -44,7 +43,7 @@ AI Management module packages are designed for various usage scenarios. Packages AI Management module adds the following items to the "Main" menu: * **AI Management**: Root menu item for AI Management module. (`AIManagement`) - * **Workspaces**: Workspace management page. (`AIManagement.Workspaces`) + * **Workspaces**: Workspace management page. (`AIManagement.Workspaces`) `AIManagementMenus` class has the constants for the menu item names. @@ -87,21 +86,21 @@ Workspaces are the core concept of the AI Management module. A workspace represe When creating or managing a workspace, you can configure the following properties: -| Property | Required | Description | -|----------|----------|-------------| -| `Name` | Yes | Unique workspace identifier (cannot contain spaces) | -| `Provider` | Yes* | AI provider name (e.g., "OpenAI", "Ollama") | -| `ModelName` | Yes* | Model identifier (e.g., "gpt-4", "mistral") | -| `ApiKey` | No | API authentication key (required by some providers) | -| `ApiBaseUrl` | No | Custom endpoint URL (defaults to provider's default) | -| `SystemPrompt` | No | Default system prompt for all conversations | -| `Temperature` | No | Response randomness (0.0-1.0, defaults to provider default) | -| `Description` | No | Workspace description | -| `IsActive` | No | Enable/disable the workspace (default: true) | -| `ApplicationName` | No | Associate workspace with specific application | -| `RequiredPermissionName` | No | Permission required to use this workspace | -| `IsSystem` | No | Whether it's a system workspace (read-only) | -| `OverrideSystemConfiguration` | No | Allow database configuration to override code-defined settings | +| Property | Required | Description | +| ----------------------------- | -------- | -------------------------------------------------------------- | +| `Name` | Yes | Unique workspace identifier (cannot contain spaces) | +| `Provider` | Yes* | AI provider name (e.g., "OpenAI", "Ollama") | +| `ModelName` | Yes* | Model identifier (e.g., "gpt-4", "mistral") | +| `ApiKey` | No | API authentication key (required by some providers) | +| `ApiBaseUrl` | No | Custom endpoint URL (defaults to provider's default) | +| `SystemPrompt` | No | Default system prompt for all conversations | +| `Temperature` | No | Response randomness (0.0-1.0, defaults to provider default) | +| `Description` | No | Workspace description | +| `IsActive` | No | Enable/disable the workspace (default: true) | +| `ApplicationName` | No | Associate workspace with specific application | +| `RequiredPermissionName` | No | Permission required to use this workspace | +| `IsSystem` | No | Whether it's a system workspace (read-only) | +| `OverrideSystemConfiguration` | No | Allow database configuration to override code-defined settings | **\*Not required for system workspaces** @@ -165,7 +164,7 @@ public class WorkspaceDataSeederContributor : IDataSeedContributor, ITransientDe workspace.ApiKey = "your-api-key"; workspace.SystemPrompt = "You are a helpful customer support assistant."; - + await _workspaceRepository.InsertAsync(workspace); } ``` @@ -180,13 +179,12 @@ public class WorkspaceDataSeederContributor : IDataSeedContributor, ITransientDe The AI Management module defines the following permissions: -| Permission | Description | Default Granted To | -|------------|-------------|-------------------| -| `AIManagement.Workspaces` | View workspaces | Admin role | -| `AIManagement.Workspaces.Create` | Create new workspaces | Admin role | -| `AIManagement.Workspaces.Update` | Edit existing workspaces | Admin role | -| `AIManagement.Workspaces.Delete` | Delete workspaces | Admin role | - +| Permission | Description | Default Granted To | +| -------------------------------- | ------------------------ | ------------------ | +| `AIManagement.Workspaces` | View workspaces | Admin role | +| `AIManagement.Workspaces.Create` | Create new workspaces | Admin role | +| `AIManagement.Workspaces.Update` | Edit existing workspaces | Admin role | +| `AIManagement.Workspaces.Delete` | Delete workspaces | Admin role | ### Workspace-Level Permissions @@ -203,6 +201,7 @@ workspace.RequiredPermissionName = MyAppPermissions.AccessPremiumWorkspaces; ``` When a workspace has a required permission: + * Only authorized users with that permission can access the workspace endpoints * Users without the permission will receive an authorization error @@ -220,6 +219,7 @@ The AI Management module is designed to support various usage patterns, from sim In this scenario, you only use the ABP Framework's AI features directly. You configure AI providers (like OpenAI) in your code and don't need any database or management UI. **Required Packages:** + - `Volo.Abp.AI` - Any Microsoft AI extensions (e.g., `Microsoft.Extensions.AI.OpenAI`) @@ -257,7 +257,7 @@ public class MyService { _chatClient = chatClient; } - + public async Task GetResponseAsync(string prompt) { var response = await _chatClient.CompleteAsync(prompt); @@ -277,10 +277,12 @@ In this scenario, you install the AI Management module with its database layer, **Required Packages:** **Minimum (backend only):** + - `Volo.AIManagement.EntityFrameworkCore` (or `Volo.AIManagement.MongoDB`) - `Volo.AIManagement.OpenAI` (or another AI provider package) **Full installation (with UI and API):** + - `Volo.AIManagement.EntityFrameworkCore` (or `Volo.AIManagement.MongoDB`) - `Volo.AIManagement.Application` - `Volo.AIManagement.HttpApi` @@ -315,6 +317,7 @@ public class YourModule : AbpModule **Option 2 - Dynamic Workspace (UI-based):** No code configuration needed. Define workspaces through: + - The AI Management UI (navigate to AI Management > Workspaces) - Data seeding in your `DataSeeder` class @@ -339,6 +342,7 @@ public class MyService In this scenario, your application communicates with a separate AI Management microservice that manages configurations and communicates with AI providers on your behalf. The AI Management service handles all AI provider interactions. **Required Packages:** + - `Volo.AIManagement.Client.HttpApi.Client` **Configuration:** @@ -399,7 +403,7 @@ public class MyService var response = await _chatService.ChatCompletionsAsync(workspaceName, request); return response.Content; } - + // For streaming responses public async IAsyncEnumerable StreamAIResponseAsync(string workspaceName, string prompt) { @@ -426,6 +430,7 @@ public class MyService This scenario builds on Scenario 3, but your application exposes its own HTTP endpoints that other applications can call. Your application then forwards these requests to the AI Management service. **Required Packages:** + - `Volo.AIManagement.Client.HttpApi.Client` (to communicate with AI Management service) - `Volo.AIManagement.Client.Application` (application services) - `Volo.AIManagement.Client.HttpApi` (to expose HTTP endpoints) @@ -438,24 +443,170 @@ Same as Scenario 3, configure the remote AI Management service in `appsettings.j **Usage:** Once configured, other applications can call your application's endpoints: + - `POST /api/ai-management-client/chat-completion` for chat completions - `POST /api/ai-management-client/stream-chat-completion` for streaming responses Your application acts as a proxy, forwarding these requests to the AI Management microservice. -## Comparison Table +### Comparison Table + +| Scenario | Database Required | Manages Config | Executes AI | Exposes API | Use Case | +| ------------------------- | ----------------- | -------------- | -------------- | ----------- | ----------------------------------------- | +| **1. No AI Management** | No | Code | Local | Optional | Simple apps, no config management needed | +| **2. Full AI Management** | Yes | Database/UI | Local | Optional | Monoliths, services managing their own AI | +| **3. Client Remote** | No | Remote Service | Remote Service | No | Microservices consuming AI centrally | +| **4. Client Proxy** | No | Remote Service | Remote Service | Yes | API Gateway pattern, proxy services | + + + +## Client Usage (MVC UI) + +AI Management uses different packages depending on the usage scenario: + +- **`Volo.AIManagement.*` packages**: These contain the core AI functionality and are used when your application hosts and manages its own AI operations. These packages don't expose any application service and endpoints to be consumed by default. + +- **`Volo.AIManagement.Client.*` packages**: These are designed for applications that need to consume AI services from a remote application. They provide both server and client side of remote access to the AI services. + +**List of packages:** +- `Volo.AIManagement.Client.Application` +- `Volo.AIManagement.Client.Application.Contracts` +- `Volo.AIManagement.Client.HttpApi` +- `Volo.AIManagement.Client.HttpApi.Client` +- `Volo.AIManagement.Client.Web` + +### The Chat Widget +The `Volo.AIManagement.Client.Web` package provides a chat widget to allow you to easily integrate a chat interface into your application that uses a specific AI workspace named `ChatClientChatViewComponent`. + +#### Basic Usage +You can invoke the `ChatClientChatViewComponent` Widget in your razor page with the following code: + +```csharp +@await Component.InvokeAsync(typeof(ChatClientChatViewComponent), new ChatClientChatViewModel +{ + WorkspaceName = "mylama", +}) +``` + +![ai-management-workspaces](../../images/ai-management-widget.png) + +#### Properties +You can customize the chat widget with the following properties: +- `WorkspaceName`: The name of the workspace to use. +- `ComponentId`: Unique identifier for accessing the component via JavaScript API (stored in abp.chatComponents). +- `ConversationId`: The unique identifier for persisting and retrieving chat history from client-side storage. +- `Title`: The title of the chat widget. +- `ShowStreamCheckbox`: Whether to show the stream checkbox. Allows user to toggle streaming on and off. Default is `false`. +- `UseStreaming`: Default streaming behavior. Can be overridden by user when `ShowStreamCheckbox` is true. + +```csharp +@await Component.InvokeAsync(typeof(ChatClientChatViewComponent), new ChatClientChatViewModel +{ + WorkspaceName = "mylama", + ComponentId = "mylama-chat", + ConversationId = "mylama-conversation-" + @CurrentUser.Id, + Title = "My Custom Title", + ShowStreamCheckbox = true, + UseStreaming = true +}) +``` + +#### Using the Conversation Id +You can use the `ConversationId` property to specify the id of the conversation to use. When the Conversation Id is provided, the chat will be stored at the client side and will be retrieved when the user revisits the page that contains the chat widget. If it's not provided or provided as **null**, the chat will be temporary and will not be saved, it'll be lost when the component lifetime ends. + +```csharp +@await Component.InvokeAsync(typeof(ChatClientChatViewComponent), new ChatClientChatViewModel +{ + WorkspaceName = "mylama", + ConversationId = "my-support-conversation-" + @CurrentUser.Id +}) +``` + +#### JavaScript API +The chat components are initialized automatically when the ViewComponent is rendered in the page. All the initialized components in the page are stored in the `abp.chatComponents` object. You can retrieve a specific component by its `ComponentId` which is defined while invoking the ViewComponent. + +```csharp +@await Component.InvokeAsync(typeof(ChatClientChatViewComponent), new ChatClientChatViewModel +{ + WorkspaceName = "mylama", + ComponentId = "mylama-chat" +}) +``` + +You can then use the JavaScript API to interact with the component. +```js +var chatComponent = abp.chatComponents.get('mylama-chat'); +``` + +Once you have the component, you can use the following functions to interact with it: + +```js +// Switch to a different conversation +chatComponent.switchConversation(conversationId); + +// Create a new conversation with a specific model +chatComponent.createConversation(conversationId, modelName); + +// Clear the current conversation history +chatComponent.clearConversation(); + +// Get the current conversation ID (returns null for ephemeral conversations) +var currentId = chatComponent.getCurrentConversationId(); + +// Initialize with a specific conversation ID +chatComponent.initialize(conversationId); + +// Send a message programmatically +chatComponent.sendMessage(); + +// Listen to events +chatComponent.on('messageSent', function(data) { + console.log('Message sent:', data.message); + console.log('Conversation ID:', data.conversationId); + console.log('Is first message:', data.isFirstMessage); +}); + +chatComponent.on('messageReceived', function(data) { + console.log('AI response:', data.message); + console.log('Conversation ID:', data.conversationId); + console.log('Is streaming:', data.isStreaming); +}); + +chatComponent.on('streamStarted', function(data) { + console.log('Streaming started for conversation:', data.conversationId); +}); -| Scenario | Database Required | Manages Config | Executes AI | Exposes API | Use Case | -|----------|------------------|----------------|-------------|-------------|----------| -| **1. No AI Management** | No | Code | Local | Optional | Simple apps, no config management needed | -| **2. Full AI Management** | Yes | Database/UI | Local | Optional | Monoliths, services managing their own AI | -| **3. Client Remote** | No | Remote Service | Remote Service | No | Microservices consuming AI centrally | -| **4. Client Proxy** | No | Remote Service | Remote Service | Yes | API Gateway pattern, proxy services | +// Remove event listeners +chatComponent.off('messageSent', callbackFunction); +``` + +**Best-practices:** +- Don't try to access the component at the page load time, it's not guaranteed to be initialized yet. Get the component whenever you need it to make sure it's **initialized** and the **latest state** is applied. + +❌ Don't do this +```js +(function(){ + var chatComponent = abp.chatComponents.get('mylama-chat'); + $('#my-button').on('click', function() { + chatComponent.clearConversation(); + }); +}); +``` +✅ Do this +```js +(function(){ + $('#my-button').on('click', function() { + var chatComponent = abp.chatComponents.get('mylama-chat'); + chatComponent.clearConversation(); + }); +}); +``` ## Using Dynamic Workspace Configurations for custom requirements + The AI Management module allows you to access only configuration of a workspace without resolving pre-constructed chat client. This is useful when you want to use a workspace for your own purposes and you don't need to use the chat client. -The `IWorkspaceConfigurationStore` service is used to access the configuration of a workspace. It has multiple implementaations according to the usage scenario. +The `IWorkspaceConfigurationStore` service is used to access the configuration of a workspace. It has multiple implementations according to the usage scenario. ```csharp public class MyService @@ -470,7 +621,7 @@ public class MyService { // Get the configuration of the workspace that can be managed dynamically. var configuration = await _workspaceConfigurationStore.GetAsync("MyWorkspace"); - + // Do something with the configuration var kernel = Kernel.CreateBuilder() .AddAzureOpenAIChatClient( @@ -550,24 +701,23 @@ public override void ConfigureServices(ServiceConfigurationContext context) > [!TIP] > For production scenarios, you may want to add validation for the factory configuration. - ### Available Configuration Properties The `ChatClientCreationConfiguration` object provides the following properties from the database: -| Property | Type | Description | -|----------|------|-------------| -| `Name` | string | Workspace name | -| `Provider` | string | Provider name (e.g., "OpenAI", "Ollama") | -| `ApiKey` | string? | API key for authentication | -| `ModelName` | string | Model identifier (e.g., "gpt-4", "mistral") | -| `SystemPrompt` | string? | Default system prompt for the workspace | -| `Temperature` | float? | Temperature setting for response generation | -| `ApiBaseUrl` | string? | Custom API endpoint URL | -| `Description` | string? | Workspace description | -| `IsActive` | bool | Whether the workspace is active | -| `IsSystem` | bool | Whether it's a system workspace | -| `RequiredPermissionName` | string? | Permission required to use this workspace | +| Property | Type | Description | +| ------------------------ | ------- | ------------------------------------------- | +| `Name` | string | Workspace name | +| `Provider` | string | Provider name (e.g., "OpenAI", "Ollama") | +| `ApiKey` | string? | API key for authentication | +| `ModelName` | string | Model identifier (e.g., "gpt-4", "mistral") | +| `SystemPrompt` | string? | Default system prompt for the workspace | +| `Temperature` | float? | Temperature setting for response generation | +| `ApiBaseUrl` | string? | Custom API endpoint URL | +| `Description` | string? | Workspace description | +| `IsActive` | bool | Whether the workspace is active | +| `IsSystem` | bool | Whether it's a system workspace | +| `RequiredPermissionName` | string? | Permission required to use this workspace | ### Example: Azure OpenAI Factory @@ -604,6 +754,7 @@ public class AzureOpenAIChatClientFactory : IChatClientFactory, ITransientDepend After implementing and registering your factory: 1. **Through UI**: Navigate to the AI Management workspaces page and create a new workspace: + - Select your provider name (e.g., "Ollama", "AzureOpenAI") - Configure the API settings - Set the model name @@ -672,6 +823,7 @@ WorkspaceConfiguration:{ApplicationName}:{WorkspaceName} ``` ### HttpApi Client Layer + - `IntegrationWorkspaceConfigurationStore`: Integration service for remote workspace configuration retrieval. Implements `IWorkspaceConfigurationStore` interface. The cache is automatically invalidated when workspaces are created, updated, or deleted. @@ -680,4 +832,5 @@ The cache is automatically invalidated when workspaces are created, updated, or - [Artificial Intelligence Infrastructure](../../framework/infrastructure/artificial-intelligence/index.md): Learn about the underlying AI workspace infrastructure - [Microsoft.Extensions.AI](https://learn.microsoft.com/en-us/dotnet/ai/): Microsoft's unified AI abstractions +- [Microsoft Agent Framework](https://learn.microsoft.com/en-us/agent-framework/overview/agent-framework-overview): Microsoft's Agent Framework - [Semantic Kernel](https://learn.microsoft.com/en-us/semantic-kernel/): Microsoft's Semantic Kernel integration diff --git a/docs/en/modules/feature-management.md b/docs/en/modules/feature-management.md index 300b232f93..e80098a04d 100644 --- a/docs/en/modules/feature-management.md +++ b/docs/en/modules/feature-management.md @@ -72,9 +72,10 @@ namespace Demo ## Feature Management Providers -Features Management Module is extensible, just like the [features system](../framework/infrastructure/features.md). You can extend it by defining feature management providers. There are 3 pre-built feature management providers registered it the following order: +Features Management Module is extensible, just like the [features system](../framework/infrastructure/features.md). You can extend it by defining feature management providers. There are 4 pre-built feature management providers registered in the following order: * `DefaultValueFeatureManagementProvider`: Gets the value from the default value of the feature definition. It can not set the default value since default values are hard-coded on the feature definition. +* `ConfigurationFeatureManagementProvider`: Gets the value from the [IConfiguration service](../framework/fundamentals/configuration.md). * `EditionFeatureManagementProvider`: Gets or sets the feature values for an edition. Edition is a group of features assigned to tenants. Edition system has not implemented by the Tenant Management module. You can implement it yourself or purchase the ABP [SaaS Module](https://abp.io/modules/Volo.Saas) which implements it and also provides more SaaS features, like subscription and payment. * `TenantFeatureManagementProvider`: Gets or sets the features values for tenants. diff --git a/docs/en/modules/saas.md b/docs/en/modules/saas.md index cf88cf716a..7d78a4e9df 100644 --- a/docs/en/modules/saas.md +++ b/docs/en/modules/saas.md @@ -40,7 +40,7 @@ Firstly, Payment module must be configured properly: ```bash abp add-module Volo.Payment ``` - _Or you can install via using ABP Suite._ + _Or you can install via using ABP Studio._ - Configure Saas module to use Payment. diff --git a/docs/en/others/penetration-test-report.md b/docs/en/others/penetration-test-report.md index 13086fb2a0..cc80d39b8e 100644 --- a/docs/en/others/penetration-test-report.md +++ b/docs/en/others/penetration-test-report.md @@ -7,7 +7,7 @@ # ABP Penetration Test Report -The ABP Commercial MVC `v9.1.0` application template has been tested against security vulnerabilities by the [OWASP ZAP v2.14.0](https://www.zaproxy.org/) tool. The demo web application was started on the `https://localhost:44349` address. The below alerts have been reported by the pentest tool. These alerts are sorted by the risk level as high, medium, and low. The informational alerts are not mentioned in this document. +The ABP Commercial MVC `v10.0.1` application template has been tested against security vulnerabilities by the [OWASP ZAP v2.14.0](https://www.zaproxy.org/) tool. The demo web application was started on the `https://localhost:44349` address. The below alerts have been reported by the pentest tool. These alerts are sorted by the risk level as high, medium, and low. The informational alerts are not mentioned in this document. Many of these alerts are **false-positive**, meaning the vulnerability scanner detected these issues, but they are not exploitable. It's clearly explained for each false-positive alert why this alert is a false-positive. @@ -17,53 +17,75 @@ In the next sections, you will find the affected URLs, attack parameters (reques There are high _(red flag)_, medium _(orange flag)_, low _(yellow flag)_, and informational _(blue flag)_ alerts. -![penetration-test-9.1.0](../images/pen-test-alert-list-9.1.png) +![penetration-test-10.0.1](../images/pen-test-alert-list-10.1.png) -> The informational alerts are not mentioned in this document. These alerts are not raising any risks on your application and they are optional. +> The informational alerts are not mentioned in this document. These alerts don't raise any risks for your application and they are optional. -### Spring4Shell [Risk: High] - False Positive +### Cross Site Scripting (Reflected) [Risk: High] - Positive -- *[POST] - https://localhost:44349/Account/ForgotPassword* (attack: **class.module.classLoader.DefaultAssertionStatus=nonsense**) -- *[POST] - https://localhost:44349/Account/Login* (attack: **class.module.classLoader.DefaultAssertionStatus=nonsense**) -- *[POST] - https://localhost:44349/Account/Login?ReturnUrl=%2FSettingManagement* (attack: **class.module.classLoader.DefaultAssertionStatus=nonsense**) +- *[GET] - https://localhost:44349/Identity/OrganizationUnits/AddMemberModal?title=SelectAUser&organizationUnitId=...&OrganizationUnitName=%3C%2Fh5%3E%3CscrIpt%3Ealert%281%29%3B%3C%2FscRipt%3E%3Ch5%3E* +- *[GET] - https://localhost:44349/Identity/OrganizationUnits/AddRoleModal?organizationUnitId=...&OrganizationUnitName=%3C%2Fh5%3E%3CscrIpt%3Ealert%281%29%3B%3C%2FscRipt%3E%3Ch5%3E* +- *[GET] - https://localhost:44349/Saas/Host/Tenants/ImpersonateTenantModal?tenantId=...&tenantName=%3C%2Fh5%3E%3CscrIpt%3Ealert%281%29%3B%3C%2FscRipt%3E%3Ch5%3E* **Description**: -The application appears to be vulnerable to CVE-2022-22965 (otherwise known as Spring4Shell) - remote code execution (RCE) via data binding. +Cross-site Scripting (XSS) is an attack technique that involves echoing attacker-supplied code into a user's browser instance. **Explanation**: -ABP Framework is built on top of ASP.NET Core and does not use the Spring Framework. This application does not rely on Java-based technologies, making it immune to vulnerabilities like Spring4Shell. The detection is a false positive as there are no Spring dependencies in the project. +This is a **Positive** alert. The application reflects the `OrganizationUnitName` and `tenantName` parameters without proper encoding in the modal headers, allowing for the execution of arbitrary JavaScript. We have created an **internal issue** to track this vulnerability, and it will be fixed in the next release. -### Absence of Anti-CSRF Tokens [Risk: Medium] — False Positive +### PII Disclosure [Risk: High] - False Positive -* *[GET] - https://localhost:44349/Account/LinkUsers/LinkUsersModal?returnUrl=/SettingManagement* -* *[GET] — https://localhost:44349/Account/Manage* (same URL with different query parameters) -* *[GET] - https://localhost:44349/HostDashboard* -* *[GET] - https://localhost:44349/SettingManagement?handler=RenderView&id=Volo.Abp.Account* (other several URLs) +- *[GET] - https://localhost:44349/* (Evidence: 639002492030480000) +- *[GET] - https://localhost:44349/?page=...* -**Description**: +**Description**: -No Anti-CSRF tokens were found in an HTML submission form. -A cross-site request forgery is an attack that involves forcing a victim to send an HTTP request to a target destination without their knowledge or intent in order to perform an action as the victim. The underlying cause is application functionality using predictable URL/form actions in a repeatable way. The nature of the attack is that CSRF exploits the trust that a website has for a user. By contrast, cross-site scripting (XSS) exploits the trust that a user has in a website. Like XSS, CSRF attacks are not necessarily cross-site, but they can be. Cross-site request forgery is also known as CSRF, XSRF, one-click attack, session riding, confused deputy, and sea surf. +The response contains Personally Identifiable Information, such as CC number, SSN and similar sensitive data. -**Explanation:** +**Explanation**: -This is a **false-positive** alert because ABP provides the Anti-CSRF token via a cookie as seen on the following screenshot: +This is a **false-positive** alert. The detected numbers (e.g., `639002492030480000`) are cache-busting timestamps (`_v` parameter) generated by the framework for static assets. They coincidentally match the pattern of Credit Card numbers (pattern matching) but are not sensitive data. -![Absence of Anti-CSRF Token](../images/pen-test-alert-remote-os-command-injection.png) +### Path Traversal [Risk: High] - False Positive -### Application Error Disclosure [Risk: Medium] - False Positive +- *[GET] - https://localhost:44349/Account/Login?returnUrl=Login* +- *[GET] - https://localhost:44349/api/account/security-logs?action=\security-logs* -- *[GET] — https://localhost:44349/AuditLogs* +**Description**: -**Description**: +The Path Traversal attack technique allows an attacker access to files, directories, and commands that potentially reside outside the web document root directory. + +**Explanation**: + +This is a **false-positive** alert. ABP Framework automatically validates `returnUrl` parameters and ensures they are local to the application or within a whitelist. The application does not return file contents based on these parameters. + +### SQL Injection [Risk: High] - False Positive + +- *[GET] - https://localhost:44349/AbpPermissionManagement/PermissionManagementModal?providerKey=AbpSolution16711_Swagger+AND+1%3D1+--+* +- *[GET] - https://localhost:44349/Account/Manage?CurrentPassword=ZAP%27+AND+%271%27%3D%271%27+--+* + +**Description**: + +SQL injection may be possible. -This page contains an error/warning message that may disclose sensitive information like the location of the file that produced the unhandled exception. This information can be used to launch further attacks against the web application. The alert could be a false positive if the error message is found inside a documentation page. +**Explanation**: + +This is a **false-positive** alert. ABP Framework uses Entity Framework Core, which inherently uses parameterized queries, preventing standard SQL injection attacks. Manual verification showed that injecting SQL syntax into parameters like `providerKey` results in the input being treated as a literal string (resulting in no match or default behavior) rather than altering the query structure. + +### SQL Injection - SQLite [Risk: High] - False Positive + +- *[POST] - https://localhost:44349/Account/ForgotPassword?returnUrl=%2FAccount%2FManage* (Attack: `case randomblob(100000) ...`) +- *[POST] - https://localhost:44349/FeatureManagement/FeatureManagementModal* + +**Description**: + +SQL injection may be possible. **Explanation**: -There are only one URL that is reported as exposing error messages. This is a **false-positive** alert. The [Audit Logging Module](../modules/audit-logging.md), shows request & response details and exception information, these are not sensitive information and only can be seen by the users whose related permissions are granted. +This is a **false-positive** alert. Similar to the standard SQL Injection alert, the application uses parameterized queries. The detected delays are likely due to application processing variations or network latency rather than successful SQL injection. ### Content Security Policy (CSP) Header Not Set [Risk: Medium] — Positive (Fixed) @@ -238,23 +260,7 @@ A cookie has been set with its `SameSite` attribute set to `none`, which means t Ensure that the `SameSite` attribute is set to either `lax` or ideally `strict` for all cookies. We discussed setting the **SameSite** attribute to `strict` in the following issue [github.com/abpframework/abp/issues/14215](https://github.com/abpframework/abp/issues/14215) and decided to leave this change to the final developer. -### Information Disclosure - Debug Error Messages [Risk: Low] — False Positive - -* *[GET] - https://localhost:44349/AuditLogs* - -**Description:** - -The response appeared to contain common error messages returned by platforms such as ASP.NET, and Web-servers such as IIS and Apache. You can configure the list of common debug messages. - -**Solution:** - -Disable debugging messages before pushing them to production. - -**Explanation:** - -The response of the endpoints above return localization texts which are not real error messages. As there is no real error in the backend side, this vulnerability is a **false-positive** alert. -![Information Disclosure - Debug Error Messages](../images/pen-test-information-disclosure.png) ### Strict-Transport-Security Header Not Set [Risk: Low] - False Positive diff --git a/docs/en/solution-templates/microservice/aspire-integration.md b/docs/en/solution-templates/microservice/aspire-integration.md new file mode 100644 index 0000000000..2da2706b34 --- /dev/null +++ b/docs/en/solution-templates/microservice/aspire-integration.md @@ -0,0 +1,372 @@ +```json +//[doc-seo] +{ + "Description": "Learn how to integrate .NET Aspire into your ABP-based solution for streamlined development, orchestration, and observability of distributed applications." +} +``` + +# .NET Aspire Integration + +````json +//[doc-nav] +{ + "Next": { + "Name": "Miscellaneous guides in the Microservice solution", + "Path": "solution-templates/microservice/guides" + } +} +```` + +> You must have an ABP Business or a higher license to be able to create a microservice solution. + +## .NET Aspire Overview + +[Aspire](https://aspire.dev/get-started/what-is-aspire/) streamlines building, running, debugging, and deploying distributed apps. Picture your app as a set of services, databases, and frontends—when they’re deployed, they all work together seamlessly, but every time you develop them they need to be individually started and connected. With Aspire, you get a unified toolchain that eliminates complex configs and makes local debugging effortless. Instantly launch and debug your entire app with a single command. Ready to deploy? Aspire lets you publish anywhere—Kubernetes, the cloud, or your own servers. It’s also fully extensible, so you can integrate your favorite tools and services with ease. It provides: + +- **Orchestration**: A code-first approach to defining and running distributed applications, managing dependencies, and launch order. +- **Integrations**: Pre-built components for common services (databases, caches, message brokers) with automatic configuration. +- **Tooling**: A developer dashboard for real-time monitoring of logs, traces, metrics, and resource health. +- **Service Discovery**: Automatic service-to-service communication without hardcoded endpoints. +- **Observability**: Built-in OpenTelemetry support for distributed tracing, metrics, and structured logging. + +## ABP Integration + +When you enable .NET Aspire in an ABP microservice solution, you get a fully integrated development experience where: + +- All microservices, gateways, and applications are orchestrated through a single entry point (AppHost). +- Infrastructure containers (databases, Redis, RabbitMQ, Elasticsearch, etc.) are managed as code. +- OpenTelemetry, health checks, and service discovery are automatically configured for all projects via the shared ServiceDefaults project. + +## Enabling Aspire + +When creating a new microservice solution via ABP Studio: + +1. In the solution creation wizard, look for the **".NET Aspire Integration"** step. +2. Toggle the option to **enable .NET Aspire**. +3. Complete the wizard—Aspire projects will be generated along with your solution. + +![Enable Aspire in ABP Studio](images/aspire-configuration.png) + +## Solution Structure Changes + +When Aspire is enabled, two additional projects are added to your solution: + +![Aspire Solution Structure](images/aspire-solution-structure.png) + +### AppHost (Orchestrator) + +[`AppHost`](https://aspire.dev/get-started/app-host/) is the .NET Aspire orchestrator project that declares all resources (services, databases, containers, applications) and their dependencies in C# code. All services, gateways, and applications in the solution have their project references added to `AppHost`. + +**Why is it added?** + +- Centralized orchestration: Start your entire microservice ecosystem with a single command. +- Code-first infrastructure: Databases, Redis, RabbitMQ, Elasticsearch, and observability tools are defined programmatically. +- Dependency management: `AppHost` ensures services start in the correct order using `WaitFor()` declarations. +- Automatic configuration: Connection strings, endpoints, and environment variables are injected automatically. + +**Key files in AppHost:** +| File | Purpose | +|--------|------------| +| `AppHost.cs` | Entry point—creates the distributed application builder and adds all resources | +| `AppHostExtensions.cs` | Extension methods for adding infrastructures, databases, microservices, gateways, and applications | + +**What it manages:** + +- Database servers (SQL Server, PostgreSQL, MySQL, or MongoDB) +- Per-service databases +- Redis (caching) +- RabbitMQ (messaging) +- Elasticsearch + Kibana (logging) +- Prometheus + Grafana (metrics) +- Jaeger (tracing) +- OpenTelemetry Collector +- All microservices, gateways, and web applications + +![Aspire AppHost Resource Topology](images/aspire-apphost-topology.png) + +### ServiceDefaults + +[`ServiceDefaults`](https://aspire.dev/fundamentals/service-defaults/) is a shared library that provides common cloud-native configuration for all projects in the solution. The `ServiceDefaults` project reference is added to all services, gateways, and applications in the solution. + +**Why is it added?** + +- Consistency: Every service uses the same observability, health check, and resilience patterns. +- Less boilerplate: Add `builder.AddServiceDefaults()` once, get all defaults automatically. +- Production-ready: OpenTelemetry, health endpoints, and HTTP resilience are preconfigured. + +**What it provides:** + +| Feature | Description | +|---------|-------------| +| OpenTelemetry | Tracing, metrics, and structured logging with automatic instrumentation | +| Health Checks | `/health` and `/alive` endpoints for Kubernetes-style probes | +| Service Discovery | Automatic resolution of service endpoints | +| HTTP Resilience | Retry policies, timeouts, and circuit breakers for HTTP clients | + +**Usage example:** + +```csharp +var builder = WebApplication.CreateBuilder(args); +builder.AddServiceDefaults(); // Adds all cloud-native defaults +// ... rest of configuration +``` + +## Running the Solution + +### Without Aspire + +1. Open **Solution Runner** in ABP Studio. +2. Start all resources in Solution Runner (services, gateways, applications, and tools such as databases, Redis, RabbitMQ, etc.) individually or collectively using the `Default` profile. + +![Solution Runner without Aspire](images/solution-runner-default-profile.png) + +### With Aspire +1. Open **Solution Runner** in ABP Studio. +2. Select the **Aspire** profile. +3. Run `MyCompanyName.MyProjectName.AppHost`. +4. AppHost automatically: + - Starts all infrastructure containers (database, Redis, RabbitMQ, Elasticsearch, etc.). + - Launches all microservices, gateways, and applications in dependency order. + - Injects connection strings and environment variables. + - Opens the Aspire Dashboard for monitoring. + +![Solution Runner with Aspire](images/solution-runner-aspire-profile.png) + +## Aspire Dashboard + +The dashboard enables real-time tracking of key aspects of your app, including logs, traces, and environment configurations. It's designed to enhance the development experience by providing a clear and insightful view of your app's state and structure. + +Key features of the dashboard include: + +- Real-time tracking of logs, traces, and environment configurations. +- User interface to stop, start, and restart resources. +- Collects and displays logs and telemetry; view structured logs, traces, and metrics in an intuitive UI. +- Enhanced debugging with GitHub Copilot, your AI-powered assistant built into the dashboard. + +### Opening the Dashboard + +When AppHost starts, the Aspire Dashboard opens automatically in ABP Studio's built-in browser at `https://localhost:15105`. Alternatively, you can right-click on AppHost in Solution Runner and select **Browse** to open it manually. + +![Aspire Dashboard Login](images/aspire-dashboard-login.png) + +**Dashboard login token**: `1q2w3E*` (default, configurable via launch settings) + +### Dashboard Features + +The dashboard includes several tabs, each offering different insights into your application: + +#### Resources + +View the status of all resources in your application, including projects, containers, and executables. Monitor health checks, view environment variables, and access endpoints for each resource. + +![Aspire Dashboard Resources Tab](images/aspire-dashboard-resources.png) + +#### Console + +Display console logs from all resources in real-time. Filter by resource and log level to quickly find relevant information during development and debugging. + +![Aspire Dashboard Console Tab](images/aspire-dashboard-console.png) + +#### Structured Logs + +View structured logs from all resources with advanced filtering capabilities. Search and filter logs by resource, log level, timestamp, and custom properties to diagnose issues efficiently. + +![Aspire Dashboard Structured Logs Tab](images/aspire-dashboard-structured-logs.png) + +#### Traces + +Explore distributed traces across your microservices to understand request flows and identify performance bottlenecks. Visualize how requests propagate through different services and examine timing information. + +![Aspire Dashboard Traces Tab](images/aspire-dashboard-traces.png) + +#### Metrics + +Monitor real-time metrics including HTTP requests, response times, garbage collection, memory usage, and custom metrics. Visualize metric trends with interactive charts to understand application performance. + +![Aspire Dashboard Metrics Tab](images/aspire-dashboard-metrics.png) + +## Tools and Their Management UIs + +AppHost pre-configures the following observability and tools. The URLs below are for their **management/dashboard interfaces** (these tools may expose additional internal endpoints for service communication). + +All URLs and configurations are defined in the `AppHost` project. If you need to change ports or other settings, you can modify them in the `AppHost` project. + +After running AppHost, you can access these tools either by opening the URLs directly in your browser or via Solution Runner **Tools** tab. + +### Database Management System Admin Tool + +The database management admin tool varies by database type: + +| Database | Tool | URL | +|----------|------|-----| +| SQL Server | DBeaver CloudBeaver | `http://localhost:8081` | +| MySQL | phpMyAdmin | `http://localhost:8082` | +| PostgreSQL | pgAdmin | `http://localhost:8083` | +| MongoDB | Mongo Express | `http://localhost:8084` | + +For example, if using PostgreSQL, access **pgAdmin** at `http://localhost:8083` or via Solution Runner Tools tab: + +![pgAdmin Dashboard](images/aspire-database-postgre-pgadmin.png) + + +### Grafana + +**URL**: `http://localhost:3001` +**Credentials**: `admin` / `admin` + +Grafana is a visualization and analytics platform for monitoring metrics. It provides interactive dashboards with charts and graphs for tracking application performance. + +![Grafana Dashboard](images/aspire-grafana-dashboard.png) + +### Jaeger + +**URL**: `http://localhost:16686` +**Credentials**: No authentication required + +Jaeger is a distributed tracing system to monitor and troubleshoot problems on interconnected software components called microservices. + +![Jaeger Traces](images/aspire-jaeger-traces.png) + +### Kibana + +**URL**: `http://localhost:5601` +**Credentials**: No authentication required + +Kibana is a visualization tool for Elasticsearch data. It provides search and data visualization capabilities for logs stored in Elasticsearch. + +![Kibana Dashboard](images/aspire-kibana-dashboard.png) + +### Prometheus + +**URL**: `http://localhost:9090` +**Credentials**: No authentication required + +Prometheus is a monitoring and alerting toolkit. It collects and stores metrics as time series data, allowing you to query and analyze application performance. + +![Prometheus Dashboard](images/aspire-prometheus-dashboard.png) + +### RabbitMQ Management + +**URL**: `http://localhost:15672` +**Credentials**: `guest` / `guest` + +RabbitMQ Management UI provides a web-based interface for managing and monitoring RabbitMQ message broker, including queues, exchanges, and message flows. + +![RabbitMQ Management](images/aspire-rabbitmq-management.png) + +### Redis Insight + +**URL**: `http://localhost:5540` +**Credentials**: No authentication required + +Redis Insight is a visual tool for Redis that allows you to browse data, run commands, and monitor Redis performance. + +![Redis Insight](images/aspire-redis-insight.png) + +## Adding New Services, Gateways, or Applications + +When you add a new microservice, gateway, or application via **ABP Studio**: + +1. `AppHost` is updated automatically - the new project is registered as a resource with appropriate configurations. +2. `ServiceDefaults` is referenced - the new project gets cloud-native defaults. + +> You don't need to manually edit `AppHost` in most cases. + +## Adding a Resource Manually + +If you need to add a resource manually (not via **ABP Studio**), follow these steps: + +### 1. Reference ServiceDefaults in your new project + +```xml + +``` + +> Adjust the path as necessary based on your solution structure. + +### 2. Add ServiceDefaults in Program.cs + +```csharp +var builder = WebApplication.CreateBuilder(args); +builder.AddServiceDefaults(); +// ... your configuration +``` + +### 3. Add Project Reference to AppHost + +Add a reference to your resource project in `MySolutionName.MyProjectName.AppHost/MySolutionName.MyProjectName.AppHost.csproj`: + +```xml + +``` + +### 4. Register Resource in AppHost + +Edit `AppHostExtensions.cs` and add your resource in the `AddAdditionalResources` method: + +```csharp +var myResource = builder + .AddProject("myresource", "MySolutionName.MyProjectName.MyResource") + .WaitFor(databases.AdministrationDb) + .WaitFor(databases.IdentityDb) + .WaitFor(databases.MyResourceDb) + .WaitFor(databases.AuditLoggingDb) + .WaitFor(databases.SaasDb) + .WaitFor(databases.LanguageManagementDb) + .WaitFor(redis) + .WaitFor(rabbitMq) + .WithReference(databases.AdministrationDb) + .WithReference(databases.IdentityDb) + .WithReference(databases.BlobStoringDb) + .WithReference(databases.MyResourceDb) + .WithReference(databases.AuditLoggingDb) + .WithReference(databases.SaasDb) + .WithReference(databases.LanguageManagementDb) + .ConfigureRabbitMq(rabbitMq, infrastructureDefaultUser, infrastructureDefaultUserPassword) + .ConfigureRedis(redis) + .ConfigureElasticSearch(elasticsearch); +applicationResources["MyResource"] = myResource; +``` +> Adjust the dependencies and configurations as necessary. + +### 5. Configure Gateway (if needed) + +If your resource should be accessible through a gateway, add the gateway configuration in the `AddAdditionalResources` method: + +```csharp +var webgateway = applicationResources.FirstOrDefault(x => x.Key == "WebGateway").Value; +if (webgateway != null) +{ + webgateway + .WaitFor(applicationResources["MyResource"]) + .WithReference(applicationResources["MyResource"]) + .WithEnvironment("ReverseProxy__Clusters__MyResource__Destinations__MyResource__Address", "http://MyResource"); +} +``` + +### 6. Configure Authentication Server (if needed) + +If your resource needs to be added to `CORS` and `RedirectAllowedUrls` configuration for the authentication server, update the `allowedUrls` variable in the `ConfigureAuthServer` method: + +```csharp +var allowedUrls = ReferenceExpression.Create($"{applicationResources["MyResource"].GetEndpoint("http")},..."); +``` + +### 7. Add Database (if needed) + +If your resource requires a dedicated database, add it in the `AddDatabases` method: + +```csharp +var myResourceDb = databaseServers.Postgres.AddDatabase("MyResource", "MySolutionName.MyProjectName_MyResource"); +``` +> Adjust the database management system as necessary. + +### 8. Add to Solution Runner Profiles (optional) + +To run your resource in **Solution Runner** profiles(Default or Aspire), add it following the instructions in the [Studio running applications documentation](../../studio/running-applications.md#add). + +## Deploying the Application + +.NET Aspire supports deployment to Azure Container Apps, Kubernetes, and other cloud platforms. For detailed deployment guidance, see the official documentation: [.NET Aspire Deployment](https://aspire.dev/deployment/overview/) + +To learn more about .NET Aspire, visit: https://aspire.dev/get-started/what-is-aspire/ diff --git a/docs/en/solution-templates/microservice/helm-charts-and-kubernetes.md b/docs/en/solution-templates/microservice/helm-charts-and-kubernetes.md index 46da70e88c..041ac00712 100644 --- a/docs/en/solution-templates/microservice/helm-charts-and-kubernetes.md +++ b/docs/en/solution-templates/microservice/helm-charts-and-kubernetes.md @@ -11,8 +11,8 @@ //[doc-nav] { "Next": { - "Name": "Miscellaneous guides in the Microservice solution", - "Path": "solution-templates/microservice/guides" + "Name": "Aspire Integration in the Microservice solution", + "Path": "solution-templates/microservice/aspire-integration" } } ```` diff --git a/docs/en/solution-templates/microservice/images/aspire-apphost-topology.png b/docs/en/solution-templates/microservice/images/aspire-apphost-topology.png new file mode 100644 index 0000000000..ae571b64e7 Binary files /dev/null and b/docs/en/solution-templates/microservice/images/aspire-apphost-topology.png differ diff --git a/docs/en/solution-templates/microservice/images/aspire-configuration.png b/docs/en/solution-templates/microservice/images/aspire-configuration.png new file mode 100644 index 0000000000..74cd5066bb Binary files /dev/null and b/docs/en/solution-templates/microservice/images/aspire-configuration.png differ diff --git a/docs/en/solution-templates/microservice/images/aspire-dashboard-console.png b/docs/en/solution-templates/microservice/images/aspire-dashboard-console.png new file mode 100644 index 0000000000..8d9d088511 Binary files /dev/null and b/docs/en/solution-templates/microservice/images/aspire-dashboard-console.png differ diff --git a/docs/en/solution-templates/microservice/images/aspire-dashboard-login.png b/docs/en/solution-templates/microservice/images/aspire-dashboard-login.png new file mode 100644 index 0000000000..a3c6ead56b Binary files /dev/null and b/docs/en/solution-templates/microservice/images/aspire-dashboard-login.png differ diff --git a/docs/en/solution-templates/microservice/images/aspire-dashboard-metrics.png b/docs/en/solution-templates/microservice/images/aspire-dashboard-metrics.png new file mode 100644 index 0000000000..b49ea441b4 Binary files /dev/null and b/docs/en/solution-templates/microservice/images/aspire-dashboard-metrics.png differ diff --git a/docs/en/solution-templates/microservice/images/aspire-dashboard-resources.png b/docs/en/solution-templates/microservice/images/aspire-dashboard-resources.png new file mode 100644 index 0000000000..ea8c3bf017 Binary files /dev/null and b/docs/en/solution-templates/microservice/images/aspire-dashboard-resources.png differ diff --git a/docs/en/solution-templates/microservice/images/aspire-dashboard-structured-logs.png b/docs/en/solution-templates/microservice/images/aspire-dashboard-structured-logs.png new file mode 100644 index 0000000000..2e732f462b Binary files /dev/null and b/docs/en/solution-templates/microservice/images/aspire-dashboard-structured-logs.png differ diff --git a/docs/en/solution-templates/microservice/images/aspire-dashboard-traces.png b/docs/en/solution-templates/microservice/images/aspire-dashboard-traces.png new file mode 100644 index 0000000000..d099f8659a Binary files /dev/null and b/docs/en/solution-templates/microservice/images/aspire-dashboard-traces.png differ diff --git a/docs/en/solution-templates/microservice/images/aspire-database-postgre-pgadmin.png b/docs/en/solution-templates/microservice/images/aspire-database-postgre-pgadmin.png new file mode 100644 index 0000000000..de857c876d Binary files /dev/null and b/docs/en/solution-templates/microservice/images/aspire-database-postgre-pgadmin.png differ diff --git a/docs/en/solution-templates/microservice/images/aspire-grafana-dashboard.png b/docs/en/solution-templates/microservice/images/aspire-grafana-dashboard.png new file mode 100644 index 0000000000..42072e3337 Binary files /dev/null and b/docs/en/solution-templates/microservice/images/aspire-grafana-dashboard.png differ diff --git a/docs/en/solution-templates/microservice/images/aspire-jaeger-traces.png b/docs/en/solution-templates/microservice/images/aspire-jaeger-traces.png new file mode 100644 index 0000000000..da6b87412c Binary files /dev/null and b/docs/en/solution-templates/microservice/images/aspire-jaeger-traces.png differ diff --git a/docs/en/solution-templates/microservice/images/aspire-kibana-dashboard.png b/docs/en/solution-templates/microservice/images/aspire-kibana-dashboard.png new file mode 100644 index 0000000000..7a31ff4fac Binary files /dev/null and b/docs/en/solution-templates/microservice/images/aspire-kibana-dashboard.png differ diff --git a/docs/en/solution-templates/microservice/images/aspire-prometheus-dashboard.png b/docs/en/solution-templates/microservice/images/aspire-prometheus-dashboard.png new file mode 100644 index 0000000000..c1f4de5b5c Binary files /dev/null and b/docs/en/solution-templates/microservice/images/aspire-prometheus-dashboard.png differ diff --git a/docs/en/solution-templates/microservice/images/aspire-rabbitmq-management.png b/docs/en/solution-templates/microservice/images/aspire-rabbitmq-management.png new file mode 100644 index 0000000000..77438c9b08 Binary files /dev/null and b/docs/en/solution-templates/microservice/images/aspire-rabbitmq-management.png differ diff --git a/docs/en/solution-templates/microservice/images/aspire-redis-insight.png b/docs/en/solution-templates/microservice/images/aspire-redis-insight.png new file mode 100644 index 0000000000..37780b38b0 Binary files /dev/null and b/docs/en/solution-templates/microservice/images/aspire-redis-insight.png differ diff --git a/docs/en/solution-templates/microservice/images/aspire-solution-structure.png b/docs/en/solution-templates/microservice/images/aspire-solution-structure.png new file mode 100644 index 0000000000..3379892b69 Binary files /dev/null and b/docs/en/solution-templates/microservice/images/aspire-solution-structure.png differ diff --git a/docs/en/solution-templates/microservice/images/solution-runner-aspire-profile.png b/docs/en/solution-templates/microservice/images/solution-runner-aspire-profile.png new file mode 100644 index 0000000000..d11edfe584 Binary files /dev/null and b/docs/en/solution-templates/microservice/images/solution-runner-aspire-profile.png differ diff --git a/docs/en/solution-templates/microservice/images/solution-runner-default-profile.png b/docs/en/solution-templates/microservice/images/solution-runner-default-profile.png new file mode 100644 index 0000000000..6867a43872 Binary files /dev/null and b/docs/en/solution-templates/microservice/images/solution-runner-default-profile.png differ diff --git a/docs/en/studio/installation.md b/docs/en/studio/installation.md index 6f12953f99..2e1d061920 100644 --- a/docs/en/studio/installation.md +++ b/docs/en/studio/installation.md @@ -65,3 +65,7 @@ When you see the "New Version Available" window, follow these steps to upgrade A 2. A progress indicator will display the download status. 3. Once the download is complete, a new modal will appear with the "Install and Relaunch" buttons. 4. Click on the "Install and Relaunch" button to complete the installation process. + +## Installing a Specific Version + +There is no official support for installing an older version of ABP Studio yet. But, if you want to install an older version of ABP Studio, you can use approach explanined here [https://github.com/enisn/AbpDevTools?tab=readme-ov-file#switch-abp-studio-version](https://github.com/enisn/AbpDevTools?tab=readme-ov-file#switch-abp-studio-version) \ No newline at end of file diff --git a/docs/en/studio/release-notes.md b/docs/en/studio/release-notes.md index c3f57deaaf..d2eba88e0f 100644 --- a/docs/en/studio/release-notes.md +++ b/docs/en/studio/release-notes.md @@ -9,6 +9,77 @@ This document contains **brief release notes** for each ABP Studio release. Release notes only include **major features** and **visible enhancements**. Therefore, they don't include all the development done in the related version. +## 2.1.3 (2025-12-15) Latest + +* Updated `createCommand` and CLI help for multi-tenancy. +* Fixed `BookController` templating problem. + +## 2.1.2 (2025-12-11) + +* Fixed `SLNX` files in templates for macOS. +* Fixed `DbMigrator` problem on nolayers template. + +## 2.1.1 (2025-12-11) + +* Fixed duplicate workspace seeding issue. +* Fixed books sample problems when solution is tiered. +* Added AI Management module to `abpmdl` file. +* Improved skip running initial tasks text. +* Fixed unit test failures. +* Added `LanguageManagementDbContext` table creation in tests. +* Removed `ConfigureHttpClientProxies` method. +* Fixed issue with adding new services to existing Microservices. +* Fixed AI Management template issues. +* Reverted browser notification overlay fix. + +## 2.1.0 (2025-12-08) + +* Enhanced Module Installation UI with improved user experience. +* Added `AI Management` option to Startup Templates (app-nolayers, app). +* Added support for new `SLNX` solution file format. +* Enhanced modularity step in solution creation process. +* Fixed Swagger authorization issues when projects run via .NET Aspire. +* Fixed browser notification overlay problems. +* Added missing `Unit of Work` namespace in solution templates. +* Fixed JSON file formatting issues. +* Updated ABP Framework to `10.0.1` and LeptonX to `5.0.1`. +* Added MySQL compatibility warnings. +* Fixed initial tasks problems. +* Improved AI Assistant control UI with better margins and borders. + +## 2.0.2 (2025-11-26) + +* Fixed `.NET 10` installation problems. +* Added custom styles for code blocks in **Markdown** view. +* Fixed `OpenIddictCoreOptions` injection to use `IOptions`. +* Added IdentityModel package after KubernetesClient. + +## 2.0.1 (2025-11-21) + +* Added build step before adding EF Core migration. +* Updated `KubernetesClient` to version `18.0.5`. + +## 2.0.0 (2025-11-20) + +* Major upgrade to `.NET 10.0` and `ABP Framework 10.0` +* Replaced `IdentityModel` with `Duende.IdentityModel`. +* Added "Open on Start in Browser" option for .NET applications in Solution Runner. +* Added `Mapperly` configuration. +* Enabled user and tenant impersonation in Blazor client modules. +* Enhanced notification system to allow text copying. +* Added environment variable support for DesignTime DbContext. +* Used C# instead of JSON for Aspire AppHost project configuration. +* Fixed MongoDB image pulling problems. +* Improved AI Assistant with better code block visibility across themes. +* Added different cache paths for each browser instance. +* Fixed various UI issues including mouse pointer problems in trees and horizontal scrolling. +* Added `FileManagement` download URL configuration for tiered projects. +* Added chat SignalR configuration to Microservice Blazor apps. +* Updated `Blazorise` packages to version `1.8.6`. +* Fixed `BackToImpersonator` button in Microservice Template. +* Added log recording while crashing for better debugging. +* Enhanced tab headers for **Solution Runner** and **Kubernetes**. + ## 1.4.2 (2025-10-30) * Upgraded template dependencies for ABP Framework and LeptonX. (targeting ABP `9.3.6`) diff --git a/docs/en/studio/version-mapping.md b/docs/en/studio/version-mapping.md index ba51cfb17d..f399c97390 100644 --- a/docs/en/studio/version-mapping.md +++ b/docs/en/studio/version-mapping.md @@ -11,6 +11,8 @@ This document provides a general overview of the relationship between various ve | **ABP Studio Version** | **ABP Version of Startup Template** | |------------------------|---------------------------| +| 2.1.0 - 2.1.3 | 10.0.1 | +| 2.0.0 to 2.0.2 | 10.0.0 | | 1.4.2 | 9.3.6 | | 1.3.3 to 1.4.1 | 9.3.5 | | 1.3.0 - 1.3.2 | 9.3.4 | @@ -19,8 +21,7 @@ This document provides a general overview of the relationship between various ve | 1.1.2 | 9.2.3 | | 1.1.0 - 1.1.1 | 9.2.2 | | 1.0.2 | 9.2.1 | -| 1.0.1 | 9.2.0 | -| 1.0.0 | 9.2.0 | +| 1.0.0 - 1.0.1 | 9.2.0 | | 0.9.26 | 9.1.1 | | 0.9.24 - 0.9.25 | 9.1.0 | | 0.9.22 - 0.9.23 | 9.0.4 | diff --git a/docs/en/tutorials/book-store-with-abp-suite/images/abp-suite-opening.png b/docs/en/tutorials/book-store-with-abp-suite/images/abp-suite-opening.png index adebe653b5..94dfb63a56 100644 Binary files a/docs/en/tutorials/book-store-with-abp-suite/images/abp-suite-opening.png and b/docs/en/tutorials/book-store-with-abp-suite/images/abp-suite-opening.png differ diff --git a/docs/en/tutorials/book-store-with-abp-suite/images/abp-suite-solution-test-projects-mongo.png b/docs/en/tutorials/book-store-with-abp-suite/images/abp-suite-solution-test-projects-mongo.png index a0ecbd469d..e3032dfbde 100644 Binary files a/docs/en/tutorials/book-store-with-abp-suite/images/abp-suite-solution-test-projects-mongo.png and b/docs/en/tutorials/book-store-with-abp-suite/images/abp-suite-solution-test-projects-mongo.png differ diff --git a/docs/en/tutorials/book-store-with-abp-suite/images/book-store-studio-run-app-angular.png b/docs/en/tutorials/book-store-with-abp-suite/images/book-store-studio-run-app-angular.png index 687dca3593..5c4a377b4a 100644 Binary files a/docs/en/tutorials/book-store-with-abp-suite/images/book-store-studio-run-app-angular.png and b/docs/en/tutorials/book-store-with-abp-suite/images/book-store-studio-run-app-angular.png differ diff --git a/docs/en/tutorials/book-store-with-abp-suite/images/book-store-studio-run-app-blazor.png b/docs/en/tutorials/book-store-with-abp-suite/images/book-store-studio-run-app-blazor.png index 5535659b45..e690e9b0e9 100644 Binary files a/docs/en/tutorials/book-store-with-abp-suite/images/book-store-studio-run-app-blazor.png and b/docs/en/tutorials/book-store-with-abp-suite/images/book-store-studio-run-app-blazor.png differ diff --git a/docs/en/tutorials/book-store-with-abp-suite/images/book-store-studio-run-app-mauiblazor.png b/docs/en/tutorials/book-store-with-abp-suite/images/book-store-studio-run-app-mauiblazor.png index 1bf3253fe7..961e7565a7 100644 Binary files a/docs/en/tutorials/book-store-with-abp-suite/images/book-store-studio-run-app-mauiblazor.png and b/docs/en/tutorials/book-store-with-abp-suite/images/book-store-studio-run-app-mauiblazor.png differ diff --git a/docs/en/tutorials/book-store-with-abp-suite/images/book-store-studio-run-app-mvc.png b/docs/en/tutorials/book-store-with-abp-suite/images/book-store-studio-run-app-mvc.png index 54860a5e9f..7a2850afc2 100644 Binary files a/docs/en/tutorials/book-store-with-abp-suite/images/book-store-studio-run-app-mvc.png and b/docs/en/tutorials/book-store-with-abp-suite/images/book-store-studio-run-app-mvc.png differ diff --git a/docs/en/tutorials/book-store-with-abp-suite/images/studio-browser-suite.png b/docs/en/tutorials/book-store-with-abp-suite/images/studio-browser-suite.png index 47a37bbd9b..bf1394a0a2 100644 Binary files a/docs/en/tutorials/book-store-with-abp-suite/images/studio-browser-suite.png and b/docs/en/tutorials/book-store-with-abp-suite/images/studio-browser-suite.png differ diff --git a/docs/en/tutorials/book-store-with-abp-suite/images/suite-book-entity-1.png b/docs/en/tutorials/book-store-with-abp-suite/images/suite-book-entity-1.png index dbb1890c14..b1e4a7d06e 100644 Binary files a/docs/en/tutorials/book-store-with-abp-suite/images/suite-book-entity-1.png and b/docs/en/tutorials/book-store-with-abp-suite/images/suite-book-entity-1.png differ diff --git a/docs/en/tutorials/book-store-with-abp-suite/part-02.md b/docs/en/tutorials/book-store-with-abp-suite/part-02.md index 11725201cb..f9462b9130 100644 --- a/docs/en/tutorials/book-store-with-abp-suite/part-02.md +++ b/docs/en/tutorials/book-store-with-abp-suite/part-02.md @@ -96,7 +96,7 @@ Here is the all details for the `Book` entity: * `Name` is **required**, it's a **string** property and maximum length is **128**. * `Type` is an **enum** and the enum file path is *\Acme.BookStore.Domain.Shared\Books\BookType.cs*. * `PublishDate` is a **DateTime** property and **not nullable**. -* `Price` is a **float** property and **required**. +* `Price` is a **float** property. You can leave the other configurations as default. diff --git a/framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor/Volo/Abp/AspNetCore/Components/MauiBlazor/MauiCurrentApplicationConfigurationCacheResetService.cs b/framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor/Volo/Abp/AspNetCore/Components/MauiBlazor/MauiCurrentApplicationConfigurationCacheResetService.cs index ad2f6ba983..26e51dbd38 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor/Volo/Abp/AspNetCore/Components/MauiBlazor/MauiCurrentApplicationConfigurationCacheResetService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor/Volo/Abp/AspNetCore/Components/MauiBlazor/MauiCurrentApplicationConfigurationCacheResetService.cs @@ -1,3 +1,4 @@ +using System; using System.Threading.Tasks; using Volo.Abp.AspNetCore.Components.Web.Configuration; using Volo.Abp.DependencyInjection; @@ -16,7 +17,7 @@ public class MauiCurrentApplicationConfigurationCacheResetService : _mauiBlazorCachedApplicationConfigurationClient = mauiBlazorCachedApplicationConfigurationClient; } - public async Task ResetAsync() + public async Task ResetAsync(Guid? userId = null) { await _mauiBlazorCachedApplicationConfigurationClient.InitializeAsync(); } diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Server/Volo/Abp/AspNetCore/Components/Server/Configuration/BlazorServerCurrentApplicationConfigurationCacheResetService.cs b/framework/src/Volo.Abp.AspNetCore.Components.Server/Volo/Abp/AspNetCore/Components/Server/Configuration/BlazorServerCurrentApplicationConfigurationCacheResetService.cs index 02de9d8bf7..144d975cb5 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Server/Volo/Abp/AspNetCore/Components/Server/Configuration/BlazorServerCurrentApplicationConfigurationCacheResetService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Server/Volo/Abp/AspNetCore/Components/Server/Configuration/BlazorServerCurrentApplicationConfigurationCacheResetService.cs @@ -1,4 +1,5 @@ -using System.Threading.Tasks; +using System; +using System.Threading.Tasks; using Volo.Abp.AspNetCore.Components.Web.Configuration; using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; using Volo.Abp.DependencyInjection; @@ -19,10 +20,8 @@ public class BlazorServerCurrentApplicationConfigurationCacheResetService : _localEventBus = localEventBus; } - public async Task ResetAsync() + public async Task ResetAsync(Guid? userId = null) { - await _localEventBus.PublishAsync( - new CurrentApplicationConfigurationCacheResetEventData() - ); + await _localEventBus.PublishAsync(new CurrentApplicationConfigurationCacheResetEventData(userId)); } } diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Configuration/ICurrentApplicationConfigurationCacheResetService.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Configuration/ICurrentApplicationConfigurationCacheResetService.cs index c3e33a9e41..2d44399f9d 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Configuration/ICurrentApplicationConfigurationCacheResetService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Configuration/ICurrentApplicationConfigurationCacheResetService.cs @@ -1,8 +1,9 @@ -using System.Threading.Tasks; +using System; +using System.Threading.Tasks; namespace Volo.Abp.AspNetCore.Components.Web.Configuration; public interface ICurrentApplicationConfigurationCacheResetService { - Task ResetAsync(); + Task ResetAsync(Guid? userId = null); } diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Configuration/NullCurrentApplicationConfigurationCacheResetService.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Configuration/NullCurrentApplicationConfigurationCacheResetService.cs index bb91d70775..1cfaee3315 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Configuration/NullCurrentApplicationConfigurationCacheResetService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Configuration/NullCurrentApplicationConfigurationCacheResetService.cs @@ -1,3 +1,4 @@ +using System; using System.Threading.Tasks; using Volo.Abp.DependencyInjection; @@ -5,7 +6,7 @@ namespace Volo.Abp.AspNetCore.Components.Web.Configuration; public class NullCurrentApplicationConfigurationCacheResetService : ICurrentApplicationConfigurationCacheResetService, ISingletonDependency { - public Task ResetAsync() + public Task ResetAsync(Guid? userId = null) { return Task.CompletedTask; } diff --git a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/Configuration/BlazorWebAssemblyCurrentApplicationConfigurationCacheResetService.cs b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/Configuration/BlazorWebAssemblyCurrentApplicationConfigurationCacheResetService.cs index 40ac508030..359678daf4 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/Configuration/BlazorWebAssemblyCurrentApplicationConfigurationCacheResetService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/Configuration/BlazorWebAssemblyCurrentApplicationConfigurationCacheResetService.cs @@ -1,3 +1,4 @@ +using System; using System.Threading.Tasks; using Volo.Abp.AspNetCore.Components.Web.Configuration; using Volo.Abp.DependencyInjection; @@ -16,7 +17,7 @@ public class BlazorWebAssemblyCurrentApplicationConfigurationCacheResetService : _webAssemblyCachedApplicationConfigurationClient = webAssemblyCachedApplicationConfigurationClient; } - public async Task ResetAsync() + public async Task ResetAsync(Guid? userId = null) { await _webAssemblyCachedApplicationConfigurationClient.InitializeAsync(); } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/MvcCachedApplicationConfigurationClientHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/MvcCachedApplicationConfigurationClientHelper.cs index cc1180fd20..ea0d778fad 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/MvcCachedApplicationConfigurationClientHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/MvcCachedApplicationConfigurationClientHelper.cs @@ -1,13 +1,26 @@ -using System.Globalization; -using Volo.Abp.Users; +using System; +using System.Globalization; +using System.Threading.Tasks; +using Volo.Abp.Caching; +using Volo.Abp.DependencyInjection; namespace Volo.Abp.AspNetCore.Mvc.Client; -public static class MvcCachedApplicationConfigurationClientHelper +public class MvcCachedApplicationConfigurationClientHelper : ITransientDependency { - public static string CreateCacheKey(ICurrentUser currentUser) + protected IDistributedCache ApplicationVersionCache { get; } + + public MvcCachedApplicationConfigurationClientHelper(IDistributedCache applicationVersionCache) + { + ApplicationVersionCache = applicationVersionCache; + } + + public virtual async Task CreateCacheKeyAsync(Guid? userId) { - var userKey = currentUser.Id?.ToString("N") ?? "Anonymous"; - return $"ApplicationConfiguration_{userKey}_{CultureInfo.CurrentUICulture.Name}"; + var appVersion = await ApplicationVersionCache.GetOrAddAsync(MvcCachedApplicationVersionCacheItem.CacheKey, + () => Task.FromResult(new MvcCachedApplicationVersionCacheItem(Guid.NewGuid().ToString("N")))) ?? + new MvcCachedApplicationVersionCacheItem(Guid.NewGuid().ToString("N")); + var userKey = userId?.ToString("N") ?? "Anonymous"; + return $"ApplicationConfiguration_{appVersion.Version}_{userKey}_{CultureInfo.CurrentUICulture.Name}"; } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/MvcCachedApplicationVersionCacheItem.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/MvcCachedApplicationVersionCacheItem.cs new file mode 100644 index 0000000000..1cd9990a44 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/MvcCachedApplicationVersionCacheItem.cs @@ -0,0 +1,13 @@ +namespace Volo.Abp.AspNetCore.Mvc.Client; + +public class MvcCachedApplicationVersionCacheItem +{ + public const string CacheKey = "Mvc_Application_Version"; + + public string Version { get; set; } + + public MvcCachedApplicationVersionCacheItem(string version) + { + Version = version; + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/RemoteDynamicClaimsPrincipalContributorCache.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/RemoteDynamicClaimsPrincipalContributorCache.cs index 8d787bec65..ba42b55d18 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/RemoteDynamicClaimsPrincipalContributorCache.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Client.Common/Volo/Abp/AspNetCore/Mvc/Client/RemoteDynamicClaimsPrincipalContributorCache.cs @@ -20,6 +20,7 @@ public class RemoteDynamicClaimsPrincipalContributorCache : RemoteDynamicClaimsP protected IHttpClientFactory HttpClientFactory { get; } protected IRemoteServiceHttpClientAuthenticator HttpClientAuthenticator { get; } protected IDistributedCache ApplicationConfigurationDtoCache { get; } + protected MvcCachedApplicationConfigurationClientHelper CacheHelper { get; } protected ICurrentUser CurrentUser { get; } public RemoteDynamicClaimsPrincipalContributorCache( @@ -28,7 +29,8 @@ public class RemoteDynamicClaimsPrincipalContributorCache : RemoteDynamicClaimsP IOptions abpClaimsPrincipalFactoryOptions, IRemoteServiceHttpClientAuthenticator httpClientAuthenticator, IDistributedCache applicationConfigurationDtoCache, - ICurrentUser currentUser) + ICurrentUser currentUser, + MvcCachedApplicationConfigurationClientHelper cacheHelper) : base(abpClaimsPrincipalFactoryOptions) { Cache = cache; @@ -36,6 +38,7 @@ public class RemoteDynamicClaimsPrincipalContributorCache : RemoteDynamicClaimsP HttpClientAuthenticator = httpClientAuthenticator; ApplicationConfigurationDtoCache = applicationConfigurationDtoCache; CurrentUser = currentUser; + CacheHelper = cacheHelper; } protected async override Task GetCacheAsync(Guid userId, Guid? tenantId = null) @@ -56,7 +59,7 @@ public class RemoteDynamicClaimsPrincipalContributorCache : RemoteDynamicClaimsP catch (Exception e) { Logger.LogWarning(e, $"Failed to refresh remote claims for user: {userId}"); - await ApplicationConfigurationDtoCache.RemoveAsync(MvcCachedApplicationConfigurationClientHelper.CreateCacheKey(CurrentUser)); + await ApplicationConfigurationDtoCache.RemoveAsync(await CacheHelper.CreateCacheKeyAsync(CurrentUser.Id)); throw; } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/MvcCachedApplicationConfigurationClient.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/MvcCachedApplicationConfigurationClient.cs index 0bff9d09b1..e3d3c12370 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/MvcCachedApplicationConfigurationClient.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/MvcCachedApplicationConfigurationClient.cs @@ -1,3 +1,4 @@ +using System; using Microsoft.AspNetCore.Http; using System.Threading.Tasks; using Microsoft.Extensions.Caching.Distributed; @@ -13,14 +14,18 @@ namespace Volo.Abp.AspNetCore.Mvc.Client; public class MvcCachedApplicationConfigurationClient : ICachedApplicationConfigurationClient, ITransientDependency { + private const string ApplicationConfigurationDtoCacheKey = "ApplicationConfigurationDto_CacheKey"; + protected IHttpContextAccessor HttpContextAccessor { get; } protected AbpApplicationConfigurationClientProxy ApplicationConfigurationAppService { get; } protected AbpApplicationLocalizationClientProxy ApplicationLocalizationClientProxy { get; } protected ICurrentUser CurrentUser { get; } + protected MvcCachedApplicationConfigurationClientHelper CacheHelper { get; } protected IDistributedCache Cache { get; } protected AbpAspNetCoreMvcClientCacheOptions Options { get; } public MvcCachedApplicationConfigurationClient( + MvcCachedApplicationConfigurationClientHelper cacheHelper, IDistributedCache cache, AbpApplicationConfigurationClientProxy applicationConfigurationAppService, ICurrentUser currentUser, @@ -33,13 +38,27 @@ public class MvcCachedApplicationConfigurationClient : ICachedApplicationConfigu HttpContextAccessor = httpContextAccessor; ApplicationLocalizationClientProxy = applicationLocalizationClientProxy; Options = options.Value; + CacheHelper = cacheHelper; Cache = cache; } - public async Task GetAsync() + public virtual async Task GetAsync() { - var cacheKey = CreateCacheKey(); + string? cacheKey = null; var httpContext = HttpContextAccessor?.HttpContext; + if (httpContext != null && httpContext.Items[ApplicationConfigurationDtoCacheKey] is string key) + { + cacheKey = key; + } + + if (cacheKey.IsNullOrWhiteSpace()) + { + cacheKey = await CreateCacheKeyAsync(); + if (httpContext != null) + { + httpContext.Items[ApplicationConfigurationDtoCacheKey] = cacheKey; + } + } if (httpContext != null && httpContext.Items[cacheKey] is ApplicationConfigurationDto configuration) { @@ -86,8 +105,21 @@ public class MvcCachedApplicationConfigurationClient : ICachedApplicationConfigu public ApplicationConfigurationDto Get() { - var cacheKey = CreateCacheKey(); + string? cacheKey = null; var httpContext = HttpContextAccessor?.HttpContext; + if (httpContext != null && httpContext.Items[ApplicationConfigurationDtoCacheKey] is string key) + { + cacheKey = key; + } + + if (cacheKey.IsNullOrWhiteSpace()) + { + cacheKey = AsyncHelper.RunSync(CreateCacheKeyAsync); + if (httpContext != null) + { + httpContext.Items[ApplicationConfigurationDtoCacheKey] = cacheKey; + } + } if (httpContext != null && httpContext.Items[cacheKey] is ApplicationConfigurationDto configuration) { @@ -97,8 +129,8 @@ public class MvcCachedApplicationConfigurationClient : ICachedApplicationConfigu return AsyncHelper.RunSync(GetAsync); } - protected virtual string CreateCacheKey() + protected virtual async Task CreateCacheKeyAsync() { - return MvcCachedApplicationConfigurationClientHelper.CreateCacheKey(CurrentUser); + return await CacheHelper.CreateCacheKeyAsync(CurrentUser.Id); } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/MvcCurrentApplicationConfigurationCacheResetEventHandler.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/MvcCurrentApplicationConfigurationCacheResetEventHandler.cs index 8bd3971779..c32b63249c 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/MvcCurrentApplicationConfigurationCacheResetEventHandler.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/MvcCurrentApplicationConfigurationCacheResetEventHandler.cs @@ -3,7 +3,6 @@ using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; using Volo.Abp.Caching; using Volo.Abp.DependencyInjection; using Volo.Abp.EventBus; -using Volo.Abp.Users; namespace Volo.Abp.AspNetCore.Mvc.Client; @@ -11,23 +10,29 @@ public class MvcCurrentApplicationConfigurationCacheResetEventHandler : ILocalEventHandler, ITransientDependency { - protected ICurrentUser CurrentUser { get; } protected IDistributedCache Cache { get; } + protected IDistributedCache ApplicationVersionCache { get; } + protected MvcCachedApplicationConfigurationClientHelper CacheHelper { get; } - public MvcCurrentApplicationConfigurationCacheResetEventHandler(ICurrentUser currentUser, - IDistributedCache cache) + public MvcCurrentApplicationConfigurationCacheResetEventHandler( + IDistributedCache cache, + IDistributedCache applicationVersionCache, + MvcCachedApplicationConfigurationClientHelper cacheHelper) { - CurrentUser = currentUser; Cache = cache; + ApplicationVersionCache = applicationVersionCache; + CacheHelper = cacheHelper; } public virtual async Task HandleEventAsync(CurrentApplicationConfigurationCacheResetEventData eventData) { - await Cache.RemoveAsync(CreateCacheKey()); - } - - protected virtual string CreateCacheKey() - { - return MvcCachedApplicationConfigurationClientHelper.CreateCacheKey(CurrentUser); + if (eventData.UserId.HasValue) + { + await Cache.RemoveAsync(await CacheHelper.CreateCacheKeyAsync(eventData.UserId)); + } + else + { + await ApplicationVersionCache.RemoveAsync(MvcCachedApplicationVersionCacheItem.CacheKey); + } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/CurrentApplicationConfigurationCacheResetEventData.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/CurrentApplicationConfigurationCacheResetEventData.cs index a50cb7b136..fccc295429 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/CurrentApplicationConfigurationCacheResetEventData.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/CurrentApplicationConfigurationCacheResetEventData.cs @@ -1,9 +1,21 @@ -namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; +using System; + +namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; /// /// This event is used to invalidate current user's cached configuration. /// public class CurrentApplicationConfigurationCacheResetEventData { + public Guid? UserId { get; set; } + + public CurrentApplicationConfigurationCacheResetEventData() + { + + } + public CurrentApplicationConfigurationCacheResetEventData(Guid? userId) + { + UserId = userId; + } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Breadcrumb/AbpBreadcrumbItemTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Breadcrumb/AbpBreadcrumbItemTagHelperService.cs index 9935b90903..f2fbb2e4bf 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Breadcrumb/AbpBreadcrumbItemTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Breadcrumb/AbpBreadcrumbItemTagHelperService.cs @@ -1,4 +1,5 @@ -using Microsoft.AspNetCore.Mvc.Rendering; +using System; +using Microsoft.AspNetCore.Mvc.Rendering; using Microsoft.AspNetCore.Razor.TagHelpers; using System.Collections.Generic; using System.Text.Encodings.Web; @@ -41,12 +42,12 @@ public class AbpBreadcrumbItemTagHelperService : AbpTagHelperService : AbpTagHelperSe } var span = new TagBuilder("span"); - span.InnerHtml.AppendHtml(TagHelper.Text!); + span.InnerHtml.Append(TagHelper.Text!); output.Content.AppendHtml(span); } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardBodyTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardBodyTagHelperService.cs index b8462f4b5b..dea75d44d7 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardBodyTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardBodyTagHelperService.cs @@ -22,7 +22,7 @@ public class AbpCardBodyTagHelperService : AbpTagHelperService var label = new TagBuilder("label"); label.Attributes.Add("for", GetIdAttributeValue(inputTag)); - label.InnerHtml.AppendHtml(_encoder.Encode(TagHelper.Label)); + label.InnerHtml.Append(TagHelper.Label); label.AddCssClass(isCheckbox ? "form-check-label" : "form-label"); diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpRadioInputTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpRadioInputTagHelperService.cs index ad2b7abe15..0dedb9c755 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpRadioInputTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpRadioInputTagHelperService.cs @@ -107,7 +107,7 @@ public class AbpRadioInputTagHelperService : AbpTagHelperService var label = new TagBuilder("label"); label.AddCssClass("form-label"); label.Attributes.Add("for", GetIdAttributeValue(selectTag)); - label.InnerHtml.AppendHtml(_encoder.Encode(TagHelper.Label)); + label.InnerHtml.Append(TagHelper.Label); label.InnerHtml.AppendHtml(GetRequiredSymbol(context, output)); return label.ToHtmlString(); diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerBaseTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerBaseTagHelperService.cs index 5088c08293..dfd9ab60e4 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerBaseTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerBaseTagHelperService.cs @@ -556,7 +556,7 @@ public abstract class AbpDatePickerBaseTagHelperService : AbpTagHelp var label = new TagBuilder("label"); label.Attributes.Add("for", GetIdAttributeValue(inputTag)); - label.InnerHtml.AppendHtml(Encoder.Encode(TagHelper.Label)); + label.InnerHtml.Append(TagHelper.Label); label.AddCssClass("form-label"); diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Modal/AbpModalHeaderTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Modal/AbpModalHeaderTagHelperService.cs index 52ced17912..a6e0b76683 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Modal/AbpModalHeaderTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Modal/AbpModalHeaderTagHelperService.cs @@ -27,7 +27,7 @@ public class AbpModalHeaderTagHelperService : AbpTagHelperService anchor.Attributes.Add(attr.Name, attr.Value.ToString()); } - anchor.InnerHtml.AppendHtml(title); + anchor.InnerHtml.Append(title); return anchor.ToHtmlString(); } @@ -73,7 +73,7 @@ public class AbpTabTagHelperService : AbpTagHelperService anchor.Attributes.Add(attr.Name, attr.Value.ToString()); } - anchor.InnerHtml.AppendHtml(title); + anchor.InnerHtml.Append(title); var listItem = new TagBuilder("li"); listItem.AddCssClass("nav-item"); diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Tab/AbpTabsTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Tab/AbpTabsTagHelperService.cs index 3026860ed6..c27be9a8c6 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Tab/AbpTabsTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Tab/AbpTabsTagHelperService.cs @@ -225,6 +225,6 @@ public class AbpTabsTagHelperService : AbpTagHelperService protected virtual string SetTabItemNameIfNotProvided(string content, int index) { - return content.Replace(TabItemNamePlaceHolder, HtmlGenerator.Encode(TagHelper.Name) + "_" + index); + return content.Replace(TabItemNamePlaceHolder, HtmlGenerator.Encode(TagHelper.Name ?? string.Empty) + "_" + index); } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpTagHelperScriptService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpTagHelperScriptService.cs index c4701aa7d5..0e87d88370 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpTagHelperScriptService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpTagHelperScriptService.cs @@ -19,9 +19,9 @@ public class AbpTagHelperScriptService : AbpTagHelperResourceService IBundleManager bundleManager, IOptions options, IWebHostEnvironment hostingEnvironment) : base( - bundleManager, - options, - hostingEnvironment) + bundleManager, + options, + hostingEnvironment) { } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpTagHelperStyleService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpTagHelperStyleService.cs index 1ed3a76fe1..a61d000d6b 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpTagHelperStyleService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpTagHelperStyleService.cs @@ -22,9 +22,9 @@ public class AbpTagHelperStyleService : AbpTagHelperResourceService IOptions options, IWebHostEnvironment hostingEnvironment, IOptions securityHeadersOptions) : base( - bundleManager, - options, - hostingEnvironment) + bundleManager, + options, + hostingEnvironment) { SecurityHeadersOptions = securityHeadersOptions.Value; } diff --git a/framework/src/Volo.Abp.AspNetCore.TestBase/Volo/Abp/AspNetCore/TestBase/AbpWebApplicationFactoryIntegratedTest.cs b/framework/src/Volo.Abp.AspNetCore.TestBase/Volo/Abp/AspNetCore/TestBase/AbpWebApplicationFactoryIntegratedTest.cs index 2e16c79809..bb2f080e71 100644 --- a/framework/src/Volo.Abp.AspNetCore.TestBase/Volo/Abp/AspNetCore/TestBase/AbpWebApplicationFactoryIntegratedTest.cs +++ b/framework/src/Volo.Abp.AspNetCore.TestBase/Volo/Abp/AspNetCore/TestBase/AbpWebApplicationFactoryIntegratedTest.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; +using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.DependencyInjection; @@ -33,6 +34,15 @@ public abstract class AbpWebApplicationFactoryIntegratedTest : WebAppl return base.CreateHost(builder); } + protected override void ConfigureWebHost(IWebHostBuilder builder) + { + builder.ConfigureAppConfiguration((hostingContext, config) => + { + hostingContext.HostingEnvironment.EnvironmentName = "Production"; + }); + base.ConfigureWebHost(builder); + } + protected virtual T? GetService() { return Services.GetService(); diff --git a/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/AbpPermissionOptions.cs b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/AbpPermissionOptions.cs index 4bdd7c0bb8..c240e1ac07 100644 --- a/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/AbpPermissionOptions.cs +++ b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/AbpPermissionOptions.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Volo.Abp.Authorization.Permissions.Resources; using Volo.Abp.Collections; namespace Volo.Abp.Authorization.Permissions; @@ -9,6 +10,8 @@ public class AbpPermissionOptions public ITypeList ValueProviders { get; } + public ITypeList ResourceValueProviders { get; } + public HashSet DeletedPermissions { get; } public HashSet DeletedPermissionGroups { get; } @@ -17,6 +20,7 @@ public class AbpPermissionOptions { DefinitionProviders = new TypeList(); ValueProviders = new TypeList(); + ResourceValueProviders = new TypeList(); DeletedPermissions = new HashSet(); DeletedPermissionGroups = new HashSet(); diff --git a/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/ICanAddChildPermission.cs b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/ICanAddChildPermission.cs index 828e4cbd01..2bcf9ba10d 100644 --- a/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/ICanAddChildPermission.cs +++ b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/ICanAddChildPermission.cs @@ -11,4 +11,4 @@ public interface ICanAddChildPermission ILocalizableString? displayName = null, MultiTenancySides multiTenancySide = MultiTenancySides.Both, bool isEnabled = true); -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/IPermissionDefinitionContext.cs b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/IPermissionDefinitionContext.cs index 7ff890bac7..5a1bef8e1d 100644 --- a/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/IPermissionDefinitionContext.cs +++ b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/IPermissionDefinitionContext.cs @@ -1,5 +1,7 @@ using System; +using JetBrains.Annotations; using Volo.Abp.Localization; +using Volo.Abp.MultiTenancy; namespace Volo.Abp.Authorization.Permissions; @@ -46,4 +48,16 @@ public interface IPermissionDefinitionContext /// Name of the permission /// PermissionDefinition? GetPermissionOrNull(string name); + + PermissionDefinition AddResourcePermission( + string name, + string resourceName, + string managementPermissionName, + ILocalizableString? displayName = null, + MultiTenancySides multiTenancySide = MultiTenancySides.Both, + bool isEnabled = true); + + PermissionDefinition? GetResourcePermissionOrNull([NotNull] string resourceName, [NotNull] string name); + + void RemoveResourcePermission([NotNull] string resourceName, [NotNull] string name); } diff --git a/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/IPermissionDefinitionManager.cs b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/IPermissionDefinitionManager.cs index ca04b110f7..d9411d54fa 100644 --- a/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/IPermissionDefinitionManager.cs +++ b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/IPermissionDefinitionManager.cs @@ -11,7 +11,14 @@ public interface IPermissionDefinitionManager Task GetOrNullAsync([NotNull] string name); + [ItemNotNull] + Task GetResourcePermissionAsync([NotNull]string resourceName, [NotNull] string name); + + Task GetResourcePermissionOrNullAsync([NotNull]string resourceName, [NotNull] string name); + Task> GetPermissionsAsync(); + Task> GetResourcePermissionsAsync(); + Task> GetGroupsAsync(); } diff --git a/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/IPermissionValueProvider.cs b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/IPermissionValueProvider.cs index ae9252632d..683f513cf4 100644 --- a/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/IPermissionValueProvider.cs +++ b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/IPermissionValueProvider.cs @@ -6,7 +6,6 @@ public interface IPermissionValueProvider { string Name { get; } - //TODO: Rename to GetResult? (CheckAsync throws exception by naming convention) Task CheckAsync(PermissionValueCheckContext context); Task CheckAsync(PermissionValuesCheckContext context); diff --git a/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/PermissionDefinition.cs b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/PermissionDefinition.cs index 0f5d713e2b..c93992ed49 100644 --- a/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/PermissionDefinition.cs +++ b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/PermissionDefinition.cs @@ -7,7 +7,7 @@ using Volo.Abp.SimpleStateChecking; namespace Volo.Abp.Authorization.Permissions; -public class PermissionDefinition : +public class PermissionDefinition : IHasSimpleStateCheckers, ICanAddChildPermission { @@ -16,6 +16,16 @@ public class PermissionDefinition : /// public string Name { get; } + /// + /// Resource name of the permission. + /// + public string? ResourceName { get; set; } + + /// + /// Management permission of the resource permission. + /// + public string? ManagementPermissionName { get; set; } + /// /// Parent of this permission if one exists. /// If set, this permission can be granted only if parent is granted. @@ -76,6 +86,19 @@ public class PermissionDefinition : set => Properties[name] = value; } + protected internal PermissionDefinition( + [NotNull] string name, + string resourceName, + string managementPermissionName, + ILocalizableString? displayName = null, + MultiTenancySides multiTenancySide = MultiTenancySides.Both, + bool isEnabled = true) + : this(name, displayName, multiTenancySide, isEnabled) + { + ResourceName = Check.NotNull(resourceName, nameof(resourceName)); + ManagementPermissionName = Check.NotNull(managementPermissionName, nameof(managementPermissionName)); + } + protected internal PermissionDefinition( [NotNull] string name, ILocalizableString? displayName = null, @@ -99,6 +122,11 @@ public class PermissionDefinition : MultiTenancySides multiTenancySide = MultiTenancySides.Both, bool isEnabled = true) { + if (ResourceName != null) + { + throw new AbpException($"Resource permission cannot have child permissions. Resource: {ResourceName}"); + } + var child = new PermissionDefinition( name, displayName, @@ -109,12 +137,12 @@ public class PermissionDefinition : }; child[PermissionDefinitionContext.KnownPropertyNames.CurrentProviderName] = this[PermissionDefinitionContext.KnownPropertyNames.CurrentProviderName]; - + _children.Add(child); return child; } - + PermissionDefinition ICanAddChildPermission.AddPermission( string name, ILocalizableString? displayName = null, @@ -124,7 +152,6 @@ public class PermissionDefinition : return this.AddChild(name, displayName, multiTenancySide, isEnabled); } - /// /// Sets a property in the dictionary. /// This is a shortcut for nested calls on this object. diff --git a/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/PermissionDefinitionContext.cs b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/PermissionDefinitionContext.cs index 394cdb9d82..c13f53866a 100644 --- a/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/PermissionDefinitionContext.cs +++ b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/PermissionDefinitionContext.cs @@ -1,7 +1,9 @@ using System; using System.Collections.Generic; +using System.Linq; using JetBrains.Annotations; using Volo.Abp.Localization; +using Volo.Abp.MultiTenancy; namespace Volo.Abp.Authorization.Permissions; @@ -11,17 +13,20 @@ public class PermissionDefinitionContext : IPermissionDefinitionContext public Dictionary Groups { get; } + public List ResourcePermissions { get; } + internal IPermissionDefinitionProvider? CurrentProvider { get; set; } public static class KnownPropertyNames { public const string CurrentProviderName = "_CurrentProviderName"; } - + public PermissionDefinitionContext(IServiceProvider serviceProvider) { ServiceProvider = serviceProvider; Groups = new Dictionary(); + ResourcePermissions = new List(); } public virtual PermissionGroupDefinition AddGroup( @@ -43,7 +48,7 @@ public class PermissionDefinitionContext : IPermissionDefinitionContext } Groups[name] = group; - + return group; } @@ -51,37 +56,23 @@ public class PermissionDefinitionContext : IPermissionDefinitionContext public virtual PermissionGroupDefinition GetGroup([NotNull] string name) { var group = GetGroupOrNull(name); - - if (group == null) - { - throw new AbpException($"Could not find a permission definition group with the given name: {name}"); - } - - return group; + return group ?? throw new AbpException($"Could not find a permission definition group with the given name: {name}"); } public virtual PermissionGroupDefinition? GetGroupOrNull([NotNull] string name) { Check.NotNull(name, nameof(name)); - - if (!Groups.ContainsKey(name)) - { - return null; - } - - return Groups[name]; + return Groups.GetOrDefault(name); } public virtual void RemoveGroup(string name) { Check.NotNull(name, nameof(name)); - if (!Groups.ContainsKey(name)) + if (!Groups.Remove(name)) { throw new AbpException($"Not found permission group with name: {name}"); } - - Groups.Remove(name); } public virtual PermissionDefinition? GetPermissionOrNull([NotNull] string name) @@ -100,4 +91,58 @@ public class PermissionDefinitionContext : IPermissionDefinitionContext return null; } + + public virtual PermissionDefinition AddResourcePermission( + string name, + string resourceName, + string managementPermissionName, + ILocalizableString? displayName = null, + MultiTenancySides multiTenancySide = MultiTenancySides.Both, + bool isEnabled = true) + { + Check.NotNull(name, nameof(name)); + Check.NotNull(resourceName, nameof(resourceName)); + Check.NotNull(managementPermissionName, nameof(managementPermissionName)); + + if (ResourcePermissions.Any(x => x.ResourceName == resourceName && x.Name == name)) + { + throw new AbpException($"There is already an existing resource permission with name: {name} for resource: {resourceName}"); + } + + var permission = new PermissionDefinition( + name, + resourceName, + managementPermissionName, + displayName, + multiTenancySide, + isEnabled) + { + [KnownPropertyNames.CurrentProviderName] = CurrentProvider?.GetType().FullName + }; + + ResourcePermissions.Add(permission); + + return permission; + } + + public virtual PermissionDefinition? GetResourcePermissionOrNull([NotNull] string resourceName, [NotNull] string name) + { + Check.NotNull(resourceName, nameof(resourceName)); + Check.NotNull(name, nameof(name)); + + return ResourcePermissions.FirstOrDefault(p => p.ResourceName == resourceName && p.Name == name); + } + + public virtual void RemoveResourcePermission([NotNull] string resourceName, [NotNull] string name) + { + Check.NotNull(resourceName, nameof(resourceName)); + Check.NotNull(name, nameof(name)); + + var resourcePermission = GetResourcePermissionOrNull(resourceName, name); + if (resourcePermission == null) + { + throw new AbpException($"Not found resource permission with name: {name} for resource: {resourceName}"); + } + ResourcePermissions.Remove(resourcePermission); + } } diff --git a/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/Resources/IHasResourcePermissions.cs b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/Resources/IHasResourcePermissions.cs new file mode 100644 index 0000000000..903f6bd2d6 --- /dev/null +++ b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/Resources/IHasResourcePermissions.cs @@ -0,0 +1,8 @@ +using System.Collections.Generic; + +namespace Volo.Abp.Authorization.Permissions.Resources; + +public interface IHasResourcePermissions : IKeyedObject +{ + Dictionary ResourcePermissions { get; } +} diff --git a/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/Resources/IResourcePermissionChecker.cs b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/Resources/IResourcePermissionChecker.cs new file mode 100644 index 0000000000..0270d2c96b --- /dev/null +++ b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/Resources/IResourcePermissionChecker.cs @@ -0,0 +1,33 @@ +using System.Security.Claims; +using System.Threading.Tasks; + +namespace Volo.Abp.Authorization.Permissions.Resources; + +public interface IResourcePermissionChecker +{ + Task IsGrantedAsync( + string name, + string resourceName, + string resourceKey + ); + + Task IsGrantedAsync( + ClaimsPrincipal? claimsPrincipal, + string name, + string resourceName, + string resourceKey + ); + + Task IsGrantedAsync( + string[] names, + string resourceName, + string resourceKey + ); + + Task IsGrantedAsync( + ClaimsPrincipal? claimsPrincipal, + string[] names, + string resourceName, + string resourceKey + ); +} diff --git a/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/Resources/IResourcePermissionStore.cs b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/Resources/IResourcePermissionStore.cs new file mode 100644 index 0000000000..42f751b4c5 --- /dev/null +++ b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/Resources/IResourcePermissionStore.cs @@ -0,0 +1,83 @@ +using System.Threading.Tasks; + +namespace Volo.Abp.Authorization.Permissions.Resources; + +public interface IResourcePermissionStore +{ + /// + /// Checks if the given permission is granted for the given resource. + /// + /// The name of the permission. + /// The name of the resource. + /// Resource key + /// The name of the provider. + /// The key of the provider. + /// + /// True if the permission is granted. + /// + Task IsGrantedAsync( + string name, + string resourceName, + string resourceKey, + string providerName, + string providerKey + ); + + /// + /// Checks if the given permissions are granted for the given resource. + /// + /// The name of the permissions. + /// The name of the resource. + /// Resource key + /// The name of the provider. + /// The key of the provider. + /// + /// A object containing the grant results for each permission. + /// + Task IsGrantedAsync( + string[] names, + string resourceName, + string resourceKey, + string providerName, + string providerKey + ); + + /// + /// Gets all permissions for the given resource. + /// + /// Resource name + /// Resource key + /// + /// A object containing the grant results for each permission. + /// + Task GetPermissionsAsync( + string resourceName, + string resourceKey + ); + + /// + /// Gets all granted permissions for the given resource. + /// + /// Resource name + /// Resource key + /// + /// An array of granted permission names. + /// + Task GetGrantedPermissionsAsync( + string resourceName, + string resourceKey + ); + + /// + /// Retrieves the keys of resources for which the specified permission is granted. + /// + /// The name of the resource. + /// The name of the permission. + /// + /// An array of resource keys where the specified permission is granted. + /// + Task GetGrantedResourceKeysAsync( + string resourceName, + string name + ); +} diff --git a/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/Resources/IResourcePermissionValueProvider.cs b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/Resources/IResourcePermissionValueProvider.cs new file mode 100644 index 0000000000..288b878e5d --- /dev/null +++ b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/Resources/IResourcePermissionValueProvider.cs @@ -0,0 +1,12 @@ +using System.Threading.Tasks; + +namespace Volo.Abp.Authorization.Permissions.Resources; + +public interface IResourcePermissionValueProvider +{ + string Name { get; } + + Task CheckAsync(ResourcePermissionValueCheckContext context); + + Task CheckAsync(ResourcePermissionValuesCheckContext context); +} diff --git a/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/Resources/IResourcePermissionValueProviderManager.cs b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/Resources/IResourcePermissionValueProviderManager.cs new file mode 100644 index 0000000000..e07e63d06a --- /dev/null +++ b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/Resources/IResourcePermissionValueProviderManager.cs @@ -0,0 +1,8 @@ +using System.Collections.Generic; + +namespace Volo.Abp.Authorization.Permissions.Resources; + +public interface IResourcePermissionValueProviderManager +{ + IReadOnlyList ValueProviders { get; } +} diff --git a/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/Resources/NullResourcePermissionStore.cs b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/Resources/NullResourcePermissionStore.cs new file mode 100644 index 0000000000..84bf38ef42 --- /dev/null +++ b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/Resources/NullResourcePermissionStore.cs @@ -0,0 +1,43 @@ +using System; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Threading; + +namespace Volo.Abp.Authorization.Permissions.Resources; + +public class NullResourcePermissionStore : IResourcePermissionStore, ISingletonDependency +{ + public ILogger Logger { get; set; } + + public NullResourcePermissionStore() + { + Logger = NullLogger.Instance; + } + + public Task IsGrantedAsync(string name, string resourceName, string resourceKey, string providerName, string providerKey) + { + return TaskCache.FalseResult; + } + + public Task IsGrantedAsync(string[] names, string resourceName, string resourceKey, string providerName, string providerKey) + { + return Task.FromResult(new MultiplePermissionGrantResult(names, PermissionGrantResult.Prohibited)); + } + + public Task GetPermissionsAsync(string resourceName, string resourceKey) + { + return Task.FromResult(new MultiplePermissionGrantResult()); + } + + public Task GetGrantedPermissionsAsync(string resourceName, string resourceKey) + { + return Task.FromResult(Array.Empty()); + } + + public Task GetGrantedResourceKeysAsync(string resourceName, string name) + { + return Task.FromResult(Array.Empty()); + } +} diff --git a/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/Resources/ResourcePermissionCheckerExtensions.cs b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/Resources/ResourcePermissionCheckerExtensions.cs new file mode 100644 index 0000000000..c1d153b246 --- /dev/null +++ b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/Resources/ResourcePermissionCheckerExtensions.cs @@ -0,0 +1,34 @@ +using System.Threading.Tasks; + +namespace Volo.Abp.Authorization.Permissions.Resources; + +public static class ResourcePermissionCheckerExtensions +{ + /// + /// Checks if a specific permission is granted for a resource with a given key. + /// + /// The type of the resource. + /// The resource permission checker instance. + /// The name of the permission to check. + /// The resource instance to check permission for. + /// The unique key identifying the resource instance. + /// A task that represents the asynchronous operation. The task result contains a boolean value indicating whether the permission is granted. + public static Task IsGrantedAsync( + this IResourcePermissionChecker resourcePermissionChecker, + string permissionName, + TResource resource, + object resourceKey + ) + { + Check.NotNull(resourcePermissionChecker, nameof(resourcePermissionChecker)); + Check.NotNullOrWhiteSpace(permissionName, nameof(permissionName)); + Check.NotNull(resource, nameof(resource)); + Check.NotNull(resourceKey, nameof(resourceKey)); + + return resourcePermissionChecker.IsGrantedAsync( + permissionName, + typeof(TResource).FullName!, + resourceKey.ToString()! + ); + } +} diff --git a/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/Resources/ResourcePermissionGrantInfo.cs b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/Resources/ResourcePermissionGrantInfo.cs new file mode 100644 index 0000000000..4eb9604454 --- /dev/null +++ b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/Resources/ResourcePermissionGrantInfo.cs @@ -0,0 +1,21 @@ +namespace Volo.Abp.Authorization.Permissions.Resources; + +public class ResourcePermissionGrantInfo : PermissionGrantInfo +{ + public string ResourceName { get; } + + public string ResourceKey { get; } + + public ResourcePermissionGrantInfo( + string name, + bool isGranted, + string resourceName, + string resourceKey, + string? providerName = null, + string? providerKey = null) + : base(name, isGranted, providerName, providerKey) + { + ResourceName = resourceName; + ResourceKey = resourceKey; + } +} diff --git a/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/Resources/ResourcePermissionStoreExtensions.cs b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/Resources/ResourcePermissionStoreExtensions.cs new file mode 100644 index 0000000000..f5604a7683 --- /dev/null +++ b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/Resources/ResourcePermissionStoreExtensions.cs @@ -0,0 +1,75 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Volo.Abp.Authorization.Permissions.Resources; + +public static class ResourcePermissionStoreExtensions +{ + /// + /// Retrieves the list of granted permissions for a specific resource with a given key. + /// + /// The type of the resource. + /// The resource permission store instance. + /// The resource instance to retrieve permissions for. + /// The unique key identifying the resource instance. + /// A task that represents the asynchronous operation. The task result contains an array of strings representing the granted permissions. + public static async Task GetGrantedPermissionsAsync( + this IResourcePermissionStore resourcePermissionStore, + TResource resource, + object resourceKey + ) + { + return (await GetPermissionsAsync(resourcePermissionStore, resource, resourceKey)).Where(x => x.Value).Select(x => x.Key).ToArray(); + } + + /// + /// Retrieves a dictionary of permissions and their granted status for the specified entity. + /// + /// The type of the resource. + /// The resource permission store instance. + /// The resource for which the permissions are being retrieved. + /// The unique key identifying the resource instance. + /// A dictionary where the keys are permission names and the values are booleans indicating whether the permission is granted. + public static async Task> GetPermissionsAsync( + this IResourcePermissionStore resourcePermissionStore, + TResource resource, + object resourceKey + ) + { + Check.NotNull(resourcePermissionStore, nameof(resourcePermissionStore)); + Check.NotNull(resource, nameof(resource)); + Check.NotNull(resourceKey, nameof(resourceKey)); + + var result = await resourcePermissionStore.GetPermissionsAsync( + typeof(TResource).FullName!, + resourceKey.ToString()! + ); + + return result.Result.ToDictionary(x => x.Key, x => x.Value == PermissionGrantResult.Granted); + } + + /// + /// Retrieves the keys of the resources granted a specific permission. + /// + /// The type of the resource. + /// The resource permission store instance. + /// The resource instance to check granted permissions for. + /// The name of the permission to check. + /// A task that represents the asynchronous operation. The task result contains an array of strings representing the granted resource keys. + public static Task GetGrantedResourceKeysAsync( + this IResourcePermissionStore resourcePermissionStore, + TResource resource, + string permissionName + ) + { + Check.NotNull(resourcePermissionStore, nameof(resourcePermissionStore)); + Check.NotNull(resource, nameof(resource)); + Check.NotNullOrWhiteSpace(permissionName, nameof(permissionName)); + + return resourcePermissionStore.GetGrantedResourceKeysAsync( + typeof(TResource).FullName!, + permissionName + ); + } +} diff --git a/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/Resources/ResourcePermissionValueCheckContext.cs b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/Resources/ResourcePermissionValueCheckContext.cs new file mode 100644 index 0000000000..dc8c78be64 --- /dev/null +++ b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/Resources/ResourcePermissionValueCheckContext.cs @@ -0,0 +1,22 @@ +using System.Security.Claims; + +namespace Volo.Abp.Authorization.Permissions.Resources; + +public class ResourcePermissionValueCheckContext : PermissionValueCheckContext +{ + public string ResourceName { get; } + + public string ResourceKey { get; } + + public ResourcePermissionValueCheckContext(PermissionDefinition permission, string resourceName, string resourceKey) + : this(permission, null, resourceName, resourceKey) + { + } + + public ResourcePermissionValueCheckContext(PermissionDefinition permission, ClaimsPrincipal? principal, string resourceName, string resourceKey) + : base(permission, principal) + { + ResourceName = resourceName; + ResourceKey = resourceKey; + } +} diff --git a/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/Resources/ResourcePermissionValueProvider.cs b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/Resources/ResourcePermissionValueProvider.cs new file mode 100644 index 0000000000..ff53f274ad --- /dev/null +++ b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/Resources/ResourcePermissionValueProvider.cs @@ -0,0 +1,20 @@ +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Authorization.Permissions.Resources; + +public abstract class ResourcePermissionValueProvider : IResourcePermissionValueProvider, ITransientDependency +{ + public abstract string Name { get; } + + protected IResourcePermissionStore ResourcePermissionStore { get; } + + protected ResourcePermissionValueProvider(IResourcePermissionStore resourcePermissionStore) + { + ResourcePermissionStore = resourcePermissionStore; + } + + public abstract Task CheckAsync(ResourcePermissionValueCheckContext context); + + public abstract Task CheckAsync(ResourcePermissionValuesCheckContext context); +} diff --git a/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/Resources/ResourcePermissionValuesCheckContext.cs b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/Resources/ResourcePermissionValuesCheckContext.cs new file mode 100644 index 0000000000..239f74fc19 --- /dev/null +++ b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/Resources/ResourcePermissionValuesCheckContext.cs @@ -0,0 +1,38 @@ +using System.Collections.Generic; +using System.Security.Claims; + +namespace Volo.Abp.Authorization.Permissions.Resources; + +public class ResourcePermissionValuesCheckContext : PermissionValuesCheckContext +{ + public string ResourceName { get; } + + public string ResourceKey { get; } + + public ResourcePermissionValuesCheckContext(PermissionDefinition permission,string resourceName, string resourceKey) + : this([permission], null, resourceName, resourceKey) + { + + } + + + public ResourcePermissionValuesCheckContext(PermissionDefinition permission, ClaimsPrincipal? principal, string resourceName, string resourceKey) + : this([permission], principal, resourceName, resourceKey) + { + + } + + public ResourcePermissionValuesCheckContext(List permissions, string resourceName, string resourceKey) + : this(permissions, null, resourceName, resourceKey) + { + ResourceName = resourceName; + ResourceKey = resourceKey; + } + + public ResourcePermissionValuesCheckContext(List permissions, ClaimsPrincipal? principal, string resourceName, string resourceKey) + : base(permissions, principal) + { + ResourceName = resourceName; + ResourceKey = resourceKey; + } +} diff --git a/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/ResourcePermissionRequirement.cs b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/ResourcePermissionRequirement.cs new file mode 100644 index 0000000000..03658f3e90 --- /dev/null +++ b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/ResourcePermissionRequirement.cs @@ -0,0 +1,21 @@ +using JetBrains.Annotations; +using Microsoft.AspNetCore.Authorization; + +namespace Volo.Abp.Authorization; + +public class ResourcePermissionRequirement : IAuthorizationRequirement +{ + public string PermissionName { get; } + + public ResourcePermissionRequirement([NotNull] string permissionName) + { + Check.NotNull(permissionName, nameof(permissionName)); + + PermissionName = permissionName; + } + + public override string ToString() + { + return $"ResourcePermissionRequirement: {PermissionName}"; + } +} diff --git a/framework/src/Volo.Abp.Authorization/Microsoft/Extensions/DependencyInjection/KeyedObjectResourcePermissionExtenstions.cs b/framework/src/Volo.Abp.Authorization/Microsoft/Extensions/DependencyInjection/KeyedObjectResourcePermissionExtenstions.cs new file mode 100644 index 0000000000..e65d5174de --- /dev/null +++ b/framework/src/Volo.Abp.Authorization/Microsoft/Extensions/DependencyInjection/KeyedObjectResourcePermissionExtenstions.cs @@ -0,0 +1,13 @@ +using Microsoft.AspNetCore.Authorization; +using Volo.Abp.Authorization.Permissions.Resources; + +namespace Microsoft.Extensions.DependencyInjection; + +public static class KeyedObjectResourcePermissionExtenstions +{ + public static IServiceCollection AddKeyedObjectResourcePermissionAuthorization(this IServiceCollection services) + { + services.AddSingleton(); + return services; + } +} diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/AbpAuthorizationModule.cs b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/AbpAuthorizationModule.cs index d8226bdee5..65b7e1b390 100644 --- a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/AbpAuthorizationModule.cs +++ b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/AbpAuthorizationModule.cs @@ -5,6 +5,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Volo.Abp.Authorization.Localization; using Volo.Abp.Authorization.Permissions; +using Volo.Abp.Authorization.Permissions.Resources; using Volo.Abp.Localization; using Volo.Abp.Localization.ExceptionHandling; using Volo.Abp.Modularity; @@ -32,6 +33,7 @@ public class AbpAuthorizationModule : AbpModule { context.Services.AddAuthorizationCore(); + context.Services.AddKeyedObjectResourcePermissionAuthorization(); context.Services.AddSingleton(); context.Services.AddSingleton(); @@ -42,6 +44,9 @@ public class AbpAuthorizationModule : AbpModule options.ValueProviders.Add(); options.ValueProviders.Add(); options.ValueProviders.Add(); + + options.ResourceValueProviders.Add(); + options.ResourceValueProviders.Add(); }); Configure(options => diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/AbpAuthorizationPolicyProvider.cs b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/AbpAuthorizationPolicyProvider.cs index 7958f2a979..a5bd19b539 100644 --- a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/AbpAuthorizationPolicyProvider.cs +++ b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/AbpAuthorizationPolicyProvider.cs @@ -40,6 +40,14 @@ public class AbpAuthorizationPolicyProvider : DefaultAuthorizationPolicyProvider return policyBuilder.Build(); } + if ((await _permissionDefinitionManager.GetResourcePermissionsAsync()).Any(x => x.Name == policyName)) + { + //TODO: Optimize & Cache! + var policyBuilder = new AuthorizationPolicyBuilder(Array.Empty()); + policyBuilder.Requirements.Add(new ResourcePermissionRequirement(policyName)); + return policyBuilder.Build(); + } + return null; } diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/MethodInvocationAuthorizationService.cs b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/MethodInvocationAuthorizationService.cs index 6316e38f52..6a34b21835 100644 --- a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/MethodInvocationAuthorizationService.cs +++ b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/MethodInvocationAuthorizationService.cs @@ -20,7 +20,7 @@ public class MethodInvocationAuthorizationService : IMethodInvocationAuthorizati _abpAuthorizationService = abpAuthorizationService; } - public async Task CheckAsync(MethodInvocationAuthorizationContext context) + public virtual async Task CheckAsync(MethodInvocationAuthorizationContext context) { if (AllowAnonymous(context)) { diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/IDynamicPermissionDefinitionStore.cs b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/IDynamicPermissionDefinitionStore.cs index 366ae2e58a..597b274035 100644 --- a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/IDynamicPermissionDefinitionStore.cs +++ b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/IDynamicPermissionDefinitionStore.cs @@ -8,6 +8,10 @@ public interface IDynamicPermissionDefinitionStore Task GetOrNullAsync(string name); Task> GetPermissionsAsync(); - + + Task GetResourcePermissionOrNullAsync(string resourceName, string name); + + Task> GetResourcePermissionsAsync(); + Task> GetGroupsAsync(); -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/IStaticPermissionDefinitionStore.cs b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/IStaticPermissionDefinitionStore.cs index 4da8423dd3..da4055d771 100644 --- a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/IStaticPermissionDefinitionStore.cs +++ b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/IStaticPermissionDefinitionStore.cs @@ -8,6 +8,10 @@ public interface IStaticPermissionDefinitionStore Task GetOrNullAsync(string name); Task> GetPermissionsAsync(); - + + Task GetResourcePermissionOrNullAsync(string resourceName, string name); + + Task> GetResourcePermissionsAsync(); + Task> GetGroupsAsync(); -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/NullDynamicPermissionDefinitionStore.cs b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/NullDynamicPermissionDefinitionStore.cs index 5b13288153..cf3a9820b4 100644 --- a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/NullDynamicPermissionDefinitionStore.cs +++ b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/NullDynamicPermissionDefinitionStore.cs @@ -9,10 +9,15 @@ namespace Volo.Abp.Authorization.Permissions; public class NullDynamicPermissionDefinitionStore : IDynamicPermissionDefinitionStore, ISingletonDependency { private readonly static Task CachedPermissionResult = Task.FromResult((PermissionDefinition?)null); - + private readonly static Task> CachedPermissionsResult = Task.FromResult((IReadOnlyList)Array.Empty().ToImmutableList()); + private readonly static Task CachedResourcePermissionResult = Task.FromResult((PermissionDefinition?)null); + + private readonly static Task> CachedResourcePermissionsResult = + Task.FromResult((IReadOnlyList)Array.Empty().ToImmutableList()); + private readonly static Task> CachedGroupsResult = Task.FromResult((IReadOnlyList)Array.Empty().ToImmutableList()); @@ -26,8 +31,18 @@ public class NullDynamicPermissionDefinitionStore : IDynamicPermissionDefinition return CachedPermissionsResult; } + public Task GetResourcePermissionOrNullAsync(string resourceName, string name) + { + return CachedResourcePermissionResult; + } + + public Task> GetResourcePermissionsAsync() + { + return CachedResourcePermissionsResult; + } + public Task> GetGroupsAsync() { return CachedGroupsResult; } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionDefinitionManager.cs b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionDefinitionManager.cs index 23f9e7883e..3dbc22fb47 100644 --- a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionDefinitionManager.cs +++ b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionDefinitionManager.cs @@ -34,17 +34,36 @@ public class PermissionDefinitionManager : IPermissionDefinitionManager, ITransi { Check.NotNull(name, nameof(name)); - return await _staticStore.GetOrNullAsync(name) ?? + return await _staticStore.GetOrNullAsync(name) ?? await _dynamicStore.GetOrNullAsync(name); } + public virtual async Task GetResourcePermissionAsync(string resourceName, string name) + { + var permission = await GetResourcePermissionOrNullAsync(resourceName, name); + if (permission == null) + { + throw new AbpException($"Undefined resource permission: {name} for resource: {resourceName}"); + } + + return permission; + } + + public virtual async Task GetResourcePermissionOrNullAsync(string resourceName, string name) + { + Check.NotNull(name, nameof(name)); + + return await _staticStore.GetResourcePermissionOrNullAsync(resourceName, name) ?? + await _dynamicStore.GetResourcePermissionOrNullAsync(resourceName, name); + } + public virtual async Task> GetPermissionsAsync() { var staticPermissions = await _staticStore.GetPermissionsAsync(); var staticPermissionNames = staticPermissions .Select(p => p.Name) .ToImmutableHashSet(); - + var dynamicPermissions = await _dynamicStore.GetPermissionsAsync(); /* We prefer static permissions over dynamics */ @@ -53,13 +72,28 @@ public class PermissionDefinitionManager : IPermissionDefinitionManager, ITransi ).ToImmutableList(); } - public async Task> GetGroupsAsync() + public virtual async Task> GetResourcePermissionsAsync() + { + var staticResourcePermissions = await _staticStore.GetResourcePermissionsAsync(); + var staticResourcePermissionNames = staticResourcePermissions + .Select(p => p.Name) + .ToImmutableHashSet(); + + var dynamicResourcePermissions = await _dynamicStore.GetResourcePermissionsAsync(); + + /* We prefer static permissions over dynamics */ + return staticResourcePermissions.Concat( + dynamicResourcePermissions.Where(d => !staticResourcePermissionNames.Contains(d.Name)) + ).ToImmutableList(); + } + + public virtual async Task> GetGroupsAsync() { var staticGroups = await _staticStore.GetGroupsAsync(); var staticGroupNames = staticGroups .Select(p => p.Name) .ToImmutableHashSet(); - + var dynamicGroups = await _dynamicStore.GetGroupsAsync(); /* We prefer static groups over dynamics */ @@ -67,4 +101,4 @@ public class PermissionDefinitionManager : IPermissionDefinitionManager, ITransi dynamicGroups.Where(d => !staticGroupNames.Contains(d.Name)) ).ToImmutableList(); } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/Resources/KeyedObjectResourcePermissionCheckerExtensions.cs b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/Resources/KeyedObjectResourcePermissionCheckerExtensions.cs new file mode 100644 index 0000000000..e8b9e38218 --- /dev/null +++ b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/Resources/KeyedObjectResourcePermissionCheckerExtensions.cs @@ -0,0 +1,28 @@ +using System.Threading.Tasks; + +namespace Volo.Abp.Authorization.Permissions.Resources; + +public static class KeyedObjectResourcePermissionCheckerExtensions +{ + /// + /// Checks if the specified permission is granted for the given resource. + /// + /// The type of the object. + /// The resource permission checker instance. + /// The name of the permission to check. + /// The resource for which the permission is being checked. + /// A task that represents the asynchronous operation. The task result is a boolean indicating whether the permission is granted. + public static Task IsGrantedAsync(this IResourcePermissionChecker resourcePermissionChecker, string permissionName, TResource resource) + where TResource : class, IKeyedObject + { + Check.NotNull(resourcePermissionChecker, nameof(resourcePermissionChecker)); + Check.NotNullOrWhiteSpace(permissionName, nameof(permissionName)); + Check.NotNull(resource, nameof(resource)); + + return resourcePermissionChecker.IsGrantedAsync( + permissionName, + resource, + resource.GetObjectKey() ?? throw new AbpException("The resource doesn't have a key.") + ); + } +} diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/Resources/KeyedObjectResourcePermissionRequirementHandler.cs b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/Resources/KeyedObjectResourcePermissionRequirementHandler.cs new file mode 100644 index 0000000000..e43b6d07b5 --- /dev/null +++ b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/Resources/KeyedObjectResourcePermissionRequirementHandler.cs @@ -0,0 +1,33 @@ +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; + +namespace Volo.Abp.Authorization.Permissions.Resources; + +public class KeyedObjectResourcePermissionRequirementHandler : AuthorizationHandler +{ + protected readonly IResourcePermissionChecker PermissionChecker; + + public KeyedObjectResourcePermissionRequirementHandler(IResourcePermissionChecker permissionChecker) + { + PermissionChecker = permissionChecker; + } + + protected override async Task HandleRequirementAsync( + AuthorizationHandlerContext context, + ResourcePermissionRequirement requirement, + IKeyedObject? resource) + { + if (resource == null) + { + return; + } + + var resourceName = resource.GetType().FullName!; + var resourceKey = resource.GetObjectKey() ?? throw new AbpException("The resource doesn't have a key."); + + if (await PermissionChecker.IsGrantedAsync(context.User, requirement.PermissionName, resourceName, resourceKey)) + { + context.Succeed(requirement); + } + } +} diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/Resources/KeyedObjectResourcePermissionStoreExtensions.cs b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/Resources/KeyedObjectResourcePermissionStoreExtensions.cs new file mode 100644 index 0000000000..3088950510 --- /dev/null +++ b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/Resources/KeyedObjectResourcePermissionStoreExtensions.cs @@ -0,0 +1,49 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Volo.Abp.Authorization.Permissions.Resources; + +public static class KeyedObjectResourcePermissionStoreExtensions +{ + /// + /// Retrieves an array of granted permissions for a specific entity. + /// + /// The type of the resource. + /// The resource permission store instance. + /// The resource for which the permissions are being checked. + /// An array of granted permission names as strings. + public static async Task GetGrantedPermissionsAsync( + this IResourcePermissionStore resourcePermissionStore, + TResource resource + ) + where TResource : class, IKeyedObject + { + Check.NotNull(resourcePermissionStore, nameof(resourcePermissionStore)); + Check.NotNull(resource, nameof(resource)); + + return (await GetPermissionsAsync(resourcePermissionStore, resource)).Where(x => x.Value).Select(x => x.Key).ToArray(); + } + + /// + /// Retrieves a dictionary of permissions and their granted status for the specified entity. + /// + /// The type of the entity. + /// The resource permission store instance. + /// The entity for which the permissions are being retrieved. + /// A dictionary where the keys are permission names and the values are booleans indicating whether the permission is granted. + public static async Task> GetPermissionsAsync( + this IResourcePermissionStore resourcePermissionStore, + TEntity entity + ) + where TEntity : class, IKeyedObject + { + Check.NotNull(resourcePermissionStore, nameof(resourcePermissionStore)); + Check.NotNull(entity, nameof(entity)); + + return await resourcePermissionStore.GetPermissionsAsync( + entity, + entity.GetObjectKey() ?? throw new AbpException("The entity doesn't have a key.") + ); + } +} diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/Resources/ResourcePermissionChecker.cs b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/Resources/ResourcePermissionChecker.cs new file mode 100644 index 0000000000..a920c2e464 --- /dev/null +++ b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/Resources/ResourcePermissionChecker.cs @@ -0,0 +1,173 @@ +using System.Collections.Generic; +using System.Linq; +using System.Security.Claims; +using System.Security.Principal; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.MultiTenancy; +using Volo.Abp.Security.Claims; +using Volo.Abp.SimpleStateChecking; + +namespace Volo.Abp.Authorization.Permissions.Resources; + +public class ResourcePermissionChecker : IResourcePermissionChecker, ITransientDependency +{ + protected IPermissionDefinitionManager PermissionDefinitionManager { get; } + protected ICurrentPrincipalAccessor PrincipalAccessor { get; } + protected ICurrentTenant CurrentTenant { get; } + protected IResourcePermissionValueProviderManager PermissionValueProviderManager { get; } + protected ISimpleStateCheckerManager StateCheckerManager { get; } + protected IPermissionChecker PermissionChecker { get; } + + public ResourcePermissionChecker( + ICurrentPrincipalAccessor principalAccessor, + IPermissionDefinitionManager permissionDefinitionManager, + ICurrentTenant currentTenant, + IResourcePermissionValueProviderManager permissionValueProviderManager, + ISimpleStateCheckerManager stateCheckerManager, + IPermissionChecker permissionChecker) + { + PrincipalAccessor = principalAccessor; + PermissionDefinitionManager = permissionDefinitionManager; + CurrentTenant = currentTenant; + PermissionValueProviderManager = permissionValueProviderManager; + StateCheckerManager = stateCheckerManager; + PermissionChecker = permissionChecker; + } + + public virtual async Task IsGrantedAsync(string name, string resourceName, string resourceKey) + { + return await IsGrantedAsync(PrincipalAccessor.Principal, name, resourceName, resourceKey); + } + + public virtual async Task IsGrantedAsync( + ClaimsPrincipal? claimsPrincipal, + string name, + string resourceName, + string resourceKey) + { + Check.NotNull(name, nameof(name)); + + var permission = await PermissionDefinitionManager.GetResourcePermissionOrNullAsync(resourceName, name); + if (permission == null) + { + return false; + } + + if (!permission.IsEnabled) + { + return false; + } + + if (!await StateCheckerManager.IsEnabledAsync(permission)) + { + return false; + } + + var multiTenancySide = claimsPrincipal?.GetMultiTenancySide() + ?? CurrentTenant.GetMultiTenancySide(); + + if (!permission.MultiTenancySide.HasFlag(multiTenancySide)) + { + return false; + } + + var isGranted = false; + var context = new ResourcePermissionValueCheckContext(permission, claimsPrincipal, resourceName, resourceKey); + foreach (var provider in PermissionValueProviderManager.ValueProviders) + { + if (context.Permission.Providers.Any() && + !context.Permission.Providers.Contains(provider.Name)) + { + continue; + } + + var result = await provider.CheckAsync(context); + + if (result == PermissionGrantResult.Granted) + { + isGranted = true; + } + else if (result == PermissionGrantResult.Prohibited) + { + return false; + } + } + + return isGranted; + } + + public async Task IsGrantedAsync(string[] names, string resourceName, string resourceKey) + { + return await IsGrantedAsync(PrincipalAccessor.Principal, names, resourceName, resourceKey); + } + + public async Task IsGrantedAsync(ClaimsPrincipal? claimsPrincipal, string[] names, string resourceName, string resourceKey) + { + Check.NotNull(names, nameof(names)); + + var result = new MultiplePermissionGrantResult(); + if (!names.Any()) + { + return result; + } + + var multiTenancySide = claimsPrincipal?.GetMultiTenancySide() ?? + CurrentTenant.GetMultiTenancySide(); + + var permissionDefinitions = new List(); + foreach (var name in names) + { + var permission = await PermissionDefinitionManager.GetResourcePermissionOrNullAsync(resourceName, name); + if (permission == null) + { + result.Result.Add(name, PermissionGrantResult.Prohibited); + continue; + } + + result.Result.Add(name, PermissionGrantResult.Undefined); + + if (permission.IsEnabled && + await StateCheckerManager.IsEnabledAsync(permission) && + permission.MultiTenancySide.HasFlag(multiTenancySide)) + { + permissionDefinitions.Add(permission); + } + } + + foreach (var provider in PermissionValueProviderManager.ValueProviders) + { + var permissions = permissionDefinitions + .Where(x => !x.Providers.Any() || x.Providers.Contains(provider.Name)) + .ToList(); + + if (permissions.IsNullOrEmpty()) + { + continue; + } + + var context = new ResourcePermissionValuesCheckContext( + permissions, + claimsPrincipal, + resourceName, + resourceKey); + + var multipleResult = await provider.CheckAsync(context); + foreach (var grantResult in multipleResult.Result.Where(grantResult => + result.Result.ContainsKey(grantResult.Key) && + result.Result[grantResult.Key] == PermissionGrantResult.Undefined && + grantResult.Value != PermissionGrantResult.Undefined)) + { + result.Result[grantResult.Key] = grantResult.Value; + permissionDefinitions.RemoveAll(x => x.Name == grantResult.Key); + } + + if (result.AllGranted || result.AllProhibited) + { + break; + } + } + + return result; + } +} diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/Resources/ResourcePermissionPopulator.cs b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/Resources/ResourcePermissionPopulator.cs new file mode 100644 index 0000000000..91dfb509d4 --- /dev/null +++ b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/Resources/ResourcePermissionPopulator.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Authorization.Permissions.Resources; + +public class ResourcePermissionPopulator : ITransientDependency +{ + protected IPermissionDefinitionManager PermissionDefinitionManager { get; } + protected IResourcePermissionChecker ResourcePermissionChecker { get; } + protected IResourcePermissionStore ResourcePermissionStore { get; } + protected IPermissionChecker PermissionChecker { get; } + + public ResourcePermissionPopulator( + IPermissionDefinitionManager permissionDefinitionManager, + IResourcePermissionChecker resourcePermissionChecker, + IResourcePermissionStore resourcePermissionStore, + IPermissionChecker permissionChecker) + { + PermissionDefinitionManager = permissionDefinitionManager; + ResourcePermissionChecker = resourcePermissionChecker; + ResourcePermissionStore = resourcePermissionStore; + PermissionChecker = permissionChecker; + } + + public virtual async Task PopulateAsync(TResource resource, string resourceName) + where TResource : IHasResourcePermissions + { + await PopulateAsync([resource], resourceName); + } + + public virtual async Task PopulateAsync(List resources, string resourceName) + where TResource : IHasResourcePermissions + { + Check.NotNull(resources, nameof(resources)); + Check.NotNullOrWhiteSpace(resourceName, nameof(resourceName)); + + var resopurcePermissions = (await PermissionDefinitionManager.GetResourcePermissionsAsync()) + .Where(x => x.ResourceName == resourceName) + .ToArray(); + + foreach (var resource in resources) + { + var resourceKey = resource.GetObjectKey(); + if (resourceKey.IsNullOrEmpty()) + { + throw new AbpException("Resource key can not be null or empty."); + } + + var results = await ResourcePermissionChecker.IsGrantedAsync(resopurcePermissions.Select(x => x.Name).ToArray(), resourceName, resourceKey); + foreach (var resopurcePermission in resopurcePermissions) + { + if (resource.ResourcePermissions == null) + { + ObjectHelper.TrySetProperty(resource, x => x.ResourcePermissions, () => new Dictionary()); + } + + var hasPermission = results.Result.TryGetValue(resopurcePermission.Name, out var granted) && granted == PermissionGrantResult.Granted; + resource.ResourcePermissions![resopurcePermission.Name] = hasPermission; + } + } + } +} diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/Resources/ResourcePermissionValueProviderManager.cs b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/Resources/ResourcePermissionValueProviderManager.cs new file mode 100644 index 0000000000..bab3180fea --- /dev/null +++ b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/Resources/ResourcePermissionValueProviderManager.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Authorization.Permissions.Resources; + +public class ResourcePermissionValueProviderManager : IResourcePermissionValueProviderManager, ISingletonDependency +{ + public IReadOnlyList ValueProviders => _lazyProviders.Value; + private readonly Lazy> _lazyProviders; + + protected AbpPermissionOptions Options { get; } + protected IServiceProvider ServiceProvider { get; } + + public ResourcePermissionValueProviderManager( + IServiceProvider serviceProvider, + IOptions options) + { + Options = options.Value; + ServiceProvider = serviceProvider; + + _lazyProviders = new Lazy>(GetProviders, true); + } + + protected virtual List GetProviders() + { + var providers = Options + .ResourceValueProviders + .Select(type => (ServiceProvider.GetRequiredService(type) as IResourcePermissionValueProvider)!) + .ToList(); + + var multipleProviders = providers.GroupBy(p => p.Name).FirstOrDefault(x => x.Count() > 1); + if(multipleProviders != null) + { + throw new AbpException($"Duplicate resource permission value provider name detected: {multipleProviders.Key}. Providers:{Environment.NewLine}{multipleProviders.Select(p => p.GetType().FullName!).JoinAsString(Environment.NewLine)}"); + } + + return providers; + } +} diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/Resources/RoleResourcePermissionValueProvider.cs b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/Resources/RoleResourcePermissionValueProvider.cs new file mode 100644 index 0000000000..fc8e91e794 --- /dev/null +++ b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/Resources/RoleResourcePermissionValueProvider.cs @@ -0,0 +1,79 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Volo.Abp.Security.Claims; + +namespace Volo.Abp.Authorization.Permissions.Resources; + +public class RoleResourcePermissionValueProvider : ResourcePermissionValueProvider +{ + public const string ProviderName = "R"; + + public override string Name => ProviderName; + + public RoleResourcePermissionValueProvider(IResourcePermissionStore resourcePermissionStore) + : base(resourcePermissionStore) + { + + } + + public override async Task CheckAsync(ResourcePermissionValueCheckContext context) + { + var roles = context.Principal?.FindAll(AbpClaimTypes.Role).Select(c => c.Value).ToArray(); + + if (roles == null || !roles.Any()) + { + return PermissionGrantResult.Undefined; + } + + foreach (var role in roles.Distinct()) + { + if (await ResourcePermissionStore.IsGrantedAsync(context.Permission.Name, context.ResourceName, context.ResourceKey, Name, role)) + { + return PermissionGrantResult.Granted; + } + } + + return PermissionGrantResult.Undefined; + } + + public override async Task CheckAsync(ResourcePermissionValuesCheckContext context) + { + var permissionNames = context.Permissions.Select(x => x.Name).Distinct().ToList(); + Check.NotNullOrEmpty(permissionNames, nameof(permissionNames)); + + var result = new MultiplePermissionGrantResult(permissionNames.ToArray()); + + var roles = context.Principal?.FindAll(AbpClaimTypes.Role).Select(c => c.Value).ToArray(); + if (roles == null || !roles.Any()) + { + return result; + } + + foreach (var role in roles.Distinct()) + { + var multipleResult = await ResourcePermissionStore.IsGrantedAsync(permissionNames.ToArray(), context.ResourceName, context.ResourceKey, Name, role); + + foreach (var grantResult in multipleResult.Result.Where(grantResult => + result.Result.ContainsKey(grantResult.Key) && + result.Result[grantResult.Key] == PermissionGrantResult.Undefined && + grantResult.Value != PermissionGrantResult.Undefined)) + { + result.Result[grantResult.Key] = grantResult.Value; + permissionNames.RemoveAll(x => x == grantResult.Key); + } + + if (result.AllGranted || result.AllProhibited) + { + break; + } + + if (permissionNames.IsNullOrEmpty()) + { + break; + } + } + + return result; + } +} diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/Resources/UserResourcePermissionValueProvider.cs b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/Resources/UserResourcePermissionValueProvider.cs new file mode 100644 index 0000000000..8cfcb8ca6f --- /dev/null +++ b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/Resources/UserResourcePermissionValueProvider.cs @@ -0,0 +1,46 @@ +using System.Linq; +using System.Threading.Tasks; +using Volo.Abp.Security.Claims; + +namespace Volo.Abp.Authorization.Permissions.Resources; + +public class UserResourcePermissionValueProvider : ResourcePermissionValueProvider +{ + public const string ProviderName = "U"; + + public override string Name => ProviderName; + + public UserResourcePermissionValueProvider(IResourcePermissionStore resourcePermissionStore) + : base(resourcePermissionStore) + { + + } + + public override async Task CheckAsync(ResourcePermissionValueCheckContext context) + { + var userId = context.Principal?.FindFirst(AbpClaimTypes.UserId)?.Value; + + if (userId == null) + { + return PermissionGrantResult.Undefined; + } + + return await ResourcePermissionStore.IsGrantedAsync(context.Permission.Name, context.ResourceName, context.ResourceKey, Name, userId) + ? PermissionGrantResult.Granted + : PermissionGrantResult.Undefined; + } + + public override async Task CheckAsync(ResourcePermissionValuesCheckContext context) + { + var permissionNames = context.Permissions.Select(x => x.Name).Distinct().ToArray(); + Check.NotNullOrEmpty(permissionNames, nameof(permissionNames)); + + var userId = context.Principal?.FindFirst(AbpClaimTypes.UserId)?.Value; + if (userId == null) + { + return new MultiplePermissionGrantResult(permissionNames); + } + + return await ResourcePermissionStore.IsGrantedAsync(permissionNames, context.ResourceName, context.ResourceKey, Name, userId); + } +} diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/StaticPermissionDefinitionChangedEvent.cs b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/StaticPermissionDefinitionChangedEvent.cs new file mode 100644 index 0000000000..6bc7b5f165 --- /dev/null +++ b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/StaticPermissionDefinitionChangedEvent.cs @@ -0,0 +1,9 @@ +using System; + +namespace Volo.Abp.Authorization.Permissions; + +[Serializable] +public class StaticPermissionDefinitionChangedEvent +{ + +} diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/StaticPermissionDefinitionStore.cs b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/StaticPermissionDefinitionStore.cs index 4e6ff0d11c..2f95f3fef6 100644 --- a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/StaticPermissionDefinitionStore.cs +++ b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/StaticPermissionDefinitionStore.cs @@ -6,74 +6,72 @@ using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Volo.Abp.DependencyInjection; +using Volo.Abp.StaticDefinitions; namespace Volo.Abp.Authorization.Permissions; public class StaticPermissionDefinitionStore : IStaticPermissionDefinitionStore, ISingletonDependency { - protected IDictionary PermissionGroupDefinitions => _lazyPermissionGroupDefinitions.Value; - private readonly Lazy> _lazyPermissionGroupDefinitions; - - protected IDictionary PermissionDefinitions => _lazyPermissionDefinitions.Value; - private readonly Lazy> _lazyPermissionDefinitions; - + protected IServiceProvider ServiceProvider { get; } protected AbpPermissionOptions Options { get; } - - private readonly IServiceProvider _serviceProvider; + protected IStaticDefinitionCache, List)> GroupCache { get; } + protected IStaticDefinitionCache> DefinitionCache { get; } public StaticPermissionDefinitionStore( IServiceProvider serviceProvider, - IOptions options) + IOptions options, + IStaticDefinitionCache, List)> groupCache, + IStaticDefinitionCache> definitionCache) { - _serviceProvider = serviceProvider; + ServiceProvider = serviceProvider; Options = options.Value; + GroupCache = groupCache; + DefinitionCache = definitionCache; + } - _lazyPermissionDefinitions = new Lazy>( - CreatePermissionDefinitions, - isThreadSafe: true - ); - - _lazyPermissionGroupDefinitions = new Lazy>( - CreatePermissionGroupDefinitions, - isThreadSafe: true - ); + public async Task GetOrNullAsync(string name) + { + var defs = await GetPermissionDefinitionsAsync(); + return defs.GetOrDefault(name); } - - protected virtual Dictionary CreatePermissionDefinitions() + + public virtual async Task> GetPermissionsAsync() { - var permissions = new Dictionary(); + var defs = await GetPermissionDefinitionsAsync(); + return defs.Values.ToImmutableList(); + } - foreach (var groupDefinition in PermissionGroupDefinitions.Values) - { - foreach (var permission in groupDefinition.Permissions) - { - AddPermissionToDictionaryRecursively(permissions, permission); - } - } + public virtual async Task GetResourcePermissionOrNullAsync(string resourceName, string name) + { + var (_, resourcePermissions) = await GetPermissionGroupDefinitionsAsync(); + return resourcePermissions.FirstOrDefault(p => p.ResourceName == resourceName && p.Name == name); + } - return permissions; + public virtual async Task> GetResourcePermissionsAsync() + { + var (_, resourcePermissions) = await GetPermissionGroupDefinitionsAsync(); + return resourcePermissions.ToImmutableList(); } - protected virtual void AddPermissionToDictionaryRecursively( - Dictionary permissions, - PermissionDefinition permission) + public async Task> GetGroupsAsync() { - if (permissions.ContainsKey(permission.Name)) - { - throw new AbpException("Duplicate permission name: " + permission.Name); - } + var (groups, _) = await GetPermissionGroupDefinitionsAsync(); + return groups.Values.ToImmutableList(); + } - permissions[permission.Name] = permission; + protected virtual async Task<(Dictionary, List)> GetPermissionGroupDefinitionsAsync() + { + return await GroupCache.GetOrCreateAsync(CreatePermissionGroupDefinitionsAsync); + } - foreach (var child in permission.Children) - { - AddPermissionToDictionaryRecursively(permissions, child); - } + protected virtual async Task> GetPermissionDefinitionsAsync() + { + return await DefinitionCache.GetOrCreateAsync(CreatePermissionDefinitionsAsync); } - protected virtual Dictionary CreatePermissionGroupDefinitions() + protected virtual Task<(Dictionary, List)> CreatePermissionGroupDefinitionsAsync() { - using (var scope = _serviceProvider.CreateScope()) + using (var scope = ServiceProvider.CreateScope()) { var context = new PermissionDefinitionContext(scope.ServiceProvider); @@ -99,29 +97,43 @@ public class StaticPermissionDefinitionStore : IStaticPermissionDefinitionStore, context.CurrentProvider = provider; provider.PostDefine(context); } - + context.CurrentProvider = null; - return context.Groups; + return Task.FromResult((context.Groups, context.ResourcePermissions)); } } - public Task GetOrNullAsync(string name) - { - return Task.FromResult(PermissionDefinitions.GetOrDefault(name)); - } - - public virtual Task> GetPermissionsAsync() + protected virtual async Task> CreatePermissionDefinitionsAsync() { - return Task.FromResult>( - PermissionDefinitions.Values.ToImmutableList() - ); + var permissions = new Dictionary(); + + var (groups, _) = await GetPermissionGroupDefinitionsAsync(); + foreach (var groupDefinition in groups.Values) + { + foreach (var permission in groupDefinition.Permissions) + { + AddPermissionToDictionaryRecursively(permissions, permission); + } + } + + return permissions; } - public Task> GetGroupsAsync() + protected virtual void AddPermissionToDictionaryRecursively( + Dictionary permissions, + PermissionDefinition permission) { - return Task.FromResult>( - PermissionGroupDefinitions.Values.ToImmutableList() - ); + 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); + } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.BlazoriseUI/Components/DataGridEntityActionsColumn.razor.cs b/framework/src/Volo.Abp.BlazoriseUI/Components/DataGridEntityActionsColumn.razor.cs index 9a39acd4fc..0180833054 100644 --- a/framework/src/Volo.Abp.BlazoriseUI/Components/DataGridEntityActionsColumn.razor.cs +++ b/framework/src/Volo.Abp.BlazoriseUI/Components/DataGridEntityActionsColumn.razor.cs @@ -23,7 +23,16 @@ public partial class DataGridEntityActionsColumn : DataGridColumn Caption = UiLocalizer["Actions"]; Width = "150px"; Sortable = false; - Field = typeof(TItem).GetProperties().First().Name; + Field = ResolveFieldName(); + return ValueTask.CompletedTask; } + + protected virtual string ResolveFieldName() + { + var props = typeof(TItem).GetProperties(); + return props.Length > 0 + ? props[0].Name + : "Id"; + } } diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/AbpCliCoreModule.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/AbpCliCoreModule.cs index 7b249f0517..8ff8ad3206 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/AbpCliCoreModule.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/AbpCliCoreModule.cs @@ -1,5 +1,8 @@ +using System; +using System.Linq; using System.Text; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; using Volo.Abp.Cli.Commands; using Volo.Abp.Cli.Commands.Internal; using Volo.Abp.Cli.Http; @@ -7,9 +10,12 @@ using Volo.Abp.Cli.ServiceProxying; using Volo.Abp.Cli.ServiceProxying.Angular; using Volo.Abp.Cli.ServiceProxying.CSharp; using Volo.Abp.Cli.ServiceProxying.JavaScript; +using Volo.Abp.Cli.Telemetry; using Volo.Abp.Domain; using Volo.Abp.Http; using Volo.Abp.IdentityModel; +using Volo.Abp.Internal.Telemetry; +using Volo.Abp.Internal.Telemetry.Activity.Providers; using Volo.Abp.Json; using Volo.Abp.Localization; using Volo.Abp.Minify; @@ -27,6 +33,8 @@ namespace Volo.Abp.Cli; )] public class AbpCliCoreModule : AbpModule { + private const string EnableTelemetryVariableName = "ABP_STUDIO_ENABLE_TELEMETRY"; + public override void ConfigureServices(ServiceConfigurationContext context) { context.Services.AddHttpClient(CliConsts.HttpClientName) @@ -82,5 +90,23 @@ public class AbpCliCoreModule : AbpModule options.Generators[AngularServiceProxyGenerator.Name] = typeof(AngularServiceProxyGenerator); options.Generators[CSharpServiceProxyGenerator.Name] = typeof(CSharpServiceProxyGenerator); }); + + ConfigureTelemetry(context.Services); + } + + private static void ConfigureTelemetry(IServiceCollection services) + { + var enableTelemetryEnvironmentVariable = Environment.GetEnvironmentVariable(EnableTelemetryVariableName , EnvironmentVariableTarget.Machine) + ?? Environment.GetEnvironmentVariable(EnableTelemetryVariableName , EnvironmentVariableTarget.User) + ?? Environment.GetEnvironmentVariable(EnableTelemetryVariableName , EnvironmentVariableTarget.Process); + + if (enableTelemetryEnvironmentVariable.IsNullOrEmpty() || !enableTelemetryEnvironmentVariable.Equals("false", StringComparison.InvariantCultureIgnoreCase)) + { + services.Remove(services.First(p => p.ImplementationType == typeof(TelemetrySessionInfoEnricher))); + } + else + { + services.Replace(ServiceDescriptor.Singleton()); + } } } diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/CliService.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/CliService.cs index 0a0c46b6a4..063ceddebf 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/CliService.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/CliService.cs @@ -14,12 +14,15 @@ using Volo.Abp.Cli.Memory; using Volo.Abp.Cli.Version; using Volo.Abp.Cli.Utils; using Volo.Abp.DependencyInjection; +using Volo.Abp.Internal.Telemetry; +using Volo.Abp.Internal.Telemetry.Constants; namespace Volo.Abp.Cli; public class CliService : ITransientDependency { private readonly MemoryService _memoryService; + private readonly ITelemetryService _telemetryService; public ILogger Logger { get; set; } protected ICommandLineArgumentParser CommandLineArgumentParser { get; } protected ICommandSelector CommandSelector { get; } @@ -35,7 +38,8 @@ public class CliService : ITransientDependency PackageVersionCheckerService nugetService, ICmdHelper cmdHelper, MemoryService memoryService, - CliVersionService cliVersionService) + CliVersionService cliVersionService, + ITelemetryService telemetryService) { _memoryService = memoryService; CommandLineArgumentParser = commandLineArgumentParser; @@ -44,6 +48,7 @@ public class CliService : ITransientDependency PackageVersionCheckerService = nugetService; CmdHelper = cmdHelper; CliVersionService = cliVersionService; + _telemetryService = telemetryService; Logger = NullLogger.Instance; } @@ -64,6 +69,7 @@ public class CliService : ITransientDependency try { + await using var _ = _telemetryService.TrackActivityAsync(ActivityNameConsts.AbpCliRun); if (commandLineArgs.IsCommand("prompt")) { await RunPromptAsync(); @@ -84,9 +90,14 @@ public class CliService : ITransientDependency } catch (Exception ex) { + await _telemetryService.AddErrorActivityAsync(ex.Message); Logger.LogException(ex); throw; } + finally + { + await _telemetryService.AddActivityAsync(ActivityNameConsts.AbpCliExit); + } } private async Task RunPromptAsync() diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/AddModuleCommand.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/AddModuleCommand.cs index 4756c3f99b..38483517da 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/AddModuleCommand.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/AddModuleCommand.cs @@ -11,6 +11,8 @@ using Volo.Abp.Cli.ProjectBuilding.Templates.MvcModule; using Volo.Abp.Cli.ProjectModification; using Volo.Abp.Cli.Utils; using Volo.Abp.DependencyInjection; +using Volo.Abp.Internal.Telemetry; +using Volo.Abp.Internal.Telemetry.Constants; namespace Volo.Abp.Cli.Commands; @@ -20,6 +22,8 @@ public class AddModuleCommand : IConsoleCommand, ITransientDependency public const string Name = "add-module"; private AddModuleInfoOutput _lastAddedModuleInfo; + private readonly ITelemetryService _telemetryService; + public ILogger Logger { get; set; } protected SolutionModuleAdder SolutionModuleAdder { get; } @@ -39,11 +43,13 @@ public class AddModuleCommand : IConsoleCommand, ITransientDependency public AddModuleCommand( SolutionModuleAdder solutionModuleAdder, SolutionPackageVersionFinder solutionPackageVersionFinder, - IOptions options) + IOptions options, + ITelemetryService telemetryService) { _options = options.Value; SolutionModuleAdder = solutionModuleAdder; SolutionPackageVersionFinder = solutionPackageVersionFinder; + _telemetryService = telemetryService; Logger = NullLogger.Instance; } @@ -66,6 +72,11 @@ public class AddModuleCommand : IConsoleCommand, ITransientDependency } var newTemplate = commandLineArgs.Options.ContainsKey(Options.NewTemplate.Long); + + await using var _ = _telemetryService.TrackActivityAsync(newTemplate + ? ActivityNameConsts.AbpCliCommandsInstallLocalModule + : ActivityNameConsts.AbpCliCommandsInstallModule); + var template = commandLineArgs.Options.GetOrNull(Options.Template.Short, Options.Template.Long); var newProTemplate = !string.IsNullOrEmpty(template) && template == ModuleProTemplate.TemplateName; var withSourceCode = newTemplate || newProTemplate || commandLineArgs.Options.ContainsKey(Options.SourceCode.Long); diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/AddPackageCommand.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/AddPackageCommand.cs index e9acb7ae24..f397ef8d6e 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/AddPackageCommand.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/AddPackageCommand.cs @@ -9,6 +9,8 @@ using Volo.Abp.Cli.Args; using Volo.Abp.Cli.ProjectModification; using Volo.Abp.Cli.Utils; using Volo.Abp.DependencyInjection; +using Volo.Abp.Internal.Telemetry; +using Volo.Abp.Internal.Telemetry.Constants; namespace Volo.Abp.Cli.Commands; @@ -16,16 +18,19 @@ public class AddPackageCommand : IConsoleCommand, ITransientDependency { public const string Name = "add-package"; + private readonly ITelemetryService _telemetryService; + public ILogger Logger { get; set; } protected ProjectNugetPackageAdder ProjectNugetPackageAdder { get; } public ProjectNpmPackageAdder ProjectNpmPackageAdder { get; } - public AddPackageCommand(ProjectNugetPackageAdder projectNugetPackageAdder, ProjectNpmPackageAdder projectNpmPackageAdder) + public AddPackageCommand(ProjectNugetPackageAdder projectNugetPackageAdder, ProjectNpmPackageAdder projectNpmPackageAdder, ITelemetryService telemetryService) { ProjectNugetPackageAdder = projectNugetPackageAdder; ProjectNpmPackageAdder = projectNpmPackageAdder; + _telemetryService = telemetryService; Logger = NullLogger.Instance; } @@ -39,6 +44,9 @@ public class AddPackageCommand : IConsoleCommand, ITransientDependency GetUsageInfo() ); } + + await using var _ = _telemetryService.TrackActivityAsync(ActivityNameConsts.AbpCliCommandsNewPackage); + await using var __ = _telemetryService.TrackActivityAsync(ActivityNameConsts.AbpCliCommandsAddPackage); var isNpmPackage = false; var isNugetPackage = true; @@ -51,11 +59,11 @@ public class AddPackageCommand : IConsoleCommand, ITransientDependency var version = commandLineArgs.Options.GetOrNull(Options.Version.Short, Options.Version.Long); var withSourceCode = commandLineArgs.Options.ContainsKey(Options.SourceCode.Long); - + if (isNugetPackage) { var addSourceCodeToSolutionFile = withSourceCode && commandLineArgs.Options.ContainsKey("add-to-solution-file"); - + await ProjectNugetPackageAdder.AddAsync( GetSolutionFile(commandLineArgs), GetProjectFile(commandLineArgs), diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/CleanCommand.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/CleanCommand.cs index 092e996095..661cf7995b 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/CleanCommand.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/CleanCommand.cs @@ -8,6 +8,8 @@ using Microsoft.Extensions.Logging.Abstractions; using Volo.Abp.Cli.Args; using Volo.Abp.Cli.Utils; using Volo.Abp.DependencyInjection; +using Volo.Abp.Internal.Telemetry; +using Volo.Abp.Internal.Telemetry.Constants; namespace Volo.Abp.Cli.Commands; @@ -16,17 +18,21 @@ public class CleanCommand : IConsoleCommand, ITransientDependency public const string Name = "clean"; public ILogger Logger { get; set; } - + protected ICmdHelper CmdHelper { get; } + + private readonly ITelemetryService _telemetryService; - public CleanCommand(ICmdHelper cmdHelper) + public CleanCommand(ICmdHelper cmdHelper, ITelemetryService telemetryService) { CmdHelper = cmdHelper; + _telemetryService = telemetryService; Logger = NullLogger.Instance; } - public Task ExecuteAsync(CommandLineArgs commandLineArgs) + public async Task ExecuteAsync(CommandLineArgs commandLineArgs) { + await using var _ = _telemetryService.TrackActivityAsync(ActivityNameConsts.AbpCliCommandsClean); var binEntries = Directory.EnumerateDirectories(Directory.GetCurrentDirectory(), "bin", SearchOption.AllDirectories); var objEntries = Directory.EnumerateDirectories(Directory.GetCurrentDirectory(), "obj", SearchOption.AllDirectories); @@ -49,7 +55,6 @@ public class CleanCommand : IConsoleCommand, ITransientDependency Logger.LogInformation($"'bin' and 'obj' folders removed successfully!"); Logger.LogInformation("Solution cleaned successfully!"); - return Task.CompletedTask; } public string GetUsageInfo() diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/Internal/RecreateInitialMigrationCommand.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/Internal/RecreateInitialMigrationCommand.cs index 857edf6bd7..2b65b9d00b 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/Internal/RecreateInitialMigrationCommand.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/Internal/RecreateInitialMigrationCommand.cs @@ -47,6 +47,7 @@ public class RecreateInitialMigrationCommand : IConsoleCommand, ITransientDepend Directory.Delete(Path.Combine(projectDir, "Migrations"), true); } + CmdHelper.RunCmd($"dotnet build", workingDirectory: projectDir); var separateDbContext = false; if (Directory.Exists(Path.Combine(projectDir, "TenantMigrations"))) { diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/ListModulesCommand.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/ListModulesCommand.cs index af685102d8..8a9d4fd6d1 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/ListModulesCommand.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/ListModulesCommand.cs @@ -7,6 +7,8 @@ using System.Threading.Tasks; using Volo.Abp.Cli.Args; using Volo.Abp.Cli.ProjectBuilding; using Volo.Abp.DependencyInjection; +using Volo.Abp.Internal.Telemetry; +using Volo.Abp.Internal.Telemetry.Constants; namespace Volo.Abp.Cli.Commands; @@ -17,15 +19,19 @@ public class ListModulesCommand : IConsoleCommand, ITransientDependency public ModuleInfoProvider ModuleInfoProvider { get; } public ILogger Logger { get; set; } + private readonly ITelemetryService _telemetryService; - public ListModulesCommand(ModuleInfoProvider moduleInfoProvider) + public ListModulesCommand(ModuleInfoProvider moduleInfoProvider, ITelemetryService telemetryService) { ModuleInfoProvider = moduleInfoProvider; + _telemetryService = telemetryService; Logger = NullLogger.Instance; } public async Task ExecuteAsync(CommandLineArgs commandLineArgs) { + await using var _ = _telemetryService.TrackActivityAsync(ActivityNameConsts.AbpCliCommandsListModules); + var modules = await ModuleInfoProvider.GetModuleListAsync(); var freeModules = modules.Where(m => !m.IsPro).ToList(); var proModules = modules.Where(m => m.IsPro).ToList(); diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs index 796a8ff757..149b477d5b 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs @@ -11,11 +11,15 @@ using Volo.Abp.Cli.Commands.Services; using Volo.Abp.Cli.LIbs; using Volo.Abp.Cli.ProjectBuilding; using Volo.Abp.Cli.ProjectBuilding.Events; +using Volo.Abp.Cli.ProjectBuilding.Templates.Module; using Volo.Abp.Cli.ProjectModification; using Volo.Abp.Cli.Utils; using Volo.Abp.Cli.Version; using Volo.Abp.DependencyInjection; using Volo.Abp.EventBus.Local; +using Volo.Abp.Internal.Telemetry; +using Volo.Abp.Internal.Telemetry.Constants; +using Volo.Abp.Internal.Telemetry.Constants.Enums; namespace Volo.Abp.Cli.Commands; @@ -26,6 +30,8 @@ public class NewCommand : ProjectCreationCommandBase, IConsoleCommand, ITransien protected TemplateProjectBuilder TemplateProjectBuilder { get; } public ITemplateInfoProvider TemplateInfoProvider { get; } + private readonly ITelemetryService _telemetryService; + public NewCommand( ConnectionStringProvider connectionStringProvider, SolutionPackageVersionFinder solutionPackageVersionFinder, @@ -40,7 +46,8 @@ public class NewCommand : ProjectCreationCommandBase, IConsoleCommand, ITransien ITemplateInfoProvider templateInfoProvider, TemplateProjectBuilder templateProjectBuilder, AngularThemeConfigurer angularThemeConfigurer, - CliVersionService cliVersionService) : + CliVersionService cliVersionService, + ITelemetryService telemetryService) : base(connectionStringProvider, solutionPackageVersionFinder, cmdHelper, @@ -56,6 +63,7 @@ public class NewCommand : ProjectCreationCommandBase, IConsoleCommand, ITransien { TemplateInfoProvider = templateInfoProvider; TemplateProjectBuilder = templateProjectBuilder; + _telemetryService = telemetryService; } public async Task ExecuteAsync(CommandLineArgs commandLineArgs) @@ -94,6 +102,19 @@ public class NewCommand : ProjectCreationCommandBase, IConsoleCommand, ITransien var result = await TemplateProjectBuilder.BuildAsync( projectArgs ); + + var activityName = ActivityNameConsts.AbpCliCommandsNewSolution; + + if (ModuleTemplateBase.IsModuleTemplate(template)) + { + activityName = ActivityNameConsts.AbpCliCommandsNewModule; + } + + await _telemetryService.AddActivityAsync(activityName, o => + { + o[ActivityPropertyNames.CreationTool] = AbpTool.OldCli; + o[ActivityPropertyNames.Template] = template; + }); ExtractProjectZip(result, projectArgs.OutputFolder); diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/UpdateCommand.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/UpdateCommand.cs index ae3cda8723..68ab346064 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/UpdateCommand.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/UpdateCommand.cs @@ -9,6 +9,8 @@ using Microsoft.Extensions.Logging.Abstractions; using Volo.Abp.Cli.Args; using Volo.Abp.Cli.ProjectModification; using Volo.Abp.DependencyInjection; +using Volo.Abp.Internal.Telemetry; +using Volo.Abp.Internal.Telemetry.Constants; namespace Volo.Abp.Cli.Commands; @@ -20,18 +22,21 @@ public class UpdateCommand : IConsoleCommand, ITransientDependency private readonly VoloNugetPackagesVersionUpdater _nugetPackagesVersionUpdater; private readonly NpmPackagesUpdater _npmPackagesUpdater; + private readonly ITelemetryService _telemetryService; public UpdateCommand(VoloNugetPackagesVersionUpdater nugetPackagesVersionUpdater, - NpmPackagesUpdater npmPackagesUpdater) + NpmPackagesUpdater npmPackagesUpdater, ITelemetryService telemetryService) { _nugetPackagesVersionUpdater = nugetPackagesVersionUpdater; _npmPackagesUpdater = npmPackagesUpdater; + _telemetryService = telemetryService; Logger = NullLogger.Instance; } public async Task ExecuteAsync(CommandLineArgs commandLineArgs) { + await using var _ = _telemetryService.TrackActivityAsync(ActivityNameConsts.AbpCliCommandsUpdate); var updateNpm = commandLineArgs.Options.ContainsKey(Options.Packages.Npm); var updateNuget = commandLineArgs.Options.ContainsKey(Options.Packages.NuGet); diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/LocalReferenceConverter.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/LocalReferenceConverter.cs index 67debab2eb..4188a75b68 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/LocalReferenceConverter.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/LocalReferenceConverter.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Threading.Tasks; using System.Xml; using System.Xml.Linq; @@ -12,9 +13,9 @@ namespace Volo.Abp.Cli.ProjectModification; public class LocalReferenceConverter : ITransientDependency { - + public ILogger Logger { get; set; } - + public async Task ConvertAsync( [NotNull] string directory, [NotNull] List localPaths) @@ -26,14 +27,14 @@ public class LocalReferenceConverter : ITransientDependency var targetProjects = Directory.GetFiles(directory, "*.csproj", SearchOption.AllDirectories); Logger.LogInformation($"Converting projects to local reference."); - + foreach (var targetProject in targetProjects) { Logger.LogInformation($"Converting to local reference: {targetProject}"); - + await ConvertProjectToLocalReferences(targetProject, localProjects); } - + Logger.LogInformation($"Converted {targetProjects.Length} projects to local references."); } @@ -41,14 +42,14 @@ public class LocalReferenceConverter : ITransientDependency { var xmlDocument = new XmlDocument() { PreserveWhitespace = true }; xmlDocument.Load(GenerateStreamFromString(File.ReadAllText(targetProject))); - + var matchedNodes = xmlDocument.SelectNodes($"/Project/ItemGroup/PackageReference[@Include]"); if (matchedNodes == null || matchedNodes.Count == 0) { return; } - + foreach (XmlNode matchedNode in matchedNodes) { var packageName = matchedNode!.Attributes!["Include"].Value; @@ -62,7 +63,7 @@ public class LocalReferenceConverter : ITransientDependency { continue; } - + var parentNode = matchedNode.ParentNode; parentNode!.RemoveChild(matchedNode); @@ -72,10 +73,10 @@ public class LocalReferenceConverter : ITransientDependency newNode.Attributes.Append(includeAttr); parentNode.AppendChild(newNode); } - + File.WriteAllText(targetProject, XDocument.Parse(xmlDocument.OuterXml).ToString()); } - + private string CalculateRelativePath(string targetProject, string localProject) { return new Uri(targetProject).MakeRelativeUri(new Uri(localProject)).ToString(); @@ -91,8 +92,12 @@ public class LocalReferenceConverter : ITransientDependency { continue; } - - list.AddRange(Directory.GetFiles(localPath, "*.csproj", SearchOption.AllDirectories)); + + var ignoreFolders = new[] { "bin", "obj", ".vs", ".idea", ".vscode", ".git" }; + var csprojFiles = Directory.GetFiles(localPath, "*.csproj", SearchOption.AllDirectories) + .Where(x => !ignoreFolders.Any(x.Contains)) + .ToList(); + list.AddRange(csprojFiles); } return list; diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Telemetry/NullTelemetryService.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Telemetry/NullTelemetryService.cs new file mode 100644 index 0000000000..4f3c695f7d --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Telemetry/NullTelemetryService.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Volo.Abp.Internal.Telemetry; +using Volo.Abp.Internal.Telemetry.Activity; + +namespace Volo.Abp.Cli.Telemetry; + +public class NullTelemetryService : ITelemetryService +{ + public IAsyncDisposable TrackActivity(ActivityEvent activityData) + { + return NullAsyncDisposable.Instance; + } + public IAsyncDisposable TrackActivityAsync(string activityName, Action>? additionalProperties = null) + { + return NullAsyncDisposable.Instance; + } + + public Task AddActivityAsync(string activityName, Action>? additionalProperties = null) + { + return Task.CompletedTask; + } + + public Task AddErrorActivityAsync(Action> additionalProperties) + { + return Task.CompletedTask; + } + + public Task AddErrorActivityAsync(string errorMessage) + { + return Task.CompletedTask; + } + + public Task AddErrorForActivityAsync(string failingActivity, string errorMessage) + { + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Utils/ProjectNameValidator.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Utils/ProjectNameValidator.cs index 0f2b287d67..5814ac4a71 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Utils/ProjectNameValidator.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Utils/ProjectNameValidator.cs @@ -18,8 +18,9 @@ public static class ProjectNameValidator private static readonly string[] IllegalKeywords = new[] { + "MauiBlazor", "Blazor" - }; + }; private static void ValidateParentDirectoryString(string projectName) { diff --git a/framework/src/Volo.Abp.Cli/Volo/Abp/Cli/Telemetry/TelemetryCliSessionProvider.cs b/framework/src/Volo.Abp.Cli/Volo/Abp/Cli/Telemetry/TelemetryCliSessionProvider.cs new file mode 100644 index 0000000000..17c340febd --- /dev/null +++ b/framework/src/Volo.Abp.Cli/Volo/Abp/Cli/Telemetry/TelemetryCliSessionProvider.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Internal.Telemetry.Activity; +using Volo.Abp.Internal.Telemetry.Activity.Contracts; +using Volo.Abp.Internal.Telemetry.Activity.Providers; +using Volo.Abp.Internal.Telemetry.Constants; +using Volo.Abp.Internal.Telemetry.Constants.Enums; + +namespace Volo.Abp.Cli.Telemetry; + +[ExposeServices(typeof(ITelemetryActivityEventEnricher))] +public class TelemetryCliSessionProvider : TelemetryActivityEventEnricher +{ + public TelemetryCliSessionProvider(IServiceProvider serviceProvider) : base(serviceProvider) + { + } + + public override int ExecutionOrder { get; set; } = 10; + + protected override Task ExecuteAsync(ActivityContext context) + { + context.Current[ActivityPropertyNames.SessionType] = SessionType.AbpCli; + context.Current[ActivityPropertyNames.SessionId] = Guid.NewGuid(); + context.Current[ActivityPropertyNames.IsFirstSession] = !File.Exists(TelemetryPaths.ActivityStorage); + context.Current["OldCli"] = true; + + if(context.Current.TryGetValue>(ActivityPropertyNames.AdditionalProperties, out var additionalProperties)) + { + additionalProperties["OldCli"] = true; + } + else + { + context.Current[ActivityPropertyNames.AdditionalProperties] = new Dictionary + { + { "OldCli", true } + }; + } + + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/IKeyedObject.cs b/framework/src/Volo.Abp.Core/Volo/Abp/IKeyedObject.cs new file mode 100644 index 0000000000..d0cbb47790 --- /dev/null +++ b/framework/src/Volo.Abp.Core/Volo/Abp/IKeyedObject.cs @@ -0,0 +1,6 @@ +namespace Volo.Abp; + +public interface IKeyedObject +{ + string? GetObjectKey(); +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Internal/InternalServiceCollectionExtensions.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Internal/InternalServiceCollectionExtensions.cs index be28f569ea..5ed5561e0e 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Internal/InternalServiceCollectionExtensions.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/Internal/InternalServiceCollectionExtensions.cs @@ -5,6 +5,7 @@ using Volo.Abp.Logging; using Volo.Abp.Modularity; using Volo.Abp.Reflection; using Volo.Abp.SimpleStateChecking; +using Volo.Abp.StaticDefinitions; namespace Volo.Abp.Internal; @@ -42,7 +43,7 @@ internal static class InternalServiceCollectionExtensions services.AddAssemblyOf(); services.AddTransient(typeof(ISimpleStateCheckerManager<>), typeof(SimpleStateCheckerManager<>)); - + services.AddSingleton(typeof(IStaticDefinitionCache<,>), typeof(StaticDefinitionCache<,>)); services.Configure(options => { options.Contributors.Add(); diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/Activity/Providers/TelemetrySolutionInfoEnricher.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/Activity/Providers/TelemetrySolutionInfoEnricher.cs index c57d51e6f2..49b4c2edbd 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/Activity/Providers/TelemetrySolutionInfoEnricher.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/Activity/Providers/TelemetrySolutionInfoEnricher.cs @@ -113,29 +113,42 @@ internal sealed class TelemetrySolutionInfoEnricher : TelemetryActivityEventEnri continue; } - var moduleJsonFileContent = File.ReadAllText(modulePath); - using var moduleDoc = JsonDocument.Parse(moduleJsonFileContent, new JsonDocumentOptions - { - AllowTrailingCommas = true - }); + ExtractModuleImportsInfo(modulePath, modules); + } + + context.Current[ActivityPropertyNames.InstalledModules] = modules; + } + + private static void ExtractModuleImportsInfo(string modulePath, List> modules) + { + var moduleJsonFileContent = File.ReadAllText(modulePath); + using var moduleDoc = JsonDocument.Parse(moduleJsonFileContent, new JsonDocumentOptions + { + AllowTrailingCommas = true + }); - if (!moduleDoc.RootElement.TryGetProperty("imports", out var imports)) + if (!moduleDoc.RootElement.TryGetProperty("imports", out var imports)) + { + return; + } + + foreach (var import in imports.EnumerateObject()) + { + var importPath = GetModuleFilePath(modulePath, import); + + if (!importPath.IsNullOrEmpty()) { + ExtractModuleImportsInfo(importPath, modules); continue; } - - foreach (var import in imports.EnumerateObject()) + + modules.Add(new Dictionary { - modules.Add(new Dictionary - { - { ActivityPropertyNames.ModuleName, import.Name }, - { ActivityPropertyNames.ModuleVersion, TelemetryJsonExtensions.GetStringOrNull(import.Value, "version") }, - { ActivityPropertyNames.ModuleInstallationTime, TelemetryJsonExtensions.GetDateTimeOffsetOrNull(import.Value, "creationTime") } - }); - } + { ActivityPropertyNames.ModuleName, import.Name }, + { ActivityPropertyNames.ModuleVersion, TelemetryJsonExtensions.GetStringOrNull(import.Value, "version") }, + { ActivityPropertyNames.ModuleInstallationTime, TelemetryJsonExtensions.GetDateTimeOffsetOrNull(import.Value, "creationTime") } + }); } - - context.Current[ActivityPropertyNames.InstalledModules] = modules; } private static string? GetModuleFilePath(string solutionPath, JsonProperty module) diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/TelemetryService.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/TelemetryService.cs index 2f684acd29..4f8f77b245 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/TelemetryService.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/TelemetryService.cs @@ -67,7 +67,7 @@ public class TelemetryService : ITelemetryService, IScopedDependency private Task AddActivityAsync(ActivityContext context) { - _ = Task.Run(async () => + var telemetryTask = Task.Run(async () => { using var scope = _serviceScopeFactory.CreateScope(); @@ -81,6 +81,30 @@ public class TelemetryService : ITelemetryService, IScopedDependency telemetryActivitySender); }); + AppDomain.CurrentDomain.ProcessExit += (_, _) => + { + try + { + telemetryTask.Wait(TimeSpan.FromSeconds(10)); + } + catch + { + // ignored + } + }; + + Console.CancelKeyPress += (_, _) => + { + try + { + telemetryTask.Wait(TimeSpan.FromSeconds(10)); + } + catch + { + // ignored + } + }; + return Task.CompletedTask; } diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/KeyedObjectHelper.cs b/framework/src/Volo.Abp.Core/Volo/Abp/KeyedObjectHelper.cs new file mode 100644 index 0000000000..9757e67ecc --- /dev/null +++ b/framework/src/Volo.Abp.Core/Volo/Abp/KeyedObjectHelper.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Volo.Abp; + +public static class KeyedObjectHelper +{ + public static string EncodeCompositeKey(params object?[] keys) + { + var raw = keys.JoinAsString("||"); + var bytes = Encoding.UTF8.GetBytes(raw); + var base64 = Convert.ToBase64String(bytes); + var base64Url = base64 + .Replace("+", "-") + .Replace("/", "_") + .TrimEnd('='); + + return base64Url; + } + + public static string DecodeCompositeKey(string encoded) + { + var base64 = encoded + .Replace("-", "+") + .Replace("_", "/"); + + switch (encoded.Length % 4) + { + case 2: base64 += "=="; break; + case 3: base64 += "="; break; + } + + var bytes = Convert.FromBase64String(base64); + var raw = Encoding.UTF8.GetString(bytes); + + return raw; + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Reflection/ReflectionHelper.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Reflection/ReflectionHelper.cs index ab26313736..5e2fa5d405 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Reflection/ReflectionHelper.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/Reflection/ReflectionHelper.cs @@ -1,6 +1,8 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; +using System.Linq.Expressions; using System.Reflection; namespace Volo.Abp.Reflection; @@ -229,4 +231,47 @@ public static class ReflectionHelper return publicConstants.ToArray(); } + + /// + /// Checks whether the property is nullable, including nullable reference types (NRT). + /// + /// Property info to check + public static bool IsNullable(PropertyInfo propertyInfo) + { + if (TypeHelper.IsNullable(propertyInfo.PropertyType)) + { + return true; + } + +#if NET6_0_OR_GREATER + var nullabilityInfoContext = new NullabilityInfoContext(); + var nullabilityInfo = nullabilityInfoContext.Create(propertyInfo); + return nullabilityInfo.ReadState == NullabilityState.Nullable; +#else + var attr = propertyInfo.GetCustomAttributes().FirstOrDefault(a => a.GetType().FullName == "System.Runtime.CompilerServices.NullableAttribute"); + if (attr != null) + { + var getter = NullableGetterCache.GetOrAdd(attr.GetType(), CreateNullableAccessor); + return getter(attr)?[0] == 2; + } + return false; +#endif + } + + private static readonly ConcurrentDictionary> NullableGetterCache = new (); + + private static Func CreateNullableAccessor(Type attrType) + { + var param = Expression.Parameter(typeof(object), "attr"); + var casted = Expression.Convert(param, attrType); + + var flagsField = attrType.GetField("NullableFlags"); + if (flagsField == null) + { + return _ => null; + } + + var access = Expression.Field(casted, flagsField); + return Expression.Lambda>(access, param).Compile(); + } } diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Reflection/TypeHelper.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Reflection/TypeHelper.cs index 0118fd9874..e423e2087f 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Reflection/TypeHelper.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/Reflection/TypeHelper.cs @@ -86,7 +86,7 @@ public static class TypeHelper { return default; } - + if (IsPrimitiveExtended(typeof(TProperty), includeEnums: true)) { var conversionType = typeof(TProperty); diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/StaticDefinitions/IStaticDefinitionCache.cs b/framework/src/Volo.Abp.Core/Volo/Abp/StaticDefinitions/IStaticDefinitionCache.cs new file mode 100644 index 0000000000..0f4bb5b5a8 --- /dev/null +++ b/framework/src/Volo.Abp.Core/Volo/Abp/StaticDefinitions/IStaticDefinitionCache.cs @@ -0,0 +1,11 @@ +using System; +using System.Threading.Tasks; + +namespace Volo.Abp.StaticDefinitions; + +public interface IStaticDefinitionCache +{ + Task GetOrCreateAsync(Func> factory); + + Task ClearAsync(); +} diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/StaticDefinitions/StaticDefinitionCache.cs b/framework/src/Volo.Abp.Core/Volo/Abp/StaticDefinitions/StaticDefinitionCache.cs new file mode 100644 index 0000000000..4f6a9bebe4 --- /dev/null +++ b/framework/src/Volo.Abp.Core/Volo/Abp/StaticDefinitions/StaticDefinitionCache.cs @@ -0,0 +1,30 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Volo.Abp.StaticDefinitions; + +public class StaticDefinitionCache : IStaticDefinitionCache +{ + private Lazy>? _lazy; + + public virtual async Task GetOrCreateAsync(Func> factory) + { + var lazy = _lazy; + if (lazy != null) + { + return await lazy.Value; + } + + var newLazy = new Lazy>(factory, LazyThreadSafetyMode.ExecutionAndPublication); + lazy = Interlocked.CompareExchange(ref _lazy, newLazy, null) ?? newLazy; + + return await lazy.Value; + } + + public virtual Task ClearAsync() + { + Interlocked.Exchange(ref _lazy, null); + return Task.CompletedTask; + } +} diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Threading/KeyedLock.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Threading/KeyedLock.cs new file mode 100644 index 0000000000..7cab60c8d7 --- /dev/null +++ b/framework/src/Volo.Abp.Core/Volo/Abp/Threading/KeyedLock.cs @@ -0,0 +1,242 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace Volo.Abp.Threading; + +/// +/// Per-key asynchronous lock for coordinating concurrent flows. +/// +/// +/// Based on the pattern described in https://stackoverflow.com/a/31194647. +/// Use within a using scope to ensure the lock is released via IDisposable.Dispose(). +/// +public static class KeyedLock +{ + private static readonly Dictionary> SemaphoreSlims = new(); + + /// + /// Acquires an exclusive asynchronous lock for the specified . + /// This method waits until the lock becomes available. + /// + /// A non-null object that identifies the lock. Objects considered equal by dictionary semantics will share the same lock. + /// An handle that must be disposed to release the lock. + /// Thrown when is . + /// + /// + /// var key = "my-critical-section"; + /// using (await KeyedLock.LockAsync(key)) + /// { + /// // protected work + /// } + /// + /// + public static async Task LockAsync(object key) + { + Check.NotNull(key, nameof(key)); + return await LockAsync(key, CancellationToken.None); + } + + /// + /// Acquires an exclusive asynchronous lock for the specified , observing a . + /// + /// A non-null object that identifies the lock. Objects considered equal by dictionary semantics will share the same lock. + /// A token to cancel the wait for the lock. + /// An handle that must be disposed to release the lock. + /// Thrown when is . + /// Thrown if the wait is canceled via . + /// + /// + /// var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5)); + /// using (await KeyedLock.LockAsync("db-update", cts.Token)) + /// { + /// // protected work + /// } + /// + /// + public static async Task LockAsync(object key, CancellationToken cancellationToken) + { + Check.NotNull(key, nameof(key)); + var semaphore = GetOrCreate(key); + try + { + await semaphore.WaitAsync(cancellationToken); + } + catch (OperationCanceledException) + { + var toDispose = DecrementRefAndMaybeRemove(key); + toDispose?.Dispose(); + throw; + } + return new Releaser(key); + } + + /// + /// Attempts to acquire an exclusive lock for the specified without waiting. + /// + /// A non-null object that identifies the lock. + /// + /// An handle if the lock was immediately acquired; otherwise . + /// + /// Thrown when is . + /// + /// + /// var handle = await KeyedLock.TryLockAsync("cache-key"); + /// if (handle != null) + /// { + /// using (handle) + /// { + /// // protected work + /// } + /// } + /// + /// + public static async Task TryLockAsync(object key) + { + Check.NotNull(key, nameof(key)); + return await TryLockAsync(key, default, CancellationToken.None); + } + + /// + /// Attempts to acquire an exclusive lock for the specified , waiting up to . + /// + /// A non-null object that identifies the lock. + /// Maximum time to wait for the lock. If set to , the method performs an immediate, non-blocking attempt. + /// A token to cancel the wait. + /// + /// An handle if the lock was acquired within the timeout; otherwise . + /// + /// Thrown when is . + /// Thrown if the wait is canceled via . + /// + /// + /// var handle = await KeyedLock.TryLockAsync("send-mail", TimeSpan.FromSeconds(1)); + /// if (handle != null) + /// { + /// using (handle) + /// { + /// // protected work + /// } + /// } + /// else + /// { + /// // lock not acquired within timeout + /// } + /// + /// + public static async Task TryLockAsync(object key, TimeSpan timeout, CancellationToken cancellationToken = default) + { + Check.NotNull(key, nameof(key)); + var semaphore = GetOrCreate(key); + bool acquired; + try + { + if (timeout == default) + { + acquired = await semaphore.WaitAsync(0, cancellationToken); + } + else + { + acquired = await semaphore.WaitAsync(timeout, cancellationToken); + } + } + catch (OperationCanceledException) + { + var toDispose = DecrementRefAndMaybeRemove(key); + toDispose?.Dispose(); + throw; + } + + if (acquired) + { + return new Releaser(key); + } + + var toDisposeOnFail = DecrementRefAndMaybeRemove(key); + toDisposeOnFail?.Dispose(); + + return null; + } + + private static SemaphoreSlim GetOrCreate(object key) + { + RefCounted item; + lock (SemaphoreSlims) + { + if (SemaphoreSlims.TryGetValue(key, out item!)) + { + ++item.RefCount; + } + else + { + item = new RefCounted(new SemaphoreSlim(1, 1)); + SemaphoreSlims[key] = item; + } + } + return item.Value; + } + + private sealed class RefCounted(T value) + { + public int RefCount { get; set; } = 1; + + public T Value { get; } = value; + } + + private sealed class Releaser(object key) : IDisposable + { + private int _disposed; + + public void Dispose() + { + if (Interlocked.Exchange(ref _disposed, 1) == 1) + { + return; + } + + RefCounted item; + var shouldDispose = false; + lock (SemaphoreSlims) + { + if (!SemaphoreSlims.TryGetValue(key, out item!)) + { + return; + } + --item.RefCount; + if (item.RefCount == 0) + { + SemaphoreSlims.Remove(key); + shouldDispose = true; + } + } + + if (shouldDispose) + { + item.Value.Dispose(); + } + else + { + item.Value.Release(); + } + } + } + + private static SemaphoreSlim? DecrementRefAndMaybeRemove(object key) + { + RefCounted? itemToDispose = null; + lock (SemaphoreSlims) + { + if (SemaphoreSlims.TryGetValue(key, out var item)) + { + --item.RefCount; + if (item.RefCount == 0) + { + SemaphoreSlims.Remove(key); + itemToDispose = item; + } + } + } + return itemToDispose?.Value; + } +} diff --git a/framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Dtos/EntityDto.cs b/framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Dtos/EntityDto.cs index 656e63b0d5..16c81c2f92 100644 --- a/framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Dtos/EntityDto.cs +++ b/framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Dtos/EntityDto.cs @@ -23,4 +23,9 @@ public abstract class EntityDto : EntityDto, IEntityDto { return $"[DTO: {GetType().Name}] Id = {Id}"; } + + public virtual string? GetObjectKey() + { + return Id?.ToString(); + } } diff --git a/framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Dtos/ExtensibleEntityDto.cs b/framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Dtos/ExtensibleEntityDto.cs index 9687af0cf7..16379f865c 100644 --- a/framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Dtos/ExtensibleEntityDto.cs +++ b/framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Dtos/ExtensibleEntityDto.cs @@ -27,6 +27,11 @@ public abstract class ExtensibleEntityDto : ExtensibleObject, IEntityDto : IEntityDto +public interface IEntityDto : IEntityDto, IKeyedObject { TKey Id { get; set; } } diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/BasicAggregateRoot.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/BasicAggregateRoot.cs index e028b55844..5680f9e857 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/BasicAggregateRoot.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/BasicAggregateRoot.cs @@ -10,36 +10,38 @@ public abstract class BasicAggregateRoot : Entity, IAggregateRoot, IGeneratesDomainEvents { - private readonly ICollection _distributedEvents = new Collection(); - private readonly ICollection _localEvents = new Collection(); + private ICollection? _distributedEvents; + private ICollection? _localEvents; public virtual IEnumerable GetLocalEvents() { - return _localEvents; + return _localEvents ?? Array.Empty(); } public virtual IEnumerable GetDistributedEvents() { - return _distributedEvents; + return _distributedEvents ?? Array.Empty(); } public virtual void ClearLocalEvents() { - _localEvents.Clear(); + _localEvents?.Clear(); } public virtual void ClearDistributedEvents() { - _distributedEvents.Clear(); + _distributedEvents?.Clear(); } protected virtual void AddLocalEvent(object eventData) { + _localEvents ??= new Collection(); _localEvents.Add(new DomainEventRecord(eventData, EventOrderGenerator.GetNext())); } protected virtual void AddDistributedEvent(object eventData) { + _distributedEvents ??= new Collection(); _distributedEvents.Add(new DomainEventRecord(eventData, EventOrderGenerator.GetNext())); } } @@ -49,8 +51,8 @@ public abstract class BasicAggregateRoot : Entity, IAggregateRoot, IGeneratesDomainEvents { - private readonly ICollection _distributedEvents = new Collection(); - private readonly ICollection _localEvents = new Collection(); + private ICollection? _distributedEvents; + private ICollection? _localEvents; protected BasicAggregateRoot() { @@ -65,31 +67,33 @@ public abstract class BasicAggregateRoot : Entity, public virtual IEnumerable GetLocalEvents() { - return _localEvents; + return _localEvents ?? Array.Empty(); } public virtual IEnumerable GetDistributedEvents() { - return _distributedEvents; + return _distributedEvents ?? Array.Empty(); } public virtual void ClearLocalEvents() { - _localEvents.Clear(); + _localEvents?.Clear(); } public virtual void ClearDistributedEvents() { - _distributedEvents.Clear(); + _distributedEvents?.Clear(); } protected virtual void AddLocalEvent(object eventData) { + _localEvents ??= new Collection(); _localEvents.Add(new DomainEventRecord(eventData, EventOrderGenerator.GetNext())); } protected virtual void AddDistributedEvent(object eventData) { + _distributedEvents ??= new Collection(); _distributedEvents.Add(new DomainEventRecord(eventData, EventOrderGenerator.GetNext())); } } diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Entity.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Entity.cs index 9e52d1abfd..93c5555a39 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Entity.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Entity.cs @@ -18,6 +18,17 @@ public abstract class Entity : IEntity return $"[ENTITY: {GetType().Name}] Keys = {GetKeys().JoinAsString(", ")}"; } + public virtual string? GetObjectKey() + { + var keys = GetKeys(); + return keys.Length switch + { + 0 => null, + 1 when keys[0] != null => keys[0]?.ToString(), + _ => KeyedObjectHelper.EncodeCompositeKey(keys) + }; + } + public abstract object?[] GetKeys(); public bool EntityEquals(IEntity other) @@ -45,7 +56,7 @@ public abstract class Entity : Entity, IEntity public override object?[] GetKeys() { - return new object?[] { Id }; + return [Id]; } /// diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/IEntity.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/IEntity.cs index 1df1b75696..db4a144539 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/IEntity.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/IEntity.cs @@ -4,7 +4,7 @@ /// Defines an entity. It's primary key may not be "Id" or it may have a composite primary key. /// Use where possible for better integration to repositories and other structures in the framework. /// -public interface IEntity +public interface IEntity : IKeyedObject { /// /// Returns an array of ordered keys for this entity. diff --git a/framework/src/Volo.Abp.DistributedLocking.Abstractions/Volo.Abp.DistributedLocking.Abstractions.csproj b/framework/src/Volo.Abp.DistributedLocking.Abstractions/Volo.Abp.DistributedLocking.Abstractions.csproj index 83f8f0076b..773c954051 100644 --- a/framework/src/Volo.Abp.DistributedLocking.Abstractions/Volo.Abp.DistributedLocking.Abstractions.csproj +++ b/framework/src/Volo.Abp.DistributedLocking.Abstractions/Volo.Abp.DistributedLocking.Abstractions.csproj @@ -18,7 +18,6 @@ - diff --git a/framework/src/Volo.Abp.DistributedLocking.Abstractions/Volo/Abp/DistributedLocking/LocalAbpDistributedLock.cs b/framework/src/Volo.Abp.DistributedLocking.Abstractions/Volo/Abp/DistributedLocking/LocalAbpDistributedLock.cs index 15956b159e..5d12d0deeb 100644 --- a/framework/src/Volo.Abp.DistributedLocking.Abstractions/Volo/Abp/DistributedLocking/LocalAbpDistributedLock.cs +++ b/framework/src/Volo.Abp.DistributedLocking.Abstractions/Volo/Abp/DistributedLocking/LocalAbpDistributedLock.cs @@ -1,19 +1,13 @@ using System; -using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; -using AsyncKeyedLock; using Volo.Abp.DependencyInjection; +using Volo.Abp.Threading; namespace Volo.Abp.DistributedLocking; public class LocalAbpDistributedLock : IAbpDistributedLock, ISingletonDependency { - private readonly AsyncKeyedLocker _localSyncObjects = new(o => - { - o.PoolSize = 20; - o.PoolInitialFill = 1; - }); protected IDistributedLockKeyNormalizer DistributedLockKeyNormalizer { get; } public LocalAbpDistributedLock(IDistributedLockKeyNormalizer distributedLockKeyNormalizer) @@ -21,7 +15,6 @@ public class LocalAbpDistributedLock : IAbpDistributedLock, ISingletonDependency DistributedLockKeyNormalizer = distributedLockKeyNormalizer; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public async Task TryAcquireAsync( string name, TimeSpan timeout = default, @@ -29,12 +22,11 @@ public class LocalAbpDistributedLock : IAbpDistributedLock, ISingletonDependency { Check.NotNullOrWhiteSpace(name, nameof(name)); var key = DistributedLockKeyNormalizer.NormalizeKey(name); - - var timeoutReleaser = await _localSyncObjects.LockOrNullAsync(key, timeout, cancellationToken); - if (timeoutReleaser is not null) + var disposable = await KeyedLock.TryLockAsync(key, timeout, cancellationToken); + if (disposable == null) { - return new LocalAbpDistributedLockHandle(timeoutReleaser); + return null; } - return null; + return new LocalAbpDistributedLockHandle(disposable); } } diff --git a/framework/src/Volo.Abp.DistributedLocking.Abstractions/Volo/Abp/DistributedLocking/NullAbpDistributedLock.cs b/framework/src/Volo.Abp.DistributedLocking.Abstractions/Volo/Abp/DistributedLocking/NullAbpDistributedLock.cs new file mode 100644 index 0000000000..165aebc64a --- /dev/null +++ b/framework/src/Volo.Abp.DistributedLocking.Abstractions/Volo/Abp/DistributedLocking/NullAbpDistributedLock.cs @@ -0,0 +1,17 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Volo.Abp.DistributedLocking; + +/// +/// This implementation of does not provide any distributed locking functionality. +/// Useful in scenarios where distributed locking is not required or during testing. +/// +public class NullAbpDistributedLock : IAbpDistributedLock +{ + public Task TryAcquireAsync(string name, TimeSpan timeout = default, CancellationToken cancellationToken = default) + { + return Task.FromResult(new LocalAbpDistributedLockHandle(NullDisposable.Instance)); + } +} diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo.Abp.EntityFrameworkCore.Oracle.csproj b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo.Abp.EntityFrameworkCore.Oracle.csproj index 7a4dbe61b1..8fd893c6c1 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo.Abp.EntityFrameworkCore.Oracle.csproj +++ b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo.Abp.EntityFrameworkCore.Oracle.csproj @@ -22,8 +22,6 @@ - - diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.Sqlite/Volo/Abp/EntityFrameworkCore/Sqlite/AbpEntityFrameworkCoreSqliteModule.cs b/framework/src/Volo.Abp.EntityFrameworkCore.Sqlite/Volo/Abp/EntityFrameworkCore/Sqlite/AbpEntityFrameworkCoreSqliteModule.cs index 6b767ceee4..87d58f2a82 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore.Sqlite/Volo/Abp/EntityFrameworkCore/Sqlite/AbpEntityFrameworkCoreSqliteModule.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore.Sqlite/Volo/Abp/EntityFrameworkCore/Sqlite/AbpEntityFrameworkCoreSqliteModule.cs @@ -1,3 +1,5 @@ +using System.Linq; +using Microsoft.EntityFrameworkCore.Sqlite.Infrastructure.Internal; using Microsoft.Extensions.DependencyInjection; using Volo.Abp.EntityFrameworkCore.GlobalFilters; using Volo.Abp.EntityFrameworkCore.Interceptors; @@ -32,7 +34,10 @@ public class AbpEntityFrameworkCoreSqliteModule : AbpModule { options.ConfigureDefaultOnConfiguring((dbContext, dbContextOptionsBuilder) => { - dbContextOptionsBuilder.AddInterceptors(new SqliteBusyTimeoutSaveChangesInterceptor(sqliteOptions.BusyTimeout.Value)); + if (dbContextOptionsBuilder.Options.Extensions.Any(extension => extension is SqliteOptionsExtension)) + { + dbContextOptionsBuilder.AddInterceptors(new SqliteBusyTimeoutSaveChangesInterceptor(sqliteOptions.BusyTimeout.Value)); + } }, overrideExisting: false); }); } diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs index 9243d4680e..6a5b608c76 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs @@ -204,6 +204,7 @@ public abstract class AbpDbContext : DbContext, IAbpEfCoreDbContext, case "Npgsql.EntityFrameworkCore.PostgreSQL": return EfCoreDatabaseProvider.PostgreSql; case "Pomelo.EntityFrameworkCore.MySql": + case "MySql.Data.MySqlClient": return EfCoreDatabaseProvider.MySql; case "Oracle.EntityFrameworkCore": case "Devart.Data.Oracle.Entity.EFCore": @@ -685,6 +686,12 @@ public abstract class AbpDbContext : DbContext, IAbpEfCoreDbContext, return; } + string? concurrencyStamp = null; + if (entry.Entity is IHasConcurrencyStamp hasConcurrencyStamp) + { + concurrencyStamp = hasConcurrencyStamp.ConcurrencyStamp; + } + ExtraPropertyDictionary? originalExtraProperties = null; if (entry.Entity is IHasExtraProperties) { @@ -693,6 +700,11 @@ public abstract class AbpDbContext : DbContext, IAbpEfCoreDbContext, entry.Reload(); + if (concurrencyStamp != null && entry.Entity is IHasConcurrencyStamp) + { + ObjectHelper.TrySetProperty(entry.Entity.As(), x => x.ConcurrencyStamp, () => concurrencyStamp); + } + if (entry.Entity is IHasExtraProperties) { ObjectHelper.TrySetProperty(entry.Entity.As(), x => x.ExtraProperties, () => originalExtraProperties); diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/EntityHistory/EntityHistoryHelper.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/EntityHistory/EntityHistoryHelper.cs index d50e6b89f6..f87a05e4a9 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/EntityHistory/EntityHistoryHelper.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/EntityHistory/EntityHistoryHelper.cs @@ -198,6 +198,13 @@ public class EntityHistoryHelper : IEntityHistoryHelper, ITransientDependency { foreach (var (navigationEntry, index) in entityEntry.Navigations.Select((value, i) => ( value, i ))) { + var propertyInfo = navigationEntry.Metadata.PropertyInfo; + if (propertyInfo != null && + propertyInfo.IsDefined(typeof(DisableAuditingAttribute), true)) + { + continue; + } + if (AbpEfCoreNavigationHelper.IsNavigationEntryModified(entityEntry, index)) { var abpNavigationEntry = AbpEfCoreNavigationHelper.GetNavigationEntry(entityEntry, index); diff --git a/framework/src/Volo.Abp.Features/Volo/Abp/Features/AbpFeaturesModule.cs b/framework/src/Volo.Abp.Features/Volo/Abp/Features/AbpFeaturesModule.cs index cde88956b5..ab39c1aba7 100644 --- a/framework/src/Volo.Abp.Features/Volo/Abp/Features/AbpFeaturesModule.cs +++ b/framework/src/Volo.Abp.Features/Volo/Abp/Features/AbpFeaturesModule.cs @@ -31,6 +31,7 @@ public class AbpFeaturesModule : AbpModule context.Services.Configure(options => { options.ValueProviders.Add(); + options.ValueProviders.Add(); options.ValueProviders.Add(); options.ValueProviders.Add(); }); diff --git a/framework/src/Volo.Abp.Features/Volo/Abp/Features/ConfigurationFeatureValueProvider.cs b/framework/src/Volo.Abp.Features/Volo/Abp/Features/ConfigurationFeatureValueProvider.cs new file mode 100644 index 0000000000..7309e99b6f --- /dev/null +++ b/framework/src/Volo.Abp.Features/Volo/Abp/Features/ConfigurationFeatureValueProvider.cs @@ -0,0 +1,26 @@ +using System.Threading.Tasks; +using Microsoft.Extensions.Configuration; + +namespace Volo.Abp.Features; + +public class ConfigurationFeatureValueProvider : FeatureValueProvider +{ + public const string ConfigurationNamePrefix = "Features:"; + + public const string ProviderName = "C"; + + public override string Name => ProviderName; + + protected IConfiguration Configuration { get; } + + public ConfigurationFeatureValueProvider(IFeatureStore featureStore, IConfiguration configuration) + : base(featureStore) + { + Configuration = configuration; + } + + public override Task GetOrNullAsync(FeatureDefinition feature) + { + return Task.FromResult(Configuration[ConfigurationNamePrefix + feature.Name]); + } +} diff --git a/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureChecker.cs b/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureChecker.cs index 04eef96e6c..39f1cf5be8 100644 --- a/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureChecker.cs +++ b/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureChecker.cs @@ -28,7 +28,12 @@ public class FeatureChecker : FeatureCheckerBase public override async Task GetOrNullAsync(string name) { - var featureDefinition = await FeatureDefinitionManager.GetAsync(name); + var featureDefinition = await FeatureDefinitionManager.GetOrNullAsync(name); + if (featureDefinition == null) + { + return null; + } + var providers = FeatureValueProviderManager.ValueProviders .Reverse(); diff --git a/framework/src/Volo.Abp.Features/Volo/Abp/Features/StaticFeatureDefinitionChangedEvent.cs b/framework/src/Volo.Abp.Features/Volo/Abp/Features/StaticFeatureDefinitionChangedEvent.cs new file mode 100644 index 0000000000..e654516d98 --- /dev/null +++ b/framework/src/Volo.Abp.Features/Volo/Abp/Features/StaticFeatureDefinitionChangedEvent.cs @@ -0,0 +1,9 @@ +using System; + +namespace Volo.Abp.Features; + +[Serializable] +public class StaticFeatureDefinitionChangedEvent +{ + +} diff --git a/framework/src/Volo.Abp.Features/Volo/Abp/Features/StaticFeatureDefinitionStore.cs b/framework/src/Volo.Abp.Features/Volo/Abp/Features/StaticFeatureDefinitionStore.cs index d0647168d3..12c21b3987 100644 --- a/framework/src/Volo.Abp.Features/Volo/Abp/Features/StaticFeatureDefinitionStore.cs +++ b/framework/src/Volo.Abp.Features/Volo/Abp/Features/StaticFeatureDefinitionStore.cs @@ -5,37 +5,27 @@ using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Volo.Abp.DependencyInjection; +using Volo.Abp.StaticDefinitions; namespace Volo.Abp.Features; public class StaticFeatureDefinitionStore: IStaticFeatureDefinitionStore, ISingletonDependency { - protected IDictionary FeatureGroupDefinitions => _lazyFeatureGroupDefinitions.Value; - private readonly Lazy> _lazyFeatureGroupDefinitions; - - protected IDictionary FeatureDefinitions => _lazyFeatureDefinitions.Value; - private readonly Lazy> _lazyFeatureDefinitions; - + protected IServiceProvider ServiceProvider { get; } protected AbpFeatureOptions Options { get; } - - private readonly IServiceProvider _serviceProvider; + protected IStaticDefinitionCache> GroupCache { get; } + protected IStaticDefinitionCache> DefinitionCache { get; } public StaticFeatureDefinitionStore( + IServiceProvider serviceProvider, IOptions options, - IServiceProvider serviceProvider) + IStaticDefinitionCache> groupCache, + IStaticDefinitionCache> definitionCache) { - _serviceProvider = serviceProvider; + ServiceProvider = serviceProvider; Options = options.Value; - - _lazyFeatureDefinitions = new Lazy>( - CreateFeatureDefinitions, - isThreadSafe: true - ); - - _lazyFeatureGroupDefinitions = new Lazy>( - CreateFeatureGroupDefinitions, - isThreadSafe: true - ); + GroupCache = groupCache; + DefinitionCache = definitionCache; } public virtual async Task GetAsync(string name) @@ -52,43 +42,39 @@ public class StaticFeatureDefinitionStore: IStaticFeatureDefinitionStore, ISingl return feature; } - protected virtual Dictionary CreateFeatureDefinitions() + public virtual async Task GetOrNullAsync(string name) { - var features = new Dictionary(); - - foreach (var groupDefinition in FeatureGroupDefinitions.Values) - { - foreach (var feature in groupDefinition.Features) - { - AddFeatureToDictionaryRecursively(features, feature); - } - } + var defs = await GetFeatureDefinitionsAsync(); + return defs.GetOrDefault(name); + } - return features; + public virtual async Task> GetFeaturesAsync() + { + var defs = await GetFeatureDefinitionsAsync(); + return defs.Values.ToList(); } - protected virtual void AddFeatureToDictionaryRecursively( - Dictionary features, - FeatureDefinition feature) + public virtual async Task> GetGroupsAsync() { - if (features.ContainsKey(feature.Name)) - { - throw new AbpException("Duplicate feature name: " + feature.Name); - } + var groups = await GetFeatureGroupDefinitionsAsync(); + return groups.Values.ToList(); + } - features[feature.Name] = feature; + protected virtual async Task> GetFeatureGroupDefinitionsAsync() + { + return await GroupCache.GetOrCreateAsync(CreateFeatureGroupDefinitionsAsync); + } - foreach (var child in feature.Children) - { - AddFeatureToDictionaryRecursively(features, child); - } + protected virtual async Task> GetFeatureDefinitionsAsync() + { + return await DefinitionCache.GetOrCreateAsync(CreateFeatureDefinitionsAsync); } - protected virtual Dictionary CreateFeatureGroupDefinitions() + protected virtual Task> CreateFeatureGroupDefinitionsAsync() { var context = new FeatureDefinitionContext(); - using (var scope = _serviceProvider.CreateScope()) + using (var scope = ServiceProvider.CreateScope()) { var providers = Options .DefinitionProviders @@ -101,21 +87,39 @@ public class StaticFeatureDefinitionStore: IStaticFeatureDefinitionStore, ISingl } } - return context.Groups; + return Task.FromResult(context.Groups); } - public virtual Task GetOrNullAsync(string name) + protected virtual async Task> CreateFeatureDefinitionsAsync() { - return Task.FromResult(FeatureDefinitions.GetOrDefault(name)); - } + var features = new Dictionary(); - public virtual Task> GetFeaturesAsync() - { - return Task.FromResult>(FeatureDefinitions.Values.ToList()); + var groups = await GetFeatureGroupDefinitionsAsync(); + foreach (var groupDefinition in groups.Values) + { + foreach (var feature in groupDefinition.Features) + { + AddFeatureToDictionaryRecursively(features, feature); + } + } + + return features; } - public virtual Task> GetGroupsAsync() + protected virtual void AddFeatureToDictionaryRecursively( + Dictionary features, + FeatureDefinition feature) { - return Task.FromResult>(FeatureGroupDefinitions.Values.ToList()); + if (features.ContainsKey(feature.Name)) + { + throw new AbpException("Duplicate feature name: " + feature.Name); + } + + features[feature.Name] = feature; + + foreach (var child in feature.Children) + { + AddFeatureToDictionaryRecursively(features, child); + } } } diff --git a/framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/PropertyApiDescriptionModel.cs b/framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/PropertyApiDescriptionModel.cs index 55891d8cd6..ed604793b0 100644 --- a/framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/PropertyApiDescriptionModel.cs +++ b/framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/PropertyApiDescriptionModel.cs @@ -3,6 +3,7 @@ using System.ComponentModel.DataAnnotations; using System.Linq; using System.Reflection; using Volo.Abp.Http.ProxyScripting.Configuration; +using Volo.Abp.Reflection; namespace Volo.Abp.Http.Modeling; @@ -29,6 +30,8 @@ public class PropertyApiDescriptionModel public string? Regex { get; set; } + public bool IsNullable { get; set; } + public static PropertyApiDescriptionModel Create(PropertyInfo propertyInfo) { var customAttributes = propertyInfo.GetCustomAttributes(true); @@ -39,6 +42,7 @@ public class PropertyApiDescriptionModel Type = ApiTypeNameHelper.GetTypeName(propertyInfo.PropertyType), TypeSimple = ApiTypeNameHelper.GetSimpleTypeName(propertyInfo.PropertyType), IsRequired = customAttributes.OfType().Any() || propertyInfo.GetCustomAttributesData().Any(attr => attr.AttributeType.Name == "RequiredMemberAttribute"), + IsNullable = ReflectionHelper.IsNullable(propertyInfo), Minimum = customAttributes.OfType().Select(x => x.Minimum).FirstOrDefault()?.ToString(), Maximum = customAttributes.OfType().Select(x => x.Maximum).FirstOrDefault()?.ToString(), MinLength = customAttributes.OfType().FirstOrDefault()?.Length ?? customAttributes.OfType().FirstOrDefault()?.MinimumLength, diff --git a/framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/MemoryDatabaseCollection.cs b/framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/MemoryDatabaseCollection.cs index 3e08c63417..484d627cb4 100644 --- a/framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/MemoryDatabaseCollection.cs +++ b/framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/MemoryDatabaseCollection.cs @@ -1,6 +1,7 @@ using System; using System.Collections; using System.Collections.Generic; +using Volo.Abp.Data; using Volo.Abp.Domain.Entities; namespace Volo.Abp.Domain.Repositories.MemoryDb; @@ -37,10 +38,21 @@ public class MemoryDatabaseCollection : IMemoryDatabaseCollection(); + if (entity is IHasConcurrencyStamp hasConcurrencyStamp && originalEntity is IHasConcurrencyStamp originalHasConcurrencyStamp) + { + if (hasConcurrencyStamp.ConcurrencyStamp != originalHasConcurrencyStamp.ConcurrencyStamp) + { + throw new AbpDbConcurrencyException("Database operation expected to affect 1 row but actually affected 0 row. Data may have been modified or deleted since entities were loaded. This exception has been thrown on optimistic concurrency check."); + } + } + + _dictionary[GetEntityKey(entity)] = _memoryDbSerializer.Serialize(entity); } public void Remove(TEntity entity) diff --git a/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/TenantResolver.cs b/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/TenantResolver.cs index 4351ef62bb..602540dd03 100644 --- a/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/TenantResolver.cs +++ b/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/TenantResolver.cs @@ -1,6 +1,8 @@ using System; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; using Volo.Abp.DependencyInjection; @@ -8,25 +10,31 @@ namespace Volo.Abp.MultiTenancy; public class TenantResolver : ITenantResolver, ITransientDependency { - private readonly IServiceProvider _serviceProvider; - private readonly AbpTenantResolveOptions _options; + public ILogger Logger { get; set; } + + protected IServiceProvider ServiceProvider { get; } + protected AbpTenantResolveOptions Options { get; } public TenantResolver(IOptions options, IServiceProvider serviceProvider) { - _serviceProvider = serviceProvider; - _options = options.Value; + Logger = NullLogger.Instance; + + ServiceProvider = serviceProvider; + Options = options.Value; } public virtual async Task ResolveTenantIdOrNameAsync() { var result = new TenantResolveResult(); - using (var serviceScope = _serviceProvider.CreateScope()) + Logger.LogDebug("Starting resolving tenant..."); + using (var serviceScope = ServiceProvider.CreateScope()) { var context = new TenantResolveContext(serviceScope.ServiceProvider); - foreach (var tenantResolver in _options.TenantResolvers) + foreach (var tenantResolver in Options.TenantResolvers) { + Logger.LogDebug("Trying to resolve tenant through '{TenantResolverName}'...", tenantResolver.Name); await tenantResolver.ResolveAsync(context); result.AppliedResolvers.Add(tenantResolver.Name); @@ -34,15 +42,21 @@ public class TenantResolver : ITenantResolver, ITransientDependency if (context.HasResolvedTenantOrHost()) { result.TenantIdOrName = context.TenantIdOrName; + Logger.LogDebug("Tenant resolved by '{TenantResolverName}' as '{TenantIdOrName}'.", tenantResolver.Name, result.TenantIdOrName ?? "Host"); break; } } } - if (result.TenantIdOrName.IsNullOrEmpty() && !string.IsNullOrWhiteSpace(_options.FallbackTenant)) + if (result.TenantIdOrName.IsNullOrEmpty() && !string.IsNullOrWhiteSpace(Options.FallbackTenant)) { - result.TenantIdOrName = _options.FallbackTenant; + result.TenantIdOrName = Options.FallbackTenant; result.AppliedResolvers.Add(TenantResolverNames.FallbackTenant); + Logger.LogDebug("No tenant resolved. Using fallback tenant as '{FallbackTenant}'.", result.TenantIdOrName); + } + else if (result.TenantIdOrName.IsNullOrEmpty()) + { + Logger.LogDebug("No tenant resolved."); } return result; diff --git a/framework/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingProvider.cs b/framework/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingProvider.cs index 2b0bdfcf9f..90c0527067 100644 --- a/framework/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingProvider.cs +++ b/framework/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingProvider.cs @@ -23,7 +23,12 @@ public class SettingProvider : ISettingProvider, ITransientDependency public virtual async Task GetOrNullAsync(string name) { - var setting = await SettingDefinitionManager.GetAsync(name); + var setting = await SettingDefinitionManager.GetOrNullAsync(name); + if (setting == null) + { + return null; + } + var providers = Enumerable .Reverse(SettingValueProviderManager.Providers); diff --git a/framework/src/Volo.Abp.Settings/Volo/Abp/Settings/StaticSettingDefinitionChangedEvent.cs b/framework/src/Volo.Abp.Settings/Volo/Abp/Settings/StaticSettingDefinitionChangedEvent.cs new file mode 100644 index 0000000000..591ff88890 --- /dev/null +++ b/framework/src/Volo.Abp.Settings/Volo/Abp/Settings/StaticSettingDefinitionChangedEvent.cs @@ -0,0 +1,9 @@ +using System; + +namespace Volo.Abp.Settings; + +[Serializable] +public class StaticSettingDefinitionChangedEvent +{ + +} diff --git a/framework/src/Volo.Abp.Settings/Volo/Abp/Settings/StaticSettingDefinitionStore.cs b/framework/src/Volo.Abp.Settings/Volo/Abp/Settings/StaticSettingDefinitionStore.cs index 653ee935f7..a35ed31b75 100644 --- a/framework/src/Volo.Abp.Settings/Volo/Abp/Settings/StaticSettingDefinitionStore.cs +++ b/framework/src/Volo.Abp.Settings/Volo/Abp/Settings/StaticSettingDefinitionStore.cs @@ -6,23 +6,24 @@ using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Volo.Abp.DependencyInjection; +using Volo.Abp.StaticDefinitions; namespace Volo.Abp.Settings; public class StaticSettingDefinitionStore : IStaticSettingDefinitionStore, ISingletonDependency { - protected Lazy> SettingDefinitions { get; } - - protected AbpSettingOptions Options { get; } - protected IServiceProvider ServiceProvider { get; } + protected AbpSettingOptions Options { get; } + protected IStaticDefinitionCache> DefinitionCache { get; } - public StaticSettingDefinitionStore(IOptions options, IServiceProvider serviceProvider) + public StaticSettingDefinitionStore( + IServiceProvider serviceProvider, + IOptions options, + IStaticDefinitionCache> definitionCache) { ServiceProvider = serviceProvider; Options = options.Value; - - SettingDefinitions = new Lazy>(CreateSettingDefinitions, true); + DefinitionCache = definitionCache; } public virtual async Task GetAsync(string name) @@ -39,17 +40,24 @@ public class StaticSettingDefinitionStore : IStaticSettingDefinitionStore, ISing return setting; } - public virtual Task> GetAllAsync() + public virtual async Task> GetAllAsync() + { + var defs = await GetSettingDefinitionsAsync(); + return defs.Values.ToImmutableList(); + } + + public virtual async Task GetOrNullAsync(string name) { - return Task.FromResult>(SettingDefinitions.Value.Values.ToImmutableList()); + var defs = await GetSettingDefinitionsAsync(); + return defs.GetOrDefault(name); } - public virtual Task GetOrNullAsync(string name) + protected virtual async Task> GetSettingDefinitionsAsync() { - return Task.FromResult(SettingDefinitions.Value.GetOrDefault(name)); + return await DefinitionCache.GetOrCreateAsync(CreateSettingDefinitionsAsync); } - protected virtual IDictionary CreateSettingDefinitions() + protected virtual Task> CreateSettingDefinitionsAsync() { var settings = new Dictionary(); @@ -66,6 +74,6 @@ public class StaticSettingDefinitionStore : IStaticSettingDefinitionStore, ISing } } - return settings; + return Task.FromResult(settings); } } diff --git a/framework/src/Volo.Abp.TextTemplating.Core/Volo/Abp/TextTemplating/StaticTemplateDefinitionChangedEvent.cs b/framework/src/Volo.Abp.TextTemplating.Core/Volo/Abp/TextTemplating/StaticTemplateDefinitionChangedEvent.cs new file mode 100644 index 0000000000..dd862b9c69 --- /dev/null +++ b/framework/src/Volo.Abp.TextTemplating.Core/Volo/Abp/TextTemplating/StaticTemplateDefinitionChangedEvent.cs @@ -0,0 +1,9 @@ +using System; + +namespace Volo.Abp.TextTemplating; + +[Serializable] +public class StaticTemplateDefinitionChangedEvent +{ + +} diff --git a/framework/src/Volo.Abp.TextTemplating.Core/Volo/Abp/TextTemplating/StaticTemplateDefinitionStore.cs b/framework/src/Volo.Abp.TextTemplating.Core/Volo/Abp/TextTemplating/StaticTemplateDefinitionStore.cs index 4231f39b4d..083a111f87 100644 --- a/framework/src/Volo.Abp.TextTemplating.Core/Volo/Abp/TextTemplating/StaticTemplateDefinitionStore.cs +++ b/framework/src/Volo.Abp.TextTemplating.Core/Volo/Abp/TextTemplating/StaticTemplateDefinitionStore.cs @@ -6,50 +6,58 @@ using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Volo.Abp.DependencyInjection; +using Volo.Abp.StaticDefinitions; namespace Volo.Abp.TextTemplating; public class StaticTemplateDefinitionStore : IStaticTemplateDefinitionStore, ISingletonDependency { - protected Lazy> TemplateDefinitions { get; } - - protected AbpTextTemplatingOptions Options { get; } - protected IServiceProvider ServiceProvider { get; } + protected AbpTextTemplatingOptions Options { get; } + protected IStaticDefinitionCache> DefinitionCache { get; } - public StaticTemplateDefinitionStore(IOptions options, IServiceProvider serviceProvider) + public StaticTemplateDefinitionStore( + IServiceProvider serviceProvider, + IOptions options, + IStaticDefinitionCache> definitionCache) { ServiceProvider = serviceProvider; Options = options.Value; - - TemplateDefinitions = new Lazy>(CreateTextTemplateDefinitions, true); + DefinitionCache = definitionCache; } - public virtual Task GetAsync(string name) + public virtual async Task GetAsync(string name) { Check.NotNull(name, nameof(name)); - var template = GetOrNullAsync(name); + var template = await GetOrNullAsync(name); if (template == null) { throw new AbpException("Undefined template: " + name); } - return template!; + return template; + } + + public virtual async Task> GetAllAsync() + { + var defs = await GetTemplateDefinitionsAsync(); + return defs.Values.ToImmutableList(); } - public virtual Task> GetAllAsync() + public virtual async Task GetOrNullAsync(string name) { - return Task.FromResult>(TemplateDefinitions.Value.Values.ToImmutableList()); + var defs = await GetTemplateDefinitionsAsync(); + return defs.GetOrDefault(name); } - public virtual Task GetOrNullAsync(string name) + protected virtual async Task> GetTemplateDefinitionsAsync() { - return Task.FromResult(TemplateDefinitions.Value.GetOrDefault(name)); + return await DefinitionCache.GetOrCreateAsync(CreateTextTemplateDefinitionsAsync); } - protected virtual IDictionary CreateTextTemplateDefinitions() + protected virtual Task> CreateTextTemplateDefinitionsAsync() { var templates = new Dictionary(); @@ -78,6 +86,6 @@ public class StaticTemplateDefinitionStore : IStaticTemplateDefinitionStore, ISi } } - return templates; + return Task.FromResult(templates); } } diff --git a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/ar.json b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/ar.json index 93cf62ab84..eb32a44fa2 100644 --- a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/ar.json +++ b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/ar.json @@ -61,6 +61,7 @@ "ProfilePicture": "الصوره الشخصيه", "Theme": "سمة", "NotAssigned": "غيرمعتمد", - "EntityActionsDisabledTooltip": "ليس لديك إذن لتنفيذ أي إجراء." + "EntityActionsDisabledTooltip": "ليس لديك إذن لتنفيذ أي إجراء.", + "ResourcePermissions": "أذونات" } } diff --git a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/cs.json b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/cs.json index ff2d09fdf8..27a174cb49 100644 --- a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/cs.json +++ b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/cs.json @@ -61,6 +61,7 @@ "ProfilePicture": "Profilový obrázek", "Theme": "Téma", "NotAssigned": "Nepřiřazena", - "EntityActionsDisabledTooltip": "Nemáte oprávnění provést žádnou akci." + "EntityActionsDisabledTooltip": "Nemáte oprávnění provést žádnou akci.", + "ResourcePermissions": "Oprávnění" } } diff --git a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/de.json b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/de.json index 37c361c1e3..869d049be1 100644 --- a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/de.json +++ b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/de.json @@ -61,6 +61,7 @@ "ProfilePicture": "Profilbild", "Theme": "Thema", "NotAssigned": "Nicht zugeordnet", - "EntityActionsDisabledTooltip": "Sie haben keine Berechtigung, Aktionen auszuführen." + "EntityActionsDisabledTooltip": "Sie haben keine Berechtigung, Aktionen auszuführen.", + "ResourcePermissions": "Berechtigungen" } } diff --git a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/el.json b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/el.json index f7d837a361..8c2dc2be1d 100644 --- a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/el.json +++ b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/el.json @@ -55,6 +55,7 @@ "OthersGroup": "άλλος", "Today": "Σήμερα", "Apply": "Ισχύουν", - "EntityActionsDisabledTooltip": "Δεν έχετε δικαίωμα να εκτελέσετε καμία ενέργεια." + "EntityActionsDisabledTooltip": "Δεν έχετε δικαίωμα να εκτελέσετε καμία ενέργεια.", + "ResourcePermissions": "Δικαιώματα", } } diff --git a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/en-GB.json b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/en-GB.json index c5aaa5957c..ce9d962d5e 100644 --- a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/en-GB.json +++ b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/en-GB.json @@ -56,6 +56,7 @@ "NotAssigned": "Not Assigned", "Today": "Today", "Apply": "Apply", - "EntityActionsDisabledTooltip": "You do not have permission to perform any action." + "EntityActionsDisabledTooltip": "You do not have permission to perform any action.", + "ResourcePermissions": "Permissions" } } diff --git a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/en.json b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/en.json index 7a5a806124..f328765a39 100644 --- a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/en.json +++ b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/en.json @@ -61,6 +61,7 @@ "ProfilePicture": "Profile picture", "Theme": "Theme", "NotAssigned": "Not Assigned", - "EntityActionsDisabledTooltip": "You do not have permission to perform any action." + "EntityActionsDisabledTooltip": "You do not have permission to perform any action.", + "ResourcePermissions": "Permissions" } } diff --git a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/es.json b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/es.json index 132243a740..9d341cb88f 100644 --- a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/es.json +++ b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/es.json @@ -61,6 +61,7 @@ "ProfilePicture": "Foto de perfil", "Theme": "Tema", "NotAssigned": "No asignado", - "EntityActionsDisabledTooltip": "No tienes permisos para realizar ninguna acción." + "EntityActionsDisabledTooltip": "No tienes permisos para realizar ninguna acción.", + "ResourcePermissions": "Permisos" } -} +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/fa.json b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/fa.json index f1738957c9..5862eab90e 100644 --- a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/fa.json +++ b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/fa.json @@ -55,6 +55,7 @@ "OthersGroup": "دیگر", "Today": "امروز", "Apply": "درخواست دادن", - "EntityActionsDisabledTooltip": "شما دسترسی به انجام هر گونه عملیات ندارید." + "EntityActionsDisabledTooltip": "شما دسترسی به انجام هر گونه عملیات ندارید.", + "ResourcePermissions": "مجوزها" } } diff --git a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/fi.json b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/fi.json index 1af59eb720..51e356559b 100644 --- a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/fi.json +++ b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/fi.json @@ -61,6 +61,7 @@ "ProfilePicture": "Profiilikuva", "Theme": "Teema", "NotAssigned": "Ei määritetty", - "EntityActionsDisabledTooltip": "Sinulla ei ole oikeutta suorittaa mitään toimintoa." + "EntityActionsDisabledTooltip": "Sinulla ei ole oikeutta suorittaa mitään toimintoa.", + "ResourcePermissions": "Käyttöoikeudet" } } diff --git a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/fr.json b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/fr.json index 8af5661d1a..79a40c57f9 100644 --- a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/fr.json +++ b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/fr.json @@ -61,6 +61,7 @@ "ProfilePicture": "Image de profil", "Theme": "Thème", "NotAssigned": "Non attribué", - "EntityActionsDisabledTooltip": "Vous n'avez pas les permissions pour effectuer une action." + "EntityActionsDisabledTooltip": "Vous n'avez pas les permissions pour effectuer une action.", + "ResourcePermissions": "Autorisations" } } diff --git a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/hi.json b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/hi.json index ba873b7685..10c1e426c1 100644 --- a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/hi.json +++ b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/hi.json @@ -61,6 +61,7 @@ "ProfilePicture": "प्रोफ़ाइल फोटो", "Theme": "विषय", "NotAssigned": "सौंपा नहीं गया है", - "EntityActionsDisabledTooltip": "आपके पास कोई कार्रवाई नहीं है जो करने के लिए है।" + "EntityActionsDisabledTooltip": "आपके पास कोई कार्रवाई नहीं है जो करने के लिए है।", + "ResourcePermissions": "अनुमतियाँ" } } diff --git a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/hr.json b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/hr.json index 5b16c84963..c36751828c 100644 --- a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/hr.json +++ b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/hr.json @@ -61,6 +61,7 @@ "ProfilePicture": "Profilna slika", "Theme": "Tema", "NotAssigned": "Nije dodijeljeno", - "EntityActionsDisabledTooltip": "Nemate dozvolu za izvođenje bilo kakve akcije." + "EntityActionsDisabledTooltip": "Nemate dozvolu za izvođenje bilo kakve akcije.", + "ResourcePermissions": "Dozvole" } } diff --git a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/hu.json b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/hu.json index 0539276498..e162a2c6c8 100644 --- a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/hu.json +++ b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/hu.json @@ -61,6 +61,7 @@ "ProfilePicture": "Profil kép", "Theme": "Téma", "NotAssigned": "Nem kijelölt", - "EntityActionsDisabledTooltip": "Nincs jogosultsága bármely művelethez." + "EntityActionsDisabledTooltip": "Nincs jogosultsága bármely művelethez.", + "ResourcePermissions": "Engedélyek" } } diff --git a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/is.json b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/is.json index 8a1eda9e4e..3d88d70d97 100644 --- a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/is.json +++ b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/is.json @@ -61,6 +61,7 @@ "ProfilePicture": "Forsíðumynd", "Theme": "Þema", "NotAssigned": "Ekki skráður", - "EntityActionsDisabledTooltip": "Þú hefur ekki aðgang að þessum aðgerðum." + "EntityActionsDisabledTooltip": "Þú hefur ekki aðgang að þessum aðgerðum.", + "ResourcePermissions": "Aðgangsheimildir" } } diff --git a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/it.json b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/it.json index 77d4742680..9bfffe181e 100644 --- a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/it.json +++ b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/it.json @@ -61,6 +61,7 @@ "ProfilePicture": "Immagine del profilo", "Theme": "Tema", "NotAssigned": "Non assegnato", - "EntityActionsDisabledTooltip": "Non hai i permessi per eseguire alcuna azione." + "EntityActionsDisabledTooltip": "Non hai i permessi per eseguire alcuna azione.", + "ResourcePermissions": "Autorizzazioni" } } diff --git a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/nl.json b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/nl.json index 38182ec9e6..51ab0bcd31 100644 --- a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/nl.json +++ b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/nl.json @@ -61,6 +61,7 @@ "ProfilePicture": "Profielfoto", "Theme": "Thema", "NotAssigned": "Niet toegekend", - "EntityActionsDisabledTooltip": "U hebt geen toegang tot deze acties." + "EntityActionsDisabledTooltip": "U hebt geen toegang tot deze acties.", + "ResourcePermissions": "Machtigingen" } } diff --git a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/pl-PL.json b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/pl-PL.json index d7f321f3d6..20b4daf7f0 100644 --- a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/pl-PL.json +++ b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/pl-PL.json @@ -61,6 +61,7 @@ "ProfilePicture": "Zdjęcie profilowe", "Theme": "Temat", "NotAssigned": "Nie przypisano", - "EntityActionsDisabledTooltip": "Nie masz uprawnień do wykonania żadnej akcji." + "EntityActionsDisabledTooltip": "Nie masz uprawnień do wykonania żadnej akcji.", + "ResourcePermissions": "Uprawnienia" } } diff --git a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/pt-BR.json b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/pt-BR.json index 13c8380fa3..9764d99fe6 100644 --- a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/pt-BR.json +++ b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/pt-BR.json @@ -61,6 +61,7 @@ "ProfilePicture": "Foto do perfil", "Theme": "Tema", "NotAssigned": "Não atribuído", - "EntityActionsDisabledTooltip": "Você não tem permissão para executar qualquer ação." + "EntityActionsDisabledTooltip": "Você não tem permissão para executar qualquer ação.", + "ResourcePermissions": "Permissões" } } diff --git a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/ro-RO.json b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/ro-RO.json index e03fd91f50..6b7367994c 100644 --- a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/ro-RO.json +++ b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/ro-RO.json @@ -61,6 +61,7 @@ "ProfilePicture": "Poză de profil", "Theme": "Temă", "NotAssigned": "Nealocat", - "EntityActionsDisabledTooltip": "Nu aveți permisiune să efectuați nicio acțiune." + "EntityActionsDisabledTooltip": "Nu aveți permisiune să efectuați nicio acțiune.", + "ResourcePermissions": "Permisiuni" } } diff --git a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/ru.json b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/ru.json index 57f3079cb4..d4f4acf5d6 100644 --- a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/ru.json +++ b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/ru.json @@ -61,6 +61,7 @@ "ProfilePicture": "Изображение профиля", "Theme": "Тема", "NotAssigned": "Не назначен", - "EntityActionsDisabledTooltip": "У вас нет прав на выполнение каких-либо действий." + "EntityActionsDisabledTooltip": "У вас нет прав на выполнение каких-либо действий.", + "ResourcePermissions": "Разрешения" } } diff --git a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/sk.json b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/sk.json index eb0ba2958f..59d8aecef4 100644 --- a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/sk.json +++ b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/sk.json @@ -61,6 +61,7 @@ "ProfilePicture": "Profilový obrázok", "Theme": "Téma", "NotAssigned": "Nepridelené", - "EntityActionsDisabledTooltip": "Nemáte oprávnenie vykonávať žiadnu akciu." + "EntityActionsDisabledTooltip": "Nemáte oprávnenie vykonávať žiadnu akciu.", + "ResourcePermissions": "Oprávnenia" } } diff --git a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/sl.json b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/sl.json index bc3f795b34..89f2691a11 100644 --- a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/sl.json +++ b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/sl.json @@ -61,6 +61,7 @@ "ProfilePicture": "Profilna slika", "Theme": "Tema", "NotAssigned": "Ni dodeljena", - "EntityActionsDisabledTooltip": "Nimate pravic za izvajanje kakršne koli dejanje." + "EntityActionsDisabledTooltip": "Nimate pravic za izvajanje kakršne koli dejanje.", + "ResourcePermissions": "Dovoljenja" } } diff --git a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/sv.json b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/sv.json index f0257637c1..fe4b692cdf 100644 --- a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/sv.json +++ b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/sv.json @@ -60,6 +60,7 @@ "ProfilePicture": "Profilbild", "Theme": "Tema", "NotAssigned": "Ej tilldelad", - "EntityActionsDisabledTooltip": "Du har inte tillgång till dessa åtgärder." + "EntityActionsDisabledTooltip": "Du har inte tillgång till dessa åtgärder.", + "ResourcePermissions": "Behörigheter" } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/tr.json b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/tr.json index b21adf632a..73a4bb5cb4 100644 --- a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/tr.json +++ b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/tr.json @@ -61,6 +61,7 @@ "ProfilePicture": "Profil resmi", "Theme": "Tema", "NotAssigned": "Atanmadı", - "EntityActionsDisabledTooltip": "Bu işlemi gerçekleştirmek için yeterli yetkiniz yok." + "EntityActionsDisabledTooltip": "Bu işlemi gerçekleştirmek için yeterli yetkiniz yok.", + "ResourcePermissions": "İzinler" } } diff --git a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/vi.json b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/vi.json index 700798895f..015ed883d3 100644 --- a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/vi.json +++ b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/vi.json @@ -61,6 +61,7 @@ "ProfilePicture": "Ảnh đại diện", "Theme": "chủ đề", "NotAssigned": "Không được chỉ định", - "EntityActionsDisabledTooltip": "Bạn không có quyền thực hiện bất kỳ hành động nào." + "EntityActionsDisabledTooltip": "Bạn không có quyền thực hiện bất kỳ hành động nào.", + "ResourcePermissions": "Quyền" } } diff --git a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/zh-Hans.json b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/zh-Hans.json index 60f70e1545..b0759d7da9 100644 --- a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/zh-Hans.json +++ b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/zh-Hans.json @@ -61,6 +61,7 @@ "ProfilePicture": "个人资料图片", "Theme": "主题", "NotAssigned": "未分配", - "EntityActionsDisabledTooltip": "您没有权限执行任何操作。" + "EntityActionsDisabledTooltip": "您没有权限执行任何操作。", + "ResourcePermissions": "权限" } } diff --git a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/zh-Hant.json b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/zh-Hant.json index 0f8fdeb254..e35bae3610 100644 --- a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/zh-Hant.json +++ b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/zh-Hant.json @@ -61,6 +61,7 @@ "ProfilePicture": "個人資料圖片", "Theme": "主題", "NotAssigned": "未分配", - "EntityActionsDisabledTooltip": "您沒有權限執行任何操作。" + "EntityActionsDisabledTooltip": "您沒有權限執行任何操作。", + "ResourcePermissions": "權限" } } diff --git a/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/Entities/AppEntityWithNavigationsAndDisableAuditing.cs b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/Entities/AppEntityWithNavigationsAndDisableAuditing.cs new file mode 100644 index 0000000000..573328387f --- /dev/null +++ b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/Entities/AppEntityWithNavigationsAndDisableAuditing.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using Volo.Abp.Domain.Entities; + +namespace Volo.Abp.Auditing.App.Entities; + +[Audited] +public class AppEntityWithNavigationsAndDisableAuditing : AggregateRoot +{ + protected AppEntityWithNavigationsAndDisableAuditing() + { + + } + + public AppEntityWithNavigationsAndDisableAuditing(Guid id, string name) + : base(id) + { + Name = name; + } + + public string Name { get; set; } + + [DisableAuditing] + public virtual List OneToMany { get; set; } +} + + +public class AppEntityWithNavigationsAndDisableAuditingChildOneToMany : Entity +{ + public Guid AppEntityWithNavigationsAndDisableAuditingId { get; set; } + + public string ChildName { get; set; } +} diff --git a/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/EntityFrameworkCore/AbpAuditingTestDbContext.cs b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/EntityFrameworkCore/AbpAuditingTestDbContext.cs index 8aeeb2cfac..91698ea5ec 100644 --- a/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/EntityFrameworkCore/AbpAuditingTestDbContext.cs +++ b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/EntityFrameworkCore/AbpAuditingTestDbContext.cs @@ -29,6 +29,8 @@ public class AbpAuditingTestDbContext : AbpDbContext public DbSet AppEntityWithNavigations { get; set; } public DbSet AppEntityWithNavigationChildOneToMany { get; set; } + public DbSet AppEntityWithNavigationsAndDisableAuditing { get; set; } + public AbpAuditingTestDbContext(DbContextOptions options) : base(options) { diff --git a/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/Auditing_Tests.cs b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/Auditing_Tests.cs index aa11d7ffde..a88cf70292 100644 --- a/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/Auditing_Tests.cs +++ b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/Auditing_Tests.cs @@ -1025,6 +1025,40 @@ public class Auditing_SaveEntityHistoryWhenNavigationChanges_Tests : AbpAuditing x.EntityChanges[0].PropertyChanges[0].PropertyName == nameof(AppEntityWithNavigationChildManyToMany.ChildName) && x.EntityChanges[0].PropertyChanges[0].PropertyTypeFullName == typeof(string).FullName)); +#pragma warning restore 4014 + } + + [Fact] + public virtual async Task Should_Not_Write_AuditLog_For_Navigation_Changes_With_DisableAuditing() + { + using (var scope = _auditingManager.BeginScope()) + { + var repository = ServiceProvider.GetRequiredService>(); + var entity = new AppEntityWithNavigationsAndDisableAuditing(Guid.NewGuid(), "test name"); + entity.OneToMany = new List + { + new AppEntityWithNavigationsAndDisableAuditingChildOneToMany + { + AppEntityWithNavigationsAndDisableAuditingId = entity.Id, + ChildName = "ChildName1" + } + }; + await repository.InsertAsync(entity); + await scope.SaveAsync(); + } + +#pragma warning disable 4014 + AuditingStore.Received().SaveAsync(Arg.Any()); +#pragma warning restore 4014 + +#pragma warning disable 4014 + AuditingStore.Received().SaveAsync(Arg.Is(x => x.EntityChanges.Count == 1 && + x.EntityChanges[0].ChangeType == EntityChangeType.Created && + x.EntityChanges[0].EntityTypeFullName == typeof(AppEntityWithNavigationsAndDisableAuditing).FullName && + x.EntityChanges[0].PropertyChanges.Count == 1 && + x.EntityChanges[0].PropertyChanges[0].PropertyName == nameof(AppEntityWithNavigationsAndDisableAuditing.Name) && + x.EntityChanges[0].PropertyChanges[0].PropertyTypeFullName == typeof(string).FullName)); + #pragma warning restore 4014 } } diff --git a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/AbpAuthorizationTestModule.cs b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/AbpAuthorizationTestModule.cs index fca3d209b0..15ec4e0641 100644 --- a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/AbpAuthorizationTestModule.cs +++ b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/AbpAuthorizationTestModule.cs @@ -1,6 +1,7 @@ using Microsoft.Extensions.DependencyInjection; using Volo.Abp.Authorization.Permissions; using Volo.Abp.Authorization.TestServices; +using Volo.Abp.Authorization.TestServices.Resources; using Volo.Abp.Autofac; using Volo.Abp.DynamicProxy; using Volo.Abp.ExceptionHandling; @@ -33,6 +34,9 @@ public class AbpAuthorizationTestModule : AbpModule options.ValueProviders.Add(); options.ValueProviders.Add(); options.ValueProviders.Add(); + + options.ResourceValueProviders.Add(); + options.ResourceValueProviders.Add(); }); } } diff --git a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/PermissionValueProviderManager_Tests.cs b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/PermissionValueProviderManager_Tests.cs index 4144df2e1e..43736f0dce 100644 --- a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/PermissionValueProviderManager_Tests.cs +++ b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/PermissionValueProviderManager_Tests.cs @@ -2,12 +2,11 @@ using System; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Shouldly; -using Volo.Abp.Authorization; using Volo.Abp.Authorization.Permissions; using Volo.Abp.Authorization.TestServices; using Xunit; -namespace Volo.Abp; +namespace Volo.Abp.Authorization; public class PermissionValueProviderManager_Tests: AuthorizationTestBase { @@ -17,7 +16,7 @@ public class PermissionValueProviderManager_Tests: AuthorizationTestBase { _permissionValueProviderManager = GetRequiredService(); } - + protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) { options.Services.Configure(permissionOptions => @@ -25,7 +24,7 @@ public class PermissionValueProviderManager_Tests: AuthorizationTestBase permissionOptions.ValueProviders.Add(); }); } - + [Fact] public void Should_Throw_Exception_If_Duplicate_Provider_Name_Detected() { @@ -33,7 +32,7 @@ public class PermissionValueProviderManager_Tests: AuthorizationTestBase { var providers = _permissionValueProviderManager.ValueProviders; }); - + exception.Message.ShouldBe($"Duplicate permission value provider name detected: TestPermissionValueProvider1. Providers:{Environment.NewLine}{typeof(TestDuplicatePermissionValueProvider).FullName}{Environment.NewLine}{typeof(TestPermissionValueProvider1).FullName}"); } } @@ -55,4 +54,4 @@ public class TestDuplicatePermissionValueProvider : PermissionValueProvider { throw new NotImplementedException(); } -} \ No newline at end of file +} diff --git a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/ResourcePermissionChecker_Tests.cs b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/ResourcePermissionChecker_Tests.cs new file mode 100644 index 0000000000..739968b603 --- /dev/null +++ b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/ResourcePermissionChecker_Tests.cs @@ -0,0 +1,62 @@ +using System.Threading.Tasks; +using Shouldly; +using Volo.Abp.Authorization.Permissions; +using Volo.Abp.Authorization.Permissions.Resources; +using Volo.Abp.Authorization.TestServices.Resources; +using Xunit; + +namespace Volo.Abp.Authorization; + +public class ResourcePermissionChecker_Tests: AuthorizationTestBase +{ + private readonly IResourcePermissionChecker _resourcePermissionChecker; + + public ResourcePermissionChecker_Tests() + { + _resourcePermissionChecker = GetRequiredService(); + } + + [Fact] + public async Task IsGrantedAsync() + { + (await _resourcePermissionChecker.IsGrantedAsync("MyResourcePermission5", TestEntityResource.ResourceName, TestEntityResource.ResourceKey5)).ShouldBe(true); + (await _resourcePermissionChecker.IsGrantedAsync("UndefinedResourcePermission", TestEntityResource.ResourceName, TestEntityResource.ResourceKey5)).ShouldBe(false); + (await _resourcePermissionChecker.IsGrantedAsync("MyResourcePermission8", TestEntityResource.ResourceName, TestEntityResource.ResourceKey5)).ShouldBe(false); + } + + [Fact] + public async Task IsGranted_Multiple_Result_Async() + { + var result = await _resourcePermissionChecker.IsGrantedAsync(new [] + { + "MyResourcePermission1", + "MyResourcePermission2", + "UndefinedPermission", + "MyResourcePermission3", + "MyResourcePermission4", + "MyResourcePermission5", + "MyResourcePermission8" + }, TestEntityResource.ResourceName, TestEntityResource.ResourceKey5); + + result.Result["MyResourcePermission1"].ShouldBe(PermissionGrantResult.Undefined); + result.Result["MyResourcePermission2"].ShouldBe(PermissionGrantResult.Prohibited); + result.Result["UndefinedPermission"].ShouldBe(PermissionGrantResult.Prohibited); + result.Result["MyResourcePermission3"].ShouldBe(PermissionGrantResult.Granted); + result.Result["MyResourcePermission4"].ShouldBe(PermissionGrantResult.Prohibited); + result.Result["MyResourcePermission5"].ShouldBe(PermissionGrantResult.Granted); + result.Result["MyResourcePermission8"].ShouldBe(PermissionGrantResult.Prohibited); + + result = await _resourcePermissionChecker.IsGrantedAsync(new [] + { + "MyResourcePermission6", + }, TestEntityResource.ResourceName, TestEntityResource.ResourceKey6); + + result.Result["MyResourcePermission6"].ShouldBe(PermissionGrantResult.Granted); + + result = await _resourcePermissionChecker.IsGrantedAsync(new [] + { + "MyResourcePermission7", + }, TestEntityResource.ResourceName, TestEntityResource.ResourceKey7); + result.Result["MyResourcePermission7"].ShouldBe(PermissionGrantResult.Granted); + } +} diff --git a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/ResourcePermissionPopulator_Test.cs b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/ResourcePermissionPopulator_Test.cs new file mode 100644 index 0000000000..2cc000c30a --- /dev/null +++ b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/ResourcePermissionPopulator_Test.cs @@ -0,0 +1,60 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Shouldly; +using Volo.Abp.Authorization.Permissions.Resources; +using Volo.Abp.Authorization.TestServices.Resources; +using Xunit; + +namespace Volo.Abp.Authorization; + +public class ResourcePermissionPopulator_Tests : AuthorizationTestBase +{ + private readonly ResourcePermissionPopulator _resourcePermissionPopulator; + + public ResourcePermissionPopulator_Tests() + { + _resourcePermissionPopulator = GetRequiredService(); + } + + [Fact] + public async Task PopulateAsync() + { + var testResourceObject = new TestEntityResource(TestEntityResource.ResourceKey5); + testResourceObject.ResourcePermissions.IsNullOrEmpty().ShouldBeTrue(); + + await _resourcePermissionPopulator.PopulateAsync( + testResourceObject, + TestEntityResource.ResourceName + ); + + testResourceObject.ResourcePermissions.ShouldNotBeNull(); + testResourceObject.ResourcePermissions.Count.ShouldBe(8); + testResourceObject.ResourcePermissions["MyResourcePermission1"].ShouldBe(false); + testResourceObject.ResourcePermissions["MyResourcePermission2"].ShouldBe(false); + testResourceObject.ResourcePermissions["MyResourcePermission3"].ShouldBe(true); + testResourceObject.ResourcePermissions["MyResourcePermission4"].ShouldBe(false); + testResourceObject.ResourcePermissions["MyResourcePermission5"].ShouldBe(true); + testResourceObject.ResourcePermissions["MyResourcePermission6"].ShouldBe(false); + testResourceObject.ResourcePermissions["MyResourcePermission7"].ShouldBe(false); + testResourceObject.ResourcePermissions["MyResourcePermission8"].ShouldBe(false); + + testResourceObject = new TestEntityResource(TestEntityResource.ResourceKey6); + testResourceObject.ResourcePermissions.IsNullOrEmpty().ShouldBeTrue(); + + await _resourcePermissionPopulator.PopulateAsync( + testResourceObject, + TestEntityResource.ResourceName + ); + + testResourceObject.ResourcePermissions.ShouldNotBeNull(); + testResourceObject.ResourcePermissions.Count.ShouldBe(8); + testResourceObject.ResourcePermissions["MyResourcePermission1"].ShouldBe(false); + testResourceObject.ResourcePermissions["MyResourcePermission2"].ShouldBe(false); + testResourceObject.ResourcePermissions["MyResourcePermission3"].ShouldBe(false); + testResourceObject.ResourcePermissions["MyResourcePermission4"].ShouldBe(false); + testResourceObject.ResourcePermissions["MyResourcePermission5"].ShouldBe(false); + testResourceObject.ResourcePermissions["MyResourcePermission6"].ShouldBe(true); + testResourceObject.ResourcePermissions["MyResourcePermission7"].ShouldBe(false); + testResourceObject.ResourcePermissions["MyResourcePermission8"].ShouldBe(false); + } +} diff --git a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/ResourcePermissionValueProviderManager_Tests.cs b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/ResourcePermissionValueProviderManager_Tests.cs new file mode 100644 index 0000000000..ceebc1ebac --- /dev/null +++ b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/ResourcePermissionValueProviderManager_Tests.cs @@ -0,0 +1,58 @@ +using System; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Shouldly; +using Volo.Abp.Authorization.Permissions; +using Volo.Abp.Authorization.Permissions.Resources; +using Volo.Abp.Authorization.TestServices.Resources; +using Xunit; + +namespace Volo.Abp.Authorization; + +public class ResourcePermissionValueProviderManager_Tests: AuthorizationTestBase +{ + private readonly IResourcePermissionValueProviderManager _resourcePermissionValueProviderManager; + + public ResourcePermissionValueProviderManager_Tests() + { + _resourcePermissionValueProviderManager = GetRequiredService(); + } + + protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) + { + options.Services.Configure(permissionOptions => + { + permissionOptions.ResourceValueProviders.Add(); + }); + } + + [Fact] + public void Should_Throw_Exception_If_Duplicate_Provider_Name_Detected() + { + var exception = Assert.Throws(() => + { + var providers = _resourcePermissionValueProviderManager.ValueProviders; + }); + + exception.Message.ShouldBe($"Duplicate resource permission value provider name detected: TestResourcePermissionValueProvider1. Providers:{Environment.NewLine}{typeof(TestDuplicateResourcePermissionValueProvider).FullName}{Environment.NewLine}{typeof(TestResourcePermissionValueProvider1).FullName}"); + } +} + +public class TestDuplicateResourcePermissionValueProvider : ResourcePermissionValueProvider +{ + public TestDuplicateResourcePermissionValueProvider(IResourcePermissionStore permissionStore) : base(permissionStore) + { + } + + public override string Name => "TestResourcePermissionValueProvider1"; + + public override Task CheckAsync(ResourcePermissionValueCheckContext context) + { + throw new NotImplementedException(); + } + + public override Task CheckAsync(ResourcePermissionValuesCheckContext context) + { + throw new NotImplementedException(); + } +} diff --git a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/StaticPermissionDefinitionStore_Tests.cs b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/StaticPermissionDefinitionStore_Tests.cs index d3a484c32f..0afaa34f10 100644 --- a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/StaticPermissionDefinitionStore_Tests.cs +++ b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/StaticPermissionDefinitionStore_Tests.cs @@ -1,6 +1,7 @@ using System.Threading.Tasks; using Shouldly; using Volo.Abp.Authorization.Permissions; +using Volo.Abp.Authorization.TestServices.Resources; using Xunit; namespace Volo.Abp.Authorization; @@ -44,4 +45,29 @@ public class StaticPermissionDefinitionStore_Tests : AuthorizationTestBase var groups = await _store.GetGroupsAsync(); groups.ShouldNotContain(x => x.Name == "TestGetGroup"); } + + [Fact] + public async Task GetResourcePermissionOrNullAsync() + { + var permission = await _store.GetResourcePermissionOrNullAsync(TestEntityResource.ResourceName, "MyResourcePermission1"); + permission.ShouldNotBeNull(); + permission.Name.ShouldBe("MyResourcePermission1"); + permission.StateCheckers.ShouldContain(x => x.GetType() == typeof(TestRequireEditionPermissionSimpleStateChecker)); + + permission = await _store.GetResourcePermissionOrNullAsync(TestEntityResource.ResourceName, "NotExists"); + permission.ShouldBeNull(); + } + + [Fact] + public async Task GetResourcePermissionsAsync() + { + var permissions = await _store.GetResourcePermissionsAsync(); + permissions.ShouldContain(x => x.Name == "MyResourcePermission1"); + permissions.ShouldContain(x => x.Name == "MyResourcePermission2"); + permissions.ShouldContain(x => x.Name == "MyResourcePermission3"); + permissions.ShouldContain(x => x.Name == "MyResourcePermission4"); + permissions.ShouldContain(x => x.Name == "MyResourcePermission5"); + permissions.ShouldContain(x => x.Name == "MyResourcePermission6"); + permissions.ShouldContain(x => x.Name == "MyResourcePermission7"); + } } diff --git a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/FakePermissionStore.cs b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/FakePermissionStore.cs index 4582417d3b..608cf94428 100644 --- a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/FakePermissionStore.cs +++ b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/FakePermissionStore.cs @@ -8,7 +8,7 @@ public class FakePermissionStore : IPermissionStore, ITransientDependency { public Task IsGrantedAsync(string name, string providerName, string providerKey) { - return Task.FromResult(name == "MyPermission3" || name == "MyPermission5"); + return Task.FromResult(name == "MyPermission3" || name == "MyPermission5" || name == "TestEntityManagementPermission"); } public Task IsGrantedAsync(string[] names, string providerName, string providerKey) @@ -16,7 +16,7 @@ public class FakePermissionStore : IPermissionStore, ITransientDependency var result = new MultiplePermissionGrantResult(); foreach (var name in names) { - result.Result.Add(name, name == "MyPermission3" || name == "MyPermission5" + result.Result.Add(name, name == "MyPermission3" || name == "MyPermission5" || name == "TestEntityManagementPermission" ? PermissionGrantResult.Granted : PermissionGrantResult.Prohibited); } diff --git a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/Resources/AuthorizationTestResourcePermissionDefinitionProvider.cs b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/Resources/AuthorizationTestResourcePermissionDefinitionProvider.cs new file mode 100644 index 0000000000..29a597c9b3 --- /dev/null +++ b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/Resources/AuthorizationTestResourcePermissionDefinitionProvider.cs @@ -0,0 +1,48 @@ +using Shouldly; +using Volo.Abp.Authorization.Permissions; +using Xunit; + +namespace Volo.Abp.Authorization.TestServices.Resources; + +public class AuthorizationTestResourcePermissionDefinitionProvider : PermissionDefinitionProvider +{ + public override void Define(IPermissionDefinitionContext context) + { + var getGroup = context.GetGroupOrNull("TestGroup"); + if (getGroup == null) + { + getGroup = context.AddGroup("TestGroup"); + } + getGroup.AddPermission("TestEntityManagementPermission"); + getGroup.AddPermission("TestEntityManagementPermission2"); + + var permission1 = context.AddResourcePermission("MyResourcePermission1", resourceName: TestEntityResource.ResourceName, "TestEntityManagementPermission"); + Assert.Throws(() => + { + permission1.AddChild("MyResourcePermission1.ChildPermission1"); + }).Message.ShouldBe($"Resource permission cannot have child permissions. Resource: {TestEntityResource.ResourceName}"); + permission1.StateCheckers.Add(new TestRequireEditionPermissionSimpleStateChecker());; + permission1[PermissionDefinitionContext.KnownPropertyNames.CurrentProviderName].ShouldBe(typeof(AuthorizationTestResourcePermissionDefinitionProvider).FullName); + + context.AddResourcePermission("MyResourcePermission2", resourceName: typeof(TestEntityResource).FullName!, "TestEntityManagementPermission"); + context.AddResourcePermission("MyResourcePermission3", resourceName: typeof(TestEntityResource).FullName!, "TestEntityManagementPermission"); + context.AddResourcePermission("MyResourcePermission4", resourceName: typeof(TestEntityResource).FullName!, "TestEntityManagementPermission"); + context.AddResourcePermission("MyResourcePermission5", resourceName: typeof(TestEntityResource).FullName!, "TestEntityManagementPermission"); + context.AddResourcePermission("MyResourcePermission6", resourceName: typeof(TestEntityResource).FullName!, "TestEntityManagementPermission").WithProviders(nameof(TestResourcePermissionValueProvider1)); + context.AddResourcePermission("MyResourcePermission7", resourceName: typeof(TestEntityResource).FullName!, "TestEntityManagementPermission").WithProviders(nameof(TestResourcePermissionValueProvider2)); + context.AddResourcePermission("MyResourcePermission8", resourceName: typeof(TestEntityResource).FullName!, "TestEntityManagementPermission2"); + + Assert.Throws(() => + { + context.AddResourcePermission("MyResourcePermission7", resourceName: typeof(TestEntityResource).FullName!, "TestEntityManagementPermission"); + }).Message.ShouldBe($"There is already an existing resource permission with name: MyResourcePermission7 for resource: {typeof(TestEntityResource).FullName}"); + + context.AddResourcePermission("MyResourcePermission7", resourceName: typeof(TestEntityResource2).FullName!, "TestEntityManagementPermission").WithProviders(nameof(TestResourcePermissionValueProvider2)); + + context.GetResourcePermissionOrNull(TestEntityResource.ResourceName, "MyResourcePermission1").ShouldNotBeNull(); + context.GetResourcePermissionOrNull(TestEntityResource.ResourceName, "MyResourcePermission7").ShouldNotBeNull(); + context.GetResourcePermissionOrNull(TestEntityResource2.ResourceName, "MyResourcePermission7").ShouldNotBeNull(); + context.GetResourcePermissionOrNull(TestEntityResource.ResourceName, "MyResourcePermission9").ShouldBeNull(); + context.GetResourcePermissionOrNull(TestEntityResource2.ResourceName, "MyResourcePermission6").ShouldBeNull(); + } +} diff --git a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/Resources/FakeResourcePermissionStore.cs b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/Resources/FakeResourcePermissionStore.cs new file mode 100644 index 0000000000..40cd542ac7 --- /dev/null +++ b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/Resources/FakeResourcePermissionStore.cs @@ -0,0 +1,46 @@ +using System.Threading.Tasks; +using Volo.Abp.Authorization.Permissions; +using Volo.Abp.Authorization.Permissions.Resources; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Authorization.TestServices.Resources; + +public class FakeResourcePermissionStore : IResourcePermissionStore, ITransientDependency +{ + public Task IsGrantedAsync(string name, string resourceName, string resourceKey, string providerName, string providerKey) + { + return Task.FromResult((name == "MyResourcePermission3" || name == "MyResourcePermission5") && + resourceName == TestEntityResource.ResourceName && + (resourceKey == TestEntityResource.ResourceKey3 || resourceKey == TestEntityResource.ResourceKey5)); + } + + public Task IsGrantedAsync(string[] names, string resourceName, string resourceKey, string providerName, string providerKey) + { + var result = new MultiplePermissionGrantResult(); + foreach (var name in names) + { + result.Result.Add(name, ((name == "MyResourcePermission3" || name == "MyResourcePermission5") && + resourceName == TestEntityResource.ResourceName && + (resourceKey == TestEntityResource.ResourceKey3 || resourceKey == TestEntityResource.ResourceKey5) + ? PermissionGrantResult.Granted + : PermissionGrantResult.Prohibited)); + } + + return Task.FromResult(result); + } + + public Task GetPermissionsAsync(string resourceName, string resourceKey) + { + throw new System.NotImplementedException(); + } + + public Task GetGrantedPermissionsAsync(string resourceName, string resourceKey) + { + throw new System.NotImplementedException(); + } + + public Task GetGrantedResourceKeysAsync(string resourceName, string name) + { + throw new System.NotImplementedException(); + } +} diff --git a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/Resources/TestEntityResource.cs b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/Resources/TestEntityResource.cs new file mode 100644 index 0000000000..f703007633 --- /dev/null +++ b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/Resources/TestEntityResource.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using Volo.Abp.Authorization.Permissions.Resources; + +namespace Volo.Abp.Authorization.TestServices.Resources; + +public class TestEntityResource : IHasResourcePermissions +{ + public static readonly string ResourceName = typeof(TestEntityResource).FullName; + + public static readonly string ResourceKey1 = Guid.NewGuid().ToString(); + public static readonly string ResourceKey2 = Guid.NewGuid().ToString(); + public static readonly string ResourceKey3 = Guid.NewGuid().ToString(); + public static readonly string ResourceKey4 = Guid.NewGuid().ToString(); + public static readonly string ResourceKey5 = Guid.NewGuid().ToString(); + public static readonly string ResourceKey6 = Guid.NewGuid().ToString(); + public static readonly string ResourceKey7 = Guid.NewGuid().ToString(); + + private string Id { get; } + + public TestEntityResource(string id) + { + Id = id; + } + + public string GetObjectKey() + { + return Id; + } + + public Dictionary ResourcePermissions { get; set; } +} + +public class TestEntityResource2 +{ + public static readonly string ResourceName = typeof(TestEntityResource2).FullName; +} diff --git a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/Resources/TestResourcePermissionValueProvider1.cs b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/Resources/TestResourcePermissionValueProvider1.cs new file mode 100644 index 0000000000..3c7dc49a83 --- /dev/null +++ b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/Resources/TestResourcePermissionValueProvider1.cs @@ -0,0 +1,43 @@ +using System.Linq; +using System.Threading.Tasks; +using Volo.Abp.Authorization.Permissions; +using Volo.Abp.Authorization.Permissions.Resources; + +namespace Volo.Abp.Authorization.TestServices.Resources; + +public class TestResourcePermissionValueProvider1 : ResourcePermissionValueProvider +{ + public TestResourcePermissionValueProvider1(IResourcePermissionStore permissionStore) : base(permissionStore) + { + } + + public override string Name => "TestResourcePermissionValueProvider1"; + + public override Task CheckAsync(ResourcePermissionValueCheckContext context) + { + var result = PermissionGrantResult.Undefined; + if (context.Permission.Name == "MyResourcePermission6" && + context.ResourceName == TestEntityResource.ResourceName && + context.ResourceKey == TestEntityResource.ResourceKey6) + { + result = PermissionGrantResult.Granted; + } + + return Task.FromResult(result); + } + + public override Task CheckAsync(ResourcePermissionValuesCheckContext context) + { + var result = new MultiplePermissionGrantResult(); + foreach (var name in context.Permissions.Select(x => x.Name)) + { + result.Result.Add(name, name == "MyResourcePermission6" && + context.ResourceName == TestEntityResource.ResourceName && + context.ResourceKey == TestEntityResource.ResourceKey6 + ? PermissionGrantResult.Granted + : PermissionGrantResult.Undefined); + } + + return Task.FromResult(result); + } +} diff --git a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/Resources/TestResourcePermissionValueProvider2.cs b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/Resources/TestResourcePermissionValueProvider2.cs new file mode 100644 index 0000000000..c4ac61cb2c --- /dev/null +++ b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/Resources/TestResourcePermissionValueProvider2.cs @@ -0,0 +1,43 @@ +using System.Linq; +using System.Threading.Tasks; +using Volo.Abp.Authorization.Permissions; +using Volo.Abp.Authorization.Permissions.Resources; + +namespace Volo.Abp.Authorization.TestServices.Resources; + +public class TestResourcePermissionValueProvider2 : ResourcePermissionValueProvider +{ + public TestResourcePermissionValueProvider2(IResourcePermissionStore permissionStore) : base(permissionStore) + { + } + + public override string Name => "TestResourcePermissionValueProvider2"; + + public override Task CheckAsync(ResourcePermissionValueCheckContext context) + { + var result = PermissionGrantResult.Undefined; + if (context.Permission.Name == "MyResourcePermission7" && + context.ResourceName == TestEntityResource.ResourceName && + context.ResourceKey == TestEntityResource.ResourceKey7) + { + result = PermissionGrantResult.Granted; + } + + return Task.FromResult(result); + } + + public override Task CheckAsync(ResourcePermissionValuesCheckContext context) + { + var result = new MultiplePermissionGrantResult(); + foreach (var name in context.Permissions.Select(x => x.Name)) + { + result.Result.Add(name, name == "MyResourcePermission7" && + context.ResourceName == TestEntityResource.ResourceName && + context.ResourceKey == TestEntityResource.ResourceKey7 + ? PermissionGrantResult.Granted + : PermissionGrantResult.Undefined); + } + + return Task.FromResult(result); + } +} diff --git a/framework/test/Volo.Abp.Core.Tests/Volo.Abp.Core.Tests.csproj b/framework/test/Volo.Abp.Core.Tests/Volo.Abp.Core.Tests.csproj index 941d5cf1da..4f91a9f854 100644 --- a/framework/test/Volo.Abp.Core.Tests/Volo.Abp.Core.Tests.csproj +++ b/framework/test/Volo.Abp.Core.Tests/Volo.Abp.Core.Tests.csproj @@ -5,6 +5,7 @@ net10.0 + enable diff --git a/framework/test/Volo.Abp.Core.Tests/Volo/Abp/ObjectWithKeyHelper_Tests.cs b/framework/test/Volo.Abp.Core.Tests/Volo/Abp/ObjectWithKeyHelper_Tests.cs new file mode 100644 index 0000000000..d5f0186b3c --- /dev/null +++ b/framework/test/Volo.Abp.Core.Tests/Volo/Abp/ObjectWithKeyHelper_Tests.cs @@ -0,0 +1,31 @@ +using System; +using Shouldly; +using Xunit; + +namespace Volo.Abp; + +public class KeyedObjectHelper_Tests +{ + [Fact] + public void EncodeCompositeKey() + { + var encoded = KeyedObjectHelper.EncodeCompositeKey("Book", "123"); + encoded.ShouldBe("Qm9va3x8MTIz"); + } + + [Fact] + public void DecodeCompositeKey() + { + var decoded = KeyedObjectHelper.DecodeCompositeKey("Qm9va3x8MTIz"); + decoded.ShouldBe("Book||123"); + } + + [Fact] + public void Encode_Decode_CompositeKey() + { + var encoded = KeyedObjectHelper.EncodeCompositeKey("User", 42, Guid.Empty); + var decoded = KeyedObjectHelper.DecodeCompositeKey(encoded); + + decoded.ShouldBe($"User||42||{Guid.Empty}"); + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.Core.Tests/Volo/Abp/Reflection/ReflectionHelper_Tests.cs b/framework/test/Volo.Abp.Core.Tests/Volo/Abp/Reflection/ReflectionHelper_Tests.cs index 1f761d3438..3b2d0afab2 100644 --- a/framework/test/Volo.Abp.Core.Tests/Volo/Abp/Reflection/ReflectionHelper_Tests.cs +++ b/framework/test/Volo.Abp.Core.Tests/Volo/Abp/Reflection/ReflectionHelper_Tests.cs @@ -84,8 +84,65 @@ public class ReflectionHelper_Tests constants.ShouldNotBeEmpty(); constants.Except(IdentityPermissions.GetAll()).Count().ShouldBe(0); } + + [Fact] + public void IsNullable_Test() + { + var prop1 = typeof(TestClass).GetProperty(nameof(TestClass.Prop1))!; + ReflectionHelper.IsNullable(prop1).ShouldBeFalse(); + + var prop2 = typeof(TestClass).GetProperty(nameof(TestClass.Prop2))!; + ReflectionHelper.IsNullable(prop2).ShouldBeTrue(); + + var prop3 = typeof(TestClass).GetProperty(nameof(TestClass.Prop3))!; + ReflectionHelper.IsNullable(prop3).ShouldBeFalse(); + + var prop4 = typeof(TestClass).GetProperty(nameof(TestClass.Prop4))!; + ReflectionHelper.IsNullable(prop4).ShouldBeTrue(); + + var prop5 = typeof(TestClass).GetProperty(nameof(TestClass.Prop5))!; + ReflectionHelper.IsNullable(prop5).ShouldBeFalse(); + + var prop6 = typeof(TestClass).GetProperty(nameof(TestClass.Prop6))!; + ReflectionHelper.IsNullable(prop6).ShouldBeTrue(); + + var prop7 = typeof(TestClass).GetProperty(nameof(TestClass.Prop7))!; + ReflectionHelper.IsNullable(prop7).ShouldBeFalse(); + + var prop8 = typeof(TestClass).GetProperty(nameof(TestClass.Prop8))!; + ReflectionHelper.IsNullable(prop8).ShouldBeTrue(); + + var prop9 = typeof(TestClass).GetProperty(nameof(TestClass.Prop9))!; + ReflectionHelper.IsNullable(prop9).ShouldBeFalse(); + + var prop10 = typeof(TestClass).GetProperty(nameof(TestClass.Prop10))!; + ReflectionHelper.IsNullable(prop10).ShouldBeTrue(); + + var prop11 = typeof(TestClass).GetProperty(nameof(TestClass.Prop11))!; + ReflectionHelper.IsNullable(prop11).ShouldBeFalse(); + + var prop12 = typeof(TestClass).GetProperty(nameof(TestClass.Prop12))!; + ReflectionHelper.IsNullable(prop12).ShouldBeTrue(); + } } +public class TestClass +{ + public string Prop1 { get; set; } = null!; + public string? Prop2 { get; set; } = null!; + public required string Prop3 { get; set; } + public required string? Prop4 { get; set; } + + public int Prop5 { get; set; } + public int? Prop6 { get; set; } + public required int Prop7 { get; set; } + public required int? Prop8 { get; set; } + + public int[] Prop9 { get; set; } = null!; + public int[]? Prop10 { get; set; } + public required int[] Prop11 { get; set; } + public required int[]? Prop12 { get; set; } +} public class BaseRole { diff --git a/framework/test/Volo.Abp.Core.Tests/Volo/Abp/StaticDefinitions/StaticDefinitionCache_Tests.cs b/framework/test/Volo.Abp.Core.Tests/Volo/Abp/StaticDefinitions/StaticDefinitionCache_Tests.cs new file mode 100644 index 0000000000..21ab9b8c59 --- /dev/null +++ b/framework/test/Volo.Abp.Core.Tests/Volo/Abp/StaticDefinitions/StaticDefinitionCache_Tests.cs @@ -0,0 +1,122 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Shouldly; +using Volo.Abp.Testing; +using Xunit; + +namespace Volo.Abp.StaticDefinitions; + +public class StaticDefinitionCache_Tests : AbpIntegratedTest +{ + protected readonly IStaticDefinitionCache> _staticDefinitionCache1; + protected readonly IStaticDefinitionCache> _staticDefinitionCache2; + + public StaticDefinitionCache_Tests() + { + _staticDefinitionCache1 = GetRequiredService>>(); + _staticDefinitionCache2 = GetRequiredService>>(); + } + + [Fact] + public async Task GetOrCreate_Test() + { + var definition1 = new StaticDefinition1 { Name = "Definition1", Value = 1 }; + var definition2 = new StaticDefinition1 { Name = "Definition2", Value = 2 }; + + var definitionsFirstRetrieval = await _staticDefinitionCache1.GetOrCreateAsync(() => + { + return Task.FromResult(new List { definition1, definition2 }); + }); + + var definitionsSecondRetrieval = await _staticDefinitionCache1.GetOrCreateAsync(() => + { + throw new AbpException("Factory should not be called on second retrieval"); + }); + + definitionsFirstRetrieval.ShouldBe(definitionsSecondRetrieval); + + definitionsSecondRetrieval.Count.ShouldBe(2); + + definitionsSecondRetrieval[0].Name.ShouldBe("Definition1"); + definitionsSecondRetrieval[0].Value.ShouldBe(1); + + definitionsSecondRetrieval[1].Name.ShouldBe("Definition2"); + definitionsSecondRetrieval[1].Value.ShouldBe(2); + } + + [Fact] + public async Task Separate_Caches_For_Different_Types_Test() + { + var definitions1 = await _staticDefinitionCache1.GetOrCreateAsync(() => + { + return Task.FromResult(new List { new StaticDefinition1 {Name = "Definition1", Value = 1} }); + }); + var definitions2 = await _staticDefinitionCache2.GetOrCreateAsync(() => + { + return Task.FromResult(new List { new StaticDefinition2 {Name = "DefinitionA", Value = 100} }); + }); + + definitions1.Count.ShouldBe(1); + definitions1[0].Name.ShouldBe("Definition1"); + definitions1[0].Value.ShouldBe(1); + + definitions2.Count.ShouldBe(1); + definitions2[0].Name.ShouldBe("DefinitionA"); + definitions2[0].Value.ShouldBe(100); + } + + [Fact] + public async Task Clear_Test() + { + var definitions1 = await _staticDefinitionCache1.GetOrCreateAsync(() => + { + return Task.FromResult(new List { new StaticDefinition1 {Name = "Definition1", Value = 1} }); + }); + var definitions2 = await _staticDefinitionCache2.GetOrCreateAsync(() => + { + return Task.FromResult(new List { new StaticDefinition2 {Name = "DefinitionA", Value = 100} }); + }); + + definitions1.Count.ShouldBe(1); + definitions1[0].Name.ShouldBe("Definition1"); + definitions1[0].Value.ShouldBe(1); + + definitions2.Count.ShouldBe(1); + definitions2[0].Name.ShouldBe("DefinitionA"); + definitions2[0].Value.ShouldBe(100); + + await _staticDefinitionCache1.ClearAsync(); + await _staticDefinitionCache2.ClearAsync(); + + var definitions1AfterClear = await _staticDefinitionCache1.GetOrCreateAsync(() => + { + return Task.FromResult(new List { new StaticDefinition1 {Name = "DefinitionNew", Value = 10} }); + }); + var definitions2AfterClear = await _staticDefinitionCache2.GetOrCreateAsync(() => + { + return Task.FromResult(new List {new StaticDefinition2 {Name = "DefinitionNewA", Value = 200}}); + }); + + definitions1AfterClear.Count.ShouldBe(1); + definitions1AfterClear[0].Name.ShouldBe("DefinitionNew"); + definitions1AfterClear[0].Value.ShouldBe(10); + + definitions2AfterClear.Count.ShouldBe(1); + definitions2AfterClear[0].Name.ShouldBe("DefinitionNewA"); + definitions2AfterClear[0].Value.ShouldBe(200); + } + + public class StaticDefinition1 + { + public string Name { get; set; } + + public int Value { get; set; } + } + + public class StaticDefinition2 + { + public string Name { get; set; } + + public int Value { get; set; } + } +} diff --git a/framework/test/Volo.Abp.Core.Tests/Volo/Abp/Threading/KeyedLock_Tests.cs b/framework/test/Volo.Abp.Core.Tests/Volo/Abp/Threading/KeyedLock_Tests.cs new file mode 100644 index 0000000000..1477bd7dd5 --- /dev/null +++ b/framework/test/Volo.Abp.Core.Tests/Volo/Abp/Threading/KeyedLock_Tests.cs @@ -0,0 +1,179 @@ +using System; +using System.Diagnostics; +using System.Threading; +using System.Linq; +using System.Threading.Tasks; +using Shouldly; +using Xunit; + +namespace Volo.Abp.Threading; + +public class KeyedLock_Tests +{ + [Fact] + public async Task TryLock_Should_Acquire_Immediately_When_Free() + { + var key = "key-try-1"; + var handle = await KeyedLock.TryLockAsync(key); + handle.ShouldNotBeNull(); + handle!.Dispose(); + + var handle2 = await KeyedLock.TryLockAsync(key); + handle2.ShouldNotBeNull(); + handle2!.Dispose(); + } + + [Fact] + public async Task TryLock_Should_Return_Null_When_Already_Locked() + { + var key = "key-try-2"; + using (await KeyedLock.LockAsync(key)) + { + var handle2 = await KeyedLock.TryLockAsync(key); + handle2.ShouldBeNull(); + } + + var handle3 = await KeyedLock.TryLockAsync(key); + handle3.ShouldNotBeNull(); + handle3!.Dispose(); + } + + [Fact] + public async Task LockAsync_Should_Block_Until_Released() + { + var key = "key-block-1"; + var sw = Stopwatch.StartNew(); + + Task inner; + using (await KeyedLock.LockAsync(key)) + { + inner = Task.Run(async () => + { + using (await KeyedLock.LockAsync(key)) + { + // Acquired only after outer lock is released + } + }); + + // While holding the outer lock, inner waiter should not complete + await Task.Delay(200); + inner.IsCompleted.ShouldBeFalse(); + } + + // After releasing, inner should complete; elapsed >= hold time + await inner; + sw.ElapsedMilliseconds.ShouldBeGreaterThanOrEqualTo(180); + } + + [Fact] + public async Task TryLock_With_Timeout_Should_Return_Null_When_Busy() + { + var key = "key-timeout-1"; + using (await KeyedLock.LockAsync(key)) + { + var handle = await KeyedLock.TryLockAsync(key, TimeSpan.FromMilliseconds(50)); + handle.ShouldBeNull(); + } + } + + [Fact] + public async Task TryLock_With_Timeout_Should_Succeed_If_Released_In_Time() + { + var key = "key-timeout-2"; + // Hold the lock manually + var outer = await KeyedLock.LockAsync(key); + var tryTask = KeyedLock.TryLockAsync(key, TimeSpan.FromMilliseconds(200)); + await Task.Delay(50); + // Release within the timeout window + outer.Dispose(); + var handle2 = await tryTask; + handle2.ShouldNotBeNull(); + handle2!.Dispose(); + } + + [Fact] + public async Task LockAsync_With_Cancellation_Should_Rollback_RefCount() + { + var key = "key-cancel-1"; + var cts = new CancellationTokenSource(); + await cts.CancelAsync(); + await Should.ThrowAsync(async () => + { + await KeyedLock.LockAsync(key, cts.Token); + }); + + // After cancellation, we should still be able to acquire the key + var handle = await KeyedLock.TryLockAsync(key); + handle.ShouldNotBeNull(); + handle!.Dispose(); + } + + [Fact] + public async Task TryLock_With_Cancellation_Should_Rollback() + { + var key = "key-cancel-2"; + // Ensure it's initially free + var h0 = await KeyedLock.TryLockAsync(key); + h0?.Dispose(); + + var cts = new CancellationTokenSource(); + await cts.CancelAsync(); + await Should.ThrowAsync(async () => + { + await KeyedLock.TryLockAsync(key, TimeSpan.FromMilliseconds(200), cts.Token); + }); + + // After cancellation, the key should be acquirable + var handle = await KeyedLock.TryLockAsync(key); + handle.ShouldNotBeNull(); + handle!.Dispose(); + } + + [Fact] + public async Task Serializes_Access_For_Same_Key() + { + var key = "key-serial-1"; + int counter = 0; + var tasks = Enumerable.Range(0, 10).Select(async _ => + { + using (await KeyedLock.LockAsync(key)) + { + var current = counter; + await Task.Delay(10); + counter = current + 1; + } + }); + + await Task.WhenAll(tasks); + counter.ShouldBe(10); + } + + [Fact] + public async Task Multiple_Keys_Should_Not_Block_Each_Other() + { + var key1 = "key-multi-1"; + var key2 = "key-multi-2"; + + using (await KeyedLock.LockAsync(key1)) + { + var handle2 = await KeyedLock.TryLockAsync(key2); + handle2.ShouldNotBeNull(); + handle2!.Dispose(); + } + } + + [Fact] + public async Task TryLock_Default_Overload_Delegates_To_Full_Overload() + { + var key = "key-default-1"; + using (await KeyedLock.LockAsync(key)) + { + var h1 = await KeyedLock.TryLockAsync(key); + h1.ShouldBeNull(); + } + + var h2 = await KeyedLock.TryLockAsync(key); + h2.ShouldNotBeNull(); + h2!.Dispose(); + } +} diff --git a/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Entities/EntityHelper_Tests.cs b/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Entities/EntityHelper_Tests.cs index 52ab34baee..adf82d74bd 100644 --- a/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Entities/EntityHelper_Tests.cs +++ b/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Entities/EntityHelper_Tests.cs @@ -55,6 +55,11 @@ public class EntityHelper_Tests { return new object[] { Id }; } + + public string GetObjectKey() + { + return Id.ToString(); + } } private class MyEntityDisablesIdGeneration : Entity diff --git a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/SoftDelete_Tests.cs b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/SoftDelete_Tests.cs index 1f403ade62..bc1b28357b 100644 --- a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/SoftDelete_Tests.cs +++ b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/SoftDelete_Tests.cs @@ -139,4 +139,26 @@ public abstract class SoftDelete_Tests : TestAppTestBase(async () => + { + await PersonRepository.DeleteAsync(douglas); + }); + + douglas = await PersonRepository.GetAsync(TestDataBuilder.UserDouglasId); + douglas.ConcurrencyStamp.ShouldNotBeNull(); + // Modify the entity to verify that a changed entity with a correct ConcurrencyStamp + // can still be soft deleted without raising a concurrency exception. + douglas.ChangeName("Changed Name"); + + // Try again with the correct ConcurrencyStamp - this will not throw an exception + await PersonRepository.DeleteAsync(douglas); + } } diff --git a/modules/basic-theme/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Account.cshtml b/modules/basic-theme/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Account.cshtml index 1e5f1b1fe7..5dc1619ec8 100644 --- a/modules/basic-theme/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Account.cshtml +++ b/modules/basic-theme/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Account.cshtml @@ -62,7 +62,7 @@
- +

@BrandingProvider.AppName

diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Tags/Default.cshtml b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Tags/Default.cshtml index 133e868a3f..7d568c4b0b 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Tags/Default.cshtml +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Tags/Default.cshtml @@ -6,26 +6,22 @@
@if (Model.Tags != null) { -
    +
    @foreach (var tag in Model.Tags) { if (Model.UrlFormat.IsNullOrWhiteSpace()) { -
  • - + @tag.Name -
  • } else { var formattedUrl = Model.UrlFormat.Replace("{TagId}", tag.Id.ToString()).Replace("{TagName}", tag.Name); -
  • - @tag.Name -
  • + @tag.Name } } -
+
}
\ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Blogs/BlogPost.cshtml b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Blogs/BlogPost.cshtml index e3f8fa1677..d94c8e199e 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Blogs/BlogPost.cshtml +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Blogs/BlogPost.cshtml @@ -60,13 +60,13 @@ -
+

@Model.ViewModel.Title

- - @@@Model.ViewModel.Author?.UserName + + @@@Model.ViewModel.Author?.UserName - @Model.ViewModel.CreationTime + @Model.ViewModel.CreationTime

@(await Component.InvokeAsync(typeof(ContentFragmentViewComponent), new DefaultContentDto { @@ -96,35 +96,41 @@ }
+ + +
+
+ + + @if (GlobalFeatureManager.Instance.IsEnabled()) + { + if (Model.RatingsFeature?.IsEnabled == true) + { + @await Component.InvokeAsync(typeof(RatingViewComponent), new + { + entityType = Volo.CmsKit.Blogs.BlogPostConsts.EntityType, + entityId = Model.ViewModel.Id.ToString() + }) + } + } + + + @if (GlobalFeatureManager.Instance.IsEnabled()) + { + if (Model.ReactionsFeature?.IsEnabled == true) + { + @await Component.InvokeAsync(typeof(ReactionSelectionViewComponent), new + { + entityType = Volo.CmsKit.Blogs.BlogPostConsts.EntityType, + entityId = Model.ViewModel.Id.ToString() + }) + } + } + + +
- - - @if (GlobalFeatureManager.Instance.IsEnabled()) - { - if (Model.ReactionsFeature?.IsEnabled == true) - { - @await Component.InvokeAsync(typeof(ReactionSelectionViewComponent), new - { - entityType = Volo.CmsKit.Blogs.BlogPostConsts.EntityType, - entityId = Model.ViewModel.Id.ToString() - }) - } - } - - - @if (GlobalFeatureManager.Instance.IsEnabled()) - { - if (Model.RatingsFeature?.IsEnabled == true) - { - @await Component.InvokeAsync(typeof(RatingViewComponent), new - { - entityType = Volo.CmsKit.Blogs.BlogPostConsts.EntityType, - entityId = Model.ViewModel.Id.ToString() - }) - } - } - - +
diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Blogs/blogPost.css b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Blogs/blogPost.css index 03ed4523e4..96e4520d9a 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Blogs/blogPost.css +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Blogs/blogPost.css @@ -42,4 +42,22 @@ span.area-title { color: #aaa; text-decoration: none; padding-left: 18px; +} + +.blockquote, blockquote{ + display: block; + padding: 1em 1.5em; + border-radius: 8px; + background: rgba(58, 130, 246, 0.1); + font-size: 1.125rem; +} + +code.hljs{ + border-radius: 8px; + background: rgba(247, 37, 133, 0.1); +} + +pre code.hljs{ + background: rgba(0, 0, 0, 0.1); + padding: 1em 1.5em; } \ No newline at end of file diff --git a/modules/cms-kit/test/Volo.CmsKit.MongoDB.Tests/MongoDB/CmsKitMongoDbTestModule.cs b/modules/cms-kit/test/Volo.CmsKit.MongoDB.Tests/MongoDB/CmsKitMongoDbTestModule.cs index 80ae993732..480d055cec 100644 --- a/modules/cms-kit/test/Volo.CmsKit.MongoDB.Tests/MongoDB/CmsKitMongoDbTestModule.cs +++ b/modules/cms-kit/test/Volo.CmsKit.MongoDB.Tests/MongoDB/CmsKitMongoDbTestModule.cs @@ -1,13 +1,15 @@ using System; using Volo.Abp.Data; using Volo.Abp.Modularity; +using Volo.Abp.SettingManagement.MongoDB; using Volo.Abp.Uow; namespace Volo.CmsKit.MongoDB; [DependsOn( typeof(CmsKitTestBaseModule), - typeof(CmsKitMongoDbModule) + typeof(CmsKitMongoDbModule), + typeof(AbpSettingManagementMongoDbModule) )] public class CmsKitMongoDbTestModule : AbpModule { diff --git a/modules/cms-kit/test/Volo.CmsKit.MongoDB.Tests/Volo.CmsKit.MongoDB.Tests.csproj b/modules/cms-kit/test/Volo.CmsKit.MongoDB.Tests/Volo.CmsKit.MongoDB.Tests.csproj index dfa0e199b5..15222835c2 100644 --- a/modules/cms-kit/test/Volo.CmsKit.MongoDB.Tests/Volo.CmsKit.MongoDB.Tests.csproj +++ b/modules/cms-kit/test/Volo.CmsKit.MongoDB.Tests/Volo.CmsKit.MongoDB.Tests.csproj @@ -11,6 +11,7 @@ +
diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/AbpFeatureManagementDomainModule.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/AbpFeatureManagementDomainModule.cs index 42ad413f3f..1c465817f2 100644 --- a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/AbpFeatureManagementDomainModule.cs +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/AbpFeatureManagementDomainModule.cs @@ -24,11 +24,14 @@ namespace Volo.Abp.FeatureManagement; )] public class AbpFeatureManagementDomainModule : AbpModule { + private readonly CancellationTokenSource _cancellationTokenSource = new(); + public override void ConfigureServices(ServiceConfigurationContext context) { Configure(options => { options.Providers.Add(); + options.Providers.Add(); options.Providers.Add(); //TODO: Should be moved to the Tenant Management module @@ -51,18 +54,16 @@ public class AbpFeatureManagementDomainModule : AbpModule } } - private readonly CancellationTokenSource _cancellationTokenSource = new(); - private Task _initializeDynamicFeaturesTask; - public override void OnApplicationInitialization(ApplicationInitializationContext context) { AsyncHelper.RunSync(() => OnApplicationInitializationAsync(context)); } - public override Task OnApplicationInitializationAsync(ApplicationInitializationContext context) + public override async Task OnApplicationInitializationAsync(ApplicationInitializationContext context) { - InitializeDynamicFeatures(context); - return Task.CompletedTask; + var rootServiceProvider = context.ServiceProvider.GetRequiredService(); + var initializer = rootServiceProvider.GetRequiredService(); + await initializer.InitializeAsync(true, _cancellationTokenSource.Token); } public override Task OnApplicationShutdownAsync(ApplicationShutdownContext context) @@ -70,123 +71,4 @@ public class AbpFeatureManagementDomainModule : AbpModule _cancellationTokenSource.Cancel(); return Task.CompletedTask; } - - public Task GetInitializeDynamicFeaturesTask() - { - return _initializeDynamicFeaturesTask ?? Task.CompletedTask; - } - - private void InitializeDynamicFeatures(ApplicationInitializationContext context) - { - var options = context - .ServiceProvider - .GetRequiredService>() - .Value; - - if (!options.SaveStaticFeaturesToDatabase && !options.IsDynamicFeatureStoreEnabled) - { - return; - } - - var rootServiceProvider = context.ServiceProvider.GetRequiredService(); - - _initializeDynamicFeaturesTask = Task.Run(async () => - { - using var scope = rootServiceProvider.CreateScope(); - var applicationLifetime = scope.ServiceProvider.GetService(); - var cancellationTokenProvider = scope.ServiceProvider.GetRequiredService(); - var cancellationToken = applicationLifetime?.ApplicationStopping ?? _cancellationTokenSource.Token; - - try - { - using (cancellationTokenProvider.Use(cancellationToken)) - { - if (cancellationTokenProvider.Token.IsCancellationRequested) - { - return; - } - - await SaveStaticFeaturesToDatabaseAsync(options, scope, cancellationTokenProvider); - - if (cancellationTokenProvider.Token.IsCancellationRequested) - { - return; - } - - await PreCacheDynamicFeaturesAsync(options, scope); - } - } - // ReSharper disable once EmptyGeneralCatchClause (No need to log since it is logged above) - catch { } - }); - } - - private static async Task SaveStaticFeaturesToDatabaseAsync( - FeatureManagementOptions options, - IServiceScope scope, - ICancellationTokenProvider cancellationTokenProvider) - { - if (!options.SaveStaticFeaturesToDatabase) - { - return; - } - - await Policy - .Handle() - .WaitAndRetryAsync( - 8, - retryAttempt => TimeSpan.FromSeconds( - RandomHelper.GetRandom( - (int)Math.Pow(2, retryAttempt) * 8, - (int)Math.Pow(2, retryAttempt) * 12) - ) - ) - .ExecuteAsync(async _ => - { - try - { - // ReSharper disable once AccessToDisposedClosure - await scope - .ServiceProvider - .GetRequiredService() - .SaveAsync(); - } - catch (Exception ex) - { - // ReSharper disable once AccessToDisposedClosure - scope.ServiceProvider - .GetService>()? - .LogException(ex); - - throw; // Polly will catch it - } - }, cancellationTokenProvider.Token); - } - - private static async Task PreCacheDynamicFeaturesAsync(FeatureManagementOptions options, IServiceScope scope) - { - if (!options.IsDynamicFeatureStoreEnabled) - { - return; - } - - try - { - // Pre-cache features, so first request doesn't wait - await scope - .ServiceProvider - .GetRequiredService() - .GetGroupsAsync(); - } - catch (Exception ex) - { - // ReSharper disable once AccessToDisposedClosure - scope - .ServiceProvider - .GetService>()? - .LogException(ex); - - throw; // It will be cached in InitializeDynamicFeatures - } - } } diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/ConfigurationFeatureManagementProvider.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/ConfigurationFeatureManagementProvider.cs new file mode 100644 index 0000000000..ac2b2e3e08 --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/ConfigurationFeatureManagementProvider.cs @@ -0,0 +1,44 @@ +using System; +using System.Threading.Tasks; +using Microsoft.Extensions.Configuration; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Features; + +namespace Volo.Abp.FeatureManagement; + +public class ConfigurationFeatureManagementProvider : IFeatureManagementProvider, ISingletonDependency +{ + public string Name => ConfigurationFeatureValueProvider.ProviderName; + + protected IConfiguration Configuration { get; } + + public ConfigurationFeatureManagementProvider(IConfiguration configuration) + { + Configuration = configuration; + } + + public virtual bool Compatible(string providerName) + { + return providerName == Name; + } + + public virtual Task HandleContextAsync(string providerName, string providerKey) + { + return Task.FromResult(NullAsyncDisposable.Instance); + } + + public virtual Task GetOrNullAsync(FeatureDefinition feature, string providerKey) + { + return Task.FromResult(Configuration[ConfigurationFeatureValueProvider.ConfigurationNamePrefix + feature.Name]); + } + + public virtual Task SetAsync(FeatureDefinition feature, string value, string providerKey) + { + throw new AbpException($"Can not set a feature value to the application configuration."); + } + + public virtual Task ClearAsync(FeatureDefinition feature, string providerKey) + { + throw new AbpException($"Can not set a feature value to the application configuration."); + } +} diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/ConfigurationValueFeatureManagerExtensions.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/ConfigurationValueFeatureManagerExtensions.cs new file mode 100644 index 0000000000..726bc4ddbd --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/ConfigurationValueFeatureManagerExtensions.cs @@ -0,0 +1,19 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using JetBrains.Annotations; +using Volo.Abp.Features; + +namespace Volo.Abp.FeatureManagement; + +public static class ConfigurationValueFeatureManagerExtensions +{ + public static Task GetOrNullConfigurationAsync(this IFeatureManager featureManager, [NotNull] string name, bool fallback = true) + { + return featureManager.GetOrNullAsync(name, ConfigurationFeatureValueProvider.ProviderName, null, fallback); + } + + public static Task> GetAllConfigurationAsync(this IFeatureManager featureManager, bool fallback = true) + { + return featureManager.GetAllAsync(ConfigurationFeatureValueProvider.ProviderName, null, fallback); + } +} diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureDynamicInitializer.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureDynamicInitializer.cs new file mode 100644 index 0000000000..32ed4cb646 --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureDynamicInitializer.cs @@ -0,0 +1,149 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.Options; +using Polly; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Features; +using Volo.Abp.Threading; + +namespace Volo.Abp.FeatureManagement; + +public class FeatureDynamicInitializer : ITransientDependency +{ + public ILogger Logger { get; set; } + + protected IServiceProvider ServiceProvider { get; } + protected IOptions Options { get; } + [CanBeNull] + protected IHostApplicationLifetime ApplicationLifetime { get; } + protected ICancellationTokenProvider CancellationTokenProvider { get; } + protected IDynamicFeatureDefinitionStore DynamicFeatureDefinitionStore { get; } + protected IStaticFeatureSaver StaticFeatureSaver { get; } + + public FeatureDynamicInitializer( + IServiceProvider serviceProvider, + IOptions options, + ICancellationTokenProvider cancellationTokenProvider, + IDynamicFeatureDefinitionStore dynamicFeatureDefinitionStore, + IStaticFeatureSaver staticFeatureSaver) + { + Logger = NullLogger.Instance; + + ServiceProvider = serviceProvider; + Options = options; + ApplicationLifetime = ServiceProvider.GetService(); + CancellationTokenProvider = cancellationTokenProvider; + DynamicFeatureDefinitionStore = dynamicFeatureDefinitionStore; + StaticFeatureSaver = staticFeatureSaver; + } + + public virtual Task InitializeAsync(bool runInBackground, CancellationToken cancellationToken = default) + { + var options = Options.Value; + + if (!options.SaveStaticFeaturesToDatabase && !options.IsDynamicFeatureStoreEnabled) + { + return Task.CompletedTask; + } + + if (runInBackground) + { + Task.Run(async () => + { + if (cancellationToken == default && ApplicationLifetime?.ApplicationStopping != null) + { + cancellationToken = ApplicationLifetime.ApplicationStopping; + } + await ExecuteInitializationAsync(options, cancellationToken); + }, cancellationToken); + return Task.CompletedTask; + } + + return ExecuteInitializationAsync(options, cancellationToken); + } + + protected virtual async Task ExecuteInitializationAsync(FeatureManagementOptions options, CancellationToken cancellationToken) + { + try + { + using (CancellationTokenProvider.Use(cancellationToken)) + { + if (CancellationTokenProvider.Token.IsCancellationRequested) + { + return; + } + + await SaveStaticFeaturesToDatabaseAsync(options, cancellationToken); + + if (CancellationTokenProvider.Token.IsCancellationRequested) + { + return; + } + + await PreCacheDynamicFeaturesAsync(options); + } + } + catch + { + // No need to log here since inner calls log + } + } + + protected virtual async Task SaveStaticFeaturesToDatabaseAsync( + FeatureManagementOptions options, + CancellationToken cancellationToken) + { + if (!options.SaveStaticFeaturesToDatabase) + { + return; + } + + await Policy + .Handle() + .WaitAndRetryAsync( + 8, + retryAttempt => TimeSpan.FromSeconds( + Volo.Abp.RandomHelper.GetRandom( + (int)Math.Pow(2, retryAttempt) * 8, + (int)Math.Pow(2, retryAttempt) * 12) + ) + ) + .ExecuteAsync(async _ => + { + try + { + await StaticFeatureSaver.SaveAsync(); + } + catch (Exception ex) + { + Logger.LogException(ex); + throw; // Polly will catch it + } + }, cancellationToken); + } + + protected virtual async Task PreCacheDynamicFeaturesAsync(FeatureManagementOptions options) + { + if (!options.IsDynamicFeatureStoreEnabled) + { + return; + } + + try + { + // Pre-cache features, so first request doesn't wait + await DynamicFeatureDefinitionStore.GetGroupsAsync(); + } + catch (Exception ex) + { + Logger.LogException(ex); + throw; // It will be cached in Initialize() + } + } +} diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/StaticFeatureDefinitionChangedEventHandler.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/StaticFeatureDefinitionChangedEventHandler.cs new file mode 100644 index 0000000000..b26b50d9b5 --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/StaticFeatureDefinitionChangedEventHandler.cs @@ -0,0 +1,36 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.EventBus; +using Volo.Abp.Features; +using Volo.Abp.StaticDefinitions; +using Volo.Abp.Threading; + +namespace Volo.Abp.FeatureManagement; + +public class StaticFeatureDefinitionChangedEventHandler : ILocalEventHandler, ITransientDependency +{ + protected IStaticDefinitionCache> GroupCache { get; } + protected IStaticDefinitionCache> DefinitionCache { get; } + protected FeatureDynamicInitializer FeatureDynamicInitializer { get; } + protected ICancellationTokenProvider CancellationTokenProvider { get; } + + public StaticFeatureDefinitionChangedEventHandler( + IStaticDefinitionCache> groupCache, + IStaticDefinitionCache> definitionCache, + FeatureDynamicInitializer featureDynamicInitializer, + ICancellationTokenProvider cancellationTokenProvider) + { + GroupCache = groupCache; + DefinitionCache = definitionCache; + FeatureDynamicInitializer = featureDynamicInitializer; + CancellationTokenProvider = cancellationTokenProvider; + } + + public virtual async Task HandleEventAsync(StaticFeatureDefinitionChangedEvent eventData) + { + await GroupCache.ClearAsync(); + await DefinitionCache.ClearAsync(); + await FeatureDynamicInitializer.InitializeAsync(false, CancellationTokenProvider.Token); + } +} diff --git a/modules/feature-management/test/Volo.Abp.FeatureManagement.EntityFrameworkCore.Tests/Volo/Abp/FeatureManagement/EntityFrameworkCore/AbpFeatureManagementEntityFrameworkCoreTestModule.cs b/modules/feature-management/test/Volo.Abp.FeatureManagement.EntityFrameworkCore.Tests/Volo/Abp/FeatureManagement/EntityFrameworkCore/AbpFeatureManagementEntityFrameworkCoreTestModule.cs index c370e5f558..71254e009f 100644 --- a/modules/feature-management/test/Volo.Abp.FeatureManagement.EntityFrameworkCore.Tests/Volo/Abp/FeatureManagement/EntityFrameworkCore/AbpFeatureManagementEntityFrameworkCoreTestModule.cs +++ b/modules/feature-management/test/Volo.Abp.FeatureManagement.EntityFrameworkCore.Tests/Volo/Abp/FeatureManagement/EntityFrameworkCore/AbpFeatureManagementEntityFrameworkCoreTestModule.cs @@ -4,6 +4,7 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Storage; using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.DependencyInjection; using Volo.Abp.EntityFrameworkCore; using Volo.Abp.EntityFrameworkCore.Sqlite; using Volo.Abp.Modularity; @@ -53,15 +54,8 @@ public class AbpFeatureManagementEntityFrameworkCoreTestModule : AbpModule public override void OnApplicationInitialization(ApplicationInitializationContext context) { - var task = context.ServiceProvider.GetRequiredService().GetInitializeDynamicFeaturesTask(); - if (!task.IsCompleted) - { - AsyncHelper.RunSync(() => Awaited(task)); - } - } - - private async static Task Awaited(Task task) - { - await task; + var rootServiceProvider = context.ServiceProvider.GetRequiredService(); + var initializer = rootServiceProvider.GetRequiredService(); + AsyncHelper.RunSync(() => initializer.InitializeAsync(false)); } } diff --git a/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/Integration/IIdentityUserIntegrationService.cs b/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/Integration/IIdentityUserIntegrationService.cs index 815c1c48c9..3a61407ddf 100644 --- a/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/Integration/IIdentityUserIntegrationService.cs +++ b/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/Integration/IIdentityUserIntegrationService.cs @@ -10,12 +10,20 @@ namespace Volo.Abp.Identity.Integration; public interface IIdentityUserIntegrationService : IApplicationService { Task GetRoleNamesAsync(Guid id); - + Task FindByIdAsync(Guid id); Task FindByUserNameAsync(string userName); Task> SearchAsync(UserLookupSearchInputDto input); + Task> SearchByIdsAsync(Guid[] ids); + Task GetCountAsync(UserLookupCountInputDto input); -} \ No newline at end of file + + Task> SearchRoleAsync(RoleLookupSearchInputDto input); + + Task> SearchRoleByNamesAsync(string[] ids); + + Task GetRoleCountAsync(RoleLookupCountInputDto input); +} diff --git a/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/RoleLookupCountInputDto.cs b/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/RoleLookupCountInputDto.cs new file mode 100644 index 0000000000..875d350f1c --- /dev/null +++ b/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/RoleLookupCountInputDto.cs @@ -0,0 +1,6 @@ +namespace Volo.Abp.Identity; + +public class RoleLookupCountInputDto +{ + public string Filter { get; set; } +} diff --git a/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/RoleLookupSearchInputDto.cs b/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/RoleLookupSearchInputDto.cs new file mode 100644 index 0000000000..ebc1707055 --- /dev/null +++ b/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/RoleLookupSearchInputDto.cs @@ -0,0 +1,8 @@ +using Volo.Abp.Application.Dtos; + +namespace Volo.Abp.Identity; + +public class RoleLookupSearchInputDto : ExtensiblePagedAndSortedResultRequestDto +{ + public string Filter { get; set; } +} diff --git a/modules/identity/src/Volo.Abp.Identity.Application/Volo/Abp/Identity/Integration/IdentityUserIntegrationService.cs b/modules/identity/src/Volo.Abp.Identity.Application/Volo/Abp/Identity/Integration/IdentityUserIntegrationService.cs index 37446ce1f3..4019cd9031 100644 --- a/modules/identity/src/Volo.Abp.Identity.Application/Volo/Abp/Identity/Integration/IdentityUserIntegrationService.cs +++ b/modules/identity/src/Volo.Abp.Identity.Application/Volo/Abp/Identity/Integration/IdentityUserIntegrationService.cs @@ -2,6 +2,7 @@ using System.Linq; using System.Threading.Tasks; using Volo.Abp.Application.Dtos; +using Volo.Abp.Domain.Repositories; using Volo.Abp.Users; namespace Volo.Abp.Identity.Integration; @@ -10,13 +11,19 @@ public class IdentityUserIntegrationService : IdentityAppServiceBase, IIdentityU { protected IUserRoleFinder UserRoleFinder { get; } protected IdentityUserRepositoryExternalUserLookupServiceProvider UserLookupServiceProvider { get; } + protected IIdentityUserRepository UserRepository { get; } + protected IIdentityRoleRepository RoleRepository { get; } public IdentityUserIntegrationService( IUserRoleFinder userRoleFinder, - IdentityUserRepositoryExternalUserLookupServiceProvider userLookupServiceProvider) + IdentityUserRepositoryExternalUserLookupServiceProvider userLookupServiceProvider, + IIdentityUserRepository userRepository, + IIdentityRoleRepository roleRepository) { UserRoleFinder = userRoleFinder; UserLookupServiceProvider = userLookupServiceProvider; + UserRepository = userRepository; + RoleRepository = roleRepository; } public virtual async Task GetRoleNamesAsync(Guid id) @@ -62,8 +69,53 @@ public class IdentityUserIntegrationService : IdentityAppServiceBase, IIdentityU ); } + public virtual async Task> SearchByIdsAsync(Guid[] ids) + { + var users = await UserRepository.GetListByIdsAsync(ids); + + return new ListResultDto( + users + .Select(u => new UserData( + u.Id, + u.UserName, + u.Email, + u.Name, + u.Surname, + u.EmailConfirmed, + u.PhoneNumber, + u.PhoneNumberConfirmed, + u.TenantId, + u.IsActive, + u.ExtraProperties)) + .ToList() + ); + } + public virtual async Task GetCountAsync(UserLookupCountInputDto input) { return await UserLookupServiceProvider.GetCountAsync(input.Filter); } + + public virtual async Task> SearchRoleAsync(RoleLookupSearchInputDto input) + { + using (RoleRepository.DisableTracking()) + { + var roles = await RoleRepository.GetListAsync(input.Filter); + return new ListResultDto(roles.Select(r => new RoleData(r.Id, r.Name, r.IsDefault, r.IsStatic, r.IsPublic, r.TenantId, r.ExtraProperties)).ToList()); + } + } + + public virtual async Task> SearchRoleByNamesAsync(string[] names) + { + using (RoleRepository.DisableTracking()) + { + var roles = await RoleRepository.GetListAsync(names); + return new ListResultDto(roles.Select(r => new RoleData(r.Id, r.Name, r.IsDefault, r.IsStatic, r.IsPublic, r.TenantId, r.ExtraProperties)).ToList()); + } + } + + public virtual async Task GetRoleCountAsync(RoleLookupCountInputDto input) + { + return await RoleRepository.GetCountAsync(input.Filter); + } } diff --git a/modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/AbpSignInManager.cs b/modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/AbpSignInManager.cs index f4b4a2d35a..09f3e4bf24 100644 --- a/modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/AbpSignInManager.cs +++ b/modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/AbpSignInManager.cs @@ -5,9 +5,7 @@ using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -using Volo.Abp.Identity.Settings; using Volo.Abp.Settings; -using Volo.Abp.Timing; namespace Volo.Abp.Identity.AspNetCore; @@ -42,7 +40,7 @@ public class AbpSignInManager : SignInManager _identityUserManager = userManager; } - public async override Task PasswordSignInAsync( + public override async Task PasswordSignInAsync( string userName, string password, bool isPersistent, @@ -86,7 +84,17 @@ public class AbpSignInManager : SignInManager return await base.PasswordSignInAsync(userName, password, isPersistent, lockoutOnFailure); } - protected async override Task PreSignInCheck(IdentityUser user) + /// + /// This is to call the protection method PreSignInCheck + /// + /// The user + /// Null if the user should be allowed to sign in, otherwise the SignInResult why they should be denied. + public virtual async Task CallPreSignInCheckAsync(IdentityUser user) + { + return await PreSignInCheck(user); + } + + protected override async Task PreSignInCheck(IdentityUser user) { if (!user.IsActive) { diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IUserRoleFinder.cs b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IUserRoleFinder.cs index 1835f38952..75261b2cc4 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IUserRoleFinder.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IUserRoleFinder.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; namespace Volo.Abp.Identity; @@ -7,6 +9,14 @@ public interface IUserRoleFinder { [Obsolete("Use GetRoleNamesAsync instead.")] Task GetRolesAsync(Guid userId); - + Task GetRoleNamesAsync(Guid userId); + + Task> SearchUserAsync(string filter, int page = 1); + + Task> SearchRoleAsync(string filter, int page = 1); + + Task> SearchUserByIdsAsync(Guid[] ids); + + Task> SearchRoleByNamesAsync(string[] names); } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityUserPasskeyConsts.cs b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityUserPasskeyConsts.cs new file mode 100644 index 0000000000..8f0971d315 --- /dev/null +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityUserPasskeyConsts.cs @@ -0,0 +1,14 @@ +namespace Volo.Abp.Identity; + +public static class IdentityUserPasskeyConsts +{ + /// + /// Default value: 256 + /// + public static int MaxPassKeyNameLength { get; set; } = 256; + + /// + /// Default value: 1024 + /// + public static int MaxCredentialIdLength { get; set; } = 1024; +} diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/ar.json b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/ar.json index 49b22071cd..71b858f834 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/ar.json +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/ar.json @@ -135,6 +135,8 @@ "ModificationTime": "وقت التعديل", "PasswordUpdateTime": "وقت تحديث كلمة المرور", "LockoutEndTime": "وقت انتهاء القفل", - "FailedAccessCount": "فشل عدد الوصول" + "FailedAccessCount": "فشل عدد الوصول", + "UserResourcePermissionProviderKeyLookupService": "المستخدم", + "RoleResourcePermissionProviderKeyLookupService": "الدور" } } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/cs.json b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/cs.json index 8f0456c601..e84320059e 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/cs.json +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/cs.json @@ -135,6 +135,8 @@ "ModificationTime": "Doba úpravy", "PasswordUpdateTime": "Čas aktualizace hesla", "LockoutEndTime": "Čas konce uzamčení", - "FailedAccessCount": "Počet neúspěšných přístupů" + "FailedAccessCount": "Počet neúspěšných přístupů", + "UserResourcePermissionProviderKeyLookupService": "Uživatel", + "RoleResourcePermissionProviderKeyLookupService": "Role" } } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/de.json b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/de.json index 19daf4d440..4064866f00 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/de.json +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/de.json @@ -135,6 +135,8 @@ "LockoutEndTime": "Endzeit der Sperrung", "FailedAccessCount": "Anzahl der fehlgeschlagenen Zugriffe", "DisplayName:Abp.Identity.SignIn.RequireEmailVerificationToRegister": "E-Mail-Verifizierung für die Registrierung erzwingen", - "Description:Abp.Identity.SignIn.RequireEmailVerificationToRegister": "Benutzerkonten werden nicht erstellt, es sei denn, sie verifizieren ihre E-Mail-Adressen." + "Description:Abp.Identity.SignIn.RequireEmailVerificationToRegister": "Benutzerkonten werden nicht erstellt, es sei denn, sie verifizieren ihre E-Mail-Adressen.", + "UserResourcePermissionProviderKeyLookupService": "Benutzer", + "RoleResourcePermissionProviderKeyLookupService": "Rolle" } } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/el.json b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/el.json index 1e2c8a23ce..ba610a993a 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/el.json +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/el.json @@ -128,6 +128,8 @@ "Description:Abp.Identity.User.IsEmailUpdateEnabled": "Εάν το email μπορεί να ενημερωθεί από τον χρήστη.", "DisplayName:Abp.Identity.SignIn.RequireEmailVerificationToRegister": "Επιβολή επαλήθευσης email για εγγραφή", "Description:Abp.Identity.SignIn.RequireEmailVerificationToRegister": "Οι λογαριασμοί χρηστών δεν θα δημιουργηθούν εκτός αν επαληθεύσουν τις διευθύνσεις email τους.", - "Details": "Λεπτομέρειες" + "Details": "Λεπτομέρειες", + "UserResourcePermissionProviderKeyLookupService": "Χρήστης", + "RoleResourcePermissionProviderKeyLookupService": "Ρόλος" } } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/en-GB.json b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/en-GB.json index 45f25bc864..9f10e1f188 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/en-GB.json +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/en-GB.json @@ -137,6 +137,8 @@ "Description:Abp.Identity.UsersCanChange": "Allow users to change their Two Factor.", "DisplayName:Abp.Identity.SignIn.RequireEmailVerificationToRegister": "Enforce email verification to register", "Description:Abp.Identity.SignIn.RequireEmailVerificationToRegister": "User accounts will not be created unless they verify their email addresses.", - "Details": "Details" + "Details": "Details", + "UserResourcePermissionProviderKeyLookupService": "User", + "RoleResourcePermissionProviderKeyLookupService": "Role" } } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/en.json b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/en.json index f2cc74cef0..0deb6a4b56 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/en.json +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/en.json @@ -135,6 +135,8 @@ "ModificationTime": "Modification time", "PasswordUpdateTime": "Password update time", "LockoutEndTime": "Lockout end time", - "FailedAccessCount": "Failed access count" + "FailedAccessCount": "Failed access count", + "UserResourcePermissionProviderKeyLookupService": "User", + "RoleResourcePermissionProviderKeyLookupService": "Role" } } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/es.json b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/es.json index ff4d4302f8..a2a9414214 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/es.json +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/es.json @@ -135,6 +135,8 @@ "ModificationTime": "tiempo de modificación", "PasswordUpdateTime": "Hora de actualización de contraseña", "LockoutEndTime": "Hora de finalización del bloqueo", - "FailedAccessCount": "Recuento de acceso fallido" + "FailedAccessCount": "Recuento de acceso fallido", + "UserResourcePermissionProviderKeyLookupService": "Usuario", + "RoleResourcePermissionProviderKeyLookupService": "Rol" } } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/fa.json b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/fa.json index 024b443fec..e9a1911b52 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/fa.json +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/fa.json @@ -139,6 +139,8 @@ "Description:Abp.Identity.SignIn.RequireConfirmedEmail": "کاربران می‌توانند حساب ایجاد کنند اما تا زمانی که آدرس ایمیل خود را تایید نکنند نمی‌توانند وارد شوند.", "Description:Abp.Identity.SignIn.RequireConfirmedPhoneNumber": "کاربران می‌توانند حساب ایجاد کنند اما تا زمانی که شماره تلفن خود را تایید نکنند نمی‌توانند وارد شوند.", "DisplayName:Abp.Identity.SignIn.RequireEmailVerificationToRegister": "اجباری کردن تایید ایمیل برای ثبت‌نام", - "Description:Abp.Identity.SignIn.RequireEmailVerificationToRegister": "حساب‌های کاربری ایجاد نخواهند شد مگر اینکه آدرس ایمیل خود را تایید کنند." + "Description:Abp.Identity.SignIn.RequireEmailVerificationToRegister": "حساب‌های کاربری ایجاد نخواهند شد مگر اینکه آدرس ایمیل خود را تایید کنند.", + "UserResourcePermissionProviderKeyLookupService": "کاربر", + "RoleResourcePermissionProviderKeyLookupService": "نقش" } } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/fi.json b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/fi.json index 71cfe15712..2a37974cbb 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/fi.json +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/fi.json @@ -135,6 +135,8 @@ "ModificationTime": "Muutosaika", "PasswordUpdateTime": "Salasanan päivityksen aika", "LockoutEndTime": "Lukituksen päättymisaika", - "FailedAccessCount": "Epäonnistuneet käyttöoikeudet" + "FailedAccessCount": "Epäonnistuneet käyttöoikeudet", + "UserResourcePermissionProviderKeyLookupService": "Käyttäjä", + "RoleResourcePermissionProviderKeyLookupService": "Rooli" } } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/fr.json b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/fr.json index 61bc1dea98..323bb5927a 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/fr.json +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/fr.json @@ -135,6 +135,8 @@ "LockoutEndTime": "Heure de fin du verrouillage", "FailedAccessCount": "Nombre d'accès ayant échoué", "DisplayName:Abp.Identity.SignIn.RequireEmailVerificationToRegister": "Exiger la vérification de l'e-mail pour s'inscrire", - "Description:Abp.Identity.SignIn.RequireEmailVerificationToRegister": "Les comptes d’utilisateurs ne seront pas créés à moins qu’ils ne vérifient leurs adresses e-mail." + "Description:Abp.Identity.SignIn.RequireEmailVerificationToRegister": "Les comptes d’utilisateurs ne seront pas créés à moins qu’ils ne vérifient leurs adresses e-mail.", + "UserResourcePermissionProviderKeyLookupService": "Utilisateur", + "RoleResourcePermissionProviderKeyLookupService": "Rôle" } } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/hi.json b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/hi.json index 464426cf34..0d5fca3dde 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/hi.json +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/hi.json @@ -135,6 +135,8 @@ "ModificationTime": "संशोधन का समय", "PasswordUpdateTime": "पासवर्ड अद्यतन समय", "LockoutEndTime": "तालाबंदी समाप्ति समय", - "FailedAccessCount": "विफल पहुंच गणना" + "FailedAccessCount": "विफल पहुंच गणना", + "UserResourcePermissionProviderKeyLookupService": "उपयोगकर्ता", + "RoleResourcePermissionProviderKeyLookupService": "भूमिका" } } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/hr.json b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/hr.json index e8af421629..0e73aa5f1e 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/hr.json +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/hr.json @@ -135,6 +135,8 @@ "ModificationTime": "Vrijeme izmjene", "PasswordUpdateTime": "Vrijeme ažuriranja lozinke", "LockoutEndTime": "Vrijeme završetka zaključavanja", - "FailedAccessCount": "Broj neuspjelih pristupa" + "FailedAccessCount": "Broj neuspjelih pristupa", + "UserResourcePermissionProviderKeyLookupService": "Korisnik", + "RoleResourcePermissionProviderKeyLookupService": "Uloga" } } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/hu.json b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/hu.json index 935a59e2f5..730a831d23 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/hu.json +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/hu.json @@ -135,6 +135,8 @@ "ModificationTime": "Módosítási idő", "PasswordUpdateTime": "A jelszó frissítési ideje", "LockoutEndTime": "A zárolás befejezési ideje", - "FailedAccessCount": "Sikertelen hozzáférések száma" + "FailedAccessCount": "Sikertelen hozzáférések száma", + "UserResourcePermissionProviderKeyLookupService": "Felhasználó", + "RoleResourcePermissionProviderKeyLookupService": "Szerep" } } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/is.json b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/is.json index ebf43ceab3..da53a10657 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/is.json +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/is.json @@ -135,6 +135,8 @@ "ModificationTime": "Breytingartími", "PasswordUpdateTime": "Uppfærslutími lykilorðs", "LockoutEndTime": "Lokatími lokunar", - "FailedAccessCount": "Misheppnuð fjöldi aðgangs" + "FailedAccessCount": "Misheppnuð fjöldi aðgangs", + "UserResourcePermissionProviderKeyLookupService": "Notandi", + "RoleResourcePermissionProviderKeyLookupService": "Hlutverk" } } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/it.json b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/it.json index 1d1d9771fd..163ff98754 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/it.json +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/it.json @@ -135,6 +135,8 @@ "LockoutEndTime": "Ora di fine del blocco", "FailedAccessCount": "Conteggio accessi non riusciti", "DisplayName:Abp.Identity.SignIn.RequireEmailVerificationToRegister": "Richiedi verifica email per la registrazione", - "Description:Abp.Identity.SignIn.RequireEmailVerificationToRegister": "Gli account utente non verranno creati a meno che non verifichino i loro indirizzi email." + "Description:Abp.Identity.SignIn.RequireEmailVerificationToRegister": "Gli account utente non verranno creati a meno che non verifichino i loro indirizzi email.", + "UserResourcePermissionProviderKeyLookupService": "Utente", + "RoleResourcePermissionProviderKeyLookupService": "Ruolo" } } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/nl.json b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/nl.json index 2b405697ae..9836137c80 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/nl.json +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/nl.json @@ -135,6 +135,8 @@ "LockoutEndTime": "Eindtijd uitsluiting", "FailedAccessCount": "Aantal mislukte toegangen", "DisplayName:Abp.Identity.SignIn.RequireEmailVerificationToRegister": "E-mailverificatie vereisen voor registratie", - "Description:Abp.Identity.SignIn.RequireEmailVerificationToRegister": "Gebruikersaccounts worden niet aangemaakt tenzij ze hun e-mailadressen verifiëren." + "Description:Abp.Identity.SignIn.RequireEmailVerificationToRegister": "Gebruikersaccounts worden niet aangemaakt tenzij ze hun e-mailadressen verifiëren.", + "UserResourcePermissionProviderKeyLookupService": "Gebruiker", + "RoleResourcePermissionProviderKeyLookupService": "Rol" } } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/pl-PL.json b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/pl-PL.json index c3c8053556..18f33d89c1 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/pl-PL.json +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/pl-PL.json @@ -135,6 +135,8 @@ "LockoutEndTime": "Czas zakończenia blokady", "FailedAccessCount": "Liczba nieudanych dostępów", "DisplayName:Abp.Identity.SignIn.RequireEmailVerificationToRegister": "Wymagaj weryfikacji e-mail do rejestracji", - "Description:Abp.Identity.SignIn.RequireEmailVerificationToRegister": "Konta użytkowników nie zostaną utworzone, dopóki nie zweryfikują swoich adresów e-mail." + "Description:Abp.Identity.SignIn.RequireEmailVerificationToRegister": "Konta użytkowników nie zostaną utworzone, dopóki nie zweryfikują swoich adresów e-mail.", + "UserResourcePermissionProviderKeyLookupService": "Użytkownik", + "RoleResourcePermissionProviderKeyLookupService": "Rola" } } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/pt-BR.json b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/pt-BR.json index 40ef80556f..cd796edbde 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/pt-BR.json +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/pt-BR.json @@ -135,6 +135,8 @@ "ModificationTime": "Hora da modificação", "PasswordUpdateTime": "Hora de atualização da senha", "LockoutEndTime": "Hora de término do bloqueio", - "FailedAccessCount": "Contagem de acessos com falha" + "FailedAccessCount": "Contagem de acessos com falha", + "UserResourcePermissionProviderKeyLookupService": "Usuário", + "RoleResourcePermissionProviderKeyLookupService": "Perfil" } } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/ro-RO.json b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/ro-RO.json index efe0c53541..3ebc98d179 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/ro-RO.json +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/ro-RO.json @@ -135,6 +135,8 @@ "ModificationTime": "Timp de modificare", "PasswordUpdateTime": "Ora actualizării parolei", "LockoutEndTime": "Ora de încheiere a blocării", - "FailedAccessCount": "Număr de acces eșuat" + "FailedAccessCount": "Număr de acces eșuat", + "UserResourcePermissionProviderKeyLookupService": "Utilizator", + "RoleResourcePermissionProviderKeyLookupService": "Rol" } } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/ru.json b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/ru.json index 6afaf14b1a..f9ddb814d4 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/ru.json +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/ru.json @@ -135,6 +135,8 @@ "LockoutEndTime": "Время окончания блокировки", "FailedAccessCount": "Количество неудачных попыток доступа", "DisplayName:Abp.Identity.SignIn.RequireEmailVerificationToRegister": "Требовать подтверждение электронной почты для регистрации", - "Description:Abp.Identity.SignIn.RequireEmailVerificationToRegister": "Учетные записи пользователей не будут созданы, пока они не подтвердят свои адреса электронной почты." + "Description:Abp.Identity.SignIn.RequireEmailVerificationToRegister": "Учетные записи пользователей не будут созданы, пока они не подтвердят свои адреса электронной почты.", + "UserResourcePermissionProviderKeyLookupService": "Пользователь", + "RoleResourcePermissionProviderKeyLookupService": "Роль" } } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/sk.json b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/sk.json index e529910a5e..26486aa6ab 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/sk.json +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/sk.json @@ -135,6 +135,8 @@ "ModificationTime": "Čas úpravy", "PasswordUpdateTime": "Čas aktualizácie hesla", "LockoutEndTime": "Čas ukončenia uzamknutia", - "FailedAccessCount": "Počet neúspešných prístupov" + "FailedAccessCount": "Počet neúspešných prístupov", + "UserResourcePermissionProviderKeyLookupService": "Používateľ", + "RoleResourcePermissionProviderKeyLookupService": "Rola" } } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/sl.json b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/sl.json index c02577c385..8ae2a2f81b 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/sl.json +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/sl.json @@ -135,6 +135,8 @@ "ModificationTime": "Čas spreminjanja", "PasswordUpdateTime": "Čas posodobitve gesla", "LockoutEndTime": "Končni čas zaklepanja", - "FailedAccessCount": "Število neuspešnih dostopov" + "FailedAccessCount": "Število neuspešnih dostopov", + "UserResourcePermissionProviderKeyLookupService": "Uporabnik", + "RoleResourcePermissionProviderKeyLookupService": "Vloga" } } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/sv.json b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/sv.json index 166f32a10a..aef65e2b11 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/sv.json +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/sv.json @@ -135,6 +135,8 @@ "ModificationTime": "Tid för modifiering", "PasswordUpdateTime": "Uppdateringstid för lösenord", "LockoutEndTime": "Sluttid för låsning", - "FailedAccessCount": "Antal misslyckade åtkomster" + "FailedAccessCount": "Antal misslyckade åtkomster", + "UserResourcePermissionProviderKeyLookupService": "Användare", + "RoleResourcePermissionProviderKeyLookupService": "Roll" } } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/tr.json b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/tr.json index 2da62b0355..0a6e4d393c 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/tr.json +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/tr.json @@ -135,6 +135,8 @@ "LockoutEndTime": "Kilitlenme bitiş zamanı", "FailedAccessCount": "Başarısız giriş denemesi sayısı", "DisplayName:Abp.Identity.SignIn.RequireEmailVerificationToRegister": "Kayıt için e-posta doğrulaması zorunlu kıl", - "Description:Abp.Identity.SignIn.RequireEmailVerificationToRegister": "Kullanıcı hesapları, e-posta adreslerini doğrulamadıkça oluşturulmayacaktır." + "Description:Abp.Identity.SignIn.RequireEmailVerificationToRegister": "Kullanıcı hesapları, e-posta adreslerini doğrulamadıkça oluşturulmayacaktır.", + "UserResourcePermissionProviderKeyLookupService": "Kullanıcı", + "RoleResourcePermissionProviderKeyLookupService": "Rol" } } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/vi.json b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/vi.json index a98baf6566..32c1dfe008 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/vi.json +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/vi.json @@ -135,6 +135,8 @@ "LockoutEndTime": "Thời gian kết thúc khóa", "FailedAccessCount": "Số lượt truy cập không thành công", "DisplayName:Abp.Identity.SignIn.RequireEmailVerificationToRegister": "Yêu cầu xác minh email để đăng ký", - "Description:Abp.Identity.SignIn.RequireEmailVerificationToRegister": "Tài khoản người dùng sẽ không được tạo ra trừ khi họ xác minh địa chỉ email của mình." + "Description:Abp.Identity.SignIn.RequireEmailVerificationToRegister": "Tài khoản người dùng sẽ không được tạo ra trừ khi họ xác minh địa chỉ email của mình.", + "UserResourcePermissionProviderKeyLookupService": "Người dùng", + "RoleResourcePermissionProviderKeyLookupService": "Vai trò" } } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/zh-Hans.json b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/zh-Hans.json index 0cfc117cf6..3a1239ddc7 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/zh-Hans.json +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/zh-Hans.json @@ -135,6 +135,8 @@ "ModificationTime": "修改时间", "PasswordUpdateTime": "密码更新时间", "LockoutEndTime": "锁定结束时间", - "FailedAccessCount": "访问失败次数" + "FailedAccessCount": "访问失败次数", + "UserResourcePermissionProviderKeyLookupService": "用户", + "RoleResourcePermissionProviderKeyLookupService": "角色" } } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/zh-Hant.json b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/zh-Hant.json index 39f6fe5267..a28cc6c7d4 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/zh-Hant.json +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/zh-Hant.json @@ -135,6 +135,8 @@ "LockoutEndTime": "鎖定結束時間", "FailedAccessCount": "訪問失敗次數", "DisplayName:Abp.Identity.SignIn.RequireEmailVerificationToRegister": "強制要求驗證電子郵件才能註冊", - "Description:Abp.Identity.SignIn.RequireEmailVerificationToRegister": "除非驗證他們的電子郵件地址,否則不會創建用戶帳戶。" + "Description:Abp.Identity.SignIn.RequireEmailVerificationToRegister": "除非驗證他們的電子郵件地址,否則不會創建用戶帳戶。", + "UserResourcePermissionProviderKeyLookupService": "使用者", + "RoleResourcePermissionProviderKeyLookupService": "角色" } } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/RoleFinderResult.cs b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/RoleFinderResult.cs new file mode 100644 index 0000000000..8fe118f13d --- /dev/null +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/RoleFinderResult.cs @@ -0,0 +1,10 @@ +using System; + +namespace Volo.Abp.Identity; + +public class RoleFinderResult +{ + public Guid Id { get; set; } + + public string RoleName { get; set; } +} diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/UserFinderResult.cs b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/UserFinderResult.cs new file mode 100644 index 0000000000..48ba5ace7a --- /dev/null +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/UserFinderResult.cs @@ -0,0 +1,10 @@ +using System; + +namespace Volo.Abp.Identity; + +public class UserFinderResult +{ + public Guid Id { get; set; } + + public string UserName { get; set; } +} diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IIdentityRoleRepository.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IIdentityRoleRepository.cs index a9eefe019e..26bd8624b9 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IIdentityRoleRepository.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IIdentityRoleRepository.cs @@ -31,11 +31,17 @@ public interface IIdentityRoleRepository : IBasicRepository bool includeDetails = false, CancellationToken cancellationToken = default ); + Task> GetListAsync( IEnumerable ids, CancellationToken cancellationToken = default ); + Task> GetListAsync( + IEnumerable names, + CancellationToken cancellationToken = default + ); + Task> GetDefaultOnesAsync( bool includeDetails = false, CancellationToken cancellationToken = default diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IIdentityUserRepository.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IIdentityUserRepository.cs index 8da3237f83..33ec2eab95 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IIdentityUserRepository.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IIdentityUserRepository.cs @@ -160,4 +160,9 @@ public interface IIdentityUserRepository : IBasicRepository Task> GetRoleNamesAsync( IEnumerable userIds, CancellationToken cancellationToken = default); + + Task FindByPasskeyIdAsync( + byte[] credentialId, + bool includeDetails = true, + CancellationToken cancellationToken = default); } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityPasskeyData.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityPasskeyData.cs new file mode 100644 index 0000000000..8c26227741 --- /dev/null +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityPasskeyData.cs @@ -0,0 +1,68 @@ +using System; + +namespace Volo.Abp.Identity; + +/// +/// Represents data associated with a passkey. +/// +public class IdentityPasskeyData +{ + /// + /// Gets or sets the public key associated with this passkey. + /// + public virtual byte[] PublicKey { get; set; } + + /// + /// Gets or sets the friendly name for this passkey. + /// + public virtual string? Name { get; set; } + + /// + /// Gets or sets the time this passkey was created. + /// + public virtual DateTimeOffset CreatedAt { get; set; } + + /// + /// Gets or sets the signature counter for this passkey. + /// + public virtual uint SignCount { get; set; } + + /// + /// Gets or sets the transports supported by this passkey. + /// + /// + /// See . + /// + public virtual string[] Transports { get; set; } + + /// + /// Gets or sets whether the passkey has a verified user. + /// + public virtual bool IsUserVerified { get; set; } + + /// + /// Gets or sets whether the passkey is eligible for backup. + /// + public virtual bool IsBackupEligible { get; set; } + + /// + /// Gets or sets whether the passkey is currently backed up. + /// + public virtual bool IsBackedUp { get; set; } + + /// + /// Gets or sets the attestation object associated with this passkey. + /// + /// + /// See . + /// + public virtual byte[] AttestationObject { get; set; } + + /// + /// Gets or sets the collected client data JSON associated with this passkey. + /// + /// + /// See . + /// + public virtual byte[] ClientDataJson { get; set; } +} diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUser.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUser.cs index be9ccfe8bb..3f4a91117c 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUser.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUser.cs @@ -127,6 +127,11 @@ public class IdentityUser : FullAuditedAggregateRoot, IUser, IHasEntityVer ///
public virtual DateTimeOffset? LastPasswordChangeTime { get; protected set; } + /// + /// Gets or sets the last sign-in time for the user. + /// + public virtual DateTimeOffset? LastSignInTime { get; protected set; } + //TODO: Can we make collections readonly collection, which will provide encapsulation. But... can work for all ORMs? /// @@ -159,6 +164,11 @@ public class IdentityUser : FullAuditedAggregateRoot, IUser, IHasEntityVer /// public virtual ICollection PasswordHistories { get; protected set; } + /// + /// Navigation property for this users passkeys. + /// + public virtual ICollection Passkeys { get; protected set; } + protected IdentityUser() { } @@ -188,6 +198,7 @@ public class IdentityUser : FullAuditedAggregateRoot, IUser, IHasEntityVer Tokens = new Collection(); OrganizationUnits = new Collection(); PasswordHistories = new Collection(); + Passkeys = new Collection(); } public virtual void AddRole(Guid roleId) @@ -403,6 +414,27 @@ public class IdentityUser : FullAuditedAggregateRoot, IUser, IHasEntityVer LastPasswordChangeTime = lastPasswordChangeTime; } + public virtual void SetLastSignInTime(DateTimeOffset? lastSignInTime) + { + LastSignInTime = lastSignInTime; + } + + [CanBeNull] + public virtual IdentityUserPasskey FindPasskey(byte[] credentialId) + { + return Passkeys.FirstOrDefault(x => x.UserId == Id && x.CredentialId.SequenceEqual(credentialId)); + } + + public virtual void AddPasskey(byte[] credentialId, IdentityPasskeyData passkeyData) + { + Passkeys.Add(new IdentityUserPasskey(Id, credentialId, passkeyData, TenantId)); + } + + public virtual void RemovePasskey(byte[] credentialId) + { + Passkeys.RemoveAll(x => x.CredentialId.SequenceEqual(credentialId)); + } + public override string ToString() { return $"{base.ToString()}, UserName = {UserName}"; diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserPasskey.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserPasskey.cs new file mode 100644 index 0000000000..59aad98ea6 --- /dev/null +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserPasskey.cs @@ -0,0 +1,53 @@ +using System; +using Volo.Abp.Domain.Entities; +using Volo.Abp.MultiTenancy; + +namespace Volo.Abp.Identity; + +/// +/// Represents a passkey credential for a user in the identity system. +/// +/// +/// See . +/// +public class IdentityUserPasskey : Entity, IMultiTenant +{ + public virtual Guid? TenantId { get; protected set; } + + /// + /// Gets or sets the primary key of the user that owns this passkey. + /// + public virtual Guid UserId { get; protected set; } + + /// + /// Gets or sets the credential ID for this passkey. + /// + public virtual byte[] CredentialId { get; set; } + + /// + /// Gets or sets additional data associated with this passkey. + /// + public virtual IdentityPasskeyData Data { get; set; } + + protected IdentityUserPasskey() + { + + } + + public IdentityUserPasskey( + Guid userId, + byte[] credentialId, + IdentityPasskeyData data, + Guid? tenantId) + { + UserId = userId; + CredentialId = credentialId; + Data = data; + TenantId = tenantId; + } + + public override object[] GetKeys() + { + return new object[] { UserId, CredentialId }; + } +} diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserPasskeyExtensions.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserPasskeyExtensions.cs new file mode 100644 index 0000000000..fd1bd3fbf5 --- /dev/null +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserPasskeyExtensions.cs @@ -0,0 +1,32 @@ +using Microsoft.AspNetCore.Identity; + +namespace Volo.Abp.Identity; + +public static class IdentityUserPasskeyExtensions +{ + public static void UpdateFromUserPasskeyInfo(this IdentityUserPasskey passkey, UserPasskeyInfo passkeyInfo) + { + passkey.Data.Name = passkeyInfo.Name; + passkey.Data.SignCount = passkeyInfo.SignCount; + passkey.Data.IsBackedUp = passkeyInfo.IsBackedUp; + passkey.Data.IsUserVerified = passkeyInfo.IsUserVerified; + } + + public static UserPasskeyInfo ToUserPasskeyInfo(this IdentityUserPasskey passkey) + { + return new UserPasskeyInfo( + passkey.CredentialId, + passkey.Data.PublicKey, + passkey.Data.CreatedAt, + passkey.Data.SignCount, + passkey.Data.Transports, + passkey.Data.IsUserVerified, + passkey.Data.IsBackupEligible, + passkey.Data.IsBackedUp, + passkey.Data.AttestationObject, + passkey.Data.ClientDataJson) + { + Name = passkey.Data.Name + }; + } +} diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserStore.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserStore.cs index 83fe745677..f407e6c977 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserStore.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserStore.cs @@ -32,6 +32,7 @@ public class IdentityUserStore : IUserAuthenticationTokenStore, IUserAuthenticatorKeyStore, IUserTwoFactorRecoveryCodeStore, + IUserPasskeyStore, ITransientDependency { private const string InternalLoginProvider = "[AspNetUserStore]"; @@ -1123,6 +1124,110 @@ public class IdentityUserStore : return Task.FromResult(RecoveryCodeTokenName); } + /// + /// Creates a new passkey credential in the store for the specified , + /// or updates an existing passkey. + /// + /// The user to create the passkey credential for. + /// + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + public virtual async Task AddOrUpdatePasskeyAsync(IdentityUser user, UserPasskeyInfo passkey, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + await UserRepository.EnsureCollectionLoadedAsync(user, u => u.Passkeys, cancellationToken); + + var userPasskey = user.FindPasskey(passkey.CredentialId); + if (userPasskey != null) + { + userPasskey.UpdateFromUserPasskeyInfo(passkey); + } + else + { + user.AddPasskey(passkey.CredentialId, new IdentityPasskeyData() + { + PublicKey = passkey.PublicKey, + Name = passkey.Name, + CreatedAt = passkey.CreatedAt, + Transports = passkey.Transports, + SignCount = passkey.SignCount, + IsUserVerified = passkey.IsUserVerified, + IsBackupEligible = passkey.IsBackupEligible, + IsBackedUp = passkey.IsBackedUp, + AttestationObject = passkey.AttestationObject, + ClientDataJson = passkey.ClientDataJson, + }); + } + } + + /// + /// Gets the passkey credentials for the specified . + /// + /// The user whose passkeys should be retrieved. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation, containing a list of the user's passkeys. + public virtual async Task> GetPasskeysAsync(IdentityUser user, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + Check.NotNull(user, nameof(user)); + + await UserRepository.EnsureCollectionLoadedAsync(user, u => u.Passkeys, cancellationToken); + + return user.Passkeys.Select(p => p.ToUserPasskeyInfo()).ToList(); + } + + /// + /// Finds and returns a user, if any, associated with the specified passkey credential identifier. + /// + /// The passkey credential id to search for. + /// The used to propagate notifications that the operation should be canceled. + /// + /// The that represents the asynchronous operation, containing the user, if any, associated with the specified passkey credential id. + /// + public virtual async Task FindByPasskeyIdAsync(byte[] credentialId, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + return await UserRepository.FindByPasskeyIdAsync(credentialId, cancellationToken: cancellationToken); + } + + /// + /// Finds a passkey for the specified user with the specified credential id. + /// + /// The user whose passkey should be retrieved. + /// The credential id to search for. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation, containing the user's passkey information. + public virtual async Task FindPasskeyAsync(IdentityUser user, byte[] credentialId, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + Check.NotNull(user, nameof(user)); + Check.NotNull(credentialId, nameof(credentialId)); + + await UserRepository.EnsureCollectionLoadedAsync(user, u => u.Passkeys, cancellationToken); + return user.FindPasskey(credentialId)?.ToUserPasskeyInfo(); + } + + /// + /// Removes a passkey credential from the specified . + /// + /// The user to remove the passkey credential from. + /// The credential id of the passkey to remove. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + public virtual async Task RemovePasskeyAsync(IdentityUser user, byte[] credentialId, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + Check.NotNull(user, nameof(user)); + Check.NotNull(credentialId, nameof(credentialId)); + + await UserRepository.EnsureCollectionLoadedAsync(user, u => u.Passkeys, cancellationToken); + user.RemovePasskey(credentialId); + } + public virtual void Dispose() { diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/UserRoleFinder.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/UserRoleFinder.cs index 3ec1b2fbb1..48d8b01a73 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/UserRoleFinder.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/UserRoleFinder.cs @@ -1,16 +1,21 @@ using System; +using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using Volo.Abp.DependencyInjection; +using Volo.Abp.Domain.Repositories; namespace Volo.Abp.Identity; public class UserRoleFinder : IUserRoleFinder, ITransientDependency { protected IIdentityUserRepository IdentityUserRepository { get; } + protected IIdentityRoleRepository IdentityRoleRepository { get; } - public UserRoleFinder(IIdentityUserRepository identityUserRepository) + public UserRoleFinder(IIdentityUserRepository identityUserRepository, IIdentityRoleRepository identityRoleRepository) { IdentityUserRepository = identityUserRepository; + IdentityRoleRepository = identityRoleRepository; } [Obsolete("Use GetRoleNamesAsync instead.")] @@ -19,8 +24,62 @@ public class UserRoleFinder : IUserRoleFinder, ITransientDependency return (await IdentityUserRepository.GetRoleNamesAsync(userId)).ToArray(); } - public async Task GetRoleNamesAsync(Guid userId) + public virtual async Task GetRoleNamesAsync(Guid userId) { return (await IdentityUserRepository.GetRoleNamesAsync(userId)).ToArray(); } + + public virtual async Task> SearchUserAsync(string filter, int page = 1) + { + using (IdentityUserRepository.DisableTracking()) + { + page = page < 1 ? 1 : page; + var users = await IdentityUserRepository.GetListAsync(filter: filter, skipCount: (page - 1) * 10, maxResultCount: 10); + return users.Select(user => new UserFinderResult + { + Id = user.Id, + UserName = user.UserName + }).ToList(); + } + } + + public virtual async Task> SearchRoleAsync(string filter, int page = 1) + { + using (IdentityUserRepository.DisableTracking()) + { + page = page < 1 ? 1 : page; + var roles = await IdentityRoleRepository.GetListAsync(filter: filter, skipCount: (page - 1) * 10, maxResultCount: 10); + return roles.Select(user => new RoleFinderResult + { + Id = user.Id, + RoleName = user.Name + }).ToList(); + } + } + + public virtual async Task> SearchUserByIdsAsync(Guid[] ids) + { + using (IdentityUserRepository.DisableTracking()) + { + var users = await IdentityUserRepository.GetListByIdsAsync(ids); + return users.Select(user => new UserFinderResult + { + Id = user.Id, + UserName = user.UserName + }).ToList(); + } + } + + public virtual async Task> SearchRoleByNamesAsync(string[] names) + { + using (IdentityUserRepository.DisableTracking()) + { + var roles = await IdentityRoleRepository.GetListAsync(names); + return roles.Select(user => new RoleFinderResult + { + Id = user.Id, + RoleName = user.Name + }).ToList(); + } + } } diff --git a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityRoleRepository.cs b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityRoleRepository.cs index ca6dc42d6f..1da7758f19 100644 --- a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityRoleRepository.cs +++ b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityRoleRepository.cs @@ -72,6 +72,13 @@ public class EfCoreIdentityRoleRepository : EfCoreRepository> GetListAsync(IEnumerable names, CancellationToken cancellationToken = default) + { + return await (await GetDbSetAsync()) + .Where(t => names.Contains(t.Name)) + .ToListAsync(GetCancellationToken(cancellationToken)); + } + public virtual async Task> GetDefaultOnesAsync( bool includeDetails = false, CancellationToken cancellationToken = default) { diff --git a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityUserRepository.cs b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityUserRepository.cs index 25646ac90a..68c05738db 100644 --- a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityUserRepository.cs +++ b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityUserRepository.cs @@ -94,6 +94,15 @@ public class EfCoreIdentityUserRepository : EfCoreRepository x.Id).Select(x => new IdentityUserIdWithRoleNames { Id = x.Key, RoleNames = x.SelectMany(y => y.RoleNames).Distinct().ToArray() }).ToList(); } + public virtual async Task FindByPasskeyIdAsync(byte[] credentialId, bool includeDetails = true, CancellationToken cancellationToken = default) + { + return await (await GetDbSetAsync()) + .IncludeDetails(includeDetails) + .Where(u => u.Passkeys.Any(x => x.CredentialId.SequenceEqual(credentialId))) + .OrderBy(x => x.Id) + .FirstOrDefaultAsync(GetCancellationToken(cancellationToken)); + } + public virtual async Task> GetRoleNamesInOrganizationUnitAsync( Guid id, CancellationToken cancellationToken = default) @@ -468,11 +477,11 @@ public class EfCoreIdentityUserRepository : EfCoreRepository x.Id == id); - } + } if (roleId.HasValue) { diff --git a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/IdentityDbContextModelBuilderExtensions.cs b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/IdentityDbContextModelBuilderExtensions.cs index 1b396754a8..2b302e4cf8 100644 --- a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/IdentityDbContextModelBuilderExtensions.cs +++ b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/IdentityDbContextModelBuilderExtensions.cs @@ -47,6 +47,7 @@ public static class IdentityDbContextModelBuilderExtensions b.HasMany(u => u.Tokens).WithOne().HasForeignKey(ur => ur.UserId).IsRequired(); b.HasMany(u => u.OrganizationUnits).WithOne().HasForeignKey(ur => ur.UserId).IsRequired(); b.HasMany(u => u.PasswordHistories).WithOne().HasForeignKey(ur => ur.UserId).IsRequired(); + b.HasMany(u => u.Passkeys).WithOne().HasForeignKey(ur => ur.UserId).IsRequired(); b.HasIndex(u => u.NormalizedUserName); b.HasIndex(u => u.NormalizedEmail); @@ -176,6 +177,20 @@ public static class IdentityDbContextModelBuilderExtensions }); } + builder.Entity(b => + { + b.ToTable(AbpIdentityDbProperties.DbTablePrefix + "UserPasskeys", AbpIdentityDbProperties.DbSchema); + + b.ConfigureByConvention(); + + b.HasKey(p => p.CredentialId); + + b.Property(p => p.CredentialId).HasMaxLength(IdentityUserPasskeyConsts.MaxCredentialIdLength); // Defined in WebAuthn spec to be no longer than 1023 bytes + b.OwnsOne(p => p.Data).ToJson(); + + b.ApplyObjectExtensionMappings(); + }); + builder.Entity(b => { b.ToTable(AbpIdentityDbProperties.DbTablePrefix + "OrganizationUnits", AbpIdentityDbProperties.DbSchema); diff --git a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/IdentityEfCoreQueryableExtensions.cs b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/IdentityEfCoreQueryableExtensions.cs index 0d51910e97..b5ce878a4f 100644 --- a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/IdentityEfCoreQueryableExtensions.cs +++ b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/IdentityEfCoreQueryableExtensions.cs @@ -18,7 +18,8 @@ public static class IdentityEfCoreQueryableExtensions .Include(x => x.Claims) .Include(x => x.Tokens) .Include(x => x.OrganizationUnits) - .Include(x => x.PasswordHistories); + .Include(x => x.PasswordHistories) + .Include(x => x.Passkeys); } public static IQueryable IncludeDetails(this IQueryable queryable, bool include = true) diff --git a/modules/identity/src/Volo.Abp.Identity.HttpApi.Client/ClientProxies/Volo/Abp/Identity/Integration/IdentityUserIntegrationClientProxy.Generated.cs b/modules/identity/src/Volo.Abp.Identity.HttpApi.Client/ClientProxies/Volo/Abp/Identity/Integration/IdentityUserIntegrationClientProxy.Generated.cs index 1c2f1b61cb..ddbb2cfc55 100644 --- a/modules/identity/src/Volo.Abp.Identity.HttpApi.Client/ClientProxies/Volo/Abp/Identity/Integration/IdentityUserIntegrationClientProxy.Generated.cs +++ b/modules/identity/src/Volo.Abp.Identity.HttpApi.Client/ClientProxies/Volo/Abp/Identity/Integration/IdentityUserIntegrationClientProxy.Generated.cs @@ -52,6 +52,14 @@ public partial class IdentityUserIntegrationClientProxy : ClientProxyBase> SearchByIdsAsync(Guid[] ids) + { + return await RequestAsync>(nameof(SearchByIdsAsync), new ClientProxyRequestTypeValue + { + { typeof(Guid[]), ids } + }); + } + public virtual async Task GetCountAsync(UserLookupCountInputDto input) { return await RequestAsync(nameof(GetCountAsync), new ClientProxyRequestTypeValue @@ -59,4 +67,28 @@ public partial class IdentityUserIntegrationClientProxy : ClientProxyBase> SearchRoleAsync(RoleLookupSearchInputDto input) + { + return await RequestAsync>(nameof(SearchRoleAsync), new ClientProxyRequestTypeValue + { + { typeof(RoleLookupSearchInputDto), input } + }); + } + + public virtual async Task> SearchRoleByNamesAsync(String[] names) + { + return await RequestAsync>(nameof(SearchRoleByNamesAsync), new ClientProxyRequestTypeValue + { + { typeof(String[]), names } + }); + } + + public virtual async Task GetRoleCountAsync(RoleLookupCountInputDto input) + { + return await RequestAsync(nameof(GetRoleCountAsync), new ClientProxyRequestTypeValue + { + { typeof(RoleLookupCountInputDto), input } + }); + } } diff --git a/modules/identity/src/Volo.Abp.Identity.HttpApi.Client/ClientProxies/identity-generate-proxy.json b/modules/identity/src/Volo.Abp.Identity.HttpApi.Client/ClientProxies/identity-generate-proxy.json index 33efd0a6e7..3937d99aa9 100644 --- a/modules/identity/src/Volo.Abp.Identity.HttpApi.Client/ClientProxies/identity-generate-proxy.json +++ b/modules/identity/src/Volo.Abp.Identity.HttpApi.Client/ClientProxies/identity-generate-proxy.json @@ -200,6 +200,18 @@ "constraintTypes": null, "bindingSourceId": "ModelBinding", "descriptorName": "input" + }, + { + "nameOnMethod": "input", + "name": "ExtraProperties", + "jsonName": null, + "type": "Volo.Abp.Data.ExtraPropertyDictionary", + "typeSimple": "{string:object}", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "input" } ], "returnValue": { @@ -673,6 +685,18 @@ "constraintTypes": null, "bindingSourceId": "ModelBinding", "descriptorName": "input" + }, + { + "nameOnMethod": "input", + "name": "ExtraProperties", + "jsonName": null, + "type": "Volo.Abp.Data.ExtraPropertyDictionary", + "typeSimple": "{string:object}", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "input" } ], "returnValue": { @@ -1220,6 +1244,18 @@ "constraintTypes": null, "bindingSourceId": "ModelBinding", "descriptorName": "input" + }, + { + "nameOnMethod": "input", + "name": "ExtraProperties", + "jsonName": null, + "type": "Volo.Abp.Data.ExtraPropertyDictionary", + "typeSimple": "{string:object}", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "input" } ], "returnValue": { @@ -1348,6 +1384,23 @@ "typeSimple": "Volo.Abp.Application.Dtos.ListResultDto" } }, + { + "name": "SearchByIdsAsync", + "parametersOnMethod": [ + { + "name": "ids", + "typeAsString": "System.Guid[], System.Private.CoreLib", + "type": "System.Guid[]", + "typeSimple": "[string]", + "isOptional": false, + "defaultValue": null + } + ], + "returnValue": { + "type": "Volo.Abp.Application.Dtos.ListResultDto", + "typeSimple": "Volo.Abp.Application.Dtos.ListResultDto" + } + }, { "name": "GetCountAsync", "parametersOnMethod": [ @@ -1364,6 +1417,57 @@ "type": "System.Int64", "typeSimple": "number" } + }, + { + "name": "SearchRoleAsync", + "parametersOnMethod": [ + { + "name": "input", + "typeAsString": "Volo.Abp.Identity.RoleLookupSearchInputDto, Volo.Abp.Identity.Application.Contracts", + "type": "Volo.Abp.Identity.RoleLookupSearchInputDto", + "typeSimple": "Volo.Abp.Identity.RoleLookupSearchInputDto", + "isOptional": false, + "defaultValue": null + } + ], + "returnValue": { + "type": "Volo.Abp.Application.Dtos.ListResultDto", + "typeSimple": "Volo.Abp.Application.Dtos.ListResultDto" + } + }, + { + "name": "SearchRoleByNamesAsync", + "parametersOnMethod": [ + { + "name": "ids", + "typeAsString": "System.String[], System.Private.CoreLib", + "type": "System.String[]", + "typeSimple": "[string]", + "isOptional": false, + "defaultValue": null + } + ], + "returnValue": { + "type": "Volo.Abp.Application.Dtos.ListResultDto", + "typeSimple": "Volo.Abp.Application.Dtos.ListResultDto" + } + }, + { + "name": "GetRoleCountAsync", + "parametersOnMethod": [ + { + "name": "input", + "typeAsString": "Volo.Abp.Identity.RoleLookupCountInputDto, Volo.Abp.Identity.Application.Contracts", + "type": "Volo.Abp.Identity.RoleLookupCountInputDto", + "typeSimple": "Volo.Abp.Identity.RoleLookupCountInputDto", + "isOptional": false, + "defaultValue": null + } + ], + "returnValue": { + "type": "System.Int64", + "typeSimple": "number" + } } ] } @@ -1544,6 +1648,55 @@ "constraintTypes": null, "bindingSourceId": "ModelBinding", "descriptorName": "input" + }, + { + "nameOnMethod": "input", + "name": "ExtraProperties", + "jsonName": null, + "type": "Volo.Abp.Data.ExtraPropertyDictionary", + "typeSimple": "{string:object}", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "input" + } + ], + "returnValue": { + "type": "Volo.Abp.Application.Dtos.ListResultDto", + "typeSimple": "Volo.Abp.Application.Dtos.ListResultDto" + }, + "allowAnonymous": null, + "implementFrom": "Volo.Abp.Identity.Integration.IIdentityUserIntegrationService" + }, + "SearchByIdsAsyncByIds": { + "uniqueName": "SearchByIdsAsyncByIds", + "name": "SearchByIdsAsync", + "httpMethod": "GET", + "url": "integration-api/identity/users/search/by-ids", + "supportedVersions": [], + "parametersOnMethod": [ + { + "name": "ids", + "typeAsString": "System.Guid[], System.Private.CoreLib", + "type": "System.Guid[]", + "typeSimple": "[string]", + "isOptional": false, + "defaultValue": null + } + ], + "parameters": [ + { + "nameOnMethod": "ids", + "name": "ids", + "jsonName": null, + "type": "System.Guid[]", + "typeSimple": "[string]", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "" } ], "returnValue": { @@ -1589,6 +1742,165 @@ }, "allowAnonymous": null, "implementFrom": "Volo.Abp.Identity.Integration.IIdentityUserIntegrationService" + }, + "SearchRoleAsyncByInput": { + "uniqueName": "SearchRoleAsyncByInput", + "name": "SearchRoleAsync", + "httpMethod": "GET", + "url": "integration-api/identity/users/search/roles", + "supportedVersions": [], + "parametersOnMethod": [ + { + "name": "input", + "typeAsString": "Volo.Abp.Identity.RoleLookupSearchInputDto, Volo.Abp.Identity.Application.Contracts", + "type": "Volo.Abp.Identity.RoleLookupSearchInputDto", + "typeSimple": "Volo.Abp.Identity.RoleLookupSearchInputDto", + "isOptional": false, + "defaultValue": null + } + ], + "parameters": [ + { + "nameOnMethod": "input", + "name": "Filter", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "input" + }, + { + "nameOnMethod": "input", + "name": "Sorting", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "input" + }, + { + "nameOnMethod": "input", + "name": "SkipCount", + "jsonName": null, + "type": "System.Int32", + "typeSimple": "number", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "input" + }, + { + "nameOnMethod": "input", + "name": "MaxResultCount", + "jsonName": null, + "type": "System.Int32", + "typeSimple": "number", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "input" + }, + { + "nameOnMethod": "input", + "name": "ExtraProperties", + "jsonName": null, + "type": "Volo.Abp.Data.ExtraPropertyDictionary", + "typeSimple": "{string:object}", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "input" + } + ], + "returnValue": { + "type": "Volo.Abp.Application.Dtos.ListResultDto", + "typeSimple": "Volo.Abp.Application.Dtos.ListResultDto" + }, + "allowAnonymous": null, + "implementFrom": "Volo.Abp.Identity.Integration.IIdentityUserIntegrationService" + }, + "SearchRoleByNamesAsyncByNames": { + "uniqueName": "SearchRoleByNamesAsyncByNames", + "name": "SearchRoleByNamesAsync", + "httpMethod": "GET", + "url": "integration-api/identity/users/search/roles/by-names", + "supportedVersions": [], + "parametersOnMethod": [ + { + "name": "names", + "typeAsString": "System.String[], System.Private.CoreLib", + "type": "System.String[]", + "typeSimple": "[string]", + "isOptional": false, + "defaultValue": null + } + ], + "parameters": [ + { + "nameOnMethod": "names", + "name": "names", + "jsonName": null, + "type": "System.String[]", + "typeSimple": "[string]", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "" + } + ], + "returnValue": { + "type": "Volo.Abp.Application.Dtos.ListResultDto", + "typeSimple": "Volo.Abp.Application.Dtos.ListResultDto" + }, + "allowAnonymous": null, + "implementFrom": "Volo.Abp.Identity.Integration.IIdentityUserIntegrationService" + }, + "GetRoleCountAsyncByInput": { + "uniqueName": "GetRoleCountAsyncByInput", + "name": "GetRoleCountAsync", + "httpMethod": "GET", + "url": "integration-api/identity/users/count/roles", + "supportedVersions": [], + "parametersOnMethod": [ + { + "name": "input", + "typeAsString": "Volo.Abp.Identity.RoleLookupCountInputDto, Volo.Abp.Identity.Application.Contracts", + "type": "Volo.Abp.Identity.RoleLookupCountInputDto", + "typeSimple": "Volo.Abp.Identity.RoleLookupCountInputDto", + "isOptional": false, + "defaultValue": null + } + ], + "parameters": [ + { + "nameOnMethod": "input", + "name": "Filter", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "input" + } + ], + "returnValue": { + "type": "System.Int64", + "typeSimple": "number" + }, + "allowAnonymous": null, + "implementFrom": "Volo.Abp.Identity.Integration.IIdentityUserIntegrationService" } } } diff --git a/modules/identity/src/Volo.Abp.Identity.HttpApi.Client/Volo/Abp/Identity/HttpClientUserRoleFinder.cs b/modules/identity/src/Volo.Abp.Identity.HttpApi.Client/Volo/Abp/Identity/HttpClientUserRoleFinder.cs index 201c9f3eb0..ed7f0f5382 100644 --- a/modules/identity/src/Volo.Abp.Identity.HttpApi.Client/Volo/Abp/Identity/HttpClientUserRoleFinder.cs +++ b/modules/identity/src/Volo.Abp.Identity.HttpApi.Client/Volo/Abp/Identity/HttpClientUserRoleFinder.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Volo.Abp.DependencyInjection; @@ -25,8 +26,59 @@ public class HttpClientUserRoleFinder : IUserRoleFinder, ITransientDependency return output.Items.Select(r => r.Name).ToArray(); } - public async Task GetRoleNamesAsync(Guid userId) + public virtual async Task GetRoleNamesAsync(Guid userId) { return await _userIntegrationService.GetRoleNamesAsync(userId); } + + public virtual async Task> SearchUserAsync(string filter, int page = 1) + { + page = page < 1 ? 1 : page; + var users = await _userIntegrationService.SearchAsync(new UserLookupSearchInputDto() + { + Filter = filter, + SkipCount = (page - 1) * 10 + }); + return users.Items.Select(u => new UserFinderResult + { + Id = u.Id, + UserName = u.UserName + }).ToList(); + } + + public virtual async Task> SearchRoleAsync(string filter, int page = 1) + { + page = page < 1 ? 1 : page; + var roles = await _userIntegrationService.SearchRoleAsync(new RoleLookupSearchInputDto() + { + Filter = filter, + SkipCount = (page - 1) * 10 + }); + + return roles.Items.Select(r => new RoleFinderResult + { + Id = r.Id, + RoleName = r.Name + }).ToList(); + } + + public virtual async Task> SearchUserByIdsAsync(Guid[] ids) + { + var users = await _userIntegrationService.SearchByIdsAsync(ids); + return users.Items.Select(u => new UserFinderResult + { + Id = u.Id, + UserName = u.UserName + }).ToList(); + } + + public virtual async Task> SearchRoleByNamesAsync(string[] names) + { + var roles = await _userIntegrationService.SearchRoleByNamesAsync(names); + return roles.Items.Select(r => new RoleFinderResult + { + Id = r.Id, + RoleName = r.Name + }).ToList(); + } } diff --git a/modules/identity/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/Integration/IdentityUserIntegrationController.cs b/modules/identity/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/Integration/IdentityUserIntegrationController.cs index 2a84385e48..a30b73d5cd 100644 --- a/modules/identity/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/Integration/IdentityUserIntegrationController.cs +++ b/modules/identity/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/Integration/IdentityUserIntegrationController.cs @@ -20,7 +20,7 @@ public class IdentityUserIntegrationController : AbpControllerBase, IIdentityUse { UserIntegrationService = userIntegrationService; } - + [HttpGet] [Route("{id}/role-names")] public virtual Task GetRoleNamesAsync(Guid id) @@ -49,10 +49,38 @@ public class IdentityUserIntegrationController : AbpControllerBase, IIdentityUse return UserIntegrationService.SearchAsync(input); } + [HttpGet] + [Route("search/by-ids")] + public virtual Task> SearchByIdsAsync(Guid[] ids) + { + return UserIntegrationService.SearchByIdsAsync(ids); + } + [HttpGet] [Route("count")] public Task GetCountAsync(UserLookupCountInputDto input) { return UserIntegrationService.GetCountAsync(input); } -} \ No newline at end of file + + [HttpGet] + [Route("search/roles")] + public virtual Task> SearchRoleAsync(RoleLookupSearchInputDto input) + { + return UserIntegrationService.SearchRoleAsync(input); + } + + [HttpGet] + [Route("search/roles/by-names")] + public virtual Task> SearchRoleByNamesAsync(string[] names) + { + return UserIntegrationService.SearchRoleByNamesAsync(names); + } + + [HttpGet] + [Route("count/roles")] + public virtual Task GetRoleCountAsync(RoleLookupCountInputDto input) + { + return UserIntegrationService.GetRoleCountAsync(input); + } +} diff --git a/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityRoleRepository.cs b/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityRoleRepository.cs index 7deee94160..f613a3ee88 100644 --- a/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityRoleRepository.cs +++ b/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityRoleRepository.cs @@ -78,6 +78,13 @@ public class MongoIdentityRoleRepository : MongoDbRepository> GetListAsync(IEnumerable names, CancellationToken cancellationToken = default) + { + return await (await GetQueryableAsync(cancellationToken)) + .Where(x => names.Contains(x.Name)) + .ToListAsync(GetCancellationToken(cancellationToken)); + } + public virtual async Task> GetDefaultOnesAsync( bool includeDetails = false, CancellationToken cancellationToken = default) diff --git a/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityUserRepository.cs b/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityUserRepository.cs index 29b9817e05..2a8654cd3b 100644 --- a/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityUserRepository.cs +++ b/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityUserRepository.cs @@ -443,6 +443,14 @@ public class MongoIdentityUserRepository : MongoDbRepository FindByPasskeyIdAsync(byte[] credentialId, bool includeDetails = true, CancellationToken cancellationToken = default) + { + return await (await GetQueryableAsync(cancellationToken)) + .Where(u => u.Passkeys.Any(x => x.CredentialId == credentialId)) + .OrderBy(x => x.Id) + .FirstOrDefaultAsync(GetCancellationToken(cancellationToken)); + } + protected virtual async Task> GetFilteredQueryableAsync( string filter = null, Guid? roleId = null, diff --git a/modules/identity/src/Volo.Abp.Identity.Web/wwwroot/client-proxies/identity-proxy.js b/modules/identity/src/Volo.Abp.Identity.Web/wwwroot/client-proxies/identity-proxy.js index 250abbc47b..6de3aeb8d5 100644 --- a/modules/identity/src/Volo.Abp.Identity.Web/wwwroot/client-proxies/identity-proxy.js +++ b/modules/identity/src/Volo.Abp.Identity.Web/wwwroot/client-proxies/identity-proxy.js @@ -20,7 +20,7 @@ volo.abp.identity.identityRole.getList = function(input, ajaxParams) { return abp.ajax($.extend(true, { - url: abp.appPath + 'api/identity/roles' + abp.utils.buildQueryString([{ name: 'filter', value: input.filter }, { name: 'sorting', value: input.sorting }, { name: 'skipCount', value: input.skipCount }, { name: 'maxResultCount', value: input.maxResultCount }]) + '', + url: abp.appPath + 'api/identity/roles' + abp.utils.buildQueryString([{ name: 'filter', value: input.filter }, { name: 'sorting', value: input.sorting }, { name: 'skipCount', value: input.skipCount }, { name: 'maxResultCount', value: input.maxResultCount }, { name: 'extraProperties', value: input.extraProperties }]) + '', type: 'GET' }, ajaxParams)); }; @@ -73,7 +73,7 @@ volo.abp.identity.identityUser.getList = function(input, ajaxParams) { return abp.ajax($.extend(true, { - url: abp.appPath + 'api/identity/users' + abp.utils.buildQueryString([{ name: 'filter', value: input.filter }, { name: 'sorting', value: input.sorting }, { name: 'skipCount', value: input.skipCount }, { name: 'maxResultCount', value: input.maxResultCount }]) + '', + url: abp.appPath + 'api/identity/users' + abp.utils.buildQueryString([{ name: 'filter', value: input.filter }, { name: 'sorting', value: input.sorting }, { name: 'skipCount', value: input.skipCount }, { name: 'maxResultCount', value: input.maxResultCount }, { name: 'extraProperties', value: input.extraProperties }]) + '', type: 'GET' }, ajaxParams)); }; @@ -163,7 +163,7 @@ volo.abp.identity.identityUserLookup.search = function(input, ajaxParams) { return abp.ajax($.extend(true, { - url: abp.appPath + 'api/identity/users/lookup/search' + abp.utils.buildQueryString([{ name: 'filter', value: input.filter }, { name: 'sorting', value: input.sorting }, { name: 'skipCount', value: input.skipCount }, { name: 'maxResultCount', value: input.maxResultCount }]) + '', + url: abp.appPath + 'api/identity/users/lookup/search' + abp.utils.buildQueryString([{ name: 'filter', value: input.filter }, { name: 'sorting', value: input.sorting }, { name: 'skipCount', value: input.skipCount }, { name: 'maxResultCount', value: input.maxResultCount }, { name: 'extraProperties', value: input.extraProperties }]) + '', type: 'GET' }, ajaxParams)); }; diff --git a/modules/identity/src/Volo.Abp.PermissionManagement.Domain.Identity/Volo/Abp/PermissionManagement/Identity/AbpPermissionManagementDomainIdentityModule.cs b/modules/identity/src/Volo.Abp.PermissionManagement.Domain.Identity/Volo/Abp/PermissionManagement/Identity/AbpPermissionManagementDomainIdentityModule.cs index 973577ccff..43399a134d 100644 --- a/modules/identity/src/Volo.Abp.PermissionManagement.Domain.Identity/Volo/Abp/PermissionManagement/Identity/AbpPermissionManagementDomainIdentityModule.cs +++ b/modules/identity/src/Volo.Abp.PermissionManagement.Domain.Identity/Volo/Abp/PermissionManagement/Identity/AbpPermissionManagementDomainIdentityModule.cs @@ -1,4 +1,5 @@ using Volo.Abp.Authorization.Permissions; +using Volo.Abp.Authorization.Permissions.Resources; using Volo.Abp.Identity; using Volo.Abp.Modularity; using Volo.Abp.Users; @@ -22,6 +23,12 @@ public class AbpPermissionManagementDomainIdentityModule : AbpModule //TODO: Can we prevent duplication of permission names without breaking the design and making the system complicated options.ProviderPolicies[UserPermissionValueProvider.ProviderName] = "AbpIdentity.Users.ManagePermissions"; options.ProviderPolicies[RolePermissionValueProvider.ProviderName] = "AbpIdentity.Roles.ManagePermissions"; + + options.ResourceManagementProviders.Add(); + options.ResourceManagementProviders.Add(); + + options.ResourcePermissionProviderKeyLookupServices.Add(); + options.ResourcePermissionProviderKeyLookupServices.Add(); }); } } diff --git a/modules/identity/src/Volo.Abp.PermissionManagement.Domain.Identity/Volo/Abp/PermissionManagement/Identity/RoleDeletedEventHandler.cs b/modules/identity/src/Volo.Abp.PermissionManagement.Domain.Identity/Volo/Abp/PermissionManagement/Identity/RoleDeletedEventHandler.cs index cb63b2681a..82200f763f 100644 --- a/modules/identity/src/Volo.Abp.PermissionManagement.Domain.Identity/Volo/Abp/PermissionManagement/Identity/RoleDeletedEventHandler.cs +++ b/modules/identity/src/Volo.Abp.PermissionManagement.Domain.Identity/Volo/Abp/PermissionManagement/Identity/RoleDeletedEventHandler.cs @@ -1,5 +1,6 @@ using System.Threading.Tasks; using Volo.Abp.Authorization.Permissions; +using Volo.Abp.Authorization.Permissions.Resources; using Volo.Abp.DependencyInjection; using Volo.Abp.Domain.Entities.Events.Distributed; using Volo.Abp.EventBus; @@ -14,15 +15,18 @@ public class RoleDeletedEventHandler : ITransientDependency { protected IPermissionManager PermissionManager { get; } + protected IResourcePermissionManager ResourcePermissionManager { get; } - public RoleDeletedEventHandler(IPermissionManager permissionManager) + public RoleDeletedEventHandler(IPermissionManager permissionManager, IResourcePermissionManager resourcePermissionManager) { PermissionManager = permissionManager; + ResourcePermissionManager = resourcePermissionManager; } [UnitOfWork] public virtual async Task HandleEventAsync(EntityDeletedEto eventData) { await PermissionManager.DeleteAsync(RolePermissionValueProvider.ProviderName, eventData.Entity.Name); + await ResourcePermissionManager.DeleteAsync(RoleResourcePermissionValueProvider.ProviderName, eventData.Entity.Name); } } diff --git a/modules/identity/src/Volo.Abp.PermissionManagement.Domain.Identity/Volo/Abp/PermissionManagement/Identity/RolePermissionManagementProvider.cs b/modules/identity/src/Volo.Abp.PermissionManagement.Domain.Identity/Volo/Abp/PermissionManagement/Identity/RolePermissionManagementProvider.cs index e97d524cd7..5cd2dda193 100644 --- a/modules/identity/src/Volo.Abp.PermissionManagement.Domain.Identity/Volo/Abp/PermissionManagement/Identity/RolePermissionManagementProvider.cs +++ b/modules/identity/src/Volo.Abp.PermissionManagement.Domain.Identity/Volo/Abp/PermissionManagement/Identity/RolePermissionManagementProvider.cs @@ -49,9 +49,8 @@ public class RolePermissionManagementProvider : PermissionManagementProvider } - if (providerName == UserPermissionValueProvider.ProviderName) + if (providerName == UserPermissionValueProvider.ProviderName && Guid.TryParse(providerKey, out var userId)) { - var userId = Guid.Parse(providerKey); var roleNames = await UserRoleFinder.GetRoleNamesAsync(userId); foreach (var roleName in roleNames) diff --git a/modules/identity/src/Volo.Abp.PermissionManagement.Domain.Identity/Volo/Abp/PermissionManagement/Identity/RoleResourcePermissionManagementProvider.cs b/modules/identity/src/Volo.Abp.PermissionManagement.Domain.Identity/Volo/Abp/PermissionManagement/Identity/RoleResourcePermissionManagementProvider.cs new file mode 100644 index 0000000000..bce070130c --- /dev/null +++ b/modules/identity/src/Volo.Abp.PermissionManagement.Domain.Identity/Volo/Abp/PermissionManagement/Identity/RoleResourcePermissionManagementProvider.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Volo.Abp.Authorization.Permissions.Resources; +using Volo.Abp.Domain.Repositories; +using Volo.Abp.Guids; +using Volo.Abp.Identity; +using Volo.Abp.MultiTenancy; + +namespace Volo.Abp.PermissionManagement.Identity; + +public class RoleResourcePermissionManagementProvider : ResourcePermissionManagementProvider +{ + public override string Name => RoleResourcePermissionValueProvider.ProviderName; + + protected IUserRoleFinder UserRoleFinder { get; } + + public RoleResourcePermissionManagementProvider( + IResourcePermissionGrantRepository resourcepPrmissionGrantRepository, + IGuidGenerator guidGenerator, + ICurrentTenant currentTenant, + IUserRoleFinder userRoleFinder) + : base( + resourcepPrmissionGrantRepository, + guidGenerator, + currentTenant) + { + UserRoleFinder = userRoleFinder; + } + + public override async Task CheckAsync(string name, string resourceName, string resourceKey, string providerName, string providerKey) + { + var multipleGrantInfo = await CheckAsync(new[] { name }, resourceName, resourceKey, providerName, providerKey); + + return multipleGrantInfo.Result.Values.First(); + } + + public override async Task CheckAsync(string[] names, string resourceName, string resourceKey, string providerName, string providerKey) + { + using (ResourcePermissionGrantRepository.DisableTracking()) + { + var multiplePermissionValueProviderGrantInfo = new MultipleResourcePermissionValueProviderGrantInfo(names); + var resourcePermissionGrants = new List(); + + if (providerName == Name) + { + resourcePermissionGrants.AddRange(await ResourcePermissionGrantRepository.GetListAsync(names, resourceName, resourceKey, providerName, providerKey)); + } + + if (providerName == UserResourcePermissionValueProvider.ProviderName && Guid.TryParse(providerKey, out var userId)) + { + var roleNames = await UserRoleFinder.GetRoleNamesAsync(userId); + + foreach (var roleName in roleNames) + { + resourcePermissionGrants.AddRange(await ResourcePermissionGrantRepository.GetListAsync(names, resourceName, resourceKey, Name, roleName)); + } + } + + resourcePermissionGrants = resourcePermissionGrants.Distinct().ToList(); + if (!resourcePermissionGrants.Any()) + { + return multiplePermissionValueProviderGrantInfo; + } + + foreach (var permissionName in names) + { + var resourcePermissionGrant = resourcePermissionGrants.FirstOrDefault(x => x.Name == permissionName); + if (resourcePermissionGrant != null) + { + multiplePermissionValueProviderGrantInfo.Result[permissionName] = new ResourcePermissionValueProviderGrantInfo(true, resourcePermissionGrant.ProviderKey); + } + } + + return multiplePermissionValueProviderGrantInfo; + } + } +} diff --git a/modules/identity/src/Volo.Abp.PermissionManagement.Domain.Identity/Volo/Abp/PermissionManagement/Identity/RoleResourcePermissionProviderKeyLookupService.cs b/modules/identity/src/Volo.Abp.PermissionManagement.Domain.Identity/Volo/Abp/PermissionManagement/Identity/RoleResourcePermissionProviderKeyLookupService.cs new file mode 100644 index 0000000000..fa9fc31f79 --- /dev/null +++ b/modules/identity/src/Volo.Abp.PermissionManagement.Domain.Identity/Volo/Abp/PermissionManagement/Identity/RoleResourcePermissionProviderKeyLookupService.cs @@ -0,0 +1,38 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.Authorization.Permissions.Resources; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Identity; +using Volo.Abp.Identity.Localization; +using Volo.Abp.Localization; + +namespace Volo.Abp.PermissionManagement.Identity; + +public class RoleResourcePermissionProviderKeyLookupService : IResourcePermissionProviderKeyLookupService, ITransientDependency +{ + public string Name => RoleResourcePermissionValueProvider.ProviderName; + + public ILocalizableString DisplayName { get; } + + protected IUserRoleFinder UserRoleFinder { get; } + + public RoleResourcePermissionProviderKeyLookupService(IUserRoleFinder userRoleFinder) + { + UserRoleFinder = userRoleFinder; + DisplayName = LocalizableString.Create(nameof(RoleResourcePermissionProviderKeyLookupService)); + } + + public virtual async Task> SearchAsync(string filter = null, int page = 1, CancellationToken cancellationToken = default) + { + var roles = await UserRoleFinder.SearchRoleAsync(filter, page); + return roles.Select(r => new ResourcePermissionProviderKeyInfo(r.RoleName, r.RoleName)).ToList(); + } + + public virtual async Task> SearchAsync(string[] keys, CancellationToken cancellationToken = default) + { + var roles = await UserRoleFinder.SearchRoleByNamesAsync(keys.Distinct().ToArray()); + return roles.Select(r => new ResourcePermissionProviderKeyInfo(r.RoleName, r.RoleName)).ToList(); + } +} diff --git a/modules/identity/src/Volo.Abp.PermissionManagement.Domain.Identity/Volo/Abp/PermissionManagement/Identity/RoleUpdateEventHandler.cs b/modules/identity/src/Volo.Abp.PermissionManagement.Domain.Identity/Volo/Abp/PermissionManagement/Identity/RoleUpdateEventHandler.cs index ca865063e0..bf9a86c06d 100644 --- a/modules/identity/src/Volo.Abp.PermissionManagement.Domain.Identity/Volo/Abp/PermissionManagement/Identity/RoleUpdateEventHandler.cs +++ b/modules/identity/src/Volo.Abp.PermissionManagement.Domain.Identity/Volo/Abp/PermissionManagement/Identity/RoleUpdateEventHandler.cs @@ -1,5 +1,6 @@ using System.Threading.Tasks; using Volo.Abp.Authorization.Permissions; +using Volo.Abp.Authorization.Permissions.Resources; using Volo.Abp.DependencyInjection; using Volo.Abp.EventBus.Distributed; using Volo.Abp.Identity; @@ -12,13 +13,19 @@ public class RoleUpdateEventHandler : { protected IPermissionManager PermissionManager { get; } protected IPermissionGrantRepository PermissionGrantRepository { get; } + protected IResourcePermissionManager ResourcePermissionManager { get; } + protected IResourcePermissionGrantRepository ResourcePermissionGrantRepository { get; } public RoleUpdateEventHandler( IPermissionManager permissionManager, - IPermissionGrantRepository permissionGrantRepository) + IPermissionGrantRepository permissionGrantRepository, + IResourcePermissionManager resourcePermissionManager, + IResourcePermissionGrantRepository resourcePermissionGrantRepository) { PermissionManager = permissionManager; PermissionGrantRepository = permissionGrantRepository; + ResourcePermissionManager = resourcePermissionManager; + ResourcePermissionGrantRepository = resourcePermissionGrantRepository; } public async Task HandleEventAsync(IdentityRoleNameChangedEto eventData) @@ -28,5 +35,11 @@ public class RoleUpdateEventHandler : { await PermissionManager.UpdateProviderKeyAsync(permissionGrant, eventData.Name); } + + var resourcePermissionGrantsInRole = await ResourcePermissionGrantRepository.GetListAsync(RoleResourcePermissionValueProvider.ProviderName, eventData.OldName); + foreach (var resourcePermissionGrant in resourcePermissionGrantsInRole) + { + await ResourcePermissionManager.UpdateProviderKeyAsync(resourcePermissionGrant, eventData.Name); + } } } diff --git a/modules/identity/src/Volo.Abp.PermissionManagement.Domain.Identity/Volo/Abp/PermissionManagement/Identity/UserDeletedEventHandler.cs b/modules/identity/src/Volo.Abp.PermissionManagement.Domain.Identity/Volo/Abp/PermissionManagement/Identity/UserDeletedEventHandler.cs index 35aaba29ae..b10cef8fe4 100644 --- a/modules/identity/src/Volo.Abp.PermissionManagement.Domain.Identity/Volo/Abp/PermissionManagement/Identity/UserDeletedEventHandler.cs +++ b/modules/identity/src/Volo.Abp.PermissionManagement.Domain.Identity/Volo/Abp/PermissionManagement/Identity/UserDeletedEventHandler.cs @@ -1,5 +1,6 @@ using System.Threading.Tasks; using Volo.Abp.Authorization.Permissions; +using Volo.Abp.Authorization.Permissions.Resources; using Volo.Abp.DependencyInjection; using Volo.Abp.Domain.Entities.Events.Distributed; using Volo.Abp.EventBus.Distributed; @@ -13,15 +14,18 @@ public class UserDeletedEventHandler : ITransientDependency { protected IPermissionManager PermissionManager { get; } + protected IResourcePermissionManager ResourcePermissionManager { get; } - public UserDeletedEventHandler(IPermissionManager permissionManager) + public UserDeletedEventHandler(IPermissionManager permissionManager, IResourcePermissionManager resourcePermissionManager) { PermissionManager = permissionManager; + ResourcePermissionManager = resourcePermissionManager; } [UnitOfWork] public virtual async Task HandleEventAsync(EntityDeletedEto eventData) { await PermissionManager.DeleteAsync(UserPermissionValueProvider.ProviderName, eventData.Entity.Id.ToString()); + await ResourcePermissionManager.DeleteAsync(UserResourcePermissionValueProvider.ProviderName, eventData.Entity.Id.ToString()); } } diff --git a/modules/identity/src/Volo.Abp.PermissionManagement.Domain.Identity/Volo/Abp/PermissionManagement/Identity/UserResourcePermissionManagementProvider.cs b/modules/identity/src/Volo.Abp.PermissionManagement.Domain.Identity/Volo/Abp/PermissionManagement/Identity/UserResourcePermissionManagementProvider.cs new file mode 100644 index 0000000000..ef165d422d --- /dev/null +++ b/modules/identity/src/Volo.Abp.PermissionManagement.Domain.Identity/Volo/Abp/PermissionManagement/Identity/UserResourcePermissionManagementProvider.cs @@ -0,0 +1,22 @@ +using Volo.Abp.Authorization.Permissions.Resources; +using Volo.Abp.Guids; +using Volo.Abp.MultiTenancy; + +namespace Volo.Abp.PermissionManagement.Identity; + +public class UserResourcePermissionManagementProvider : ResourcePermissionManagementProvider +{ + public override string Name => UserResourcePermissionValueProvider.ProviderName; + + public UserResourcePermissionManagementProvider( + IResourcePermissionGrantRepository resourcePermissionGrantRepository, + IGuidGenerator guidGenerator, + ICurrentTenant currentTenant) + : base( + resourcePermissionGrantRepository, + guidGenerator, + currentTenant) + { + + } +} diff --git a/modules/identity/src/Volo.Abp.PermissionManagement.Domain.Identity/Volo/Abp/PermissionManagement/Identity/UserResourcePermissionProviderKeyLookupService.cs b/modules/identity/src/Volo.Abp.PermissionManagement.Domain.Identity/Volo/Abp/PermissionManagement/Identity/UserResourcePermissionProviderKeyLookupService.cs new file mode 100644 index 0000000000..83f0e79099 --- /dev/null +++ b/modules/identity/src/Volo.Abp.PermissionManagement.Domain.Identity/Volo/Abp/PermissionManagement/Identity/UserResourcePermissionProviderKeyLookupService.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.Authorization.Permissions.Resources; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Identity; +using Volo.Abp.Identity.Localization; +using Volo.Abp.Localization; + +namespace Volo.Abp.PermissionManagement.Identity; + +public class UserResourcePermissionProviderKeyLookupService : IResourcePermissionProviderKeyLookupService, ITransientDependency +{ + public string Name => UserResourcePermissionValueProvider.ProviderName; + + public ILocalizableString DisplayName { get; } + + protected IUserRoleFinder UserRoleFinder { get; } + + public UserResourcePermissionProviderKeyLookupService(IUserRoleFinder userRoleFinder) + { + UserRoleFinder = userRoleFinder; + DisplayName = LocalizableString.Create(nameof(UserResourcePermissionProviderKeyLookupService)); + } + + public virtual async Task> SearchAsync(string filter = null, int page = 1, CancellationToken cancellationToken = default) + { + var users = await UserRoleFinder.SearchUserAsync(filter, page); + return users.Select(u => new ResourcePermissionProviderKeyInfo(u.Id.ToString(), u.UserName)).ToList(); + } + + public virtual async Task> SearchAsync(string[] keys, CancellationToken cancellationToken = default) + { + var ids = keys + .Select(key => Guid.TryParse(key, out var id) ? (Guid?)id : null) + .Where(id => id.HasValue) + .Select(id => id.Value) + .Distinct() + .ToArray(); + var users = await UserRoleFinder.SearchUserByIdsAsync(ids.ToArray()); + return users.Select(u => new ResourcePermissionProviderKeyInfo(u.Id.ToString(), u.UserName)).ToList(); + } +} diff --git a/modules/identity/src/Volo.Abp.PermissionManagement.Domain.Identity/Volo/Abp/PermissionManagement/RoleResourcePermissionManagerExtensions.cs b/modules/identity/src/Volo.Abp.PermissionManagement.Domain.Identity/Volo/Abp/PermissionManagement/RoleResourcePermissionManagerExtensions.cs new file mode 100644 index 0000000000..53ff327f7b --- /dev/null +++ b/modules/identity/src/Volo.Abp.PermissionManagement.Domain.Identity/Volo/Abp/PermissionManagement/RoleResourcePermissionManagerExtensions.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using JetBrains.Annotations; +using Volo.Abp.Authorization.Permissions; + +namespace Volo.Abp.PermissionManagement; + +public static class RoleResourceresourcePermissionManagerExtensions +{ + public static Task GetForRoleAsync([NotNull] this IResourcePermissionManager resourcePermissionManager, string roleName, string permissionName, [NotNull] string resourceName, [NotNull] string resourceKey) + { + Check.NotNull(resourcePermissionManager, nameof(resourcePermissionManager)); + + return resourcePermissionManager.GetAsync(permissionName, resourceName, resourceKey, RolePermissionValueProvider.ProviderName, roleName); + } + + public static Task> GetAllForRoleAsync([NotNull] this IResourcePermissionManager resourcePermissionManager, string roleName, [NotNull] string resourceName, [NotNull] string resourceKey) + { + Check.NotNull(resourcePermissionManager, nameof(resourcePermissionManager)); + + return resourcePermissionManager.GetAllAsync(resourceName, resourceKey, RolePermissionValueProvider.ProviderName, roleName); + } + + public static Task SetForRoleAsync([NotNull] this IResourcePermissionManager resourcePermissionManager, string roleName, [NotNull] string permissionName, [NotNull] string resourceName, [NotNull] string resourceKey, bool isGranted) + { + Check.NotNull(resourcePermissionManager, nameof(resourcePermissionManager)); + + return resourcePermissionManager.SetAsync(permissionName, resourceName, resourceKey, RolePermissionValueProvider.ProviderName, roleName, isGranted); + } +} diff --git a/modules/identity/src/Volo.Abp.PermissionManagement.Domain.Identity/Volo/Abp/PermissionManagement/UserResourcePermissionManagerExtensions.cs b/modules/identity/src/Volo.Abp.PermissionManagement.Domain.Identity/Volo/Abp/PermissionManagement/UserResourcePermissionManagerExtensions.cs new file mode 100644 index 0000000000..c0fe2eb845 --- /dev/null +++ b/modules/identity/src/Volo.Abp.PermissionManagement.Domain.Identity/Volo/Abp/PermissionManagement/UserResourcePermissionManagerExtensions.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using JetBrains.Annotations; +using Volo.Abp.Authorization.Permissions.Resources; + +namespace Volo.Abp.PermissionManagement; + +public static class UserResourcePermissionManagerExtensions +{ + public static Task> GetAllForUserAsync([NotNull] this IResourcePermissionManager resourcePermissionManager, Guid userId, [NotNull] string resourceName, [NotNull] string resourceKey) + { + Check.NotNull(resourcePermissionManager, nameof(resourcePermissionManager)); + + return resourcePermissionManager.GetAllAsync(resourceName, resourceKey, UserResourcePermissionValueProvider.ProviderName, userId.ToString()); + } + + public static Task SetForUserAsync([NotNull] this IResourcePermissionManager resourcePermissionManager, Guid userId, [NotNull] string name, [NotNull] string resourceName, [NotNull] string resourceKey, bool isGranted) + { + Check.NotNull(resourcePermissionManager, nameof(resourcePermissionManager)); + + return resourcePermissionManager.SetAsync(name, resourceName, resourceKey, UserResourcePermissionValueProvider.ProviderName, userId.ToString(), isGranted); + } +} diff --git a/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/IdentityUserStore_Tests.cs b/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/IdentityUserStore_Tests.cs index 65c3ff5a39..b3285a4353 100644 --- a/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/IdentityUserStore_Tests.cs +++ b/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/IdentityUserStore_Tests.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Security.Claims; +using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Identity; using Shouldly; @@ -766,4 +768,103 @@ public class IdentityUserStore_Tests : AbpIdentityDomainTestBase await uow.CompleteAsync(); } } + + [Fact] + public async Task AddOrUpdatePasskeyAsync() + { + using (var uow = _unitOfWorkManager.Begin()) + { + var credentialId = (byte[]) [1, 2]; + var user = await _identityUserStore.FindByIdAsync(_testData.UserBobId.ToString()); + user.Passkeys.ShouldBeEmpty(); + + var passkey = new UserPasskeyInfo(credentialId, null!, default, 0, null, false, false, false, null!, null!); + await _identityUserStore.AddOrUpdatePasskeyAsync(user, passkey, CancellationToken.None); + + user = await _identityUserStore.FindByIdAsync(_testData.UserBobId.ToString()); + user.Passkeys.ShouldNotBeEmpty(); + user.FindPasskey(credentialId).ShouldNotBeNull(); + + await uow.CompleteAsync(); + } + } + + [Fact] + public async Task GetPasskeysAsync() + { + using (var uow = _unitOfWorkManager.Begin()) + { + var user = await _identityUserStore.FindByIdAsync(_testData.UserJohnId.ToString()); + var passkeys = await _identityUserStore.GetPasskeysAsync(user, CancellationToken.None); + passkeys.Count.ShouldBe(2); + + user = await _identityUserStore.FindByIdAsync(_testData.UserBobId.ToString()); + passkeys = await _identityUserStore.GetPasskeysAsync(user, CancellationToken.None); + passkeys.ShouldBeEmpty(); + + await uow.CompleteAsync(); + } + } + + [Fact] + public async Task FindByPasskeyIdAsync() + { + using (var uow = _unitOfWorkManager.Begin()) + { + var user = await _identityUserStore.FindByPasskeyIdAsync(_testData.PasskeyCredentialId1, CancellationToken.None); + user.ShouldNotBeNull(); + user.Id.ShouldBe(_testData.UserJohnId); + + user = await _identityUserStore.FindByPasskeyIdAsync(_testData.PasskeyCredentialId2, CancellationToken.None); + user.ShouldNotBeNull(); + user.Id.ShouldBe(_testData.UserJohnId); + + user = await _identityUserStore.FindByPasskeyIdAsync(_testData.PasskeyCredentialId3, CancellationToken.None); + user.ShouldNotBeNull(); + user.Id.ShouldBe(_testData.UserNeoId); + + await uow.CompleteAsync(); + } + } + + [Fact] + public async Task FindPasskeyAsync() + { + using (var uow = _unitOfWorkManager.Begin()) + { + var user = await _identityUserStore.FindByIdAsync(_testData.UserJohnId.ToString()); + var passkey = await _identityUserStore.FindPasskeyAsync(user, _testData.PasskeyCredentialId1, CancellationToken.None); + passkey.ShouldNotBeNull(); + passkey.CredentialId.ShouldBe(_testData.PasskeyCredentialId1); + + passkey = await _identityUserStore.FindPasskeyAsync(user, _testData.PasskeyCredentialId2, CancellationToken.None); + passkey.ShouldNotBeNull(); + passkey.CredentialId.ShouldBe(_testData.PasskeyCredentialId2); + + passkey = await _identityUserStore.FindPasskeyAsync(user, _testData.PasskeyCredentialId3, CancellationToken.None); + passkey.ShouldBeNull(); + + await uow.CompleteAsync(); + } + } + + [Fact] + public async Task RemovePasskeyAsync() + { + using (var uow = _unitOfWorkManager.Begin()) + { + var user = await _identityUserStore.FindByIdAsync(_testData.UserJohnId.ToString()); + user.Passkeys.Count.ShouldBe(2); + + var credentialId = user.Passkeys.First().CredentialId; + + await _identityUserStore.RemovePasskeyAsync(user, credentialId, CancellationToken.None); + + user = await _identityUserStore.FindByIdAsync(_testData.UserJohnId.ToString()); + user.Passkeys.Count.ShouldBe(1); + user.FindPasskey(credentialId).ShouldBeNull(); + + await uow.CompleteAsync(); + } + } } diff --git a/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/UserRoleFinder_Tests.cs b/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/UserRoleFinder_Tests.cs index 524c7c25f3..56f9049b92 100644 --- a/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/UserRoleFinder_Tests.cs +++ b/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/UserRoleFinder_Tests.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Threading.Tasks; +using System.Threading.Tasks; using Shouldly; using Xunit; @@ -19,11 +16,47 @@ public class UserRoleFinder_Tests : AbpIdentityDomainTestBase } [Fact] - public async Task GetRolesAsync() + public async Task GetRoleNamesAsync() { - var roleNames = await _userRoleFinder.GetRolesAsync(_testData.UserJohnId); + var roleNames = await _userRoleFinder.GetRoleNamesAsync(_testData.UserJohnId); roleNames.ShouldNotBeEmpty(); roleNames.ShouldContain(x => x == "moderator"); roleNames.ShouldContain(x => x == "supporter"); } + + [Fact] + public async Task SearchUserAsync() + { + var userResults = await _userRoleFinder.SearchUserAsync("john"); + userResults.ShouldNotBeEmpty(); + userResults.ShouldContain(x => x.Id == _testData.UserJohnId); + } + + [Fact] + public async Task SearchRoleAsync() + { + var roleResults = await _userRoleFinder.SearchRoleAsync("moderator"); + roleResults.ShouldNotBeEmpty(); + roleResults.ShouldContain(x => x.RoleName == "moderator"); + } + + [Fact] + public async Task SearchUserByIdsAsync() + { + var userResults = await _userRoleFinder.SearchUserByIdsAsync(new[] { _testData.UserJohnId, _testData.UserBobId }); + userResults.ShouldNotBeEmpty(); + userResults.Count.ShouldBe(2); + userResults.ShouldContain(x => x.Id == _testData.UserJohnId && x.UserName == "john.nash"); + userResults.ShouldContain(x => x.Id == _testData.UserBobId && x.UserName == "bob"); + } + + [Fact] + public async Task SearchRoleByNamesAsync() + { + var roleResults = await _userRoleFinder.SearchRoleByNamesAsync(new[] { "moderator", "manager" }); + roleResults.ShouldNotBeEmpty(); + roleResults.Count.ShouldBe(2); + roleResults.ShouldContain(x => x.RoleName == "moderator"); + roleResults.ShouldContain(x => x.RoleName == "manager"); + } } diff --git a/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/AbpIdentityTestDataBuilder.cs b/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/AbpIdentityTestDataBuilder.cs index 944c6928c1..15ef80b4c4 100644 --- a/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/AbpIdentityTestDataBuilder.cs +++ b/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/AbpIdentityTestDataBuilder.cs @@ -140,6 +140,8 @@ public class AbpIdentityTestDataBuilder : ITransientDependency john.AddLogin(new UserLoginInfo("twitter", "johnx", "John Nash")); john.AddClaim(_guidGenerator, new Claim("TestClaimType", "42")); john.SetToken("test-provider", "test-name", "test-value"); + john.AddPasskey(_testData.PasskeyCredentialId1, new IdentityPasskeyData()); + john.AddPasskey(_testData.PasskeyCredentialId2, new IdentityPasskeyData()); await _userRepository.InsertAsync(john); var david = new IdentityUser(_testData.UserDavidId, "david", "david@abp.io"); @@ -152,6 +154,7 @@ public class AbpIdentityTestDataBuilder : ITransientDependency neo.AddRole(_supporterRole.Id); neo.AddClaim(_guidGenerator, new Claim("TestClaimType", "43")); neo.AddOrganizationUnit(_ou111.Id); + neo.AddPasskey(_testData.PasskeyCredentialId3, new IdentityPasskeyData()); await _userRepository.InsertAsync(neo); var bob = new IdentityUser(_testData.UserBobId, "bob", "bob@abp.io"); diff --git a/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/IdentityTestData.cs b/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/IdentityTestData.cs index 0172dc016a..b6cb14de5c 100644 --- a/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/IdentityTestData.cs +++ b/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/IdentityTestData.cs @@ -15,4 +15,7 @@ public class IdentityTestData : ISingletonDependency public Guid UserBobId { get; } = Guid.NewGuid(); public Guid AgeClaimId { get; } = Guid.NewGuid(); public Guid EducationClaimId { get; } = Guid.NewGuid(); + public byte[] PasskeyCredentialId1 { get; } = (byte[])[1, 2, 3, 4, 5, 6, 7, 8]; + public byte[] PasskeyCredentialId2 { get; } = (byte[])[8, 7, 6, 5, 4, 3, 2, 1]; + public byte[] PasskeyCredentialId3 { get; } = (byte[])[1, 2, 3, 4, 8, 7, 6, 5,]; } diff --git a/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/IdentityUserRepository_Tests.cs b/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/IdentityUserRepository_Tests.cs index e81c0cbd38..58dc072359 100644 --- a/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/IdentityUserRepository_Tests.cs +++ b/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/IdentityUserRepository_Tests.cs @@ -159,7 +159,7 @@ public abstract class IdentityUserRepository_Tests : AbpIdentity StringComparison.OrdinalIgnoreCase ).ShouldBeGreaterThan(0); } - + users = await UserRepository.GetListAsync(null, 5, 0, null, roleId: TestData.RoleManagerId); users.ShouldContain(x => x.UserName == "john.nash"); users.ShouldContain(x => x.UserName == "neo"); @@ -294,4 +294,23 @@ public abstract class IdentityUserRepository_Tests : AbpIdentity ou112Users.ShouldContain(x => x.UserName == "john.nash"); ou112Users.ShouldContain(x => x.UserName == "neo"); } + + + [Fact] + public async Task FindByPasskeyIdAsync() + { + var user = await UserRepository.FindByPasskeyIdAsync(TestData.PasskeyCredentialId1); + user.ShouldNotBeNull(); + user.Id.ShouldBe(TestData.UserJohnId); + + user = await UserRepository.FindByPasskeyIdAsync(TestData.PasskeyCredentialId2); + user.ShouldNotBeNull(); + user.Id.ShouldBe(TestData.UserJohnId); + + user = await UserRepository.FindByPasskeyIdAsync(TestData.PasskeyCredentialId3); + user.ShouldNotBeNull(); + user.Id.ShouldBe(TestData.UserNeoId); + + (await UserRepository.FindByPasskeyIdAsync((byte[])[1, 2, 3])).ShouldBeNull(); + } } diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AspNetIdentity/AbpResourceOwnerPasswordValidator.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AspNetIdentity/AbpResourceOwnerPasswordValidator.cs index d4e0fb5490..b5694cb2fa 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AspNetIdentity/AbpResourceOwnerPasswordValidator.cs +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AspNetIdentity/AbpResourceOwnerPasswordValidator.cs @@ -322,6 +322,9 @@ public class AbpResourceOwnerPasswordValidator : IResourceOwnerPasswordValidator additionalClaims.ToArray() ); + user.SetLastSignInTime(DateTimeOffset.UtcNow); + await UserManager.UpdateAsync(user); + await IdentitySecurityLogManager.SaveAsync( new IdentitySecurityLogContext { diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/TokenController.Password.cs b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/TokenController.Password.cs index 5059c7c5ca..07be60da44 100644 --- a/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/TokenController.Password.cs +++ b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/TokenController.Password.cs @@ -405,6 +405,9 @@ public partial class TokenController } ); + user.SetLastSignInTime(DateTimeOffset.UtcNow); + await UserManager.UpdateAsync(user); + return SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); } diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Application.Contracts/Volo/Abp/PermissionManagement/GetResourcePermissionDefinitionListResultDto.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Application.Contracts/Volo/Abp/PermissionManagement/GetResourcePermissionDefinitionListResultDto.cs new file mode 100644 index 0000000000..ea1ce882df --- /dev/null +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Application.Contracts/Volo/Abp/PermissionManagement/GetResourcePermissionDefinitionListResultDto.cs @@ -0,0 +1,8 @@ +using System.Collections.Generic; + +namespace Volo.Abp.PermissionManagement; + +public class GetResourcePermissionDefinitionListResultDto +{ + public List Permissions { get; set; } +} diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Application.Contracts/Volo/Abp/PermissionManagement/GetResourcePermissionListResultDto.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Application.Contracts/Volo/Abp/PermissionManagement/GetResourcePermissionListResultDto.cs new file mode 100644 index 0000000000..b9c4db2190 --- /dev/null +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Application.Contracts/Volo/Abp/PermissionManagement/GetResourcePermissionListResultDto.cs @@ -0,0 +1,8 @@ +using System.Collections.Generic; + +namespace Volo.Abp.PermissionManagement; + +public class GetResourcePermissionListResultDto +{ + public List Permissions { get; set; } +} diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Application.Contracts/Volo/Abp/PermissionManagement/GetResourcePermissionWithProviderListResultDto.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Application.Contracts/Volo/Abp/PermissionManagement/GetResourcePermissionWithProviderListResultDto.cs new file mode 100644 index 0000000000..dcf4556d04 --- /dev/null +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Application.Contracts/Volo/Abp/PermissionManagement/GetResourcePermissionWithProviderListResultDto.cs @@ -0,0 +1,8 @@ +using System.Collections.Generic; + +namespace Volo.Abp.PermissionManagement; + +public class GetResourcePermissionWithProviderListResultDto +{ + public List Permissions { get; set; } +} diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Application.Contracts/Volo/Abp/PermissionManagement/GetResourceProviderListResultDto.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Application.Contracts/Volo/Abp/PermissionManagement/GetResourceProviderListResultDto.cs new file mode 100644 index 0000000000..ede31a6ca5 --- /dev/null +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Application.Contracts/Volo/Abp/PermissionManagement/GetResourceProviderListResultDto.cs @@ -0,0 +1,8 @@ +using System.Collections.Generic; + +namespace Volo.Abp.PermissionManagement; + +public class GetResourceProviderListResultDto +{ + public List Providers { get; set; } +} diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Application.Contracts/Volo/Abp/PermissionManagement/GrantedResourcePermissionDto.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Application.Contracts/Volo/Abp/PermissionManagement/GrantedResourcePermissionDto.cs new file mode 100644 index 0000000000..bbb1de112a --- /dev/null +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Application.Contracts/Volo/Abp/PermissionManagement/GrantedResourcePermissionDto.cs @@ -0,0 +1,8 @@ +namespace Volo.Abp.PermissionManagement; + +public class GrantedResourcePermissionDto +{ + public string Name { get; set; } + + public string DisplayName { get; set; } +} diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Application.Contracts/Volo/Abp/PermissionManagement/IPermissionAppService.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Application.Contracts/Volo/Abp/PermissionManagement/IPermissionAppService.cs index 819d643a21..dd24d436be 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Application.Contracts/Volo/Abp/PermissionManagement/IPermissionAppService.cs +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Application.Contracts/Volo/Abp/PermissionManagement/IPermissionAppService.cs @@ -11,4 +11,18 @@ public interface IPermissionAppService : IApplicationService Task GetByGroupAsync([NotNull] string groupName, [NotNull] string providerName, [NotNull] string providerKey); Task UpdateAsync([NotNull] string providerName, [NotNull] string providerKey, UpdatePermissionsDto input); + + Task GetResourceProviderKeyLookupServicesAsync(string resourceName); + + Task SearchResourceProviderKeyAsync(string resourceName, string serviceName, string filter, int page); + + Task GetResourceDefinitionsAsync([NotNull] string resourceName); + + Task GetResourceAsync([NotNull] string resourceName, [NotNull] string resourceKey); + + Task GetResourceByProviderAsync([NotNull] string resourceName, [NotNull] string resourceKey, [NotNull] string providerName, [NotNull] string providerKey); + + Task UpdateResourceAsync([NotNull] string resourceName, [NotNull] string resourceKey, UpdateResourcePermissionsDto input); + + Task DeleteResourceAsync([NotNull] string resourceName, [NotNull] string resourceKey, [NotNull] string providerName, [NotNull] string providerKey); } diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Application.Contracts/Volo/Abp/PermissionManagement/ResourcePermissionDefinitionDto.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Application.Contracts/Volo/Abp/PermissionManagement/ResourcePermissionDefinitionDto.cs new file mode 100644 index 0000000000..b8218b562c --- /dev/null +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Application.Contracts/Volo/Abp/PermissionManagement/ResourcePermissionDefinitionDto.cs @@ -0,0 +1,8 @@ +namespace Volo.Abp.PermissionManagement; + +public class ResourcePermissionDefinitionDto +{ + public string Name { get; set; } + + public string DisplayName { get; set; } +} diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Application.Contracts/Volo/Abp/PermissionManagement/ResourcePermissionGrantInfoDto.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Application.Contracts/Volo/Abp/PermissionManagement/ResourcePermissionGrantInfoDto.cs new file mode 100644 index 0000000000..cf286815a5 --- /dev/null +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Application.Contracts/Volo/Abp/PermissionManagement/ResourcePermissionGrantInfoDto.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; + +namespace Volo.Abp.PermissionManagement; + +public class ResourcePermissionGrantInfoDto +{ + public string ProviderName { get; set; } + + public string ProviderKey { get; set; } + + public string ProviderDisplayName { get; set; } + + public string ProviderNameDisplayName { get; set; } + + public List Permissions { get; set; } +} diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Application.Contracts/Volo/Abp/PermissionManagement/ResourcePermissionWithProdiverGrantInfoDto.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Application.Contracts/Volo/Abp/PermissionManagement/ResourcePermissionWithProdiverGrantInfoDto.cs new file mode 100644 index 0000000000..d56e217cd6 --- /dev/null +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Application.Contracts/Volo/Abp/PermissionManagement/ResourcePermissionWithProdiverGrantInfoDto.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; + +namespace Volo.Abp.PermissionManagement; + +public class ResourcePermissionWithProdiverGrantInfoDto +{ + public string Name { get; set; } + + public string DisplayName { get; set; } + + public List Providers { get; set; } + + public bool IsGranted { get; set; } +} diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Application.Contracts/Volo/Abp/PermissionManagement/ResourceProviderDto.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Application.Contracts/Volo/Abp/PermissionManagement/ResourceProviderDto.cs new file mode 100644 index 0000000000..98ee22c6b7 --- /dev/null +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Application.Contracts/Volo/Abp/PermissionManagement/ResourceProviderDto.cs @@ -0,0 +1,8 @@ +namespace Volo.Abp.PermissionManagement; + +public class ResourceProviderDto +{ + public string Name { get; set; } + + public string DisplayName { get; set; } +} diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Application.Contracts/Volo/Abp/PermissionManagement/SearchProviderKeyInfo.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Application.Contracts/Volo/Abp/PermissionManagement/SearchProviderKeyInfo.cs new file mode 100644 index 0000000000..efc6ae2b18 --- /dev/null +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Application.Contracts/Volo/Abp/PermissionManagement/SearchProviderKeyInfo.cs @@ -0,0 +1,8 @@ +namespace Volo.Abp.PermissionManagement; + +public class SearchProviderKeyInfo +{ + public string ProviderKey { get; set; } + + public string ProviderDisplayName { get; set; } +} diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Application.Contracts/Volo/Abp/PermissionManagement/SearchProviderKeyListResultDto.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Application.Contracts/Volo/Abp/PermissionManagement/SearchProviderKeyListResultDto.cs new file mode 100644 index 0000000000..7fe4edd6e7 --- /dev/null +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Application.Contracts/Volo/Abp/PermissionManagement/SearchProviderKeyListResultDto.cs @@ -0,0 +1,8 @@ +using System.Collections.Generic; + +namespace Volo.Abp.PermissionManagement; + +public class SearchProviderKeyListResultDto +{ + public List Keys { get; set; } +} diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Application.Contracts/Volo/Abp/PermissionManagement/UpdateResourcePermissionsDto.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Application.Contracts/Volo/Abp/PermissionManagement/UpdateResourcePermissionsDto.cs new file mode 100644 index 0000000000..f82c27ff02 --- /dev/null +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Application.Contracts/Volo/Abp/PermissionManagement/UpdateResourcePermissionsDto.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; + +namespace Volo.Abp.PermissionManagement; + +public class UpdateResourcePermissionsDto +{ + public string ProviderName { get; set; } + + public string ProviderKey { get; set; } + + public List Permissions { get; set; } +} diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Application/Volo/Abp/PermissionManagement/PermissionAppService.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Application/Volo/Abp/PermissionManagement/PermissionAppService.cs index e628c39644..e26e387fae 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Application/Volo/Abp/PermissionManagement/PermissionAppService.cs +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Application/Volo/Abp/PermissionManagement/PermissionAppService.cs @@ -18,12 +18,18 @@ public class PermissionAppService : ApplicationService, IPermissionAppService { protected PermissionManagementOptions Options { get; } protected IPermissionManager PermissionManager { get; } + protected IPermissionChecker PermissionChecker { get; } + protected IResourcePermissionManager ResourcePermissionManager { get; } + protected IResourcePermissionGrantRepository ResourcePermissionGrantRepository { get; } protected IPermissionDefinitionManager PermissionDefinitionManager { get; } protected ISimpleStateCheckerManager SimpleStateCheckerManager { get; } public PermissionAppService( IPermissionManager permissionManager, + IPermissionChecker permissionChecker, IPermissionDefinitionManager permissionDefinitionManager, + IResourcePermissionManager resourcePermissionManager, + IResourcePermissionGrantRepository resourcePermissionGrantRepository, IOptions options, ISimpleStateCheckerManager simpleStateCheckerManager) { @@ -32,6 +38,9 @@ public class PermissionAppService : ApplicationService, IPermissionAppService Options = options.Value; PermissionManager = permissionManager; + PermissionChecker = permissionChecker; + ResourcePermissionManager = resourcePermissionManager; + ResourcePermissionGrantRepository = resourcePermissionGrantRepository; PermissionDefinitionManager = permissionDefinitionManager; SimpleStateCheckerManager = simpleStateCheckerManager; } @@ -160,6 +169,206 @@ public class PermissionAppService : ApplicationService, IPermissionAppService } } + public virtual async Task GetResourceProviderKeyLookupServicesAsync(string resourceName) + { + var resourcePermissions = await ResourcePermissionManager.GetAvailablePermissionsAsync(resourceName); + if (!resourcePermissions.Any() || + !await AuthorizationService.IsGrantedAnyAsync(resourcePermissions.Select(p => p.ManagementPermissionName!).ToArray())) + { + return new GetResourceProviderListResultDto + { + Providers = new List() + }; + } + + var lookupServices = await ResourcePermissionManager.GetProviderKeyLookupServicesAsync(); + return new GetResourceProviderListResultDto + { + Providers = lookupServices.Select(s => new ResourceProviderDto + { + Name = s.Name, + DisplayName = s.DisplayName.Localize(StringLocalizerFactory), + }).ToList() + }; + } + + public virtual async Task SearchResourceProviderKeyAsync(string resourceName, string serviceName, string filter, int page) + { + var resourcePermissions = await ResourcePermissionManager.GetAvailablePermissionsAsync(resourceName); + if (resourcePermissions.IsNullOrEmpty() || + !await AuthorizationService.IsGrantedAnyAsync(resourcePermissions.Select(p => p.ManagementPermissionName!).ToArray())) + { + return new SearchProviderKeyListResultDto(); + } + + var lookupService = await ResourcePermissionManager.GetProviderKeyLookupServiceAsync(serviceName); + var keys = await lookupService.SearchAsync(filter, page); + return new SearchProviderKeyListResultDto + { + Keys = keys.Select(x => new SearchProviderKeyInfo + { + ProviderKey = x.ProviderKey, + ProviderDisplayName = x.ProviderDisplayName, + }).ToList() + }; + } + + public virtual async Task GetResourceDefinitionsAsync(string resourceName) + { + var result = new GetResourcePermissionDefinitionListResultDto + { + Permissions = new List() + }; + + var resourcePermissions = await ResourcePermissionManager.GetAvailablePermissionsAsync(resourceName); + var permissionGrants = (await PermissionChecker.IsGrantedAsync(resourcePermissions + .Select(rp => rp.ManagementPermissionName!) + .Distinct().ToArray())).Result.Where(x => x.Value == PermissionGrantResult.Granted).Select(x => x.Key) + .ToHashSet(); + foreach (var resourcePermission in resourcePermissions) + { + if (!permissionGrants.Contains(resourcePermission.ManagementPermissionName)) + { + continue; + } + + result.Permissions.Add(new ResourcePermissionDefinitionDto + { + Name = resourcePermission.Name, + DisplayName = resourcePermission.DisplayName?.Localize(StringLocalizerFactory), + }); + } + + return result; + } + + public virtual async Task GetResourceAsync(string resourceName, string resourceKey) + { + var result = new GetResourcePermissionListResultDto + { + Permissions = new List() + }; + + var resourcePermissions = await ResourcePermissionManager.GetAvailablePermissionsAsync(resourceName); + var resourcePermissionGrants = await ResourcePermissionManager.GetAllGroupAsync(resourceName, resourceKey); + var permissionGrants = (await PermissionChecker.IsGrantedAsync(resourcePermissions + .Select(rp => rp.ManagementPermissionName!) + .Distinct().ToArray())).Result.Where(x => x.Value == PermissionGrantResult.Granted).Select(x => x.Key) + .ToHashSet(); + foreach (var resourcePermissionGrant in resourcePermissionGrants) + { + var resourcePermissionGrantInfoDto = new ResourcePermissionGrantInfoDto + { + ProviderName = resourcePermissionGrant.ProviderName, + ProviderKey = resourcePermissionGrant.ProviderKey, + ProviderDisplayName = resourcePermissionGrant.ProviderDisplayName, + ProviderNameDisplayName = resourcePermissionGrant.ProviderNameDisplayName?.Localize(StringLocalizerFactory), + Permissions = new List() + }; + foreach (var permission in resourcePermissionGrant.Permissions) + { + var resourcePermission = resourcePermissions.FirstOrDefault(x => x.Name == permission); + if (resourcePermission == null) + { + continue; + } + + if (!permissionGrants.Contains(resourcePermission.ManagementPermissionName)) + { + continue; + } + + resourcePermissionGrantInfoDto.Permissions.Add(new GrantedResourcePermissionDto() + { + Name = permission, + DisplayName = resourcePermission?.DisplayName.Localize(StringLocalizerFactory), + }); + } + + if(resourcePermissionGrantInfoDto.Permissions.Any()) + { + result.Permissions.Add(resourcePermissionGrantInfoDto); + } + } + + return result; + } + + public virtual async Task GetResourceByProviderAsync(string resourceName, string resourceKey, string providerName, string providerKey) + { + var result = new GetResourcePermissionWithProviderListResultDto + { + Permissions = new List() + }; + + var resourcePermissions = await ResourcePermissionManager.GetAvailablePermissionsAsync(resourceName); + var resourcePermissionGrants = await ResourcePermissionManager.GetAllAsync(resourceName, resourceKey, providerName, providerKey); + var permissionGrants = (await PermissionChecker.IsGrantedAsync(resourcePermissions + .Select(rp => rp.ManagementPermissionName!) + .Distinct().ToArray())).Result.Where(x => x.Value == PermissionGrantResult.Granted).Select(x => x.Key) + .ToHashSet(); + foreach (var resourcePermissionGrant in resourcePermissionGrants) + { + var resourcePermission = resourcePermissions.FirstOrDefault(x => x.Name == resourcePermissionGrant.Name); + if (resourcePermission == null) + { + continue; + } + + if (!permissionGrants.Contains(resourcePermission.ManagementPermissionName)) + { + continue; + } + + result.Permissions.Add(new ResourcePermissionWithProdiverGrantInfoDto + { + Name = resourcePermissionGrant.Name, + DisplayName = resourcePermission?.DisplayName.Localize(StringLocalizerFactory), + Providers = resourcePermissionGrant.Providers.Select(x => x.Name).ToList(), + IsGranted = resourcePermissionGrant.IsGranted + }); + } + + return result; + } + + public virtual async Task UpdateResourceAsync(string resourceName, string resourceKey, UpdateResourcePermissionsDto input) + { + var resourcePermissions = await ResourcePermissionManager.GetAvailablePermissionsAsync(resourceName); + var permissionGrants = (await PermissionChecker.IsGrantedAsync(resourcePermissions + .Select(rp => rp.ManagementPermissionName!) + .Distinct().ToArray())).Result.Where(x => x.Value == PermissionGrantResult.Granted).Select(x => x.Key) + .ToHashSet(); + foreach (var resourcePermission in resourcePermissions) + { + if (!permissionGrants.Contains(resourcePermission.ManagementPermissionName)) + { + continue; + } + + var isGranted = !input.Permissions.IsNullOrEmpty() && input.Permissions.Any(p => p == resourcePermission.Name); + await ResourcePermissionManager.SetAsync(resourcePermission.Name, resourceName, resourceKey, input.ProviderName, input.ProviderKey, isGranted); + } + } + + public virtual async Task DeleteResourceAsync(string resourceName, string resourceKey, string providerName, string providerKey) + { + var resourcePermissions = await ResourcePermissionManager.GetAvailablePermissionsAsync(resourceName); + var permissionGrants = (await PermissionChecker.IsGrantedAsync(resourcePermissions + .Select(rp => rp.ManagementPermissionName!) + .Distinct().ToArray())).Result.Where(x => x.Value == PermissionGrantResult.Granted).Select(x => x.Key) + .ToHashSet(); + foreach (var resourcePermission in resourcePermissions) + { + if (!permissionGrants.Contains(resourcePermission.ManagementPermissionName)) + { + continue; + } + + await ResourcePermissionManager.DeleteAsync(resourcePermission.Name, resourceName, resourceKey, providerName, providerKey); + } + } + protected virtual async Task CheckProviderPolicy(string providerName) { var policyName = Options.ProviderPolicies.GetOrDefault(providerName); diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Blazor/Components/PermissionManagementModal.razor.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Blazor/Components/PermissionManagementModal.razor.cs index 2a618b9d4b..b3c917177b 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Blazor/Components/PermissionManagementModal.razor.cs +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Blazor/Components/PermissionManagementModal.razor.cs @@ -6,6 +6,7 @@ using Blazorise; using Microsoft.AspNetCore.Components; using Microsoft.Extensions.Options; using Volo.Abp.AspNetCore.Components.Web.Configuration; +using Volo.Abp.Authorization.Permissions; using Volo.Abp.Localization; using Volo.Abp.PermissionManagement.Localization; @@ -153,7 +154,13 @@ public partial class PermissionManagementModal await PermissionAppService.UpdateAsync(_providerName, _providerKey, updateDto); - await CurrentApplicationConfigurationCacheResetService.ResetAsync(); + Guid? userId = null; + if (_providerName == UserPermissionValueProvider.ProviderName && Guid.TryParse(_providerKey, out var parsedUserId)) + { + userId = parsedUserId; + } + + await CurrentApplicationConfigurationCacheResetService.ResetAsync(userId); await InvokeAsync(_modal.Hide); await Notify.Success(L["SavedSuccessfully"]); diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Blazor/Components/ResourcePermissionManagementModal.razor b/modules/permission-management/src/Volo.Abp.PermissionManagement.Blazor/Components/ResourcePermissionManagementModal.razor new file mode 100644 index 0000000000..bd12891063 --- /dev/null +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Blazor/Components/ResourcePermissionManagementModal.razor @@ -0,0 +1,179 @@ +@using Blazorise.Components +@using Volo.Abp.BlazoriseUI.Components +@using Volo.Abp.PermissionManagement.Localization +@inherits Volo.Abp.AspNetCore.Components.AbpComponentBase +@inject AbpBlazorMessageLocalizerHelper LH + + + + + @L["ResourcePermissions"] - @ResourceDisplayName + + + + @if(HasAnyResourcePermission && HasAnyResourceProviderKeyLookupService) + { +
+ +
+ + + + + + + @L["Actions"] + + + + @L["Edit"] + + + @L["Delete"] + + + + + + + + @{ + + @context.ProviderName + + @context.ProviderDisplayName + } + + + + + @{ + foreach (var permission in context.Permissions) + { + @permission.DisplayName + } + } + + + + + @L["NoDataAvailableInDatatable"] + + + } + else + { + + } +
+ + + +
+
+ + + +
+ + @L["AddResourcePermission"] + + + + +
+ + @foreach(var keyLookupService in ResourceProviderKeyLookupServices) + { + @keyLookupService.DisplayName + } + + + + + + + + + + +
+
+

@L["ResourcePermissionPermissions"]

+ @L["GrantAllResourcePermissions"] +
+ @foreach (var permission in CreateEntity.Permissions) + { + @permission.DisplayName + } +
+
+
+
+ + + + +
+
+
+ + + +
+ + @L["UpdateResourcePermission"] + + + + +
+

@L["ResourcePermissionPermissions"]

+ @L["GrantAllResourcePermissions"] +
+ @foreach (var permission in EditEntity.Permissions) + { + @permission.DisplayName + } +
+
+
+
+ + + + +
+
+
diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Blazor/Components/ResourcePermissionManagementModal.razor.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Blazor/Components/ResourcePermissionManagementModal.razor.cs new file mode 100644 index 0000000000..a69eacea3c --- /dev/null +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Blazor/Components/ResourcePermissionManagementModal.razor.cs @@ -0,0 +1,317 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Blazorise; +using Blazorise.Components; +using Microsoft.AspNetCore.Components; +using Volo.Abp.AspNetCore.Components.Messages; +using Volo.Abp.PermissionManagement.Localization; + +namespace Volo.Abp.PermissionManagement.Blazor.Components; + +public partial class ResourcePermissionManagementModal +{ + [Inject] protected IPermissionAppService PermissionAppService { get; set; } + + [Inject] protected IUiMessageService UiMessageService { get; set; } + + protected Modal Modal { get; set; } + + public bool HasAnyResourcePermission { get; set; } + public bool HasAnyResourceProviderKeyLookupService { get; set; } + protected string ResourceName { get; set; } + protected string ResourceKey { get; set; } + protected string ResourceDisplayName { get; set; } + protected int PageSize { get; set; } = 10; + + protected Modal CreateModal { get; set; } + protected Validations CreateValidationsRef { get; set; } + protected CreateModel CreateEntity { get; set; } = new CreateModel + { + Permissions = [] + }; + protected Autocomplete ProviderKeyAutocompleteRef { get; set; } + protected Blazorise.Validation ProviderKeyValidationRef { get; set; } + public GetResourcePermissionDefinitionListResultDto ResourcePermissionDefinitions { get; set; } = new() + { + Permissions = [] + }; + protected string CurrentLookupService { get; set; } + protected string ProviderKey { get; set; } + protected string ProviderDisplayName { get; set; } + protected List ResourceProviderKeyLookupServices { get; set; } = new(); + protected List ProviderKeys { get; set; } = new(); + protected GetResourcePermissionListResultDto ResourcePermissionList = new() + { + Permissions = [] + }; + + protected Validations EditValidationsRef { get; set; } + protected Modal EditModal { get; set; } + protected EditModel EditEntity { get; set; } = new EditModel + { + Permissions = [] + }; + + public ResourcePermissionManagementModal() + { + LocalizationResource = typeof(AbpPermissionManagementResource); + } + + public virtual async Task OpenAsync(string resourceName, string resourceKey, string resourceDisplayName) + { + try + { + ResourceName = resourceName; + ResourceKey = resourceKey; + ResourceDisplayName = resourceDisplayName; + + ResourcePermissionDefinitions = await PermissionAppService.GetResourceDefinitionsAsync(ResourceName); + ResourceProviderKeyLookupServices = (await PermissionAppService.GetResourceProviderKeyLookupServicesAsync(ResourceName)).Providers; + + HasAnyResourcePermission = ResourcePermissionDefinitions.Permissions.Any(); + if (HasAnyResourcePermission) + { + HasAnyResourceProviderKeyLookupService = ResourceProviderKeyLookupServices.Count > 0; + } + + await InvokeAsync(StateHasChanged); + + ResourcePermissionList = await PermissionAppService.GetResourceAsync(ResourceName, ResourceKey); + + await Modal.Show(); + + } + catch (Exception ex) + { + await HandleErrorAsync(ex); + } + } + + protected virtual async Task CloseModal() + { + await Modal.Hide(); + } + + protected virtual Task ClosingModal(ModalClosingEventArgs eventArgs) + { + eventArgs.Cancel = eventArgs.CloseReason == CloseReason.FocusLostClosing; + return Task.CompletedTask; + } + + protected virtual async Task OpenCreateModalAsync() + { + CurrentLookupService = ResourceProviderKeyLookupServices.FirstOrDefault()?.Name; + + ProviderKey = null; + ProviderDisplayName = null; + ProviderKeys = new List(); + await ProviderKeyAutocompleteRef.Clear(); + await CreateValidationsRef.ClearAll(); + + CreateEntity = new CreateModel + { + Permissions = ResourcePermissionDefinitions.Permissions.Select(x => new ResourcePermissionModel + { + Name = x.Name, + DisplayName = x.DisplayName, + IsGranted = false + }).ToList() + }; + + await CreateModal.Show(); + await InvokeAsync(StateHasChanged); + } + + protected virtual async Task SelectedProviderKeyAsync(string value) + { + ProviderKey = value; + ProviderDisplayName = ProviderKeys.FirstOrDefault(p => p.ProviderKey == value)?.ProviderDisplayName; + + var permissionGrants = await PermissionAppService.GetResourceByProviderAsync(ResourceName, ResourceKey, CurrentLookupService, ProviderKey); + foreach (var permission in CreateEntity.Permissions) + { + permission.IsGranted = permissionGrants.Permissions.Any(p => p.Name == permission.Name && p.Providers.Contains(CurrentLookupService) && p.IsGranted); + } + + await InvokeAsync(StateHasChanged); + } + + private async Task SearchProviderKeyAsync(AutocompleteReadDataEventArgs autocompleteReadDataEventArgs) + { + if ( !autocompleteReadDataEventArgs.CancellationToken.IsCancellationRequested ) + { + if (autocompleteReadDataEventArgs.SearchValue.IsNullOrWhiteSpace()) + { + ProviderKeys = new List(); + return; + } + + ProviderKeys = (await PermissionAppService.SearchResourceProviderKeyAsync(ResourceName, CurrentLookupService, autocompleteReadDataEventArgs.SearchValue, 1)).Keys; + + await InvokeAsync(StateHasChanged); + } + } + + protected virtual async Task OnPermissionCheckedChanged(ResourcePermissionModel permission, bool value) + { + permission.IsGranted = value; + await InvokeAsync(StateHasChanged); + } + + protected virtual async Task GrantAllAsync(bool value) + { + foreach (var permission in CreateEntity.Permissions) + { + permission.IsGranted = value; + } + + foreach (var permission in EditEntity.Permissions) + { + permission.IsGranted = value; + } + + await InvokeAsync(StateHasChanged); + } + + protected virtual async Task OpenEditModalAsync(ResourcePermissionGrantInfoDto permission) + { + var resourcePermissions = await PermissionAppService.GetResourceByProviderAsync(ResourceName, ResourceKey, permission.ProviderName, permission.ProviderKey); + EditEntity = new EditModel + { + ProviderName = permission.ProviderName, + ProviderKey = permission.ProviderKey, + Permissions = resourcePermissions.Permissions.Select(x => new ResourcePermissionModel + { + Name = x.Name, + DisplayName = x.DisplayName, + IsGranted = x.IsGranted + }).ToList() + }; + + await EditModal.Show(); + } + + protected virtual Task ClosingCreateModal(ModalClosingEventArgs eventArgs) + { + eventArgs.Cancel = eventArgs.CloseReason == CloseReason.FocusLostClosing; + return Task.CompletedTask; + } + + protected virtual Task ClosingEditModal(ModalClosingEventArgs eventArgs) + { + eventArgs.Cancel = eventArgs.CloseReason == CloseReason.FocusLostClosing; + return Task.CompletedTask; + } + + protected virtual async Task CloseCreateModalAsync() + { + await CreateModal.Hide(); + } + + protected virtual async Task CloseEditModalAsync() + { + await EditModal.Hide(); + } + + protected virtual async Task OnLookupServiceCheckedValueChanged(string value) + { + CurrentLookupService = value; + ProviderKey = null; + ProviderDisplayName = null; + await ProviderKeyAutocompleteRef.Clear(); + await CreateValidationsRef.ClearAll(); + await InvokeAsync(StateHasChanged); + } + + protected virtual void ValidateProviderKey(ValidatorEventArgs validatorEventArgs) + { + validatorEventArgs.Status = ProviderKey.IsNullOrWhiteSpace() + ? ValidationStatus.Error + : ValidationStatus.Success; + validatorEventArgs.ErrorText = L["ThisFieldIsRequired."]; + } + + protected virtual async Task CreateResourcePermissionAsync() + { + if (await CreateValidationsRef.ValidateAll()) + { + await PermissionAppService.UpdateResourceAsync( + ResourceName, + ResourceKey, + new UpdateResourcePermissionsDto + { + ProviderName = CurrentLookupService, + ProviderKey = ProviderKey, + Permissions = CreateEntity.Permissions.Where(p => p.IsGranted).Select(p => p.Name).ToList() + } + ); + + await CloseCreateModalAsync(); + ResourcePermissionList = await PermissionAppService.GetResourceAsync(ResourceName, ResourceKey); + await InvokeAsync(StateHasChanged); + } + } + + protected virtual async Task UpdateResourcePermissionAsync() + { + if (await EditValidationsRef.ValidateAll()) + { + await PermissionAppService.UpdateResourceAsync( + ResourceName, + ResourceKey, + new UpdateResourcePermissionsDto + { + ProviderName = EditEntity.ProviderName, + ProviderKey = EditEntity.ProviderKey, + Permissions = EditEntity.Permissions.Where(p => p.IsGranted).Select(p => p.Name).ToList() + } + ); + + await CloseEditModalAsync(); + ResourcePermissionList = await PermissionAppService.GetResourceAsync(ResourceName, ResourceKey); + await InvokeAsync(StateHasChanged); + } + } + + protected virtual async Task DeleteResourcePermissionAsync(ResourcePermissionGrantInfoDto permission) + { + if(await UiMessageService.Confirm(L["ResourcePermissionDeletionConfirmationMessage"])) + { + await PermissionAppService.DeleteResourceAsync( + ResourceName, + ResourceKey, + permission.ProviderName, + permission.ProviderKey + ); + + ResourcePermissionList = await PermissionAppService.GetResourceAsync(ResourceName, ResourceKey); + await Notify.Success(L["DeletedSuccessfully"]); + await InvokeAsync(StateHasChanged); + } + } + + public class CreateModel + { + public List Permissions { get; set; } + } + + public class EditModel + { + public string ProviderName { get; set; } + + public string ProviderKey { get; set; } + + public List Permissions { get; set; } + } + + public class ResourcePermissionModel + { + public string Name { get; set; } + + public string DisplayName { get; set; } + + public bool IsGranted { get; set; } + } +} diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/ar.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/ar.json index 30f4b647e8..c1a8e40b7b 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/ar.json +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/ar.json @@ -8,6 +8,22 @@ "SelectAllInThisTab": "تحديد الكل", "SaveWithoutAnyPermissionsWarningMessage": "هل أنت متأكد أنك تريد الحفظ بدون أي أذونات؟", "PermissionGroup": "مجموعة الأذونات", - "Filter": "تصفية" + "Filter": "تصفية", + "ResourcePermissions": "الأذونات", + "ResourcePermissionTarget": "الهدف", + "ResourcePermissionPermissions": "الأذونات", + "AddResourcePermission": "إضافة إذن", + "ResourcePermissionDeletionConfirmationMessage": "هل أنت متأكد أنك تريد حذف جميع الأذونات؟", + "UpdateResourcePermission": "تحديث الإذن", + "GrantAllResourcePermissions": "منح الكل", + "NoResourceProviderKeyLookupServiceFound": "لم يتم العثور على خدمة البحث عن مفتاح المزود", + "NoResourcePermissionFound": "لا توجد أي أذونات محددة.", + "UpdatePermission": "تحديث الإذن", + "NoPermissionsAssigned": "لم يتم تعيين أذونات", + "SelectProvider": "اختر المزود", + "SearchProviderKey": "بحث عن مفتاح المزود", + "Provider": "المزود", + "ErrorLoadingPermissions": "خطأ في تحميل الأذونات", + "PleaseSelectProviderAndPermissions": "يرجى اختيار المزود والأذونات" } } \ No newline at end of file diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/cs.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/cs.json index 814b8e423c..f8f9e642c3 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/cs.json +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/cs.json @@ -8,6 +8,22 @@ "SelectAllInThisTab": "Vybrat vše", "SaveWithoutAnyPermissionsWarningMessage": "Opravdu chcete ukládat bez jakýchkoli oprávnění?", "PermissionGroup": "Skupina oprávnění", - "Filter": "Filtr" + "Filter": "Filtr", + "ResourcePermissions": "Oprávnění", + "ResourcePermissionTarget": "Cíl", + "ResourcePermissionPermissions": "Oprávnění", + "AddResourcePermission": "Přidat oprávnění", + "ResourcePermissionDeletionConfirmationMessage": "Opravdu chcete smazat všechna oprávnění?", + "UpdateResourcePermission": "Aktualizovat oprávnění", + "GrantAllResourcePermissions": "Udělit vše", + "NoResourceProviderKeyLookupServiceFound": "Nebyla nalezena služba pro vyhledávání klíče poskytovatele zdrojů", + "NoResourcePermissionFound": "Pro aktuální prostředek není definováno žádné oprávnění.", + "UpdatePermission": "Aktualizovat oprávnění", + "NoPermissionsAssigned": "Nejsou přiřazena žádná oprávnění", + "SelectProvider": "Vybrat poskytovatele", + "SearchProviderKey": "Hledat klíč poskytovatele", + "Provider": "Poskytovatel", + "ErrorLoadingPermissions": "Chyba při načítání oprávnění", + "PleaseSelectProviderAndPermissions": "Vyberte prosím poskytovatele a oprávnění" } } \ No newline at end of file diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/de.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/de.json index 7a40c5d4d4..dcf9cf08e9 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/de.json +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/de.json @@ -8,6 +8,22 @@ "SelectAllInThisTab": "Alle auswählen", "SaveWithoutAnyPermissionsWarningMessage": "Sind Sie sicher, dass Sie ohne Berechtigungen speichern möchten?", "PermissionGroup": "Berechtigungsgruppe", - "Filter": "Filtern" + "Filter": "Filtern", + "ResourcePermissions": "Berechtigungen", + "ResourcePermissionTarget": "Ziel", + "ResourcePermissionPermissions": "Berechtigungen", + "AddResourcePermission": "Berechtigung hinzufügen", + "ResourcePermissionDeletionConfirmationMessage": "Sind Sie sicher, dass Sie alle Berechtigungen löschen möchten?", + "UpdateResourcePermission": "Berechtigung aktualisieren", + "GrantAllResourcePermissions": "Alle gewähren", + "NoResourceProviderKeyLookupServiceFound": "Es wurde kein Dienst zum Nachschlagen des Anbieterschlüssels gefunden", + "NoResourcePermissionFound": "Es ist keine Berechtigung definiert.", + "UpdatePermission": "Berechtigung aktualisieren", + "NoPermissionsAssigned": "Keine Berechtigungen zugewiesen", + "SelectProvider": "Anbieter auswählen", + "SearchProviderKey": "Anbieterschlüssel suchen", + "Provider": "Anbieter", + "ErrorLoadingPermissions": "Fehler beim Laden der Berechtigungen", + "PleaseSelectProviderAndPermissions": "Bitte wählen Sie einen Anbieter und Berechtigungen aus" } } \ No newline at end of file diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/el.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/el.json index 3449b7fab6..926943701c 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/el.json +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/el.json @@ -8,6 +8,22 @@ "SelectAllInThisTab": "Επιλογή όλων", "SaveWithoutAnyPermissionsWarningMessage": "Είστε βέβαιοι ότι θέλετε να αποθηκεύσετε χωρίς δικαιώματα;", "PermissionGroup": "Ομάδα δικαιωμάτων", - "Filter": "Φίλτρο" + "Filter": "Φίλτρο", + "ResourcePermissions": "Δικαιώματα", + "ResourcePermissionTarget": "Στόχος", + "ResourcePermissionPermissions": "Δικαιώματα", + "AddResourcePermission": "Προσθήκη δικαιώματος", + "ResourcePermissionDeletionConfirmationMessage": "Είστε βέβαιοι ότι θέλετε να διαγράψετε όλα τα δικαιώματα;", + "UpdateResourcePermission": "Ενημέρωση δικαιώματος", + "GrantAllResourcePermissions": "Παραχώρηση όλων", + "NoResourceProviderKeyLookupServiceFound": "Δεν βρέθηκε υπηρεσία αναζήτησης κλειδιού παρόχου", + "NoResourcePermissionFound": "Δεν έχει οριστεί καμία άδεια.", + "UpdatePermission": "Ενημέρωση δικαιώματος", + "NoPermissionsAssigned": "Δεν έχουν εκχωρηθεί δικαιώματα", + "SelectProvider": "Επιλέξτε πάροχο", + "SearchProviderKey": "Αναζήτηση κλειδιού παρόχου", + "Provider": "Πάροχος", + "ErrorLoadingPermissions": "Σφάλμα κατά τη φόρτωση δικαιωμάτων", + "PleaseSelectProviderAndPermissions": "Επιλέξτε πάροχο και δικαιώματα" } } \ No newline at end of file diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/en-GB.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/en-GB.json index be6cbcd1f5..11370fd6ff 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/en-GB.json +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/en-GB.json @@ -8,6 +8,22 @@ "SelectAllInThisTab": "Select all", "SaveWithoutAnyPermissionsWarningMessage": "Are you sure you want to save without any permissions?", "PermissionGroup": "Permission Group", - "Filter": "Filter" + "Filter": "Filter", + "ResourcePermissions": "Permissions", + "ResourcePermissionTarget": "Target", + "ResourcePermissionPermissions": "Permissions", + "AddResourcePermission": "Add permission", + "ResourcePermissionDeletionConfirmationMessage": "Are you sure you want to delete all permissions?", + "UpdateResourcePermission": "Update permission", + "GrantAllResourcePermissions": "Grant all", + "NoResourceProviderKeyLookupServiceFound": "There is no provider key lookup service was found", + "NoResourcePermissionFound": "There is no permission defined.", + "UpdatePermission": "Update permission", + "NoPermissionsAssigned": "No permissions assigned", + "SelectProvider": "Select provider", + "SearchProviderKey": "Search provider key", + "Provider": "Provider", + "ErrorLoadingPermissions": "Error loading permissions", + "PleaseSelectProviderAndPermissions": "Please select provider and permissions" } } \ No newline at end of file diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/en.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/en.json index b8299d4e5b..6a4c8d41ab 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/en.json +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/en.json @@ -8,6 +8,22 @@ "SelectAllInThisTab": "Select all", "SaveWithoutAnyPermissionsWarningMessage": "Are you sure you want to save without any permissions?", "PermissionGroup": "Permission Group", - "Filter": "Filter" + "Filter": "Filter", + "ResourcePermissions": "Permissions", + "ResourcePermissionTarget": "Target", + "ResourcePermissionPermissions": "Permissions", + "AddResourcePermission": "Add permission", + "ResourcePermissionDeletionConfirmationMessage": "Are you sure you want to delete all permissions?", + "UpdateResourcePermission": "Update permission", + "GrantAllResourcePermissions": "Grant all", + "NoResourceProviderKeyLookupServiceFound": "There is no provider key lookup service was found", + "NoResourcePermissionFound": "There is no permission defined.", + "UpdatePermission": "Update permission", + "NoPermissionsAssigned": "No permissions assigned", + "SelectProvider": "Select provider", + "SearchProviderKey": "Search provider key", + "Provider": "Provider", + "ErrorLoadingPermissions": "Error loading permissions", + "PleaseSelectProviderAndPermissions": "Please select provider and permissions" } } \ No newline at end of file diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/es.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/es.json index 622883b259..c39091d8a6 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/es.json +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/es.json @@ -8,6 +8,22 @@ "SelectAllInThisTab": "Seleccionar todo", "SaveWithoutAnyPermissionsWarningMessage": "¿Estás seguro de que quieres guardar sin ningún permiso?", "PermissionGroup": "Grupo de permisos", - "Filter": "Filtrar" + "Filter": "Filtrar", + "ResourcePermissions": "Permisos", + "ResourcePermissionTarget": "Objetivo", + "ResourcePermissionPermissions": "Permisos", + "AddResourcePermission": "Agregar permiso", + "ResourcePermissionDeletionConfirmationMessage": "¿Está seguro de que desea eliminar todos los permisos?", + "UpdateResourcePermission": "Actualizar permiso", + "GrantAllResourcePermissions": "Conceder todos", + "NoResourceProviderKeyLookupServiceFound": "No se encontró ningún servicio de búsqueda de clave de proveedor", + "NoResourcePermissionFound": "No hay ningún permiso definido.", + "UpdatePermission": "Actualizar permiso", + "NoPermissionsAssigned": "No hay permisos asignados", + "SelectProvider": "Seleccionar proveedor", + "SearchProviderKey": "Buscar clave de proveedor", + "Provider": "Proveedor", + "ErrorLoadingPermissions": "Error al cargar permisos", + "PleaseSelectProviderAndPermissions": "Por favor, selecciona proveedor y permisos" } -} +} \ No newline at end of file diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/fa.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/fa.json index 7ea9a6c4f3..21c00d539f 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/fa.json +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/fa.json @@ -8,6 +8,22 @@ "SelectAllInThisTab": "انتخاب همه", "SaveWithoutAnyPermissionsWarningMessage": "آیا مطمئن هستید که می خواهید بدون هیچ دسترسی ذخیره کنید؟", "PermissionGroup": "گروه دسترسی", - "Filter": "فیلتر" + "Filter": "فیلتر", + "ResourcePermissions": "دسترسی‌ها", + "ResourcePermissionTarget": "هدف", + "ResourcePermissionPermissions": "دسترسی‌ها", + "AddResourcePermission": "افزودن مجوز", + "ResourcePermissionDeletionConfirmationMessage": "آیا مطمئن هستید که می‌خواهید همه مجوزها را حذف کنید؟", + "UpdateResourcePermission": "به‌روزرسانی مجوز", + "GrantAllResourcePermissions": "اعطای همه", + "NoResourceProviderKeyLookupServiceFound": "هیچ سرویس جستجوی کلید ارائه‌دهنده یافت نشد", + "NoResourcePermissionFound": "هیچ مجوزی تعریف نشده است.", + "UpdatePermission": "به‌روزرسانی مجوز", + "NoPermissionsAssigned": "هیچ مجوزی اختصاص داده نشده است", + "SelectProvider": "انتخاب کننده‌", + "SearchProviderKey": "جستجوی کلید تامین‌کننده", + "Provider": "تامین‌کننده", + "ErrorLoadingPermissions": "خطا در بارگذاری مجوزها", + "PleaseSelectProviderAndPermissions": "لطفاً تامین‌کننده و مجوزها را انتخاب کنید" } } \ No newline at end of file diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/fi.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/fi.json index f9b828ade4..43d3ded466 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/fi.json +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/fi.json @@ -8,6 +8,22 @@ "SelectAllInThisTab": "Valitse kaikki", "SaveWithoutAnyPermissionsWarningMessage": "Haluatko varmasti tallentaa ilman käyttöoikeuksia?", "PermissionGroup": "Käyttöoikeus", - "Filter": "Suodatus" + "Filter": "Suodatus", + "ResourcePermissions": "Käyttöoikeudet", + "ResourcePermissionTarget": "Kohde", + "ResourcePermissionPermissions": "Käyttöoikeudet", + "AddResourcePermission": "Lisää käyttöoikeus", + "ResourcePermissionDeletionConfirmationMessage": "Haluatko varmasti poistaa kaikki käyttöoikeudet?", + "UpdateResourcePermission": "Päivitä käyttöoikeus", + "GrantAllResourcePermissions": "Myönnä kaikki", + "NoResourceProviderKeyLookupServiceFound": "Palveluntarjoajan avaimen hakupalvelua ei löytynyt", + "NoResourcePermissionFound": "Ei käyttöoikeuksia määritetty.", + "UpdatePermission": "Päivitä lupa", + "NoPermissionsAssigned": "Ei osoitettuja oikeuksia", + "SelectProvider": "Valitse palveluntarjoaja", + "SearchProviderKey": "Hae palveluntarjoajan avainta", + "Provider": "Palveluntarjoaja", + "ErrorLoadingPermissions": "Virhe ladataessa oikeuksia", + "PleaseSelectProviderAndPermissions": "Valitse palveluntarjoaja ja oikeudet" } } \ No newline at end of file diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/fr.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/fr.json index 79f4e68377..2d184a2714 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/fr.json +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/fr.json @@ -8,6 +8,22 @@ "SelectAllInThisTab": "Sélectionner tous les", "SaveWithoutAnyPermissionsWarningMessage": "Êtes-vous sûr de vouloir enregistrer sans aucune autorisation ?", "PermissionGroup": "Groupe d'autorisations", - "Filter": "Filtrer" + "Filter": "Filtrer", + "ResourcePermissions": "Autorisations", + "ResourcePermissionTarget": "Cible", + "ResourcePermissionPermissions": "Autorisations", + "AddResourcePermission": "Ajouter une autorisation", + "ResourcePermissionDeletionConfirmationMessage": "Êtes-vous sûr de vouloir supprimer toutes les autorisations ?", + "UpdateResourcePermission": "Mettre à jour l'autorisation", + "GrantAllResourcePermissions": "Accorder tout", + "NoResourceProviderKeyLookupServiceFound": "Aucun service de recherche de clé de fournisseur n'a été trouvé", + "NoResourcePermissionFound": "Aucune autorisation n'est définie.", + "UpdatePermission": "Mettre à jour l'autorisation", + "NoPermissionsAssigned": "Aucune autorisation assignée", + "SelectProvider": "Sélectionner le fournisseur", + "SearchProviderKey": "Rechercher la clé du fournisseur", + "Provider": "Fournisseur", + "ErrorLoadingPermissions": "Erreur lors du chargement des autorisations", + "PleaseSelectProviderAndPermissions": "Veuillez sélectionner le fournisseur et les autorisations" } } \ No newline at end of file diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/hi.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/hi.json index cdc030e8ef..f00d5718b9 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/hi.json +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/hi.json @@ -8,6 +8,22 @@ "SelectAllInThisTab": "सभी का चयन करे", "SaveWithoutAnyPermissionsWarningMessage": "क्या आप वाकई बिना किसी अनुमति के सहेजना चाहते हैं?", "PermissionGroup": "अनुमति समूह", - "Filter": "फ़िल्टर" + "Filter": "फ़िल्टर", + "ResourcePermissions": "अनुमतियाँ", + "ResourcePermissionTarget": "लक्ष्य", + "ResourcePermissionPermissions": "अनुमतियाँ", + "AddResourcePermission": "अनुमति जोड़ें", + "ResourcePermissionDeletionConfirmationMessage": "क्या आप वाकई सभी अनुमतियां हटाना चाहते हैं?", + "UpdateResourcePermission": "अनुमति अपडेट करें", + "GrantAllResourcePermissions": "सभी प्रदान करें", + "NoResourceProviderKeyLookupServiceFound": "कोई प्रदाता कुंजी खोज सेवा नहीं मिली", + "NoResourcePermissionFound": "कोई अनुमति परिभाषित नहीं है।", + "UpdatePermission": "अनुमति अपडेट करें", + "NoPermissionsAssigned": "कोई अनुमति असाइन नहीं की गई", + "SelectProvider": "प्रदाता चुनें", + "SearchProviderKey": "प्रदाता कुंजी खोजें", + "Provider": "प्रदाता", + "ErrorLoadingPermissions": "अनुमतियाँ लोड करने में त्रुटि", + "PleaseSelectProviderAndPermissions": "कृपया प्रदाता और अनुमतियाँ चुनें" } } \ No newline at end of file diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/hr.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/hr.json index 531d0a35d2..af74766105 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/hr.json +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/hr.json @@ -1,13 +1,29 @@ { - "culture": "hr", - "texts": { - "Permissions": "Dozvole", - "OnlyProviderPermissons": "Samo ovaj pružatelj usluga", - "All": "Svi", - "SelectAllInAllTabs": "Dodijelite sva dopuštenja", - "SelectAllInThisTab": "Odaberi sve", - "SaveWithoutAnyPermissionsWarningMessage": "Jeste li sigurni da želite spremiti bez ikakvih dopuštenja?", - "PermissionGroup": "Grupa dozvola", - "Filter": "Filtriraj" - } -} + "culture": "hr", + "texts": { + "Permissions": "Dozvole", + "OnlyProviderPermissons": "Samo ovaj pružatelj usluga", + "All": "Svi", + "SelectAllInAllTabs": "Dodijelite sva dopuštenja", + "SelectAllInThisTab": "Odaberi sve", + "SaveWithoutAnyPermissionsWarningMessage": "Jeste li sigurni da želite spremiti bez ikakvih dopuštenja?", + "PermissionGroup": "Grupa dozvola", + "Filter": "Filtriraj", + "ResourcePermissions": "Dozvole", + "ResourcePermissionTarget": "Cilj", + "ResourcePermissionPermissions": "Dozvole", + "AddResourcePermission": "Dodaj dozvolu", + "ResourcePermissionDeletionConfirmationMessage": "Jeste li sigurni da želite izbrisati sve dozvole?", + "UpdateResourcePermission": "Ažuriraj dozvolu", + "GrantAllResourcePermissions": "Dodijeli sve", + "NoResourceProviderKeyLookupServiceFound": "Nije pronađena usluga za pronalaženje ključa pružatelja", + "NoResourcePermissionFound": "Nijedna dozvola nije definirana.", + "UpdatePermission": "Ažuriraj dopuštenje", + "NoPermissionsAssigned": "Nema dodijeljenih dopuštenja", + "SelectProvider": "Odaberi pružatelja", + "SearchProviderKey": "Traži ključ pružatelja", + "Provider": "Pružatelj", + "ErrorLoadingPermissions": "Pogreška pri učitavanju dopuštenja", + "PleaseSelectProviderAndPermissions": "Molimo odaberite pružatelja i dopuštenja" + } +} \ No newline at end of file diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/hu.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/hu.json index 3177ef8fa7..e55d15188c 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/hu.json +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/hu.json @@ -8,6 +8,22 @@ "SelectAllInThisTab": "Mindet kiválaszt", "SaveWithoutAnyPermissionsWarningMessage": "Biztos, hogy engedélyek nélkül akar menteni?", "PermissionGroup": "Engedélycsoport", - "Filter": "Szűrő" + "Filter": "Szűrő", + "ResourcePermissions": "Engedélyek", + "ResourcePermissionTarget": "Cél", + "ResourcePermissionPermissions": "Engedélyek", + "AddResourcePermission": "Engedély hozzáadása", + "ResourcePermissionDeletionConfirmationMessage": "Biztosan törölni szeretné az összes engedélyt?", + "UpdateResourcePermission": "Engedély frissítése", + "GrantAllResourcePermissions": "Összes engedély megadása", + "NoResourceProviderKeyLookupServiceFound": "Nem található szolgáltató kulcs kereső szolgáltatás", + "NoResourcePermissionFound": "Nincs meghatározva engedély.", + "UpdatePermission": "Jogosultság frissítése", + "NoPermissionsAssigned": "Nincsenek jogosultságok hozzárendelve", + "SelectProvider": "Szolgáltató kiválasztása", + "SearchProviderKey": "Szolgáltatói kulcs keresése", + "Provider": "Szolgáltató", + "ErrorLoadingPermissions": "Hiba a jogosultságok betöltésekor", + "PleaseSelectProviderAndPermissions": "Kérjük, válassza ki a szolgáltatót és a jogosultságokat" } } \ No newline at end of file diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/is.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/is.json index 7db32eb99e..be40ed7020 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/is.json +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/is.json @@ -8,6 +8,22 @@ "SelectAllInThisTab": "Velja allt", "SaveWithoutAnyPermissionsWarningMessage": "Ertu viss um að þú viljir vista án nokkurra heimilda?", "PermissionGroup": "Heimildahópur", - "Filter": "Sía" + "Filter": "Sía", + "ResourcePermissions": "Heimildir", + "ResourcePermissionTarget": "Markmið", + "ResourcePermissionPermissions": "Heimildir", + "AddResourcePermission": "Bæta við heimild", + "ResourcePermissionDeletionConfirmationMessage": "Ertu viss um að þú viljir eyða öllum heimildum?", + "UpdateResourcePermission": "Uppfæra heimild", + "GrantAllResourcePermissions": "Veita allt", + "NoResourceProviderKeyLookupServiceFound": "Engin þjónusta fannst til að leita að lykli veitanda", + "NoResourcePermissionFound": "Engin heimild er skilgreind.", + "UpdatePermission": "Uppfæra leyfi", + "NoPermissionsAssigned": "Engar heimildir skráðar", + "SelectProvider": "Velja þjónustuaðila", + "SearchProviderKey": "Leita að lykli þjónustuaðila", + "Provider": "Þjónustuaðili", + "ErrorLoadingPermissions": "Villa við að hlaða leyfum", + "PleaseSelectProviderAndPermissions": "Vinsamlegast veldu þjónustuaðila og leyfi" } } \ No newline at end of file diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/it.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/it.json index e473c7b310..af689bad29 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/it.json +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/it.json @@ -8,6 +8,22 @@ "SelectAllInThisTab": "Seleziona tutto", "SaveWithoutAnyPermissionsWarningMessage": "Sei sicuro di voler salvare senza alcuna autorizzazione?", "PermissionGroup": "Gruppo di autorizzazioni", - "Filter": "Filtro" + "Filter": "Filtro", + "ResourcePermissions": "Autorizzazioni", + "ResourcePermissionTarget": "Obiettivo", + "ResourcePermissionPermissions": "Autorizzazioni", + "AddResourcePermission": "Aggiungi autorizzazione", + "ResourcePermissionDeletionConfirmationMessage": "Sei sicuro di voler eliminare tutte le autorizzazioni?", + "UpdateResourcePermission": "Aggiorna autorizzazione", + "GrantAllResourcePermissions": "Concedi tutto", + "NoResourceProviderKeyLookupServiceFound": "Non è stato trovato alcun servizio di ricerca chiave del provider", + "NoResourcePermissionFound": "Non è definita alcuna autorizzazione.", + "UpdatePermission": "Aggiorna permesso", + "NoPermissionsAssigned": "Nessun permesso assegnato", + "SelectProvider": "Seleziona fornitore", + "SearchProviderKey": "Cerca chiave fornitore", + "Provider": "Fornitore", + "ErrorLoadingPermissions": "Errore durante il caricamento dei permessi", + "PleaseSelectProviderAndPermissions": "Selezionare fornitore e permessi" } } \ No newline at end of file diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/nl.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/nl.json index 4b928c4d03..5c86bd91da 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/nl.json +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/nl.json @@ -8,6 +8,22 @@ "SelectAllInThisTab": "Selecteer alles", "SaveWithoutAnyPermissionsWarningMessage": "Weet u zeker dat u zonder rechten wilt opslaan?", "PermissionGroup": "Rechtengroep", - "Filter": "Filter" + "Filter": "Filter", + "ResourcePermissions": "Rechten", + "ResourcePermissionTarget": "Doel", + "ResourcePermissionPermissions": "Rechten", + "AddResourcePermission": "Recht toevoegen", + "ResourcePermissionDeletionConfirmationMessage": "Weet u zeker dat u alle rechten wilt verwijderen?", + "UpdateResourcePermission": "Recht bijwerken", + "GrantAllResourcePermissions": "Alles toekennen", + "NoResourceProviderKeyLookupServiceFound": "Er is geen service gevonden voor het opzoeken van de sleutel van de provider", + "NoResourcePermissionFound": "Er is geen machtiging gedefinieerd.", + "UpdatePermission": "Machtiging bijwerken", + "NoPermissionsAssigned": "Geen machtigingen toegewezen", + "SelectProvider": "Selecteer provider", + "SearchProviderKey": "Zoek provider sleutel", + "Provider": "Provider", + "ErrorLoadingPermissions": "Fout bij laden van machtigingen", + "PleaseSelectProviderAndPermissions": "Selecteer provider en machtigingen" } } \ No newline at end of file diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/pl-PL.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/pl-PL.json index ee483651eb..ce24bc88a0 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/pl-PL.json +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/pl-PL.json @@ -8,6 +8,22 @@ "SelectAllInThisTab": "Zaznacz wszystkie", "SaveWithoutAnyPermissionsWarningMessage": "Czy na pewno chcesz zapisać bez żadnych uprawnień?", "PermissionGroup": "Grupa uprawnień", - "Filter": "Filtr" + "Filter": "Filtr", + "ResourcePermissions": "Uprawnienia", + "ResourcePermissionTarget": "Cel", + "ResourcePermissionPermissions": "Uprawnienia", + "AddResourcePermission": "Dodaj uprawnienie", + "ResourcePermissionDeletionConfirmationMessage": "Czy na pewno chcesz usunąć wszystkie uprawnienia?", + "UpdateResourcePermission": "Zaktualizuj uprawnienie", + "GrantAllResourcePermissions": "Przyznaj wszystko", + "NoResourceProviderKeyLookupServiceFound": "Nie znaleziono usługi wyszukiwania klucza dostawcy", + "NoResourcePermissionFound": "Nie zdefiniowano żadnych uprawnień.", + "UpdatePermission": "Aktualizuj uprawnienie", + "NoPermissionsAssigned": "Brak przypisanych uprawnień", + "SelectProvider": "Wybierz dostawcę", + "SearchProviderKey": "Szukaj klucza dostawcy", + "Provider": "Dostawca", + "ErrorLoadingPermissions": "Błąd podczas ładowania uprawnień", + "PleaseSelectProviderAndPermissions": "Wybierz dostawcę i uprawnienia" } } \ No newline at end of file diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/pt-BR.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/pt-BR.json index 28231b227f..50c65d248a 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/pt-BR.json +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/pt-BR.json @@ -8,6 +8,22 @@ "SelectAllInThisTab": "Selecionar todos", "SaveWithoutAnyPermissionsWarningMessage": "Tem certeza que deseja salvar sem nenhuma permissão?", "PermissionGroup": "Grupo de permissão", - "Filter": "Filtrar" + "Filter": "Filtrar", + "ResourcePermissions": "Permissões", + "ResourcePermissionTarget": "Alvo", + "ResourcePermissionPermissions": "Permissões", + "AddResourcePermission": "Adicionar permissão", + "ResourcePermissionDeletionConfirmationMessage": "Tem certeza de que deseja excluir todas as permissões?", + "UpdateResourcePermission": "Atualizar permissão", + "GrantAllResourcePermissions": "Conceder tudo", + "NoResourceProviderKeyLookupServiceFound": "Nenhum serviço de pesquisa de chave do provedor foi encontrado", + "NoResourcePermissionFound": "Nenhuma permissão foi definida.", + "UpdatePermission": "Atualizar permissão", + "NoPermissionsAssigned": "Nenhuma permissão atribuída", + "SelectProvider": "Selecionar provedor", + "SearchProviderKey": "Pesquisar chave do provedor", + "Provider": "Provedor", + "ErrorLoadingPermissions": "Erro ao carregar permissões", + "PleaseSelectProviderAndPermissions": "Por favor, selecione o provedor e as permissões" } } \ No newline at end of file diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/ro-RO.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/ro-RO.json index af7db26acb..8cab09b9d5 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/ro-RO.json +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/ro-RO.json @@ -8,6 +8,22 @@ "SelectAllInThisTab": "Selectează toate", "SaveWithoutAnyPermissionsWarningMessage": "Sigur doriți să salvați fără nicio permisiune?", "PermissionGroup": "Grup de permisiuni", - "Filter": "Filtru" + "Filter": "Filtru", + "ResourcePermissions": "Permisiuni", + "ResourcePermissionTarget": "Țintă", + "ResourcePermissionPermissions": "Permisiuni", + "AddResourcePermission": "Adăugați permisiune", + "ResourcePermissionDeletionConfirmationMessage": "Sigur doriți să ștergeți toate permisiunile?", + "UpdateResourcePermission": "Actualizați permisiunea", + "GrantAllResourcePermissions": "Acordați toate", + "NoResourceProviderKeyLookupServiceFound": "Nu a fost găsit niciun serviciu de căutare a cheii furnizorului", + "NoResourcePermissionFound": "Nu există nicio permisiune definită.", + "UpdatePermission": "Actualizează permisiunea", + "NoPermissionsAssigned": "Nicio permisiune atribuită", + "SelectProvider": "Selectează furnizor", + "SearchProviderKey": "Caută cheie furnizor", + "Provider": "Furnizor", + "ErrorLoadingPermissions": "Eroare la încărcarea permisiunilor", + "PleaseSelectProviderAndPermissions": "Vă rugăm să selectați furnizorul și permisiunile" } } \ No newline at end of file diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/ru.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/ru.json index 6041357b0e..81e0160345 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/ru.json +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/ru.json @@ -8,6 +8,22 @@ "SelectAllInThisTab": "Выбрать все", "SaveWithoutAnyPermissionsWarningMessage": "Вы уверены, что хотите сохранить без каких-либо разрешений?", "PermissionGroup": "Группа разрешений", - "Filter": "Фильтр" + "Filter": "Фильтр", + "ResourcePermissions": "Разрешения", + "ResourcePermissionTarget": "Цель", + "ResourcePermissionPermissions": "Разрешения", + "AddResourcePermission": "Добавить разрешение", + "ResourcePermissionDeletionConfirmationMessage": "Вы уверены, что хотите удалить все разрешения?", + "UpdateResourcePermission": "Обновить разрешение", + "GrantAllResourcePermissions": "Предоставить все", + "NoResourceProviderKeyLookupServiceFound": "Служба поиска ключа поставщика не найдена", + "NoResourcePermissionFound": "Не определено ни одного разрешения.", + "UpdatePermission": "Обновить разрешение", + "NoPermissionsAssigned": "Нет назначенных разрешений", + "SelectProvider": "Выбрать провайдера", + "SearchProviderKey": "Поиск ключа провайдера", + "Provider": "Провайдер", + "ErrorLoadingPermissions": "Ошибка при загрузке разрешений", + "PleaseSelectProviderAndPermissions": "Пожалуйста, выберите провайдера и разрешения" } } \ No newline at end of file diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/sk.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/sk.json index c079b8eba1..80e5719fc7 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/sk.json +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/sk.json @@ -8,6 +8,22 @@ "SelectAllInThisTab": "Vybrať všetky", "SaveWithoutAnyPermissionsWarningMessage": "Naozaj chcete ukladať bez akýchkoľvek povolení?", "PermissionGroup": "Skupina oprávnení", - "Filter": "Filtrovať" + "Filter": "Filtrovať", + "ResourcePermissions": "Oprávnenia", + "ResourcePermissionTarget": "Cieľ", + "ResourcePermissionPermissions": "Oprávnenia", + "AddResourcePermission": "Pridať oprávnenie", + "ResourcePermissionDeletionConfirmationMessage": "Naozaj chcete odstrániť všetky oprávnenia?", + "UpdateResourcePermission": "Aktualizovať oprávnenie", + "GrantAllResourcePermissions": "Udeľ všetko", + "NoResourceProviderKeyLookupServiceFound": "Nebola nájdená služba na vyhľadávanie kľúča poskytovateľa", + "NoResourcePermissionFound": "Nie je definované žiadne povolenie.", + "UpdatePermission": "Aktualizovať oprávnenie", + "NoPermissionsAssigned": "Nie sú priradené žiadne oprávnenia", + "SelectProvider": "Vybrat poskytovateľa", + "SearchProviderKey": "Hľadať kľúč poskytovateľa", + "Provider": "Poskytovateľ", + "ErrorLoadingPermissions": "Chyba pri načítaní oprávnení", + "PleaseSelectProviderAndPermissions": "Vyberte poskytovateľa a oprávnenia" } } \ No newline at end of file diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/sl.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/sl.json index 9c906f7387..c5e5c61382 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/sl.json +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/sl.json @@ -8,6 +8,22 @@ "SelectAllInThisTab": "Izberi vse", "SaveWithoutAnyPermissionsWarningMessage": "Ali ste prepričani, da želite shraniti brez kakršnih koli dovoljenj?", "PermissionGroup": "Skupina dovoljenj", - "Filter": "Filtriraj" + "Filter": "Filtriraj", + "ResourcePermissions": "Dovoljenja", + "ResourcePermissionTarget": "Cilj", + "ResourcePermissionPermissions": "Dovoljenja", + "AddResourcePermission": "Dodaj dovoljenje", + "ResourcePermissionDeletionConfirmationMessage": "Ali ste prepričani, da želite izbrisati vsa dovoljenja?", + "UpdateResourcePermission": "Posodobi dovoljenje", + "GrantAllResourcePermissions": "Dodeli vse", + "NoResourceProviderKeyLookupServiceFound": "Ni bilo mogoče najti storitve za iskanje ključa ponudnika", + "NoResourcePermissionFound": "Nobeno dovoljenje ni določeno.", + "UpdatePermission": "Posodobi dovoljenje", + "NoPermissionsAssigned": "Ni dodeljenih dovoljenj", + "SelectProvider": "Izberi ponudnika", + "SearchProviderKey": "Išči ključ ponudnika", + "Provider": "Ponudnik", + "ErrorLoadingPermissions": "Napaka pri nalaganju dovoljenj", + "PleaseSelectProviderAndPermissions": "Izberite ponudnika in dovoljenja" } } \ No newline at end of file diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/sv.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/sv.json index d46fca4dc4..c666cbd8c0 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/sv.json +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/sv.json @@ -8,6 +8,22 @@ "SelectAllInThisTab": "Välj alla", "SaveWithoutAnyPermissionsWarningMessage": "Är du säker på att du vill spara utan några behörigheter?", "PermissionGroup": "Behörighetsgrupp", - "Filter": "Filtrera" + "Filter": "Filtrera", + "ResourcePermissions": "Behörigheter", + "ResourcePermissionTarget": "Mål", + "ResourcePermissionPermissions": "Behörigheter", + "AddResourcePermission": "Lägg till behörighet", + "ResourcePermissionDeletionConfirmationMessage": "Är du säker på att du vill ta bort alla behörigheter?", + "UpdateResourcePermission": "Uppdatera behörighet", + "GrantAllResourcePermissions": "Bevilja alla", + "NoResourceProviderKeyLookupServiceFound": "Ingen tjänst för att söka efter leverantörsnyckel hittades", + "NoResourcePermissionFound": "Ingen behörighet är definierad.", + "UpdatePermission": "Uppdatera behörighet", + "NoPermissionsAssigned": "Inga behörigheter tilldelade", + "SelectProvider": "Välj leverantör", + "SearchProviderKey": "Sök leverantörsnyckel", + "Provider": "Leverantör", + "ErrorLoadingPermissions": "Fel vid laddning av behörigheter", + "PleaseSelectProviderAndPermissions": "Välj leverantör och behörigheter" } } \ No newline at end of file diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/tr.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/tr.json index 960cd8cf02..b6eb5f1c7b 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/tr.json +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/tr.json @@ -8,6 +8,22 @@ "SelectAllInThisTab": "Hepsini seç", "SaveWithoutAnyPermissionsWarningMessage": "Hiçbir izin olmadan kaydetmek istediğinize emin misiniz?", "PermissionGroup": "İzin Grubu", - "Filter": "Filtre" + "Filter": "Filtre", + "ResourcePermissions": "İzinler", + "ResourcePermissionTarget": "Hedef", + "ResourcePermissionPermissions": "İzinler", + "AddResourcePermission": "İzin ekle", + "ResourcePermissionDeletionConfirmationMessage": "Tüm izinleri silmek istediğinizden emin misiniz?", + "UpdateResourcePermission": "İzni güncelle", + "GrantAllResourcePermissions": "Tümünü ver", + "NoResourceProviderKeyLookupServiceFound": "Herhangi bir sağlayıcı anahtar arama hizmeti bulunamadı", + "NoResourcePermissionFound": "Herhangi bir izin tanımlı değil.", + "UpdatePermission": "İzni güncelle", + "NoPermissionsAssigned": "Atanmış izin bulunamadı", + "SelectProvider": "Sağlayıcı seç", + "SearchProviderKey": "Sağlayıcı anahtarını ara", + "Provider": "Sağlayıcı", + "ErrorLoadingPermissions": "İzinler yüklenirken hata oluştu", + "PleaseSelectProviderAndPermissions": "Lütfen sağlayıcı ve izinleri seçin" } -} +} \ No newline at end of file diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/vi.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/vi.json index 5e9a9d8565..0a26e43253 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/vi.json +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/vi.json @@ -8,6 +8,22 @@ "SelectAllInThisTab": "Chọn tất cả", "SaveWithoutAnyPermissionsWarningMessage": "Bạn có chắc chắn muốn lưu mà không có bất kỳ quyền nào không?", "PermissionGroup": "Nhóm quyền", - "Filter": "Lọc" + "Filter": "Lọc", + "ResourcePermissions": "Quyền", + "ResourcePermissionTarget": "Mục tiêu", + "ResourcePermissionPermissions": "Quyền", + "AddResourcePermission": "Thêm quyền", + "ResourcePermissionDeletionConfirmationMessage": "Bạn có chắc chắn muốn xóa tất cả quyền không?", + "UpdateResourcePermission": "Cập nhật quyền", + "GrantAllResourcePermissions": "Cấp tất cả", + "NoResourceProviderKeyLookupServiceFound": "Không tìm thấy dịch vụ tra cứu khóa nhà cung cấp", + "NoResourcePermissionFound": "Không có quyền nào được định nghĩa.", + "UpdatePermission": "Cập nhật quyền", + "NoPermissionsAssigned": "Chưa được cấp quyền", + "SelectProvider": "Chọn nhà cung cấp", + "SearchProviderKey": "Tìm kiếm khóa nhà cung cấp", + "Provider": "Nhà cung cấp", + "ErrorLoadingPermissions": "Lỗi khi tải quyền", + "PleaseSelectProviderAndPermissions": "Vui lòng chọn nhà cung cấp và quyền" } } \ No newline at end of file diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/zh-Hans.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/zh-Hans.json index df844ca2bf..2c84e3462d 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/zh-Hans.json +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/zh-Hans.json @@ -8,6 +8,22 @@ "SelectAllInThisTab": "全选", "SaveWithoutAnyPermissionsWarningMessage": "您确定要在没有任何权限的情况下保存吗?", "PermissionGroup": "权限组", - "Filter": "过滤" + "Filter": "过滤", + "ResourcePermissions": "权限", + "ResourcePermissionTarget": "目标", + "ResourcePermissionPermissions": "权限", + "AddResourcePermission": "添加权限", + "ResourcePermissionDeletionConfirmationMessage": "您确定要删除所有权限吗?", + "UpdateResourcePermission": "更新权限", + "GrantAllResourcePermissions": "授予所有", + "NoResourceProviderKeyLookupServiceFound": "未找到提供者键查找服务", + "NoResourcePermissionFound": "未定义任何权限。", + "UpdatePermission": "更新权限", + "NoPermissionsAssigned": "未分配权限", + "SelectProvider": "选择提供程序", + "SearchProviderKey": "搜索提供程序密钥", + "Provider": "提供程序", + "ErrorLoadingPermissions": "加载权限时出错", + "PleaseSelectProviderAndPermissions": "请选择提供程序和权限" } } \ No newline at end of file diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/zh-Hant.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/zh-Hant.json index 72af56c960..afd9d65459 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/zh-Hant.json +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/zh-Hant.json @@ -8,6 +8,22 @@ "SelectAllInThisTab": "全選", "SaveWithoutAnyPermissionsWarningMessage": "您確定要在沒有任何權限的情況下保存嗎?", "PermissionGroup": "權限組", - "Filter": "過濾" + "Filter": "過濾", + "ResourcePermissions": "權限", + "ResourcePermissionTarget": "目標", + "ResourcePermissionPermissions": "權限", + "AddResourcePermission": "添加權限", + "ResourcePermissionDeletionConfirmationMessage": "您確定要刪除所有權限嗎?", + "UpdateResourcePermission": "更新權限", + "GrantAllResourcePermissions": "授予所有", + "NoResourceProviderKeyLookupServiceFound": "未找到提供者鍵查找服務", + "NoResourcePermissionFound": "未定義任何權限。", + "UpdatePermission": "更新權限", + "NoPermissionsAssigned": "未分配權限", + "SelectProvider": "選擇提供者", + "SearchProviderKey": "搜尋提供者金鑰", + "Provider": "提供者", + "ErrorLoadingPermissions": "載入權限時出錯", + "PleaseSelectProviderAndPermissions": "請選擇提供者和權限" } } \ No newline at end of file diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/PermissionDefinitionRecordConsts.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/PermissionDefinitionRecordConsts.cs index 93b1b465c6..0aadef7940 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/PermissionDefinitionRecordConsts.cs +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/PermissionDefinitionRecordConsts.cs @@ -6,10 +6,14 @@ public class PermissionDefinitionRecordConsts /// Default value: 128 ///
public static int MaxNameLength { get; set; } = 128; - + public static int MaxDisplayNameLength { get; set; } = 256; public static int MaxProvidersLength { get; set; } = 128; - + public static int MaxStateCheckersLength { get; set; } = 256; -} \ No newline at end of file + + public static int MaxResourceNameLength { get; set; } = 256; + + public static int MaxManagementPermissionNameLength { get; set; } = 128; +} diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/PermissionGrantConsts.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/PermissionGrantConsts.cs index 630f5dc72e..d1008bcbdb 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/PermissionGrantConsts.cs +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/PermissionGrantConsts.cs @@ -11,4 +11,14 @@ public static class PermissionGrantConsts /// Default value: 64 /// public static int MaxProviderKeyLength { get; set; } = 64; + + /// + /// Default value: 256 + /// + public static int MaxResourceNameLength { get; set; } = 256; + + /// + /// Default value: 256 + /// + public static int MaxResourceKeyLength { get; set; } = 256; } diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/AbpPermissionManagementDomainModule.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/AbpPermissionManagementDomainModule.cs index 0f40e3c039..3029625ffb 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/AbpPermissionManagementDomainModule.cs +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/AbpPermissionManagementDomainModule.cs @@ -2,6 +2,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -11,6 +12,7 @@ using Volo.Abp.Authorization.Permissions; using Volo.Abp.Caching; using Volo.Abp.Data; using Volo.Abp.DependencyInjection; +using Volo.Abp.DistributedLocking; using Volo.Abp.Domain; using Volo.Abp.Json; using Volo.Abp.Modularity; @@ -26,9 +28,11 @@ namespace Volo.Abp.PermissionManagement; public class AbpPermissionManagementDomainModule : AbpModule { private readonly CancellationTokenSource _cancellationTokenSource = new(); - private Task _initializeDynamicPermissionsTask; + public override void ConfigureServices(ServiceConfigurationContext context) { + context.Services.Replace(ServiceDescriptor.Singleton()); + if (context.Services.IsDataMigrationEnvironment()) { Configure(options => @@ -44,10 +48,11 @@ public class AbpPermissionManagementDomainModule : AbpModule AsyncHelper.RunSync(() => OnApplicationInitializationAsync(context)); } - public override Task OnApplicationInitializationAsync(ApplicationInitializationContext context) + public override async Task OnApplicationInitializationAsync(ApplicationInitializationContext context) { - InitializeDynamicPermissions(context); - return Task.CompletedTask; + var rootServiceProvider = context.ServiceProvider.GetRequiredService(); + var initializer = rootServiceProvider.GetRequiredService(); + await initializer.InitializeAsync(true, _cancellationTokenSource.Token); } public override Task OnApplicationShutdownAsync(ApplicationShutdownContext context) @@ -55,123 +60,4 @@ public class AbpPermissionManagementDomainModule : AbpModule _cancellationTokenSource.Cancel(); return Task.CompletedTask; } - - public Task GetInitializeDynamicPermissionsTask() - { - return _initializeDynamicPermissionsTask ?? Task.CompletedTask; - } - - private void InitializeDynamicPermissions(ApplicationInitializationContext context) - { - var options = context - .ServiceProvider - .GetRequiredService>() - .Value; - - if (!options.SaveStaticPermissionsToDatabase && !options.IsDynamicPermissionStoreEnabled) - { - return; - } - - var rootServiceProvider = context.ServiceProvider.GetRequiredService(); - - _initializeDynamicPermissionsTask = Task.Run(async () => - { - using var scope = rootServiceProvider.CreateScope(); - var applicationLifetime = scope.ServiceProvider.GetService(); - var cancellationTokenProvider = scope.ServiceProvider.GetRequiredService(); - var cancellationToken = applicationLifetime?.ApplicationStopping ?? _cancellationTokenSource.Token; - - try - { - using (cancellationTokenProvider.Use(cancellationToken)) - { - if (cancellationTokenProvider.Token.IsCancellationRequested) - { - return; - } - - await SaveStaticPermissionsToDatabaseAsync(options, scope, cancellationTokenProvider); - - if (cancellationTokenProvider.Token.IsCancellationRequested) - { - return; - } - - await PreCacheDynamicPermissionsAsync(options, scope); - } - } - // ReSharper disable once EmptyGeneralCatchClause (No need to log since it is logged above) - catch { } - }); - } - - private async static Task SaveStaticPermissionsToDatabaseAsync( - PermissionManagementOptions options, - IServiceScope scope, - ICancellationTokenProvider cancellationTokenProvider) - { - if (!options.SaveStaticPermissionsToDatabase) - { - return; - } - - await Policy - .Handle() - .WaitAndRetryAsync( - 8, - retryAttempt => TimeSpan.FromSeconds( - RandomHelper.GetRandom( - (int)Math.Pow(2, retryAttempt) * 8, - (int)Math.Pow(2, retryAttempt) * 12) - ) - ) - .ExecuteAsync(async _ => - { - try - { - // ReSharper disable once AccessToDisposedClosure - await scope - .ServiceProvider - .GetRequiredService() - .SaveAsync(); - } - catch (Exception ex) - { - // ReSharper disable once AccessToDisposedClosure - scope.ServiceProvider - .GetService>()? - .LogException(ex); - - throw; // Polly will catch it - } - }, cancellationTokenProvider.Token); - } - - private async static Task PreCacheDynamicPermissionsAsync(PermissionManagementOptions options, IServiceScope scope) - { - if (!options.IsDynamicPermissionStoreEnabled) - { - return; - } - - try - { - // Pre-cache permissions, so first request doesn't wait - await scope - .ServiceProvider - .GetRequiredService() - .GetGroupsAsync(); - } - catch (Exception ex) - { - // ReSharper disable once AccessToDisposedClosure - scope - .ServiceProvider - .GetService>()? - .LogException(ex); - - throw; // It will be cached in InitializeDynamicPermissions - } - } } diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/DynamicPermissionDefinitionStore.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/DynamicPermissionDefinitionStore.cs index f2e73cefc0..ff525ab263 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/DynamicPermissionDefinitionStore.cs +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/DynamicPermissionDefinitionStore.cs @@ -23,7 +23,7 @@ public class DynamicPermissionDefinitionStore : IDynamicPermissionDefinitionStor protected IAbpDistributedLock DistributedLock { get; } public PermissionManagementOptions PermissionManagementOptions { get; } protected AbpDistributedCacheOptions CacheOptions { get; } - + public DynamicPermissionDefinitionStore( IPermissionGroupDefinitionRecordRepository permissionGroupRepository, IPermissionDefinitionRecordRepository permissionRepository, @@ -72,6 +72,34 @@ public class DynamicPermissionDefinitionStore : IDynamicPermissionDefinitionStor } } + public virtual async Task GetResourcePermissionOrNullAsync(string resourceName, string name) + { + if (!PermissionManagementOptions.IsDynamicPermissionStoreEnabled) + { + return null; + } + + using (await StoreCache.SyncSemaphore.LockAsync()) + { + await EnsureCacheIsUptoDateAsync(); + return StoreCache.GetResourcePermissionOrNull(resourceName, name); + } + } + + public virtual async Task> GetResourcePermissionsAsync() + { + if (!PermissionManagementOptions.IsDynamicPermissionStoreEnabled) + { + return Array.Empty(); + } + + using (await StoreCache.SyncSemaphore.LockAsync()) + { + await EnsureCacheIsUptoDateAsync(); + return StoreCache.GetResourcePermissions().ToImmutableList(); + } + } + public virtual async Task> GetGroupsAsync() { if (!PermissionManagementOptions.IsDynamicPermissionStoreEnabled) @@ -94,9 +122,9 @@ public class DynamicPermissionDefinitionStore : IDynamicPermissionDefinitionStor /* We get the latest permission with a small delay for optimization */ return; } - + var stampInDistributedCache = await GetOrSetStampInDistributedCache(); - + if (stampInDistributedCache == StoreCache.CacheStamp) { StoreCache.LastCheckTime = DateTime.Now; @@ -145,7 +173,7 @@ public class DynamicPermissionDefinitionStore : IDynamicPermissionDefinitionStor } stampInDistributedCache = Guid.NewGuid().ToString(); - + await DistributedCache.SetStringAsync( cacheKey, stampInDistributedCache, @@ -163,9 +191,9 @@ public class DynamicPermissionDefinitionStore : IDynamicPermissionDefinitionStor { return $"{CacheOptions.KeyPrefix}_AbpInMemoryPermissionCacheStamp"; } - + protected virtual string GetCommonDistributedLockKey() { return $"{CacheOptions.KeyPrefix}_Common_AbpPermissionUpdateLock"; } -} \ No newline at end of file +} diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/DynamicPermissionDefinitionStoreInMemoryCache.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/DynamicPermissionDefinitionStoreInMemoryCache.cs index 1040ea84a0..15a98c84dd 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/DynamicPermissionDefinitionStoreInMemoryCache.cs +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/DynamicPermissionDefinitionStoreInMemoryCache.cs @@ -18,6 +18,7 @@ public class DynamicPermissionDefinitionStoreInMemoryCache : protected IDictionary PermissionGroupDefinitions { get; } protected IDictionary PermissionDefinitions { get; } + protected IList ResourcePermissionDefinitions { get; } protected ISimpleStateCheckerSerializer StateCheckerSerializer { get; } protected ILocalizableStringSerializer LocalizableStringSerializer { get; } @@ -34,6 +35,7 @@ public class DynamicPermissionDefinitionStoreInMemoryCache : PermissionGroupDefinitions = new Dictionary(); PermissionDefinitions = new Dictionary(); + ResourcePermissionDefinitions = new List(); } public Task FillAsync( @@ -42,9 +44,22 @@ public class DynamicPermissionDefinitionStoreInMemoryCache : { PermissionGroupDefinitions.Clear(); PermissionDefinitions.Clear(); + ResourcePermissionDefinitions.Clear(); var context = new PermissionDefinitionContext(null); + var resourcePermissions = permissionRecords.Where(x => !x.ResourceName.IsNullOrWhiteSpace()); + foreach (var resourcePermission in resourcePermissions) + { + context.AddResourcePermission(resourcePermission.Name, + resourcePermission.ResourceName, + resourcePermission.ManagementPermissionName, + resourcePermission.DisplayName != null ? LocalizableStringSerializer.Deserialize(resourcePermission.DisplayName) : null, + resourcePermission.MultiTenancySide, + resourcePermission.IsEnabled); + } + + var permissions = permissionRecords.Where(x => x.ResourceName.IsNullOrWhiteSpace()).ToList(); foreach (var permissionGroupRecord in permissionGroupRecords) { var permissionGroup = context.AddGroup( @@ -59,12 +74,12 @@ public class DynamicPermissionDefinitionStoreInMemoryCache : permissionGroup[property.Key] = property.Value; } - var permissionRecordsInThisGroup = permissionRecords + var permissionRecordsInThisGroup = permissions .Where(p => p.GroupName == permissionGroup.Name); foreach (var permissionRecord in permissionRecordsInThisGroup.Where(x => x.ParentName == null)) { - AddPermissionRecursively(permissionGroup, permissionRecord, permissionRecords); + AddPermissionRecursively(permissionGroup, permissionRecord, permissions); } } @@ -86,6 +101,16 @@ public class DynamicPermissionDefinitionStoreInMemoryCache : return PermissionGroupDefinitions.Values.ToList(); } + public PermissionDefinition GetResourcePermissionOrNull(string resourceName, string name) + { + return ResourcePermissionDefinitions.FirstOrDefault(p => p.ResourceName == resourceName && p.Name == name); + } + + public IReadOnlyList GetResourcePermissions() + { + return ResourcePermissionDefinitions.ToList(); + } + private void AddPermissionRecursively(ICanAddChildPermission permissionContainer, PermissionDefinitionRecord permissionRecord, List allPermissionRecords) diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/IDynamicPermissionDefinitionStoreInMemoryCache.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/IDynamicPermissionDefinitionStoreInMemoryCache.cs index 2dab588ebd..eb079f6c76 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/IDynamicPermissionDefinitionStoreInMemoryCache.cs +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/IDynamicPermissionDefinitionStoreInMemoryCache.cs @@ -9,9 +9,9 @@ namespace Volo.Abp.PermissionManagement; public interface IDynamicPermissionDefinitionStoreInMemoryCache { string CacheStamp { get; set; } - + SemaphoreSlim SyncSemaphore { get; } - + DateTime? LastCheckTime { get; set; } Task FillAsync( @@ -19,8 +19,12 @@ public interface IDynamicPermissionDefinitionStoreInMemoryCache List permissionRecords); PermissionDefinition GetPermissionOrNull(string name); - + IReadOnlyList GetPermissions(); - + IReadOnlyList GetGroups(); -} \ No newline at end of file + + PermissionDefinition GetResourcePermissionOrNull(string resourceName, string name); + + IReadOnlyList GetResourcePermissions(); +} diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/IPermissionDefinitionSerializer.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/IPermissionDefinitionSerializer.cs index 8ed09a4380..3c5f4a8783 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/IPermissionDefinitionSerializer.cs +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/IPermissionDefinitionSerializer.cs @@ -10,10 +10,13 @@ public interface IPermissionDefinitionSerializer Task<(PermissionGroupDefinitionRecord[], PermissionDefinitionRecord[])> SerializeAsync(IEnumerable permissionGroups); + Task SerializeAsync( + IEnumerable permissions); + Task SerializeAsync( PermissionGroupDefinition permissionGroup); Task SerializeAsync( PermissionDefinition permission, [CanBeNull] PermissionGroupDefinition permissionGroup); -} \ No newline at end of file +} diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/IPermissionManager.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/IPermissionManager.cs index 7ec69ad100..ffb934321b 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/IPermissionManager.cs +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/IPermissionManager.cs @@ -4,19 +4,39 @@ using JetBrains.Annotations; namespace Volo.Abp.PermissionManagement; -//TODO: Write extension methods for simple IsGranted check - public interface IPermissionManager { - Task GetAsync(string permissionName, string providerName, string providerKey); - - Task GetAsync(string[] permissionNames, string provideName, string providerKey); - - Task> GetAllAsync([NotNull] string providerName, [NotNull] string providerKey); - - Task SetAsync(string permissionName, string providerName, string providerKey, bool isGranted); - - Task UpdateProviderKeyAsync(PermissionGrant permissionGrant, string providerKey); - - Task DeleteAsync(string providerName, string providerKey); -} + Task GetAsync( + string permissionName, + string providerName, + string providerKey + ); + + Task GetAsync( + string[] permissionNames, + string provideName, + string providerKey + ); + + Task> GetAllAsync( + [NotNull] string providerName, + [NotNull] string providerKey + ); + + Task SetAsync( + string permissionName, + string providerName, + string providerKey, + bool isGranted + ); + + Task UpdateProviderKeyAsync( + PermissionGrant permissionGrant, + string providerKey + ); + + Task DeleteAsync( + string providerName, + string providerKey + ); +} \ No newline at end of file diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/IResourcePermissionGrantRepository.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/IResourcePermissionGrantRepository.cs new file mode 100644 index 0000000000..82060c7ea7 --- /dev/null +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/IResourcePermissionGrantRepository.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.Domain.Repositories; + +namespace Volo.Abp.PermissionManagement; + +public interface IResourcePermissionGrantRepository : IBasicRepository +{ + Task FindAsync( + string name, + string resourceName, + string resourceKey, + string providerName, + string providerKey, + CancellationToken cancellationToken = default + ); + + Task> GetListAsync( + string resourceName, + string resourceKey, + string providerName, + string providerKey, + CancellationToken cancellationToken = default + ); + + Task> GetListAsync( + string[] names, + string resourceName, + string resourceKey, + string providerName, + string providerKey, + CancellationToken cancellationToken = default + ); + + Task> GetListAsync( + string providerName, + string providerKey, + CancellationToken cancellationToken = default + ); + + Task> GetPermissionsAsync( + string resourceName, + string resourceKey, + CancellationToken cancellationToken = default + ); + + Task> GetResourceKeys( + string resourceName, + string name, + CancellationToken cancellationToken = default + ); +} diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/IResourcePermissionManagementProvider.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/IResourcePermissionManagementProvider.cs new file mode 100644 index 0000000000..429e6f6e85 --- /dev/null +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/IResourcePermissionManagementProvider.cs @@ -0,0 +1,34 @@ +using System.Threading.Tasks; +using JetBrains.Annotations; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.PermissionManagement; + +public interface IResourcePermissionManagementProvider : ISingletonDependency //TODO: Consider to remove this pre-assumption +{ + string Name { get; } + + Task CheckAsync( + [NotNull] string name, + [NotNull] string resourceName, + [NotNull] string resourceKey, + [NotNull] string providerName, + [NotNull] string providerKey + ); + + Task CheckAsync( + [NotNull] string[] names, + [NotNull] string resourceName, + [NotNull] string resourceKey, + [NotNull] string providerName, + [NotNull] string providerKey + ); + + Task SetAsync( + [NotNull] string name, + [NotNull] string resourceName, + [NotNull] string resourceKey, + [NotNull] string providerKey, + bool isGranted + ); +} diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/IResourcePermissionManager.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/IResourcePermissionManager.cs new file mode 100644 index 0000000000..5bd2d143ca --- /dev/null +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/IResourcePermissionManager.cs @@ -0,0 +1,81 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Volo.Abp.Authorization.Permissions; + +namespace Volo.Abp.PermissionManagement; + +public interface IResourcePermissionManager +{ + Task> GetProviderKeyLookupServicesAsync(); + + Task GetProviderKeyLookupServiceAsync(string providerName); + + Task> GetAvailablePermissionsAsync(string resourceName); + + Task GetAsync( + string permissionName, + string resourceName, + string resourceKey, + string providerName, + string providerKey + ); + + Task GetAsync( + string[] permissionNames, + string resourceName, + string resourceKey, + string providerName, + string providerKey + ); + + Task> GetAllAsync( + string resourceName, + string resourceKey + ); + + Task> GetAllAsync( + string resourceName, + string resourceKey, + string providerName, + string providerKey + ); + + Task> GetAllGroupAsync( + string resourceName, + string resourceKey + ); + + Task SetAsync( + string permissionName, + string resourceName, + string resourceKey, + string providerName, + string providerKey, + bool isGranted + ); + + Task UpdateProviderKeyAsync( + ResourcePermissionGrant resourcePermissionGrant, + string providerKey + ); + + Task DeleteAsync( + string resourceName, + string resourceKey, + string providerName, + string providerKey + ); + + Task DeleteAsync( + string name, + string resourceName, + string resourceKey, + string providerName, + string providerKey + ); + + Task DeleteAsync( + string providerName, + string providerKey + ); +} diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/IResourcePermissionProviderKeyLookupService.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/IResourcePermissionProviderKeyLookupService.cs new file mode 100644 index 0000000000..aa6ad2a06e --- /dev/null +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/IResourcePermissionProviderKeyLookupService.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.Localization; + +namespace Volo.Abp.PermissionManagement; + +public interface IResourcePermissionProviderKeyLookupService +{ + public string Name { get; } + + public ILocalizableString DisplayName { get; } + + Task> SearchAsync(string filter = null, int page = 1, CancellationToken cancellationToken = default); + + Task> SearchAsync(string[] keys, CancellationToken cancellationToken = default); +} diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/MultipleResourcePermissionValueProviderGrantInfo.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/MultipleResourcePermissionValueProviderGrantInfo.cs new file mode 100644 index 0000000000..4ff1790488 --- /dev/null +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/MultipleResourcePermissionValueProviderGrantInfo.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; + +namespace Volo.Abp.PermissionManagement; + +public class MultipleResourcePermissionValueProviderGrantInfo +{ + public Dictionary Result { get; } + + public MultipleResourcePermissionValueProviderGrantInfo() + { + Result = new Dictionary(); + } + + public MultipleResourcePermissionValueProviderGrantInfo(string[] names) + { + Check.NotNull(names, nameof(names)); + + Result = new Dictionary(); + + foreach (var name in names) + { + Result.Add(name, ResourcePermissionValueProviderGrantInfo.NonGranted); + } + } +} diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionDefinitionRecord.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionDefinitionRecord.cs index 7473eb2589..61a6ba1f14 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionDefinitionRecord.cs +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionDefinitionRecord.cs @@ -1,4 +1,5 @@ using System; +using Volo.Abp.Authorization.Permissions; using Volo.Abp.Data; using Volo.Abp.Domain.Entities; using Volo.Abp.MultiTenancy; @@ -11,6 +12,10 @@ public class PermissionDefinitionRecord : BasicAggregateRoot, IHasExtraPro public string Name { get; set; } + public string ResourceName { get; set; } + + public string ManagementPermissionName { get; set; } + public string ParentName { get; set; } public string DisplayName { get; set; } @@ -41,6 +46,8 @@ public class PermissionDefinitionRecord : BasicAggregateRoot, IHasExtraPro Guid id, string groupName, string name, + string resourceName, + string managementPermissionName, string parentName, string displayName, bool isEnabled = true, @@ -49,8 +56,14 @@ public class PermissionDefinitionRecord : BasicAggregateRoot, IHasExtraPro string stateCheckers = null) : base(id) { - GroupName = Check.NotNullOrWhiteSpace(groupName, nameof(groupName), PermissionGroupDefinitionRecordConsts.MaxNameLength); + GroupName = groupName; + if (resourceName == null) + { + GroupName = Check.NotNullOrWhiteSpace(groupName, nameof(groupName), PermissionGroupDefinitionRecordConsts.MaxNameLength); + } Name = Check.NotNullOrWhiteSpace(name, nameof(name), PermissionDefinitionRecordConsts.MaxNameLength); + ResourceName = resourceName; + ManagementPermissionName = managementPermissionName; ParentName = Check.Length(parentName, nameof(parentName), PermissionDefinitionRecordConsts.MaxNameLength); DisplayName = Check.NotNullOrWhiteSpace(displayName, nameof(displayName), PermissionDefinitionRecordConsts.MaxDisplayNameLength); IsEnabled = isEnabled; @@ -69,6 +82,16 @@ public class PermissionDefinitionRecord : BasicAggregateRoot, IHasExtraPro return false; } + if (ResourceName != otherRecord.ResourceName) + { + return false; + } + + if (ManagementPermissionName != otherRecord.ManagementPermissionName) + { + return false; + } + if (GroupName != otherRecord.GroupName) { return false; @@ -119,6 +142,16 @@ public class PermissionDefinitionRecord : BasicAggregateRoot, IHasExtraPro Name = otherRecord.Name; } + if (ResourceName != otherRecord.ResourceName) + { + ResourceName = otherRecord.ResourceName; + } + + if (ManagementPermissionName != otherRecord.ManagementPermissionName) + { + ManagementPermissionName = otherRecord.ManagementPermissionName; + } + if (GroupName != otherRecord.GroupName) { GroupName = otherRecord.GroupName; diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionDefinitionSerializer.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionDefinitionSerializer.cs index 8e63342502..707a3e89ea 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionDefinitionSerializer.cs +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionDefinitionSerializer.cs @@ -19,7 +19,7 @@ public class PermissionDefinitionSerializer : IPermissionDefinitionSerializer, I public PermissionDefinitionSerializer( IGuidGenerator guidGenerator, - ISimpleStateCheckerSerializer stateCheckerSerializer, + ISimpleStateCheckerSerializer stateCheckerSerializer, ILocalizableStringSerializer localizableStringSerializer) { StateCheckerSerializer = stateCheckerSerializer; @@ -27,16 +27,16 @@ public class PermissionDefinitionSerializer : IPermissionDefinitionSerializer, I GuidGenerator = guidGenerator; } - public async Task<(PermissionGroupDefinitionRecord[], PermissionDefinitionRecord[])> + public virtual async Task<(PermissionGroupDefinitionRecord[], PermissionDefinitionRecord[])> SerializeAsync(IEnumerable permissionGroups) { var permissionGroupRecords = new List(); var permissionRecords = new List(); - + foreach (var permissionGroup in permissionGroups) { permissionGroupRecords.Add(await SerializeAsync(permissionGroup)); - + foreach (var permission in permissionGroup.GetPermissionsWithChildren()) { permissionRecords.Add(await SerializeAsync(permission, permissionGroup)); @@ -45,8 +45,19 @@ public class PermissionDefinitionSerializer : IPermissionDefinitionSerializer, I return (permissionGroupRecords.ToArray(), permissionRecords.ToArray()); } - - public Task SerializeAsync(PermissionGroupDefinition permissionGroup) + + public virtual async Task SerializeAsync(IEnumerable permissions) + { + var permissionRecords = new List(); + foreach (var permission in permissions) + { + permissionRecords.Add(await SerializeAsync(permission, null)); + } + + return permissionRecords.ToArray(); + } + + public virtual Task SerializeAsync(PermissionGroupDefinition permissionGroup) { using (CultureHelper.Use(CultureInfo.InvariantCulture)) { @@ -60,12 +71,12 @@ public class PermissionDefinitionSerializer : IPermissionDefinitionSerializer, I { permissionGroupRecord.SetProperty(property.Key, property.Value); } - + return Task.FromResult(permissionGroupRecord); } } - - public Task SerializeAsync( + + public virtual Task SerializeAsync( PermissionDefinition permission, PermissionGroupDefinition permissionGroup) { @@ -75,6 +86,8 @@ public class PermissionDefinitionSerializer : IPermissionDefinitionSerializer, I GuidGenerator.Create(), permissionGroup?.Name, permission.Name, + permission.ResourceName, + permission.ManagementPermissionName, permission.Parent?.Name, LocalizableStringSerializer.Serialize(permission.DisplayName), permission.IsEnabled, @@ -87,11 +100,11 @@ public class PermissionDefinitionSerializer : IPermissionDefinitionSerializer, I { permissionRecord.SetProperty(property.Key, property.Value); } - + return Task.FromResult(permissionRecord); } } - + protected virtual string SerializeProviders(ICollection providers) { return providers.Any() @@ -103,4 +116,4 @@ public class PermissionDefinitionSerializer : IPermissionDefinitionSerializer, I { return StateCheckerSerializer.Serialize(stateCheckers); } -} \ No newline at end of file +} diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionDynamicInitializer.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionDynamicInitializer.cs new file mode 100644 index 0000000000..74bdf1821b --- /dev/null +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionDynamicInitializer.cs @@ -0,0 +1,151 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.Options; +using Polly; +using Volo.Abp.Authorization.Permissions; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Threading; + +namespace Volo.Abp.PermissionManagement; + +public class PermissionDynamicInitializer : ITransientDependency +{ + public ILogger Logger { get; set; } + + protected IServiceProvider ServiceProvider { get; } + protected IOptions Options { get; } + [CanBeNull] + protected IHostApplicationLifetime ApplicationLifetime { get; } + protected ICancellationTokenProvider CancellationTokenProvider { get; } + protected IDynamicPermissionDefinitionStore DynamicPermissionDefinitionStore { get; } + protected IStaticPermissionSaver StaticPermissionSaver { get; } + + public PermissionDynamicInitializer( + IServiceProvider serviceProvider, + IOptions options, + ICancellationTokenProvider cancellationTokenProvider, + IDynamicPermissionDefinitionStore dynamicPermissionDefinitionStore, + IStaticPermissionSaver staticPermissionSaver) + { + Logger = NullLogger.Instance; + + ServiceProvider = serviceProvider; + Options = options; + ApplicationLifetime = ServiceProvider.GetService(); + CancellationTokenProvider = cancellationTokenProvider; + DynamicPermissionDefinitionStore = dynamicPermissionDefinitionStore; + StaticPermissionSaver = staticPermissionSaver; + } + + public virtual Task InitializeAsync(bool runInBackground, CancellationToken cancellationToken = default) + { + var options = Options.Value; + + if (!options.SaveStaticPermissionsToDatabase && !options.IsDynamicPermissionStoreEnabled) + { + return Task.CompletedTask; + } + + if (runInBackground) + { + Task.Run(async () => + { + if (cancellationToken == default && ApplicationLifetime?.ApplicationStopping != null) + { + cancellationToken = ApplicationLifetime.ApplicationStopping; + } + await ExecuteInitializationAsync(options, cancellationToken); + }, cancellationToken); + return Task.CompletedTask; + } + + return ExecuteInitializationAsync(options, cancellationToken); + } + + protected virtual async Task ExecuteInitializationAsync(PermissionManagementOptions options, CancellationToken cancellationToken) + { + try + { + using (CancellationTokenProvider.Use(cancellationToken)) + { + if (CancellationTokenProvider.Token.IsCancellationRequested) + { + return; + } + + await SaveStaticPermissionsToDatabaseAsync(options, cancellationToken); + + if (CancellationTokenProvider.Token.IsCancellationRequested) + { + return; + } + + await PreCacheDynamicPermissionsAsync(options); + } + } + catch + { + // No need to log here since inner calls log + } + } + + protected virtual async Task SaveStaticPermissionsToDatabaseAsync( + PermissionManagementOptions options, + CancellationToken cancellationToken) + { + if (!options.SaveStaticPermissionsToDatabase) + { + return; + } + + await Policy + .Handle() + .WaitAndRetryAsync( + 8, + retryAttempt => TimeSpan.FromSeconds( + Volo.Abp.RandomHelper.GetRandom( + (int)Math.Pow(2, retryAttempt) * 8, + (int)Math.Pow(2, retryAttempt) * 12) + ) + ) + .ExecuteAsync(async _ => + { + try + { + await StaticPermissionSaver.SaveAsync(); + } + catch (Exception ex) + { + Logger.LogException(ex); + + throw; // Polly will catch it + } + }, cancellationToken); + } + + protected virtual async Task PreCacheDynamicPermissionsAsync(PermissionManagementOptions options) + { + if (!options.IsDynamicPermissionStoreEnabled) + { + return; + } + + try + { + // Pre-cache permissions, so first request doesn't wait + await DynamicPermissionDefinitionStore.GetGroupsAsync(); + } + catch (Exception ex) + { + Logger.LogException(ex); + + throw; // It will be cached in Initialize() + } + } +} diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionManagementOptions.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionManagementOptions.cs index 489ae26e3d..2971cc18ae 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionManagementOptions.cs +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionManagementOptions.cs @@ -9,6 +9,10 @@ public class PermissionManagementOptions public Dictionary ProviderPolicies { get; } + public ITypeList ResourceManagementProviders { get; } + + public ITypeList ResourcePermissionProviderKeyLookupServices { get; } + /// /// Default: true. /// @@ -23,5 +27,8 @@ public class PermissionManagementOptions { ManagementProviders = new TypeList(); ProviderPolicies = new Dictionary(); + + ResourceManagementProviders = new TypeList(); + ResourcePermissionProviderKeyLookupServices = new TypeList(); } } diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionProviderWithPermissions.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionProviderWithPermissions.cs new file mode 100644 index 0000000000..a6507ef0bc --- /dev/null +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionProviderWithPermissions.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; +using Volo.Abp.Localization; + +namespace Volo.Abp.PermissionManagement; + +public class PermissionProviderWithPermissions +{ + public string ProviderName { get; set; } + + public string ProviderKey { get; set; } + + public string ProviderDisplayName { get; set; } + + public ILocalizableString ProviderNameDisplayName { get; set; } + + public List Permissions { get; set; } + + public PermissionProviderWithPermissions(string providerName, string providerKey, string providerDisplayName) + { + ProviderName = providerName; + ProviderKey = providerKey; + ProviderDisplayName = providerDisplayName; + Permissions = new List(); + } +} diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/ResourcePermissionGrant.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/ResourcePermissionGrant.cs new file mode 100644 index 0000000000..b74da527ae --- /dev/null +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/ResourcePermissionGrant.cs @@ -0,0 +1,52 @@ +using System; +using JetBrains.Annotations; +using Volo.Abp.Domain.Entities; +using Volo.Abp.MultiTenancy; + +namespace Volo.Abp.PermissionManagement; + +//TODO: To aggregate root? +public class ResourcePermissionGrant : Entity, IMultiTenant +{ + public virtual Guid? TenantId { get; protected set; } + + [NotNull] + public virtual string Name { get; protected set; } + + [NotNull] + public virtual string ProviderName { get; protected set; } + + [NotNull] + public virtual string ProviderKey { get; protected internal set; } + + [NotNull] + public virtual string ResourceName { get; protected set; } + + [NotNull] + public virtual string ResourceKey { get; protected set; } + + protected ResourcePermissionGrant() + { + + } + + public ResourcePermissionGrant( + Guid id, + [NotNull] string name, + [NotNull] string resourceName, + [NotNull] string resourceKey, + [NotNull] string providerName, + [CanBeNull] string providerKey, + Guid? tenantId = null) + { + Check.NotNull(name, nameof(name)); + + Id = id; + Name = Check.NotNullOrWhiteSpace(name, nameof(name)); + ResourceName = Check.NotNullOrWhiteSpace(resourceName, nameof(resourceName)); + ResourceKey = Check.NotNullOrWhiteSpace(resourceKey, nameof(resourceKey)); + ProviderName = Check.NotNullOrWhiteSpace(providerName, nameof(providerName)); + ProviderKey = Check.NotNullOrWhiteSpace(providerKey, nameof(providerKey)); + TenantId = tenantId; + } +} diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/ResourcePermissionGrantCacheItem.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/ResourcePermissionGrantCacheItem.cs new file mode 100644 index 0000000000..59362f6f5f --- /dev/null +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/ResourcePermissionGrantCacheItem.cs @@ -0,0 +1,34 @@ +using System; +using System.Linq; +using Volo.Abp.Text.Formatting; + +namespace Volo.Abp.PermissionManagement; + +[Serializable] +public class ResourcePermissionGrantCacheItem +{ + private const string CacheKeyFormat = "rn:{0},rk:{1},pn:{2},pk:{3},n:{4}"; + + public bool IsGranted { get; set; } + + public ResourcePermissionGrantCacheItem() + { + + } + + public ResourcePermissionGrantCacheItem(bool isGranted) + { + IsGranted = isGranted; + } + + public static string CalculateCacheKey(string name, string resourceName, string resourceKey, string providerName, string providerKey) + { + return string.Format(CacheKeyFormat, resourceName, resourceKey, providerName, providerKey, name); + } + + public static string GetPermissionNameFormCacheKeyOrNull(string cacheKey) + { + var result = FormattedStringValueExtracter.Extract(cacheKey, CacheKeyFormat, true); + return result.IsMatch ? result.Matches.Last().Value : null; + } +} diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/ResourcePermissionGrantCacheItemInvalidator.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/ResourcePermissionGrantCacheItemInvalidator.cs new file mode 100644 index 0000000000..97c116de56 --- /dev/null +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/ResourcePermissionGrantCacheItemInvalidator.cs @@ -0,0 +1,44 @@ +using System.Threading.Tasks; +using Volo.Abp.Caching; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Domain.Entities.Events; +using Volo.Abp.EventBus; +using Volo.Abp.MultiTenancy; + +namespace Volo.Abp.PermissionManagement; + +public class ResourcePermissionGrantCacheItemInvalidator : + ILocalEventHandler>, + ITransientDependency +{ + protected ICurrentTenant CurrentTenant { get; } + + protected IDistributedCache Cache { get; } + + public ResourcePermissionGrantCacheItemInvalidator(IDistributedCache cache, ICurrentTenant currentTenant) + { + Cache = cache; + CurrentTenant = currentTenant; + } + + public virtual async Task HandleEventAsync(EntityChangedEventData eventData) + { + var cacheKey = CalculateCacheKey( + eventData.Entity.Name, + eventData.Entity.ResourceName, + eventData.Entity.ResourceKey, + eventData.Entity.ProviderName, + eventData.Entity.ProviderKey + ); + + using (CurrentTenant.Change(eventData.Entity.TenantId)) + { + await Cache.RemoveAsync(cacheKey, considerUow: true); + } + } + + protected virtual string CalculateCacheKey(string name, string resourceName, string resourceKey, string providerName, string providerKey) + { + return ResourcePermissionGrantCacheItem.CalculateCacheKey(name, resourceName, resourceKey, providerName, providerKey); + } +} diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/ResourcePermissionManagementProvider.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/ResourcePermissionManagementProvider.cs new file mode 100644 index 0000000000..90d4e176d3 --- /dev/null +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/ResourcePermissionManagementProvider.cs @@ -0,0 +1,87 @@ +using System.Linq; +using System.Threading.Tasks; +using Volo.Abp.Domain.Repositories; +using Volo.Abp.Guids; +using Volo.Abp.MultiTenancy; + +namespace Volo.Abp.PermissionManagement; + +public abstract class ResourcePermissionManagementProvider : IResourcePermissionManagementProvider +{ + public abstract string Name { get; } + + protected IResourcePermissionGrantRepository ResourcePermissionGrantRepository { get; } + + protected IGuidGenerator GuidGenerator { get; } + + protected ICurrentTenant CurrentTenant { get; } + + protected ResourcePermissionManagementProvider( + IResourcePermissionGrantRepository resourcePermissionGrantRepository, + IGuidGenerator guidGenerator, + ICurrentTenant currentTenant) + { + ResourcePermissionGrantRepository = resourcePermissionGrantRepository; + GuidGenerator = guidGenerator; + CurrentTenant = currentTenant; + } + + public virtual async Task CheckAsync(string name, string resourceName,string resourceKey, string providerName, string providerKey) + { + var multiplePermissionValueProviderGrantInfo = await CheckAsync(new[] { name }, resourceName, resourceKey, providerName, providerKey); + + return multiplePermissionValueProviderGrantInfo.Result.First().Value; + } + + public virtual async Task CheckAsync(string[] names, string resourceName, string resourceKey, string providerName, string providerKey) + { + using (ResourcePermissionGrantRepository.DisableTracking()) + { + var multiplePermissionValueProviderGrantInfo = new MultipleResourcePermissionValueProviderGrantInfo(names); + if (providerName != Name) + { + return multiplePermissionValueProviderGrantInfo; + } + + var resourcePermissionGrants = await ResourcePermissionGrantRepository.GetListAsync(names, resourceName, resourceKey, providerName, providerKey); + + foreach (var permissionName in names) + { + var isGrant = resourcePermissionGrants.Any(x => x.Name == permissionName); + multiplePermissionValueProviderGrantInfo.Result[permissionName] = new ResourcePermissionValueProviderGrantInfo(isGrant, providerKey); + } + + return multiplePermissionValueProviderGrantInfo; + } + } + + public virtual Task SetAsync(string name, string resourceName,string resourceKey, string providerKey, bool isGranted) + { + return isGranted + ? GrantAsync(name, resourceName, resourceKey, providerKey) + : RevokeAsync(name, resourceName, resourceKey, providerKey); + } + + protected virtual async Task GrantAsync(string name, string resourceName, string resourceKey, string providerKey) + { + var resourcePermissionGrants = await ResourcePermissionGrantRepository.FindAsync(name, resourceName, resourceKey, Name, providerKey); + if (resourcePermissionGrants != null) + { + return; + } + + resourcePermissionGrants = new ResourcePermissionGrant(GuidGenerator.Create(), name, resourceName, resourceKey, Name, providerKey, CurrentTenant.Id); + await ResourcePermissionGrantRepository.InsertAsync(resourcePermissionGrants, true); + } + + protected virtual async Task RevokeAsync(string name, string resourceName,string resourceKey, string providerKey) + { + var resourcePermissionGrants = await ResourcePermissionGrantRepository.FindAsync(name, resourceName, resourceKey, Name, providerKey); + if (resourcePermissionGrants == null) + { + return; + } + + await ResourcePermissionGrantRepository.DeleteAsync(resourcePermissionGrants, true); + } +} diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/ResourcePermissionManager.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/ResourcePermissionManager.cs new file mode 100644 index 0000000000..c4b898e761 --- /dev/null +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/ResourcePermissionManager.cs @@ -0,0 +1,405 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using Volo.Abp.Authorization.Permissions; +using Volo.Abp.Caching; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Guids; +using Volo.Abp.MultiTenancy; +using Volo.Abp.SimpleStateChecking; + +namespace Volo.Abp.PermissionManagement; + +public class ResourcePermissionManager : IResourcePermissionManager, ISingletonDependency +{ + protected IResourcePermissionGrantRepository ResourcePermissionGrantRepository { get; } + + protected IPermissionDefinitionManager PermissionDefinitionManager { get; } + + protected ISimpleStateCheckerManager SimpleStateCheckerManager { get; } + + protected IGuidGenerator GuidGenerator { get; } + + protected ICurrentTenant CurrentTenant { get; } + + protected IReadOnlyList ManagementProviders => _lazyProviders.Value; + + protected PermissionManagementOptions Options { get; } + + protected IDistributedCache Cache { get; } + + private readonly Lazy> _lazyProviders; + + private readonly Lazy> _lazyProviderKeyLookupServices; + + public ResourcePermissionManager( + IPermissionDefinitionManager permissionDefinitionManager, + ISimpleStateCheckerManager simpleStateCheckerManager, + IResourcePermissionGrantRepository resourcePermissionGrantRepository, + IServiceProvider serviceProvider, + IGuidGenerator guidGenerator, + IOptions options, + ICurrentTenant currentTenant, + IDistributedCache cache) + { + GuidGenerator = guidGenerator; + CurrentTenant = currentTenant; + Cache = cache; + SimpleStateCheckerManager = simpleStateCheckerManager; + ResourcePermissionGrantRepository = resourcePermissionGrantRepository; + PermissionDefinitionManager = permissionDefinitionManager; + Options = options.Value; + + _lazyProviders = new Lazy>( + () => Options + .ResourceManagementProviders + .Select(c => serviceProvider.GetRequiredService(c) as IResourcePermissionManagementProvider) + .ToList(), + true + ); + + _lazyProviderKeyLookupServices = new Lazy>( + () => Options + .ResourcePermissionProviderKeyLookupServices + .Select(c => serviceProvider.GetRequiredService(c) as IResourcePermissionProviderKeyLookupService) + .ToList(), + true + ); + } + + public virtual Task> GetProviderKeyLookupServicesAsync() + { + return Task.FromResult(_lazyProviderKeyLookupServices.Value); + } + + public virtual Task GetProviderKeyLookupServiceAsync(string serviceName) + { + var service = _lazyProviderKeyLookupServices.Value.FirstOrDefault(s => s.Name == serviceName); + return service == null + ? throw new AbpException("Unknown resource permission provider key lookup service: " + serviceName) + : Task.FromResult(service); + } + + public virtual async Task> GetAvailablePermissionsAsync(string resourceName) + { + var multiTenancySide = CurrentTenant.GetMultiTenancySide(); + var resourcePermissions = new List(); + foreach (var resourcePermission in (await PermissionDefinitionManager.GetResourcePermissionsAsync()) + .Where(x => x.IsEnabled && x.MultiTenancySide.HasFlag(multiTenancySide) && x.ResourceName == resourceName)) + { + if (await SimpleStateCheckerManager.IsEnabledAsync(resourcePermission)) + { + resourcePermissions.Add(resourcePermission); + } + } + + return resourcePermissions; + } + + public virtual async Task GetAsync(string permissionName, string resourceName, string resourceKey, string providerName, string providerKey) + { + var permission = await PermissionDefinitionManager.GetResourcePermissionOrNullAsync(resourceName, permissionName); + if (permission == null || permission.ResourceName != resourceName) + { + return new PermissionWithGrantedProviders(permissionName, false); + } + + return await GetInternalAsync( + permission, + resourceName, + resourceKey, + providerName, + providerKey + ); + } + + public virtual async Task GetAsync(string[] permissionNames, string resourceName, string resourceKey, string providerName, string providerKey) + { + var permissions = new List(); + var undefinedPermissions = new List(); + + foreach (var permissionName in permissionNames) + { + var permission = await PermissionDefinitionManager.GetResourcePermissionOrNullAsync(resourceName, permissionName); + if (permission != null && permission.ResourceName == resourceName) + { + permissions.Add(permission); + } + else + { + undefinedPermissions.Add(permissionName); + } + } + + if (!permissions.Any()) + { + return new MultiplePermissionWithGrantedProviders(undefinedPermissions.ToArray()); + } + + var result = await GetInternalAsync( + permissions.ToArray(), + resourceName, + resourceKey, + providerName, + providerKey + ); + + foreach (var undefinedPermission in undefinedPermissions) + { + result.Result.Add(new PermissionWithGrantedProviders(undefinedPermission, false)); + } + + return result; + } + + public virtual async Task> GetAllAsync(string resourceName, string resourceKey) + { + var resourcePermissionDefinitions = await GetAvailablePermissionsAsync(resourceName); + var resourcePermissionGrants = await ResourcePermissionGrantRepository.GetPermissionsAsync(resourceName, resourceKey); + var result = new List(); + foreach (var resourcePermissionDefinition in resourcePermissionDefinitions) + { + var permissionWithGrantedProviders = new PermissionWithGrantedProviders(resourcePermissionDefinition.Name, false); + + var grantedPermissions = resourcePermissionGrants + .Where(x => x.Name == resourcePermissionDefinition.Name && x.ResourceName == resourceName && x.ResourceKey == resourceKey) + .ToList(); + + if (grantedPermissions.Any()) + { + permissionWithGrantedProviders.IsGranted = true; + foreach (var grantedPermission in grantedPermissions) + { + permissionWithGrantedProviders.Providers.Add(new PermissionValueProviderInfo(grantedPermission.ProviderName, grantedPermission.ProviderKey)); + } + } + + result.Add(permissionWithGrantedProviders); + } + + return result; + } + + public virtual async Task> GetAllAsync(string resourceName, string resourceKey, string providerName, string providerKey) + { + var permissionDefinitions = await GetAvailablePermissionsAsync(resourceName); + var multiplePermissionWithGrantedProviders = await GetInternalAsync(permissionDefinitions.ToArray(), resourceName, resourceKey, providerName, providerKey); + return multiplePermissionWithGrantedProviders.Result; + } + + public virtual async Task> GetAllGroupAsync(string resourceName, string resourceKey) + { + var resourcePermissions = await GetAvailablePermissionsAsync(resourceName); + var resourcePermissionGrants = await ResourcePermissionGrantRepository.GetPermissionsAsync(resourceName, resourceKey); + resourcePermissionGrants = resourcePermissionGrants.Where(x => resourcePermissions.Any(rp => rp.Name == x.Name)).ToList(); + var resourcePermissionGrantsGroup = resourcePermissionGrants.GroupBy(x => new { x.ProviderName, x.ProviderKey }); + var result = new List(); + foreach (var resourcePermissionGrant in resourcePermissionGrantsGroup) + { + result.Add(new PermissionProviderWithPermissions(resourcePermissionGrant.Key.ProviderName, resourcePermissionGrant.Key.ProviderKey, resourcePermissionGrant.Key.ProviderKey) + { + Permissions = resourcePermissionGrant.Select(x => x.Name).ToList() + }); + } + + if (result.Any()) + { + var providerKeyInfos = new Dictionary>(); + var resourcePermissionProviderGroup = resourcePermissionGrants.GroupBy(x => x.ProviderName); + var providerKeyLookupServices = await GetProviderKeyLookupServicesAsync(); + foreach (var resourcePermissionProvider in resourcePermissionProviderGroup) + { + var providerKeyLookupService = providerKeyLookupServices.FirstOrDefault(s => s.Name == resourcePermissionProvider.Key); + if (providerKeyLookupService == null) + { + continue; + } + var keys = resourcePermissionProvider.Select(rp => rp.ProviderKey).Distinct().ToList(); + providerKeyInfos.Add(resourcePermissionProvider.Key, await providerKeyLookupService.SearchAsync(keys.ToArray())); + } + + foreach (var item in result) + { + if (!providerKeyInfos.TryGetValue(item.ProviderName, out var providerKeyInfoList)) + { + continue; + } + + var providerKeyInfo = providerKeyInfoList.FirstOrDefault(p => p.ProviderKey == item.ProviderKey); + if (providerKeyInfo != null) + { + item.ProviderDisplayName = providerKeyInfo.ProviderDisplayName; + item.ProviderNameDisplayName = providerKeyLookupServices + .FirstOrDefault(s => s.Name == item.ProviderName)?.DisplayName; + } + } + } + + return result; + } + + public virtual async Task SetAsync(string permissionName, string resourceName, string resourceKey, string providerName, string providerKey, bool isGranted) + { + var permission = await PermissionDefinitionManager.GetResourcePermissionOrNullAsync(resourceName, permissionName); + if (permission == null || permission.ResourceName != resourceName) + { + /* Silently ignore undefined permissions, + maybe they were removed from dynamic permission definition store */ + return; + } + + if (!permission.IsEnabled || !await SimpleStateCheckerManager.IsEnabledAsync(permission)) + { + //TODO: BusinessException + throw new ApplicationException($"The resource permission named '{permission.Name}' is disabled!"); + } + + if (permission.Providers.Any() && !permission.Providers.Contains(providerName)) + { + //TODO: BusinessException + throw new ApplicationException($"The resource permission named '{permission.Name}' has not compatible with the provider named '{providerName}'"); + } + + if (!permission.MultiTenancySide.HasFlag(CurrentTenant.GetMultiTenancySide())) + { + //TODO: BusinessException + throw new ApplicationException($"The resource permission named '{permission.Name}' has multitenancy side '{permission.MultiTenancySide}' which is not compatible with the current multitenancy side '{CurrentTenant.GetMultiTenancySide()}'"); + } + + var currentGrantInfo = await GetInternalAsync(permission, resourceName, resourceKey, providerName, providerKey); + if (currentGrantInfo.IsGranted == isGranted && currentGrantInfo.Providers.Any(x => x.Name == providerName && x.Key == providerKey)) + { + return; + } + + var provider = ManagementProviders.FirstOrDefault(m => m.Name == providerName); + if (provider == null) + { + //TODO: BusinessException + throw new AbpException("Unknown resource permission management provider: " + providerName); + } + + await provider.SetAsync(permissionName, resourceName, resourceKey, providerKey, isGranted); + } + + public virtual async Task UpdateProviderKeyAsync(ResourcePermissionGrant resourcePermissionGrant, string providerKey) + { + using (CurrentTenant.Change(resourcePermissionGrant.TenantId)) + { + //Invalidating the cache for the old key + await Cache.RemoveAsync( + ResourcePermissionGrantCacheItem.CalculateCacheKey( + resourcePermissionGrant.Name, + resourcePermissionGrant.ResourceName, + resourcePermissionGrant.ResourceKey, + resourcePermissionGrant.ProviderName, + resourcePermissionGrant.ProviderKey + ) + ); + } + + resourcePermissionGrant.ProviderKey = providerKey; + return await ResourcePermissionGrantRepository.UpdateAsync(resourcePermissionGrant, true); + } + + public virtual async Task UpdateProviderKeyAsync(ResourcePermissionGrant resourcePermissionGrant, string resourceName, string resourceKey, string providerKey) + { + using (CurrentTenant.Change(resourcePermissionGrant.TenantId)) + { + //Invalidating the cache for the old key + await Cache.RemoveAsync( + ResourcePermissionGrantCacheItem.CalculateCacheKey( + resourcePermissionGrant.Name, + resourcePermissionGrant.ResourceName, + resourcePermissionGrant.ResourceKey, + resourcePermissionGrant.ProviderName, + resourcePermissionGrant.ProviderKey + ) + ); + } + + resourcePermissionGrant.ProviderKey = providerKey; + return await ResourcePermissionGrantRepository.UpdateAsync(resourcePermissionGrant, true); + } + + public virtual async Task DeleteAsync(string resourceName, string resourceKey, string providerName, string providerKey) + { + var permissionGrants = await ResourcePermissionGrantRepository.GetListAsync(resourceName, resourceKey, providerName, providerKey); + foreach (var permissionGrant in permissionGrants) + { + await ResourcePermissionGrantRepository.DeleteAsync(permissionGrant, true); + } + } + + public virtual async Task DeleteAsync(string name, string resourceName, string resourceKey, string providerName, string providerKey) + { + var permissionGrant = await ResourcePermissionGrantRepository.FindAsync(name, resourceName, resourceKey, providerName, providerKey); + if (permissionGrant != null) + { + await ResourcePermissionGrantRepository.DeleteAsync(permissionGrant, true); + } + } + + public virtual async Task DeleteAsync(string providerName, string providerKey) + { + var permissionGrants = await ResourcePermissionGrantRepository.GetListAsync(providerName, providerKey); + foreach (var permissionGrant in permissionGrants) + { + await ResourcePermissionGrantRepository.DeleteAsync(permissionGrant, true); + } + } + + protected virtual async Task GetInternalAsync(PermissionDefinition permission, string resourceName, string resourceKey, string providerName, string providerKey) + { + var multiplePermissionWithGrantedProviders = await GetInternalAsync( + new[] { permission }, + resourceName, + resourceKey, + providerName, + providerKey + ); + + return multiplePermissionWithGrantedProviders.Result.First(); + } + + protected virtual async Task GetInternalAsync(PermissionDefinition[] permissions, string resourceName, string resourceKey, string providerName, string providerKey) + { + var permissionNames = permissions.Select(x => x.Name).ToArray(); + var multiplePermissionWithGrantedProviders = new MultiplePermissionWithGrantedProviders(permissionNames); + + var resourcePermissions = await GetAvailablePermissionsAsync(resourceName); + if (!resourcePermissions.Any()) + { + return multiplePermissionWithGrantedProviders; + } + + foreach (var provider in ManagementProviders) + { + permissionNames = resourcePermissions.Select(x => x.Name).ToArray(); + var multiplePermissionValueProviderGrantInfo = await provider.CheckAsync(permissionNames, resourceName, resourceKey, providerName, providerKey); + + foreach (var providerResultDict in multiplePermissionValueProviderGrantInfo.Result) + { + if (providerResultDict.Value.IsGranted) + { + var permissionWithGrantedProvider = multiplePermissionWithGrantedProviders.Result + .FirstOrDefault(x => x.Name == providerResultDict.Key); + + if (permissionWithGrantedProvider == null) + { + continue; + } + + permissionWithGrantedProvider.IsGranted = true; + permissionWithGrantedProvider.Providers.Add( + new PermissionValueProviderInfo(provider.Name, providerResultDict.Value.ProviderKey)); + } + } + } + + return multiplePermissionWithGrantedProviders; + } +} diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/ResourcePermissionProviderKeyInfo.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/ResourcePermissionProviderKeyInfo.cs new file mode 100644 index 0000000000..841277514a --- /dev/null +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/ResourcePermissionProviderKeyInfo.cs @@ -0,0 +1,16 @@ +using Volo.Abp.ObjectExtending; + +namespace Volo.Abp.PermissionManagement; + +public class ResourcePermissionProviderKeyInfo +{ + public string ProviderKey { get; set; } + + public string ProviderDisplayName { get; set; } + + public ResourcePermissionProviderKeyInfo(string providerKey, string providerDisplayName) + { + ProviderKey = providerKey; + ProviderDisplayName = providerDisplayName; + } +} diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/ResourcePermissionStore.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/ResourcePermissionStore.cs new file mode 100644 index 0000000000..9e09ff2db4 --- /dev/null +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/ResourcePermissionStore.cs @@ -0,0 +1,249 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Volo.Abp.Authorization.Permissions; +using Volo.Abp.Authorization.Permissions.Resources; +using Volo.Abp.Caching; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Domain.Repositories; + +namespace Volo.Abp.PermissionManagement; + +public class ResourcePermissionStore : IResourcePermissionStore, ITransientDependency +{ + public ILogger Logger { get; set; } + + protected IResourcePermissionGrantRepository ResourcePermissionGrantRepository { get; } + + protected IPermissionDefinitionManager PermissionDefinitionManager { get; } + + protected IDistributedCache Cache { get; } + + public ResourcePermissionStore( + IResourcePermissionGrantRepository resourcePermissionGrantRepository, + IDistributedCache cache, + IPermissionDefinitionManager permissionDefinitionManager) + { + ResourcePermissionGrantRepository = resourcePermissionGrantRepository; + Cache = cache; + PermissionDefinitionManager = permissionDefinitionManager; + Logger = NullLogger.Instance; + } + + public virtual async Task IsGrantedAsync(string name, string resourceName, string resourceKey, string providerName, string providerKey) + { + return (await GetCacheItemAsync(name, resourceName, resourceKey, providerName, providerKey)).IsGranted; + } + + protected virtual async Task GetCacheItemAsync(string name, string resourceName, string resourceKey, string providerName, string providerKey) + { + var cacheKey = CalculateCacheKey(name, providerName, providerKey, resourceName, resourceKey); + + Logger.LogDebug($"ResourcePermissionStore.GetCacheItemAsync: {cacheKey}"); + + var cacheItem = await Cache.GetAsync(cacheKey); + + if (cacheItem != null) + { + Logger.LogDebug($"Found in the cache: {cacheKey}"); + return cacheItem; + } + + Logger.LogDebug($"Not found in the cache: {cacheKey}"); + + cacheItem = new ResourcePermissionGrantCacheItem(false); + + await SetCacheItemsAsync(resourceName, resourceKey, providerName, providerKey, name, cacheItem); + + return cacheItem; + } + + protected virtual async Task SetCacheItemsAsync(string resourceName, string resourceKey, string providerName, string providerKey, string currentName, ResourcePermissionGrantCacheItem currentCacheItem) + { + using (ResourcePermissionGrantRepository.DisableTracking()) + { + var permissions = await PermissionDefinitionManager.GetResourcePermissionsAsync(); + + Logger.LogDebug($"Getting all granted resource permissions from the repository for resource name,key:{resourceName},{resourceKey} and provider name,key: {providerName},{providerKey}"); + + var grantedPermissionsHashSet = new HashSet( + (await ResourcePermissionGrantRepository.GetListAsync(resourceName, resourceKey, providerName, providerKey)).Select(p => p.Name) + ); + + Logger.LogDebug($"Setting the cache items. Count: {permissions.Count}"); + + var cacheItems = new List>(); + + foreach (var permission in permissions) + { + var isGranted = grantedPermissionsHashSet.Contains(permission.Name); + + cacheItems.Add(new KeyValuePair( + CalculateCacheKey(permission.Name, resourceName, resourceKey, providerName, providerKey), + new ResourcePermissionGrantCacheItem(isGranted)) + ); + + if (permission.Name == currentName) + { + currentCacheItem.IsGranted = isGranted; + } + } + + await Cache.SetManyAsync(cacheItems); + + Logger.LogDebug($"Finished setting the cache items. Count: {permissions.Count}"); + } + } + + public virtual async Task IsGrantedAsync(string[] names, string resourceName, string resourceKey, string providerName, string providerKey) + { + Check.NotNullOrEmpty(names, nameof(names)); + + var result = new MultiplePermissionGrantResult(); + + if (names.Length == 1) + { + var name = names.First(); + result.Result.Add(name, + await IsGrantedAsync(names.First(), resourceName, resourceKey, providerName, providerKey) + ? PermissionGrantResult.Granted + : PermissionGrantResult.Undefined); + return result; + } + + var cacheItems = await GetCacheItemsAsync(names, resourceName, resourceKey, providerName, providerKey); + foreach (var item in cacheItems) + { + result.Result.Add(GetPermissionNameFormCacheKeyOrNull(item.Key), + item.Value != null && item.Value.IsGranted + ? PermissionGrantResult.Granted + : PermissionGrantResult.Undefined); + } + + return result; + } + + protected virtual async Task>> GetCacheItemsAsync(string[] names, string resourceName, string resourceKey, string providerName, string providerKey) + { + var cacheKeys = names.Select(x => CalculateCacheKey(x, resourceName, resourceKey, providerName, providerKey)).ToList(); + + Logger.LogDebug($"ResourcePermissionStore.GetCacheItemAsync: {string.Join(",", cacheKeys)}"); + + var cacheItems = (await Cache.GetManyAsync(cacheKeys)).ToList(); + if (cacheItems.All(x => x.Value != null)) + { + Logger.LogDebug($"Found in the cache: {string.Join(",", cacheKeys)}"); + return cacheItems; + } + + var notCacheKeys = cacheItems.Where(x => x.Value == null).Select(x => x.Key).ToList(); + + Logger.LogDebug($"Not found in the cache: {string.Join(",", notCacheKeys)}"); + + var newCacheItems = await SetCacheItemsAsync(resourceName, resourceKey, providerName, providerKey, notCacheKeys); + + var result = new List>(); + foreach (var key in cacheKeys) + { + var item = newCacheItems.FirstOrDefault(x => x.Key == key); + if (item.Value == null) + { + item = cacheItems.FirstOrDefault(x => x.Key == key); + } + + result.Add(new KeyValuePair(key, item.Value)); + } + + return result; + } + + protected virtual async Task>> SetCacheItemsAsync(string resourceName, string resourceKey, string providerName, string providerKey, List notCacheKeys) + { + using (ResourcePermissionGrantRepository.DisableTracking()) + { + var permissionNames = new HashSet(notCacheKeys.Select(GetPermissionNameFormCacheKeyOrNull)); + var permissions = (await PermissionDefinitionManager.GetResourcePermissionsAsync()) + .Where(x => permissionNames.Contains(x.Name)) + .ToList(); + + Logger.LogDebug($"Getting not cache granted permissions from the repository for resource name,key:{resourceName},{resourceKey} and provider name,key: {providerName},{providerKey}"); + + var grantedPermissionsHashSet = new HashSet( + (await ResourcePermissionGrantRepository.GetListAsync(permissionNames.ToArray(), resourceName, resourceKey, providerName, providerKey)).Select(p => p.Name) + ); + + Logger.LogDebug($"Setting the cache items. Count: {permissions.Count}"); + + var cacheItems = new List>(); + + foreach (var permission in permissions) + { + var isGranted = grantedPermissionsHashSet.Contains(permission.Name); + + cacheItems.Add(new KeyValuePair( + CalculateCacheKey(permission.Name, resourceName, resourceKey, providerName, providerKey), + new ResourcePermissionGrantCacheItem(isGranted)) + ); + } + + await Cache.SetManyAsync(cacheItems); + + Logger.LogDebug($"Finished setting the cache items. Count: {permissions.Count}"); + + return cacheItems; + } + } + + public virtual async Task GetPermissionsAsync(string resourceName, string resourceKey) + { + using (ResourcePermissionGrantRepository.DisableTracking()) + { + var result = new MultiplePermissionGrantResult(); + + var resourcePermissions = (await PermissionDefinitionManager.GetResourcePermissionsAsync()).Where(x => x.ResourceName == resourceName).ToList(); + var permissionGrants = await ResourcePermissionGrantRepository.GetPermissionsAsync(resourceName, resourceKey); + foreach (var resourcePermission in resourcePermissions) + { + var isGranted = permissionGrants.Any(x => x.Name == resourcePermission.Name); + result.Result.Add(resourcePermission.Name, isGranted ? PermissionGrantResult.Granted : PermissionGrantResult.Undefined); + } + + return result; + } + } + + public virtual async Task GetGrantedPermissionsAsync(string resourceName, string resourceKey) + { + var resourcePermissions = (await PermissionDefinitionManager.GetResourcePermissionsAsync()).Where(x => x.ResourceName == resourceName).ToList(); + var grantedPermissions = await ResourcePermissionGrantRepository.GetPermissionsAsync(resourceName, resourceKey); + + var result = new List(); + foreach (var grantedPermission in grantedPermissions) + { + if (resourcePermissions.Any(x => x.Name == grantedPermission.Name)) + { + result.Add(grantedPermission.Name); + } + } + + return result.ToArray(); + } + + public virtual async Task GetGrantedResourceKeysAsync(string resourceName, string name) + { + return (await ResourcePermissionGrantRepository.GetResourceKeys(resourceName, name)).Select(x => x.ResourceKey).ToArray(); + } + + protected virtual string GetPermissionNameFormCacheKeyOrNull(string key) + { + //TODO: throw ex when name is null? + return ResourcePermissionGrantCacheItem.GetPermissionNameFormCacheKeyOrNull(key); + } + + protected virtual string CalculateCacheKey(string name, string resourceName, string resourceKey, string providerName, string providerKey) + { + return ResourcePermissionGrantCacheItem.CalculateCacheKey(name, resourceName, resourceKey, providerName, providerKey); + } +} diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/ResourcePermissionValueProviderGrantInfo.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/ResourcePermissionValueProviderGrantInfo.cs new file mode 100644 index 0000000000..5cb0d5013a --- /dev/null +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/ResourcePermissionValueProviderGrantInfo.cs @@ -0,0 +1,18 @@ +using JetBrains.Annotations; + +namespace Volo.Abp.PermissionManagement; + +public class ResourcePermissionValueProviderGrantInfo //TODO: Rename to ResourcePermissionGrantInfo +{ + public static ResourcePermissionValueProviderGrantInfo NonGranted { get; } = new ResourcePermissionValueProviderGrantInfo(false); + + public virtual bool IsGranted { get; set; } + + public virtual string ProviderKey { get; set; } + + public ResourcePermissionValueProviderGrantInfo(bool isGranted, [CanBeNull] string providerKey = null) + { + IsGranted = isGranted; + ProviderKey = providerKey; + } +} diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/StaticPermissionDefinitionChangedEventHandler.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/StaticPermissionDefinitionChangedEventHandler.cs new file mode 100644 index 0000000000..5f6dd1e700 --- /dev/null +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/StaticPermissionDefinitionChangedEventHandler.cs @@ -0,0 +1,36 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.EventBus; +using Volo.Abp.Authorization.Permissions; +using Volo.Abp.StaticDefinitions; +using Volo.Abp.Threading; + +namespace Volo.Abp.PermissionManagement; + +public class StaticPermissionDefinitionChangedEventHandler : ILocalEventHandler, ITransientDependency +{ + protected IStaticDefinitionCache, List)> GroupCache { get; } + protected IStaticDefinitionCache> DefinitionCache { get; } + protected PermissionDynamicInitializer PermissionDynamicInitializer { get; } + protected ICancellationTokenProvider CancellationTokenProvider { get; } + + public StaticPermissionDefinitionChangedEventHandler( + IStaticDefinitionCache, List)> groupCache, + IStaticDefinitionCache> definitionCache, + PermissionDynamicInitializer permissionDynamicInitializer, + ICancellationTokenProvider cancellationTokenProvider) + { + GroupCache = groupCache; + DefinitionCache = definitionCache; + PermissionDynamicInitializer = permissionDynamicInitializer; + CancellationTokenProvider = cancellationTokenProvider; + } + + public virtual async Task HandleEventAsync(StaticPermissionDefinitionChangedEvent eventData) + { + await GroupCache.ClearAsync(); + await DefinitionCache.ClearAsync(); + await PermissionDynamicInitializer.InitializeAsync(false, CancellationTokenProvider.Token); + } +} diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/StaticPermissionSaver.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/StaticPermissionSaver.cs index d1dff35f35..c672207f4f 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/StaticPermissionSaver.cs +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/StaticPermissionSaver.cs @@ -85,6 +85,12 @@ public class StaticPermissionSaver : IStaticPermissionSaver, ITransientDependenc await StaticStore.GetGroupsAsync() ); + var resourcePermissions = await PermissionSerializer.SerializeAsync( + await StaticStore.GetResourcePermissionsAsync() + ); + + permissionRecords = permissionRecords.Union(resourcePermissions).ToArray(); + var currentHash = CalculateHash( permissionGroupRecords, permissionRecords, diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.EntityFrameworkCore/Volo/Abp/PermissionManagement/EntityFrameworkCore/AbpPermissionManagementDbContextModelBuilderExtensions.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.EntityFrameworkCore/Volo/Abp/PermissionManagement/EntityFrameworkCore/AbpPermissionManagementDbContextModelBuilderExtensions.cs index fa6862abf6..c7bf3f2a5a 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.EntityFrameworkCore/Volo/Abp/PermissionManagement/EntityFrameworkCore/AbpPermissionManagementDbContextModelBuilderExtensions.cs +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.EntityFrameworkCore/Volo/Abp/PermissionManagement/EntityFrameworkCore/AbpPermissionManagementDbContextModelBuilderExtensions.cs @@ -27,6 +27,23 @@ public static class AbpPermissionManagementDbContextModelBuilderExtensions b.ApplyObjectExtensionMappings(); }); + builder.Entity(b => + { + b.ToTable(AbpPermissionManagementDbProperties.DbTablePrefix + "ResourcePermissionGrants", AbpPermissionManagementDbProperties.DbSchema); + + b.ConfigureByConvention(); + + b.Property(x => x.Name).HasMaxLength(PermissionDefinitionRecordConsts.MaxNameLength).IsRequired(); + b.Property(x => x.ResourceName).HasMaxLength(PermissionGrantConsts.MaxResourceNameLength).IsRequired(); + b.Property(x => x.ResourceKey).HasMaxLength(PermissionGrantConsts.MaxResourceKeyLength).IsRequired(); + b.Property(x => x.ProviderName).HasMaxLength(PermissionGrantConsts.MaxProviderNameLength).IsRequired(); + b.Property(x => x.ProviderKey).HasMaxLength(PermissionGrantConsts.MaxProviderKeyLength).IsRequired(); + + b.HasIndex(x => new { x.TenantId, x.Name, x.ResourceName, x.ResourceKey, x.ProviderName, x.ProviderKey }).IsUnique(); + + b.ApplyObjectExtensionMappings(); + }); + if (builder.IsHostDatabase()) { builder.Entity(b => @@ -52,16 +69,16 @@ public static class AbpPermissionManagementDbContextModelBuilderExtensions b.ConfigureByConvention(); - b.Property(x => x.GroupName).HasMaxLength(PermissionGroupDefinitionRecordConsts.MaxNameLength) - .IsRequired(); + b.Property(x => x.GroupName).HasMaxLength(PermissionGroupDefinitionRecordConsts.MaxNameLength); b.Property(x => x.Name).HasMaxLength(PermissionDefinitionRecordConsts.MaxNameLength).IsRequired(); + b.Property(x => x.ResourceName).HasMaxLength(PermissionDefinitionRecordConsts.MaxResourceNameLength); + b.Property(x => x.ManagementPermissionName).HasMaxLength(PermissionDefinitionRecordConsts.MaxManagementPermissionNameLength); b.Property(x => x.ParentName).HasMaxLength(PermissionDefinitionRecordConsts.MaxNameLength); - b.Property(x => x.DisplayName).HasMaxLength(PermissionDefinitionRecordConsts.MaxDisplayNameLength) - .IsRequired(); + b.Property(x => x.DisplayName).HasMaxLength(PermissionDefinitionRecordConsts.MaxDisplayNameLength).IsRequired(); b.Property(x => x.Providers).HasMaxLength(PermissionDefinitionRecordConsts.MaxProvidersLength); b.Property(x => x.StateCheckers).HasMaxLength(PermissionDefinitionRecordConsts.MaxStateCheckersLength); - b.HasIndex(x => new { x.Name }).IsUnique(); + b.HasIndex(x => new { x.ResourceName, x.Name }).IsUnique(); b.HasIndex(x => new { x.GroupName }); b.ApplyObjectExtensionMappings(); diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.EntityFrameworkCore/Volo/Abp/PermissionManagement/EntityFrameworkCore/AbpPermissionManagementEntityFrameworkCoreModule.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.EntityFrameworkCore/Volo/Abp/PermissionManagement/EntityFrameworkCore/AbpPermissionManagementEntityFrameworkCoreModule.cs index ebcc8527c1..da6dfe8b06 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.EntityFrameworkCore/Volo/Abp/PermissionManagement/EntityFrameworkCore/AbpPermissionManagementEntityFrameworkCoreModule.cs +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.EntityFrameworkCore/Volo/Abp/PermissionManagement/EntityFrameworkCore/AbpPermissionManagementEntityFrameworkCoreModule.cs @@ -17,6 +17,7 @@ public class AbpPermissionManagementEntityFrameworkCoreModule : AbpModule options.AddRepository(); options.AddRepository(); options.AddRepository(); + options.AddRepository(); }); } } diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.EntityFrameworkCore/Volo/Abp/PermissionManagement/EntityFrameworkCore/EfCoreResourcePermissionGrantRepository.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.EntityFrameworkCore/Volo/Abp/PermissionManagement/EntityFrameworkCore/EfCoreResourcePermissionGrantRepository.cs new file mode 100644 index 0000000000..385a45f989 --- /dev/null +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.EntityFrameworkCore/Volo/Abp/PermissionManagement/EntityFrameworkCore/EfCoreResourcePermissionGrantRepository.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Volo.Abp.Domain.Repositories.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore; + +namespace Volo.Abp.PermissionManagement.EntityFrameworkCore; + +public class EfCoreResourcePermissionGrantRepository : EfCoreRepository, IResourcePermissionGrantRepository +{ + public EfCoreResourcePermissionGrantRepository(IDbContextProvider dbContextProvider) + : base(dbContextProvider) + { + + } + + public virtual async Task FindAsync(string name, string resourceName, string resourceKey, string providerName, string providerKey, CancellationToken cancellationToken = default) + { + return await (await GetDbSetAsync()) + .OrderBy(x => x.Id) + .FirstOrDefaultAsync(s => + s.Name == name && + s.ResourceName == resourceName && + s.ResourceKey == resourceKey && + s.ProviderName == providerName && + s.ProviderKey == providerKey, + GetCancellationToken(cancellationToken) + ); + } + + public virtual async Task> GetListAsync(string resourceName, string resourceKey, string providerName, string providerKey, CancellationToken cancellationToken = default) + { + return await (await GetDbSetAsync()) + .Where(s => + s.ResourceName == resourceName && + s.ResourceKey == resourceKey && + s.ProviderName == providerName && + s.ProviderKey == providerKey + ).ToListAsync(GetCancellationToken(cancellationToken)); + } + + public virtual async Task> GetListAsync(string[] names, string resourceName, string resourceKey, string providerName, string providerKey, CancellationToken cancellationToken = default) + { + return await (await GetDbSetAsync()) + .Where(s => + names.Contains(s.Name) && + s.ResourceName == resourceName && + s.ResourceKey == resourceKey && + s.ProviderName == providerName && + s.ProviderKey == providerKey + ).ToListAsync(GetCancellationToken(cancellationToken)); + } + + public virtual async Task> GetListAsync(string providerName, string providerKey, CancellationToken cancellationToken = default) + { + return await (await GetDbSetAsync()) + .Where(s => + s.ProviderName == providerName && + s.ProviderKey == providerKey + ).ToListAsync(GetCancellationToken(cancellationToken)); + } + + public virtual async Task> GetPermissionsAsync(string resourceName, string resourceKey, CancellationToken cancellationToken = default) + { + return await (await GetDbSetAsync()) + .Where(s => + s.ResourceName == resourceName && + s.ResourceKey == resourceKey + ).ToListAsync(GetCancellationToken(cancellationToken)); + } + + public virtual async Task> GetResourceKeys(string resourceName, string name, CancellationToken cancellationToken = default) + { + return await (await GetDbSetAsync()) + .Where(s => + s.ResourceName == resourceName && + s.Name == name + ).ToListAsync(GetCancellationToken(cancellationToken)); + } +} diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.EntityFrameworkCore/Volo/Abp/PermissionManagement/EntityFrameworkCore/IPermissionManagementDbContext.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.EntityFrameworkCore/Volo/Abp/PermissionManagement/EntityFrameworkCore/IPermissionManagementDbContext.cs index c294c1b248..de02ab70ec 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.EntityFrameworkCore/Volo/Abp/PermissionManagement/EntityFrameworkCore/IPermissionManagementDbContext.cs +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.EntityFrameworkCore/Volo/Abp/PermissionManagement/EntityFrameworkCore/IPermissionManagementDbContext.cs @@ -8,8 +8,10 @@ namespace Volo.Abp.PermissionManagement.EntityFrameworkCore; public interface IPermissionManagementDbContext : IEfCoreDbContext { DbSet PermissionGroups { get; } - + DbSet Permissions { get; } - + DbSet PermissionGrants { get; } + + DbSet ResourcePermissionGrants { get; } } diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.EntityFrameworkCore/Volo/Abp/PermissionManagement/EntityFrameworkCore/PermissionManagementDbContext.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.EntityFrameworkCore/Volo/Abp/PermissionManagement/EntityFrameworkCore/PermissionManagementDbContext.cs index fe143fa8ae..59c724bfb7 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.EntityFrameworkCore/Volo/Abp/PermissionManagement/EntityFrameworkCore/PermissionManagementDbContext.cs +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.EntityFrameworkCore/Volo/Abp/PermissionManagement/EntityFrameworkCore/PermissionManagementDbContext.cs @@ -10,6 +10,7 @@ public class PermissionManagementDbContext : AbpDbContext PermissionGroups { get; set; } public DbSet Permissions { get; set; } public DbSet PermissionGrants { get; set; } + public DbSet ResourcePermissionGrants { get; set; } public PermissionManagementDbContext(DbContextOptions options) : base(options) diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.HttpApi.Client/ClientProxies/Volo/Abp/PermissionManagement/PermissionsClientProxy.Generated.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.HttpApi.Client/ClientProxies/Volo/Abp/PermissionManagement/PermissionsClientProxy.Generated.cs index 2fe22472f0..bd6e2c5e8e 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.HttpApi.Client/ClientProxies/Volo/Abp/PermissionManagement/PermissionsClientProxy.Generated.cs +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.HttpApi.Client/ClientProxies/Volo/Abp/PermissionManagement/PermissionsClientProxy.Generated.cs @@ -45,4 +45,72 @@ public partial class PermissionsClientProxy : ClientProxyBase GetResourceProviderKeyLookupServicesAsync(string resourceName) + { + return await RequestAsync(nameof(GetResourceProviderKeyLookupServicesAsync), new ClientProxyRequestTypeValue + { + { typeof(string), resourceName } + }); + } + + public virtual async Task SearchResourceProviderKeyAsync(string resourceName, string serviceName, string filter, int page) + { + return await RequestAsync(nameof(SearchResourceProviderKeyAsync), new ClientProxyRequestTypeValue + { + { typeof(string), resourceName }, + { typeof(string), serviceName }, + { typeof(string), filter }, + { typeof(int), page } + }); + } + + public virtual async Task GetResourceDefinitionsAsync(string resourceName) + { + return await RequestAsync(nameof(GetResourceDefinitionsAsync), new ClientProxyRequestTypeValue + { + { typeof(string), resourceName } + }); + } + + public virtual async Task GetResourceAsync(string resourceName, string resourceKey) + { + return await RequestAsync(nameof(GetResourceAsync), new ClientProxyRequestTypeValue + { + { typeof(string), resourceName }, + { typeof(string), resourceKey } + }); + } + + public virtual async Task GetResourceByProviderAsync(string resourceName, string resourceKey, string providerName, string providerKey) + { + return await RequestAsync(nameof(GetResourceByProviderAsync), new ClientProxyRequestTypeValue + { + { typeof(string), resourceName }, + { typeof(string), resourceKey }, + { typeof(string), providerName }, + { typeof(string), providerKey } + }); + } + + public virtual async Task UpdateResourceAsync(string resourceName, string resourceKey, UpdateResourcePermissionsDto input) + { + await RequestAsync(nameof(UpdateResourceAsync), new ClientProxyRequestTypeValue + { + { typeof(string), resourceName }, + { typeof(string), resourceKey }, + { typeof(UpdateResourcePermissionsDto), input } + }); + } + + public virtual async Task DeleteResourceAsync(string resourceName, string resourceKey, string providerName, string providerKey) + { + await RequestAsync(nameof(DeleteResourceAsync), new ClientProxyRequestTypeValue + { + { typeof(string), resourceName }, + { typeof(string), resourceKey }, + { typeof(string), providerName }, + { typeof(string), providerKey } + }); + } } diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.HttpApi.Client/ClientProxies/permissionManagement-generate-proxy.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.HttpApi.Client/ClientProxies/permissionManagement-generate-proxy.json index 208271e67d..8b95d9be21 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.HttpApi.Client/ClientProxies/permissionManagement-generate-proxy.json +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.HttpApi.Client/ClientProxies/permissionManagement-generate-proxy.json @@ -21,7 +21,7 @@ "parametersOnMethod": [ { "name": "input", - "typeAsString": "System.Collections.Generic.List`1[[Volo.Abp.PermissionManagement.IsGrantedRequest, Volo.Abp.PermissionManagement.Domain.Shared, Version=9.3.0.0, Culture=neutral, PublicKeyToken=null]], System.Private.CoreLib", + "typeAsString": "System.Collections.Generic.List`1[[Volo.Abp.PermissionManagement.IsGrantedRequest, Volo.Abp.PermissionManagement.Domain.Shared, Version=10.1.0.0, Culture=neutral, PublicKeyToken=null]], System.Private.CoreLib", "type": "System.Collections.Generic.List", "typeSimple": "[Volo.Abp.PermissionManagement.IsGrantedRequest]", "isOptional": false, @@ -46,7 +46,7 @@ "parametersOnMethod": [ { "name": "input", - "typeAsString": "System.Collections.Generic.List`1[[Volo.Abp.PermissionManagement.IsGrantedRequest, Volo.Abp.PermissionManagement.Domain.Shared, Version=9.3.0.0, Culture=neutral, PublicKeyToken=null]], System.Private.CoreLib", + "typeAsString": "System.Collections.Generic.List`1[[Volo.Abp.PermissionManagement.IsGrantedRequest, Volo.Abp.PermissionManagement.Domain.Shared, Version=10.1.0.0, Culture=neutral, PublicKeyToken=null]], System.Private.CoreLib", "type": "System.Collections.Generic.List", "typeSimple": "[Volo.Abp.PermissionManagement.IsGrantedRequest]", "isOptional": false, @@ -178,6 +178,221 @@ "type": "System.Void", "typeSimple": "System.Void" } + }, + { + "name": "GetResourceProviderKeyLookupServicesAsync", + "parametersOnMethod": [ + { + "name": "resourceName", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + } + ], + "returnValue": { + "type": "Volo.Abp.PermissionManagement.GetResourceProviderListResultDto", + "typeSimple": "Volo.Abp.PermissionManagement.GetResourceProviderListResultDto" + } + }, + { + "name": "SearchResourceProviderKeyAsync", + "parametersOnMethod": [ + { + "name": "resourceName", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "serviceName", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "filter", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "page", + "typeAsString": "System.Int32, System.Private.CoreLib", + "type": "System.Int32", + "typeSimple": "number", + "isOptional": false, + "defaultValue": null + } + ], + "returnValue": { + "type": "Volo.Abp.PermissionManagement.SearchProviderKeyListResultDto", + "typeSimple": "Volo.Abp.PermissionManagement.SearchProviderKeyListResultDto" + } + }, + { + "name": "GetResourceDefinitionsAsync", + "parametersOnMethod": [ + { + "name": "resourceName", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + } + ], + "returnValue": { + "type": "Volo.Abp.PermissionManagement.GetResourcePermissionDefinitionListResultDto", + "typeSimple": "Volo.Abp.PermissionManagement.GetResourcePermissionDefinitionListResultDto" + } + }, + { + "name": "GetResourceAsync", + "parametersOnMethod": [ + { + "name": "resourceName", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "resourceKey", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + } + ], + "returnValue": { + "type": "Volo.Abp.PermissionManagement.GetResourcePermissionListResultDto", + "typeSimple": "Volo.Abp.PermissionManagement.GetResourcePermissionListResultDto" + } + }, + { + "name": "GetResourceByProviderAsync", + "parametersOnMethod": [ + { + "name": "resourceName", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "resourceKey", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "providerName", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "providerKey", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + } + ], + "returnValue": { + "type": "Volo.Abp.PermissionManagement.GetResourcePermissionWithProviderListResultDto", + "typeSimple": "Volo.Abp.PermissionManagement.GetResourcePermissionWithProviderListResultDto" + } + }, + { + "name": "UpdateResourceAsync", + "parametersOnMethod": [ + { + "name": "resourceName", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "resourceKey", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "input", + "typeAsString": "Volo.Abp.PermissionManagement.UpdateResourcePermissionsDto, Volo.Abp.PermissionManagement.Application.Contracts", + "type": "Volo.Abp.PermissionManagement.UpdateResourcePermissionsDto", + "typeSimple": "Volo.Abp.PermissionManagement.UpdateResourcePermissionsDto", + "isOptional": false, + "defaultValue": null + } + ], + "returnValue": { + "type": "System.Void", + "typeSimple": "System.Void" + } + }, + { + "name": "DeleteResourceAsync", + "parametersOnMethod": [ + { + "name": "resourceName", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "resourceKey", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "providerName", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "providerKey", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + } + ], + "returnValue": { + "type": "System.Void", + "typeSimple": "System.Void" + } } ] } @@ -393,6 +608,505 @@ }, "allowAnonymous": null, "implementFrom": "Volo.Abp.PermissionManagement.IPermissionAppService" + }, + "GetResourceProviderKeyLookupServicesAsyncByResourceName": { + "uniqueName": "GetResourceProviderKeyLookupServicesAsyncByResourceName", + "name": "GetResourceProviderKeyLookupServicesAsync", + "httpMethod": "GET", + "url": "api/permission-management/permissions/resource-provider-key-lookup-services", + "supportedVersions": [], + "parametersOnMethod": [ + { + "name": "resourceName", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + } + ], + "parameters": [ + { + "nameOnMethod": "resourceName", + "name": "resourceName", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "" + } + ], + "returnValue": { + "type": "Volo.Abp.PermissionManagement.GetResourceProviderListResultDto", + "typeSimple": "Volo.Abp.PermissionManagement.GetResourceProviderListResultDto" + }, + "allowAnonymous": null, + "implementFrom": "Volo.Abp.PermissionManagement.IPermissionAppService" + }, + "SearchResourceProviderKeyAsyncByResourceNameAndServiceNameAndFilterAndPage": { + "uniqueName": "SearchResourceProviderKeyAsyncByResourceNameAndServiceNameAndFilterAndPage", + "name": "SearchResourceProviderKeyAsync", + "httpMethod": "GET", + "url": "api/permission-management/permissions/search-resource-provider-keys", + "supportedVersions": [], + "parametersOnMethod": [ + { + "name": "resourceName", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "serviceName", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "filter", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "page", + "typeAsString": "System.Int32, System.Private.CoreLib", + "type": "System.Int32", + "typeSimple": "number", + "isOptional": false, + "defaultValue": null + } + ], + "parameters": [ + { + "nameOnMethod": "resourceName", + "name": "resourceName", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "" + }, + { + "nameOnMethod": "serviceName", + "name": "serviceName", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "" + }, + { + "nameOnMethod": "filter", + "name": "filter", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "" + }, + { + "nameOnMethod": "page", + "name": "page", + "jsonName": null, + "type": "System.Int32", + "typeSimple": "number", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "" + } + ], + "returnValue": { + "type": "Volo.Abp.PermissionManagement.SearchProviderKeyListResultDto", + "typeSimple": "Volo.Abp.PermissionManagement.SearchProviderKeyListResultDto" + }, + "allowAnonymous": null, + "implementFrom": "Volo.Abp.PermissionManagement.IPermissionAppService" + }, + "GetResourceDefinitionsAsyncByResourceName": { + "uniqueName": "GetResourceDefinitionsAsyncByResourceName", + "name": "GetResourceDefinitionsAsync", + "httpMethod": "GET", + "url": "api/permission-management/permissions/resource-definitions", + "supportedVersions": [], + "parametersOnMethod": [ + { + "name": "resourceName", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + } + ], + "parameters": [ + { + "nameOnMethod": "resourceName", + "name": "resourceName", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "" + } + ], + "returnValue": { + "type": "Volo.Abp.PermissionManagement.GetResourcePermissionDefinitionListResultDto", + "typeSimple": "Volo.Abp.PermissionManagement.GetResourcePermissionDefinitionListResultDto" + }, + "allowAnonymous": null, + "implementFrom": "Volo.Abp.PermissionManagement.IPermissionAppService" + }, + "GetResourceAsyncByResourceNameAndResourceKey": { + "uniqueName": "GetResourceAsyncByResourceNameAndResourceKey", + "name": "GetResourceAsync", + "httpMethod": "GET", + "url": "api/permission-management/permissions/resource", + "supportedVersions": [], + "parametersOnMethod": [ + { + "name": "resourceName", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "resourceKey", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + } + ], + "parameters": [ + { + "nameOnMethod": "resourceName", + "name": "resourceName", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "" + }, + { + "nameOnMethod": "resourceKey", + "name": "resourceKey", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "" + } + ], + "returnValue": { + "type": "Volo.Abp.PermissionManagement.GetResourcePermissionListResultDto", + "typeSimple": "Volo.Abp.PermissionManagement.GetResourcePermissionListResultDto" + }, + "allowAnonymous": null, + "implementFrom": "Volo.Abp.PermissionManagement.IPermissionAppService" + }, + "GetResourceByProviderAsyncByResourceNameAndResourceKeyAndProviderNameAndProviderKey": { + "uniqueName": "GetResourceByProviderAsyncByResourceNameAndResourceKeyAndProviderNameAndProviderKey", + "name": "GetResourceByProviderAsync", + "httpMethod": "GET", + "url": "api/permission-management/permissions/resource/by-provider", + "supportedVersions": [], + "parametersOnMethod": [ + { + "name": "resourceName", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "resourceKey", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "providerName", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "providerKey", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + } + ], + "parameters": [ + { + "nameOnMethod": "resourceName", + "name": "resourceName", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "" + }, + { + "nameOnMethod": "resourceKey", + "name": "resourceKey", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "" + }, + { + "nameOnMethod": "providerName", + "name": "providerName", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "" + }, + { + "nameOnMethod": "providerKey", + "name": "providerKey", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "" + } + ], + "returnValue": { + "type": "Volo.Abp.PermissionManagement.GetResourcePermissionWithProviderListResultDto", + "typeSimple": "Volo.Abp.PermissionManagement.GetResourcePermissionWithProviderListResultDto" + }, + "allowAnonymous": null, + "implementFrom": "Volo.Abp.PermissionManagement.IPermissionAppService" + }, + "UpdateResourceAsyncByResourceNameAndResourceKeyAndInput": { + "uniqueName": "UpdateResourceAsyncByResourceNameAndResourceKeyAndInput", + "name": "UpdateResourceAsync", + "httpMethod": "PUT", + "url": "api/permission-management/permissions/resource", + "supportedVersions": [], + "parametersOnMethod": [ + { + "name": "resourceName", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "resourceKey", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "input", + "typeAsString": "Volo.Abp.PermissionManagement.UpdateResourcePermissionsDto, Volo.Abp.PermissionManagement.Application.Contracts", + "type": "Volo.Abp.PermissionManagement.UpdateResourcePermissionsDto", + "typeSimple": "Volo.Abp.PermissionManagement.UpdateResourcePermissionsDto", + "isOptional": false, + "defaultValue": null + } + ], + "parameters": [ + { + "nameOnMethod": "resourceName", + "name": "resourceName", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "" + }, + { + "nameOnMethod": "resourceKey", + "name": "resourceKey", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "" + }, + { + "nameOnMethod": "input", + "name": "input", + "jsonName": null, + "type": "Volo.Abp.PermissionManagement.UpdateResourcePermissionsDto", + "typeSimple": "Volo.Abp.PermissionManagement.UpdateResourcePermissionsDto", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "Body", + "descriptorName": "" + } + ], + "returnValue": { + "type": "System.Void", + "typeSimple": "System.Void" + }, + "allowAnonymous": null, + "implementFrom": "Volo.Abp.PermissionManagement.IPermissionAppService" + }, + "DeleteResourceAsyncByResourceNameAndResourceKeyAndProviderNameAndProviderKey": { + "uniqueName": "DeleteResourceAsyncByResourceNameAndResourceKeyAndProviderNameAndProviderKey", + "name": "DeleteResourceAsync", + "httpMethod": "DELETE", + "url": "api/permission-management/permissions/resource", + "supportedVersions": [], + "parametersOnMethod": [ + { + "name": "resourceName", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "resourceKey", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "providerName", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "providerKey", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + } + ], + "parameters": [ + { + "nameOnMethod": "resourceName", + "name": "resourceName", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "" + }, + { + "nameOnMethod": "resourceKey", + "name": "resourceKey", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "" + }, + { + "nameOnMethod": "providerName", + "name": "providerName", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "" + }, + { + "nameOnMethod": "providerKey", + "name": "providerKey", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "" + } + ], + "returnValue": { + "type": "System.Void", + "typeSimple": "System.Void" + }, + "allowAnonymous": null, + "implementFrom": "Volo.Abp.PermissionManagement.IPermissionAppService" } } } diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.HttpApi/Volo/Abp/PermissionManagement/PermissionsController.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.HttpApi/Volo/Abp/PermissionManagement/PermissionsController.cs index ff6d7e6128..c1f65e353e 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.HttpApi/Volo/Abp/PermissionManagement/PermissionsController.cs +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.HttpApi/Volo/Abp/PermissionManagement/PermissionsController.cs @@ -34,4 +34,50 @@ public class PermissionsController : AbpControllerBase, IPermissionAppService { return PermissionAppService.UpdateAsync(providerName, providerKey, input); } + + [HttpGet("resource-provider-key-lookup-services")] + public virtual Task GetResourceProviderKeyLookupServicesAsync(string resourceName) + { + return PermissionAppService.GetResourceProviderKeyLookupServicesAsync(resourceName); + } + + [HttpGet("search-resource-provider-keys")] + public virtual Task SearchResourceProviderKeyAsync(string resourceName, string serviceName, string filter, int page) + { + return PermissionAppService.SearchResourceProviderKeyAsync(resourceName, serviceName, filter, page); + } + + [HttpGet("resource-definitions")] + public virtual Task GetResourceDefinitionsAsync(string resourceName) + { + return PermissionAppService.GetResourceDefinitionsAsync(resourceName); + } + + [HttpGet] + [Route("resource")] + public virtual Task GetResourceAsync(string resourceName, string resourceKey) + { + return PermissionAppService.GetResourceAsync(resourceName, resourceKey); + } + + [HttpGet] + [Route("resource/by-provider")] + public virtual Task GetResourceByProviderAsync(string resourceName, string resourceKey, string providerName, string providerKey) + { + return PermissionAppService.GetResourceByProviderAsync(resourceName, resourceKey, providerName, providerKey); + } + + [HttpPut] + [Route("resource")] + public virtual Task UpdateResourceAsync(string resourceName, string resourceKey, UpdateResourcePermissionsDto input) + { + return PermissionAppService.UpdateResourceAsync(resourceName, resourceKey, input); + } + + [HttpDelete] + [Route("resource")] + public virtual Task DeleteResourceAsync(string resourceName, string resourceKey, string providerName, string providerKey) + { + return PermissionAppService.DeleteResourceAsync(resourceName, resourceKey, providerName, providerKey); + } } diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.MongoDB/Volo/Abp/PermissionManagement/MongoDb/AbpPermissionManagementMongoDbContextExtensions.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.MongoDB/Volo/Abp/PermissionManagement/MongoDb/AbpPermissionManagementMongoDbContextExtensions.cs index 9c7c90af58..4eb8eea3b6 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.MongoDB/Volo/Abp/PermissionManagement/MongoDb/AbpPermissionManagementMongoDbContextExtensions.cs +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.MongoDB/Volo/Abp/PermissionManagement/MongoDb/AbpPermissionManagementMongoDbContextExtensions.cs @@ -13,15 +13,20 @@ public static class AbpPermissionManagementMongoDbContextExtensions { b.CollectionName = AbpPermissionManagementDbProperties.DbTablePrefix + "PermissionGroups"; }); - + builder.Entity(b => { b.CollectionName = AbpPermissionManagementDbProperties.DbTablePrefix + "Permissions"; }); - + builder.Entity(b => { b.CollectionName = AbpPermissionManagementDbProperties.DbTablePrefix + "PermissionGrants"; }); + + builder.Entity(b => + { + b.CollectionName = AbpPermissionManagementDbProperties.DbTablePrefix + "ResourcePermissionGrants"; + }); } } diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.MongoDB/Volo/Abp/PermissionManagement/MongoDb/AbpPermissionManagementMongoDbModule.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.MongoDB/Volo/Abp/PermissionManagement/MongoDb/AbpPermissionManagementMongoDbModule.cs index 80831660f8..686d219295 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.MongoDB/Volo/Abp/PermissionManagement/MongoDb/AbpPermissionManagementMongoDbModule.cs +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.MongoDB/Volo/Abp/PermissionManagement/MongoDb/AbpPermissionManagementMongoDbModule.cs @@ -19,6 +19,7 @@ public class AbpPermissionManagementMongoDbModule : AbpModule options.AddRepository(); options.AddRepository(); options.AddRepository(); + options.AddRepository(); }); } } diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.MongoDB/Volo/Abp/PermissionManagement/MongoDb/IPermissionManagementMongoDbContext.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.MongoDB/Volo/Abp/PermissionManagement/MongoDb/IPermissionManagementMongoDbContext.cs index 98015484fb..454875f517 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.MongoDB/Volo/Abp/PermissionManagement/MongoDb/IPermissionManagementMongoDbContext.cs +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.MongoDB/Volo/Abp/PermissionManagement/MongoDb/IPermissionManagementMongoDbContext.cs @@ -8,8 +8,10 @@ namespace Volo.Abp.PermissionManagement.MongoDB; public interface IPermissionManagementMongoDbContext : IAbpMongoDbContext { IMongoCollection PermissionGroups { get; } - + IMongoCollection Permissions { get; } - + IMongoCollection PermissionGrants { get; } + + IMongoCollection ResourcePermissionGrants { get; } } diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.MongoDB/Volo/Abp/PermissionManagement/MongoDb/MongoResourcePermissionGrantRepository.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.MongoDB/Volo/Abp/PermissionManagement/MongoDb/MongoResourcePermissionGrantRepository.cs new file mode 100644 index 0000000000..30db095854 --- /dev/null +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.MongoDB/Volo/Abp/PermissionManagement/MongoDb/MongoResourcePermissionGrantRepository.cs @@ -0,0 +1,89 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using MongoDB.Driver.Linq; +using Volo.Abp.Domain.Repositories.MongoDB; +using Volo.Abp.MongoDB; + +namespace Volo.Abp.PermissionManagement.MongoDB; + +public class MongoResourcePermissionGrantRepository : MongoDbRepository, IResourcePermissionGrantRepository +{ + public MongoResourcePermissionGrantRepository(IMongoDbContextProvider dbContextProvider) + : base(dbContextProvider) + { + + } + + public virtual async Task FindAsync(string name, string resourceName, string resourceKey, string providerName, string providerKey, CancellationToken cancellationToken = default) + { + cancellationToken = GetCancellationToken(cancellationToken); + return await (await GetQueryableAsync(cancellationToken)) + .OrderBy(x => x.Id) + .FirstOrDefaultAsync(s => + s.Name == name && + s.ResourceName == resourceName && + s.ResourceKey == resourceKey && + s.ProviderName == providerName && + s.ProviderKey == providerKey, + cancellationToken + ); + } + + public virtual async Task> GetListAsync(string resourceName, string resourceKey, string providerName, string providerKey, CancellationToken cancellationToken = default) + { + cancellationToken = GetCancellationToken(cancellationToken); + return await (await GetQueryableAsync(cancellationToken)) + .Where(s => + s.ResourceName == resourceName && + s.ResourceKey == resourceKey && + s.ProviderName == providerName && + s.ProviderKey == providerKey + ).ToListAsync(cancellationToken); + } + + public virtual async Task> GetListAsync(string[] names, string resourceName, string resourceKey, string providerName, string providerKey, CancellationToken cancellationToken = default) + { + cancellationToken = GetCancellationToken(cancellationToken); + return await (await GetQueryableAsync(cancellationToken)) + .Where(s => + names.AsEnumerable().Contains(s.Name) && + s.ResourceName == resourceName && + s.ResourceKey == resourceKey && + s.ProviderName == providerName && + s.ProviderKey == providerKey + ).ToListAsync(cancellationToken); + } + + public virtual async Task> GetListAsync(string providerName, string providerKey, CancellationToken cancellationToken = default) + { + cancellationToken = GetCancellationToken(cancellationToken); + return await (await GetQueryableAsync(cancellationToken)) + .Where(s => + s.ProviderName == providerName && + s.ProviderKey == providerKey + ).ToListAsync(cancellationToken); + } + + public virtual async Task> GetPermissionsAsync(string resourceName, string resourceKey, CancellationToken cancellationToken = default) + { + cancellationToken = GetCancellationToken(cancellationToken); + return await (await GetQueryableAsync(cancellationToken)) + .Where(s => + s.ResourceName == resourceName && + s.ResourceKey == resourceKey + ).ToListAsync(cancellationToken); + } + + public virtual async Task> GetResourceKeys(string resourceName, string name, CancellationToken cancellationToken = default) + { + cancellationToken = GetCancellationToken(cancellationToken); + return await (await GetQueryableAsync(cancellationToken)) + .Where(s => + s.ResourceName == resourceName && + s.Name == name + ).ToListAsync(cancellationToken); + } +} diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.MongoDB/Volo/Abp/PermissionManagement/MongoDb/PermissionManagementMongoDbContext.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.MongoDB/Volo/Abp/PermissionManagement/MongoDb/PermissionManagementMongoDbContext.cs index c62db52c11..c9ea6ea984 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.MongoDB/Volo/Abp/PermissionManagement/MongoDb/PermissionManagementMongoDbContext.cs +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.MongoDB/Volo/Abp/PermissionManagement/MongoDb/PermissionManagementMongoDbContext.cs @@ -10,6 +10,7 @@ public class PermissionManagementMongoDbContext : AbpMongoDbContext, IPermission public IMongoCollection PermissionGroups => Collection(); public IMongoCollection Permissions => Collection(); public IMongoCollection PermissionGrants => Collection(); + public IMongoCollection ResourcePermissionGrants => Collection(); protected override void CreateModel(IMongoModelBuilder modelBuilder) { diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/AbpPermissionManagementWebModule.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/AbpPermissionManagementWebModule.cs index baa27b44ff..e20e38c53a 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/AbpPermissionManagementWebModule.cs +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/AbpPermissionManagementWebModule.cs @@ -20,7 +20,7 @@ public class AbpPermissionManagementWebModule : AbpModule { options.AddAssemblyResource( typeof(AbpPermissionManagementResource), - typeof(AbpPermissionManagementWebModule).Assembly, + typeof(AbpPermissionManagementWebModule).Assembly, typeof(AbpPermissionManagementApplicationContractsModule).Assembly ); }); diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/AddResourcePermissionManagementModal.cshtml b/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/AddResourcePermissionManagementModal.cshtml new file mode 100644 index 0000000000..be12c7cd1d --- /dev/null +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/AddResourcePermissionManagementModal.cshtml @@ -0,0 +1,54 @@ +@page +@using Microsoft.AspNetCore.Mvc.Localization +@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal +@using Volo.Abp.Localization +@using Volo.Abp.PermissionManagement.Localization +@using Volo.Abp.PermissionManagement.Web.Pages.AbpPermissionManagement +@model AddResourcePermissionManagementModal +@inject IHtmlLocalizer L + +@{ + Layout = null; +} + + + + +
+ + + + + + +
+
+ @foreach (var provider in Model.ResourceProviders.Providers.Select((value, i) => new { i, value })) + { +
+ + +
+ } +
+ + +
+
+

@L["ResourcePermissionPermissions"]

+
+ + +
+ @foreach (var permission in Model.ResourcePermissionDefinitions.Permissions) + { +
+ + +
+ } +
+
+ +
+
diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/AddResourcePermissionManagementModal.cshtml.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/AddResourcePermissionManagementModal.cshtml.cs new file mode 100644 index 0000000000..d9828ee661 --- /dev/null +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/AddResourcePermissionManagementModal.cshtml.cs @@ -0,0 +1,78 @@ +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Volo.Abp.AspNetCore.Mvc.UI.RazorPages; + +namespace Volo.Abp.PermissionManagement.Web.Pages.AbpPermissionManagement; + +public class AddResourcePermissionManagementModal : AbpPageModel +{ + [Required] + [HiddenInput] + [BindProperty(SupportsGet = true)] + public string ResourceName { get; set; } + + [Required] + [HiddenInput] + [BindProperty(SupportsGet = true)] + public string ResourceKey { get; set; } + + [BindProperty(SupportsGet = true)] + [HiddenInput] + public string ResourceDisplayName { get; set; } + + [BindProperty] + public ResourcePermissionViewModel AddModel { get; set; } + + public GetResourcePermissionDefinitionListResultDto ResourcePermissionDefinitions { get; set; } + public GetResourceProviderListResultDto ResourceProviders { get; set; } + + protected IPermissionAppService PermissionAppService { get; } + + public AddResourcePermissionManagementModal(IPermissionAppService permissionAppService) + { + ObjectMapperContext = typeof(AbpPermissionManagementWebModule); + + PermissionAppService = permissionAppService; + } + + public virtual async Task OnGetAsync() + { + ValidateModel(); + + ResourcePermissionDefinitions = await PermissionAppService.GetResourceDefinitionsAsync(ResourceName); + ResourceProviders = await PermissionAppService.GetResourceProviderKeyLookupServicesAsync(ResourceName); + + return Page(); + } + + public virtual async Task OnPostAsync() + { + ValidateModel(); + + await PermissionAppService.UpdateResourceAsync( + ResourceName, + ResourceKey, + new UpdateResourcePermissionsDto() + { + ProviderName = AddModel.ProviderName, + ProviderKey = AddModel.ProviderKey, + Permissions = AddModel.Permissions ?? new List() + } + ); + + return NoContent(); + } + + public class ResourcePermissionViewModel + { + [Required] + public string ProviderName { get; set; } + + [Required] + public string ProviderKey { get; set; } + + public List Permissions { get; set; } + } +} diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/PermissionManagementModal.cshtml b/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/PermissionManagementModal.cshtml index a9caf5c348..c1241af454 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/PermissionManagementModal.cshtml +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/PermissionManagementModal.cshtml @@ -79,5 +79,3 @@ - - \ No newline at end of file diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/PermissionManagementModal.cshtml.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/PermissionManagementModal.cshtml.cs index 2c9d2dcc1c..6f6d6113e9 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/PermissionManagementModal.cshtml.cs +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/PermissionManagementModal.cshtml.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; @@ -6,6 +7,7 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; using Volo.Abp.AspNetCore.Mvc.UI.RazorPages; +using Volo.Abp.Authorization.Permissions; using Volo.Abp.EventBus.Local; using Volo.Abp.Localization; using Volo.Abp.PermissionManagement.Web.Utils; @@ -105,9 +107,13 @@ public class PermissionManagementModal : AbpPageModel } ); - await LocalEventBus.PublishAsync( - new CurrentApplicationConfigurationCacheResetEventData() - ); + Guid? userId = null; + if (ProviderName == UserPermissionValueProvider.ProviderName && Guid.TryParse(ProviderKey, out var parsedUserId)) + { + userId = parsedUserId; + } + + await LocalEventBus.PublishAsync(new CurrentApplicationConfigurationCacheResetEventData(userId)); return NoContent(); } @@ -130,7 +136,7 @@ public class PermissionManagementModal : AbpPageModel public bool IsDisabled(string currentProviderName) { var grantedProviders = Permissions.SelectMany(x => x.GrantedProviders); - + return Permissions.All(x => x.IsGranted) && grantedProviders.All(p => p.ProviderName != currentProviderName); } } diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/ResourcePermissionManagementModal.cshtml b/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/ResourcePermissionManagementModal.cshtml new file mode 100644 index 0000000000..15a5c0bff4 --- /dev/null +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/ResourcePermissionManagementModal.cshtml @@ -0,0 +1,52 @@ +@page +@using System.Web; +@using Microsoft.AspNetCore.Mvc.Localization +@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal +@using Volo.Abp.Localization +@using Volo.Abp.PermissionManagement.Localization +@using Volo.Abp.PermissionManagement.Web.Pages.AbpPermissionManagement +@model ResourcePermissionManagementModal +@inject IHtmlLocalizer L + +@{ + Layout = null; +} + +@if(Model.HasAnyResourcePermission && Model.HasAnyResourceProviderKeyLookupService) +{ + + +
+ + + + + +
+ +
+ +
+ +
+
+} +else +{ + + + + + + + +} diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/ResourcePermissionManagementModal.cshtml.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/ResourcePermissionManagementModal.cshtml.cs new file mode 100644 index 0000000000..1b1373e017 --- /dev/null +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/ResourcePermissionManagementModal.cshtml.cs @@ -0,0 +1,51 @@ +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Volo.Abp.AspNetCore.Mvc.UI.RazorPages; + +namespace Volo.Abp.PermissionManagement.Web.Pages.AbpPermissionManagement; + +public class ResourcePermissionManagementModal : AbpPageModel +{ + [Required] + [HiddenInput] + [BindProperty(SupportsGet = true)] + public string ResourceName { get; set; } + + [Required] + [HiddenInput] + [BindProperty(SupportsGet = true)] + public string ResourceKey { get; set; } + + [HiddenInput] + [BindProperty(SupportsGet = true)] + public string ResourceDisplayName { get; set; } + + public bool HasAnyResourcePermission { get; set; } + public bool HasAnyResourceProviderKeyLookupService { get; set; } + + protected IPermissionAppService PermissionAppService { get; } + + public ResourcePermissionManagementModal(IPermissionAppService permissionAppService) + { + ObjectMapperContext = typeof(AbpPermissionManagementWebModule); + + PermissionAppService = permissionAppService; + } + + public virtual async Task OnGetAsync() + { + HasAnyResourcePermission = (await PermissionAppService.GetResourceDefinitionsAsync(ResourceName)).Permissions.Any(); + if (HasAnyResourcePermission) + { + HasAnyResourceProviderKeyLookupService = (await PermissionAppService.GetResourceProviderKeyLookupServicesAsync(ResourceName)).Providers.Count > 0; + } + return Page(); + } + + public virtual Task OnPostAsync() + { + return Task.FromResult(NoContent()); + } +} diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/UpdateResourcePermissionManagementModal.cshtml b/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/UpdateResourcePermissionManagementModal.cshtml new file mode 100644 index 0000000000..3e90319e02 --- /dev/null +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/UpdateResourcePermissionManagementModal.cshtml @@ -0,0 +1,42 @@ +@page +@using Microsoft.AspNetCore.Mvc.Localization +@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal +@using Volo.Abp.Localization +@using Volo.Abp.PermissionManagement.Localization +@using Volo.Abp.PermissionManagement.Web.Pages.AbpPermissionManagement +@model UpdateResourcePermissionManagementModal +@inject IHtmlLocalizer L + +@{ + Layout = null; +} + + + + +
+ + + + + + + +
+

@L["ResourcePermissionPermissions"]

+
+ + +
+ @foreach (var permission in Model.ResourcePermissions.Permissions) + { +
+ + +
+ } +
+
+ +
+
diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/UpdateResourcePermissionManagementModal.cshtml.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/UpdateResourcePermissionManagementModal.cshtml.cs new file mode 100644 index 0000000000..db2e661993 --- /dev/null +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/UpdateResourcePermissionManagementModal.cshtml.cs @@ -0,0 +1,79 @@ +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Volo.Abp.AspNetCore.Mvc.UI.RazorPages; + +namespace Volo.Abp.PermissionManagement.Web.Pages.AbpPermissionManagement; + +public class UpdateResourcePermissionManagementModal : AbpPageModel +{ + [Required] + [HiddenInput] + [BindProperty(SupportsGet = true)] + public string ResourceName { get; set; } + + [Required] + [HiddenInput] + [BindProperty(SupportsGet = true)] + public string ResourceKey { get; set; } + + [BindProperty(SupportsGet = true)] + public string ResourceDisplayName { get; set; } + + [Required] + [HiddenInput] + [BindProperty(SupportsGet = true)] + public string ProviderName { get; set; } + + [Required] + [HiddenInput] + [BindProperty(SupportsGet = true)] + public string ProviderKey { get; set; } + + [BindProperty(SupportsGet = true)] + public ResourcePermissionViewModel UpdateModel { get; set; } + + public GetResourcePermissionWithProviderListResultDto ResourcePermissions { get; set; } + + protected IPermissionAppService PermissionAppService { get; } + + public UpdateResourcePermissionManagementModal(IPermissionAppService permissionAppService) + { + ObjectMapperContext = typeof(AbpPermissionManagementWebModule); + + PermissionAppService = permissionAppService; + } + + public virtual async Task OnGetAsync() + { + ValidateModel(); + + ResourcePermissions = await PermissionAppService.GetResourceByProviderAsync(ResourceName, ResourceKey, ProviderName, ProviderKey); + + return Page(); + } + + public virtual async Task OnPostAsync() + { + ValidateModel(); + + await PermissionAppService.UpdateResourceAsync( + ResourceName, + ResourceKey, + new UpdateResourcePermissionsDto() + { + ProviderName = ProviderName, + ProviderKey = ProviderKey, + Permissions = UpdateModel.Permissions ?? new List() + } + ); + + return NoContent(); + } + + public class ResourcePermissionViewModel + { + public List Permissions { get; set; } + } +} diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/add-resource-permission-management-modal.js b/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/add-resource-permission-management-modal.js new file mode 100644 index 0000000000..2fa7f06550 --- /dev/null +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/add-resource-permission-management-modal.js @@ -0,0 +1,91 @@ +var abp = abp || {}; +(function ($) { + var $all = $("#grantAllresourcePermissions"); + var $items = $("#permissionList input[type='checkbox']").not("#grantAllresourcePermissions"); + $all.on("change", function () { + $items.prop("checked", this.checked); + }); + $items.on("change", function () { + $all.prop("checked", $items.length === $items.filter(":checked").length); + }); + + var $permissionManagementForm = $("#addResourcePermissionManagementForm"); + var $providerKey = $("#AddModel_ProviderKey"); + $providerKey.select2({ + ajax: { + url: '/api/permission-management/permissions/search-resource-provider-keys', + delay: 250, + dataType: "json", + data: function (params) { + var query = {}; + query["resourceName"] = $('#ResourceName').val(); + query["serviceName"] = $('input[name="AddModel.ProviderName"]:checked').val(); + query["page"] = params.page || 1; + query["filter"] = params.term; + return query; + }, + processResults: function (data) { + var keyValues = []; + data.keys.forEach(function (item, index) { + keyValues.push({ + id: item["providerKey"], + text: item["providerDisplayName"], + displayName: item["providerDisplayName"] + }) + }); + return { + results: keyValues, + pagination: { + more: keyValues.length == 10 + } + }; + } + }, + width: '100%', + dropdownParent: $('#addResourcePermissionManagementModal'), + language: abp.localization.currentCulture.cultureName + }); + + $('input[name="AddModel.ProviderName"]').change(function () { + $providerKey.val(null).trigger('change'); + }); + + $providerKey.change(function () { + if ($providerKey.val()) { + $permissionManagementForm.valid(); + } + var providerKey = $providerKey.val(); + if (!providerKey) { + $items.prop('checked', false); + $all.prop("checked", false); + return; + } + + abp.ui.setBusy('#permissionList'); + var resourceName = $("#ResourceName").val(); + var resourceKey = $("#ResourceKey").val(); + var providerName = $('input[name="AddModel.ProviderName"]:checked').val(); + volo.abp.permissionManagement.permissions.getResourceByProvider(resourceName, resourceKey, providerName, providerKey).then(function (result) { + abp.ui.clearBusy(); + var grantedPermissionNames = result.permissions.filter(function (p) { + return p.providers.indexOf(providerName) >= 0 && p.isGranted === true; + }).map(function (p) { + return p.name; + }); + $items.each(function () { + var $checkbox = $(this); + if (grantedPermissionNames.indexOf($checkbox.val()) >= 0) { + $checkbox.prop('checked', true); + } else { + $checkbox.prop('checked', false); + } + }); + $all.prop("checked", $items.length === $items.filter(":checked").length); + }); + }); + + $permissionManagementForm.submit(function () { + $(this).valid(); + }); + +})(jQuery); diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/resource-permission-management-modal.js b/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/resource-permission-management-modal.js new file mode 100644 index 0000000000..7a6c100c07 --- /dev/null +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/resource-permission-management-modal.js @@ -0,0 +1,131 @@ +var abp = abp || {}; +(function ($) { + var l = abp.localization.getResource('AbpPermissionManagement'); + var _dataTable = null; + abp.ui.extensions.entityActions.get('permissionManagement.resource').addContributor( + function (actionList) { + return actionList.addManyTail( + [ + { + text: l('Edit'), + action: function (data) { + var _updateResourcePermissionsModal = new abp.ModalManager(abp.appPath + "AbpPermissionManagement/UpdateResourcePermissionManagementModal"); + _updateResourcePermissionsModal.open({ + resourceName: $("#ResourceName").val(), + resourceKey: $("#ResourceKey").val(), + resourceDisplayName: $("#ResourceDisplayName").val(), + providerName: data.record.providerName, + providerKey: data.record.providerKey + }); + _updateResourcePermissionsModal.onResult(function () { + _dataTable.ajax.reloadEx(function (json) { + _dataTable.columns.adjust(); + }); + }); + }, + }, + { + text: l('Delete'), + confirmMessage: function (data) { + return l( + 'ResourcePermissionDeletionConfirmationMessage', + data.record.name + ); + }, + action: function (data) { + volo.abp.permissionManagement.permissions.deleteResource($("#ResourceName").val(), $("#ResourceKey").val(), data.record.providerName, data.record.providerKey).then(function () { + abp.notify.info(l('DeletedSuccessfully')); + _dataTable.ajax.reloadEx(function (json) { + _dataTable.columns.adjust(); + }); + }); + }, + } + ] + ); + } + ); + + abp.ui.extensions.tableColumns.get('permissionManagement.resource').addContributor( + function (columnList) { + columnList.addManyTail( + [ + { + title: l("Actions"), + rowAction: { + items: abp.ui.extensions.entityActions.get('permissionManagement.resource').actions.toArray() + } + }, + { + title: l("ResourcePermissionTarget"), + data: 'providerName', + render: function (data, type, row) { + return '' + row.providerName + '' + row.providerDisplayName; + }, + }, + { + title: l("ResourcePermissionPermissions"), + data: 'permissions', + render: function (data, type, row) { + var spans = ''; + for (var i = 0; i < row.permissions.length; i++) { + spans += '' + row.permissions[i].displayName + ''; + } + return spans; + }, + } + ] + ); + }, + 0 //adds as the first contributor + ); + + abp.modals = abp.modals || {}; + + abp.modals.ResourcePermissionManagement = function () { + var initModal = function (publicApi, args) { + _dataTable = $('#resourcePermissionTable').DataTable( + abp.libs.datatables.normalizeConfiguration({ + order: [], + searching: false, + processing: true, + scrollX: false, + serverSide: false, + paging: true, + ajax: function () { + return function (requestData, callback, settings) { + if (callback) { + volo.abp.permissionManagement.permissions.getResource(args.resourceName, args.resourceKey).then(function (result) { + callback({ + recordsTotal: result.permissions.length, + recordsFiltered: result.permissions.length, + data: result.permissions + }); + }); + } + } + }(), + columnDefs: abp.ui.extensions.tableColumns.get('permissionManagement.resource').columns.toArray(), + }) + ); + + $("#addPermission").click(function () { + var _addResourcePermissionsModal = new abp.ModalManager(abp.appPath + "AbpPermissionManagement/AddResourcePermissionManagementModal"); + _addResourcePermissionsModal.open({ + resourceName: $("#ResourceName").val(), + resourceKey: $("#ResourceKey").val(), + resourceDisplayName: $("#ResourceDisplayName").val() + }); + _addResourcePermissionsModal.onResult(function () { + _dataTable.ajax.reloadEx(function (json) { + _dataTable.columns.adjust(); + }); + }); + }); + }; + + return { + initModal: initModal, + }; + }; +})(jQuery); diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/update-resource-permission-management-modal.js b/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/update-resource-permission-management-modal.js new file mode 100644 index 0000000000..d1e1f02fa4 --- /dev/null +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/update-resource-permission-management-modal.js @@ -0,0 +1,11 @@ +var abp = abp || {}; +(function ($) { + var $all = $("#grantAllresourcePermissions"); + var $items = $("#permissionList input[type='checkbox']").not("#grantAllresourcePermissions"); + $all.on("change", function () { + $items.prop("checked", this.checked); + }); + $items.on("change", function () { + $all.prop("checked", $items.length === $items.filter(":checked").length); + }); +})(jQuery); diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/wwwroot/client-proxies/permissionManagement-proxy.js b/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/wwwroot/client-proxies/permissionManagement-proxy.js index a296877917..893c86ab97 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/wwwroot/client-proxies/permissionManagement-proxy.js +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/wwwroot/client-proxies/permissionManagement-proxy.js @@ -34,6 +34,58 @@ }, ajaxParams)); }; + volo.abp.permissionManagement.permissions.getResourceProviderKeyLookupServices = function(resourceName, ajaxParams) { + return abp.ajax($.extend(true, { + url: abp.appPath + 'api/permission-management/permissions/resource-provider-key-lookup-services' + abp.utils.buildQueryString([{ name: 'resourceName', value: resourceName }]) + '', + type: 'GET' + }, ajaxParams)); + }; + + volo.abp.permissionManagement.permissions.searchResourceProviderKey = function(resourceName, serviceName, filter, page, ajaxParams) { + return abp.ajax($.extend(true, { + url: abp.appPath + 'api/permission-management/permissions/search-resource-provider-keys' + abp.utils.buildQueryString([{ name: 'resourceName', value: resourceName }, { name: 'serviceName', value: serviceName }, { name: 'filter', value: filter }, { name: 'page', value: page }]) + '', + type: 'GET' + }, ajaxParams)); + }; + + volo.abp.permissionManagement.permissions.getResourceDefinitions = function(resourceName, ajaxParams) { + return abp.ajax($.extend(true, { + url: abp.appPath + 'api/permission-management/permissions/resource-definitions' + abp.utils.buildQueryString([{ name: 'resourceName', value: resourceName }]) + '', + type: 'GET' + }, ajaxParams)); + }; + + volo.abp.permissionManagement.permissions.getResource = function(resourceName, resourceKey, ajaxParams) { + return abp.ajax($.extend(true, { + url: abp.appPath + 'api/permission-management/permissions/resource' + abp.utils.buildQueryString([{ name: 'resourceName', value: resourceName }, { name: 'resourceKey', value: resourceKey }]) + '', + type: 'GET' + }, ajaxParams)); + }; + + volo.abp.permissionManagement.permissions.getResourceByProvider = function(resourceName, resourceKey, providerName, providerKey, ajaxParams) { + return abp.ajax($.extend(true, { + url: abp.appPath + 'api/permission-management/permissions/resource/by-provider' + abp.utils.buildQueryString([{ name: 'resourceName', value: resourceName }, { name: 'resourceKey', value: resourceKey }, { name: 'providerName', value: providerName }, { name: 'providerKey', value: providerKey }]) + '', + type: 'GET' + }, ajaxParams)); + }; + + volo.abp.permissionManagement.permissions.updateResource = function(resourceName, resourceKey, input, ajaxParams) { + return abp.ajax($.extend(true, { + url: abp.appPath + 'api/permission-management/permissions/resource' + abp.utils.buildQueryString([{ name: 'resourceName', value: resourceName }, { name: 'resourceKey', value: resourceKey }]) + '', + type: 'PUT', + dataType: null, + data: JSON.stringify(input) + }, ajaxParams)); + }; + + volo.abp.permissionManagement.permissions.deleteResource = function(resourceName, resourceKey, providerName, providerKey, ajaxParams) { + return abp.ajax($.extend(true, { + url: abp.appPath + 'api/permission-management/permissions/resource' + abp.utils.buildQueryString([{ name: 'resourceName', value: resourceName }, { name: 'resourceKey', value: resourceKey }, { name: 'providerName', value: providerName }, { name: 'providerKey', value: providerKey }]) + '', + type: 'DELETE', + dataType: null + }, ajaxParams)); + }; + })(); })(); diff --git a/modules/permission-management/test/Volo.Abp.PermissionManagement.Application.Tests/Volo/Abp/PermissionManagement/AbpPermissionManagementApplicationTestModule.cs b/modules/permission-management/test/Volo.Abp.PermissionManagement.Application.Tests/Volo/Abp/PermissionManagement/AbpPermissionManagementApplicationTestModule.cs index a5e8461a28..e43cc0fa71 100644 --- a/modules/permission-management/test/Volo.Abp.PermissionManagement.Application.Tests/Volo/Abp/PermissionManagement/AbpPermissionManagementApplicationTestModule.cs +++ b/modules/permission-management/test/Volo.Abp.PermissionManagement.Application.Tests/Volo/Abp/PermissionManagement/AbpPermissionManagementApplicationTestModule.cs @@ -22,6 +22,7 @@ public class AbpPermissionManagementApplicationTestModule : AbpModule options.ProviderPolicies[UserPermissionValueProvider.ProviderName] = UserPermissionValueProvider.ProviderName; options.ProviderPolicies["Test"] = "Test"; options.ManagementProviders.Add(); + options.ResourceManagementProviders.Add(); }); } } diff --git a/modules/permission-management/test/Volo.Abp.PermissionManagement.Application.Tests/Volo/Abp/PermissionManagement/PermissionAppService_Tests.cs b/modules/permission-management/test/Volo.Abp.PermissionManagement.Application.Tests/Volo/Abp/PermissionManagement/PermissionAppService_Tests.cs index 11e9e8ca04..61f984a27b 100644 --- a/modules/permission-management/test/Volo.Abp.PermissionManagement.Application.Tests/Volo/Abp/PermissionManagement/PermissionAppService_Tests.cs +++ b/modules/permission-management/test/Volo.Abp.PermissionManagement.Application.Tests/Volo/Abp/PermissionManagement/PermissionAppService_Tests.cs @@ -32,34 +32,39 @@ public class PermissionAppService_Tests : AbpPermissionManagementApplicationTest permissionListResultDto.ShouldNotBeNull(); permissionListResultDto.EntityDisplayName.ShouldBe(PermissionTestDataBuilder.User1Id.ToString()); - permissionListResultDto.Groups.Count.ShouldBe(2); + + permissionListResultDto.Groups.Count.ShouldBe(3); permissionListResultDto.Groups.ShouldContain(x => x.Name == "TestGroup"); - permissionListResultDto.Groups.First().Permissions.ShouldContain(x => x.Name == "MyPermission1"); - permissionListResultDto.Groups.First().Permissions.ShouldContain(x => x.Name == "MyPermission2"); - permissionListResultDto.Groups.First().Permissions.ShouldContain(x => x.Name == "MyPermission2.ChildPermission1"); - permissionListResultDto.Groups.First().Permissions.ShouldContain(x => x.Name == "MyPermission3"); - permissionListResultDto.Groups.First().Permissions.ShouldContain(x => x.Name == "MyPermission4"); + var testGroup = permissionListResultDto.Groups.FirstOrDefault(g => g.Name == "TestGroup"); + testGroup.ShouldNotBeNull(); + testGroup.Permissions.ShouldContain(x => x.Name == "MyPermission1"); + testGroup.Permissions.ShouldContain(x => x.Name == "MyPermission2"); + testGroup.Permissions.ShouldContain(x => x.Name == "MyPermission2.ChildPermission1"); + testGroup.Permissions.ShouldContain(x => x.Name == "MyPermission3"); + testGroup.Permissions.ShouldContain(x => x.Name == "MyPermission4"); - permissionListResultDto.Groups.First().Permissions.ShouldNotContain(x => x.Name == "MyPermission5"); - permissionListResultDto.Groups.First().Permissions.ShouldNotContain(x => x.Name == "MyPermission5.ChildPermission1"); + testGroup.Permissions.ShouldNotContain(x => x.Name == "MyPermission5"); + testGroup.Permissions.ShouldNotContain(x => x.Name == "MyPermission5.ChildPermission1"); using (_currentPrincipalAccessor.Change(new Claim(AbpClaimTypes.Role, "super-admin"))) { var result = await _permissionAppService.GetAsync(UserPermissionValueProvider.ProviderName, PermissionTestDataBuilder.User1Id.ToString()); - result.Groups.First().Permissions.ShouldContain(x => x.Name == "MyPermission5"); - result.Groups.First().Permissions.ShouldContain(x => x.Name == "MyPermission5.ChildPermission1"); + var testGroupWithRole = result.Groups.FirstOrDefault(g => g.Name == "TestGroup"); + testGroupWithRole.ShouldNotBeNull(); + testGroupWithRole.Permissions.ShouldContain(x => x.Name == "MyPermission5"); + testGroupWithRole.Permissions.ShouldContain(x => x.Name == "MyPermission5.ChildPermission1"); } - permissionListResultDto.Groups.First().Permissions.ShouldContain(x => x.Name == "MyPermission6"); - permissionListResultDto.Groups.First().Permissions.ShouldNotContain(x => x.Name == "MyPermission6.ChildDisabledPermission1"); - permissionListResultDto.Groups.First().Permissions.ShouldContain(x => x.Name == "MyPermission6.ChildPermission2"); + testGroup.Permissions.ShouldContain(x => x.Name == "MyPermission6"); + testGroup.Permissions.ShouldNotContain(x => x.Name == "MyPermission6.ChildDisabledPermission1"); + testGroup.Permissions.ShouldContain(x => x.Name == "MyPermission6.ChildPermission2"); - permissionListResultDto.Groups.First().Permissions.ShouldNotContain(x => x.Name == "MyDisabledPermission1"); - permissionListResultDto.Groups.First().Permissions.ShouldNotContain(x => x.Name == "MyDisabledPermission2"); - permissionListResultDto.Groups.First().Permissions.ShouldNotContain(x => x.Name == "MyDisabledPermission2.ChildPermission1"); - permissionListResultDto.Groups.First().Permissions.ShouldNotContain(x => x.Name == "MyDisabledPermission2.ChildPermission2"); - permissionListResultDto.Groups.First().Permissions.ShouldNotContain(x => x.Name == "MyDisabledPermission2.ChildPermission2.ChildPermission1"); + testGroup.Permissions.ShouldNotContain(x => x.Name == "MyDisabledPermission1"); + testGroup.Permissions.ShouldNotContain(x => x.Name == "MyDisabledPermission2"); + testGroup.Permissions.ShouldNotContain(x => x.Name == "MyDisabledPermission2.ChildPermission1"); + testGroup.Permissions.ShouldNotContain(x => x.Name == "MyDisabledPermission2.ChildPermission2"); + testGroup.Permissions.ShouldNotContain(x => x.Name == "MyDisabledPermission2.ChildPermission2.ChildPermission1"); } [Fact] diff --git a/modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo/Abp/PermissionManagement/CalculateHash_Tests.cs b/modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo/Abp/PermissionManagement/CalculateHash_Tests.cs index 2e862c8f85..cfec0d27a2 100644 --- a/modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo/Abp/PermissionManagement/CalculateHash_Tests.cs +++ b/modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo/Abp/PermissionManagement/CalculateHash_Tests.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Text.Json; using System.Text.Json.Serialization.Metadata; using Shouldly; +using Volo.Abp.Authorization.Permissions; using Volo.Abp.Json.SystemTextJson.Modifiers; using Xunit; @@ -34,7 +35,7 @@ public class CalculateHash_Tests: PermissionTestBase json.ShouldNotContain(id.ToString("D")); json = JsonSerializer.Serialize(new List() { - new PermissionDefinitionRecord(id, "Test", "Test", "Test", "Test") + new PermissionDefinitionRecord(id, "Test", "Test", "Test", "Test", "Test", "Test") }, jsonSerializerOptions); json.ShouldNotContain("\"Id\""); diff --git a/modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo/Abp/PermissionManagement/PermissionDefinitionRecordRepository_Tests.cs b/modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo/Abp/PermissionManagement/PermissionDefinitionRecordRepository_Tests.cs index d7ca406624..62369869c4 100644 --- a/modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo/Abp/PermissionManagement/PermissionDefinitionRecordRepository_Tests.cs +++ b/modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo/Abp/PermissionManagement/PermissionDefinitionRecordRepository_Tests.cs @@ -26,4 +26,19 @@ public abstract class PermissionDefinitionRecordRepository_Tests permission.ShouldNotBeNull(); permission.Name.ShouldBe("MyPermission2"); } + + [Fact] + public async Task FindByResourceNameAsync() + { + var qq = await PermissionDefinitionRecordRepository.GetListAsync(); + var permission = await PermissionDefinitionRecordRepository.FindByNameAsync("MyResourcePermission1"); + permission.ShouldNotBeNull(); + permission.ResourceName.ShouldBe(TestEntityResource.ResourceName); + permission.Name.ShouldBe("MyResourcePermission1"); + + permission = await PermissionDefinitionRecordRepository.FindByNameAsync("MyResourcePermission2"); + permission.ShouldNotBeNull(); + permission.ResourceName.ShouldBe(TestEntityResource.ResourceName); + permission.Name.ShouldBe("MyResourcePermission2"); + } } diff --git a/modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo/Abp/PermissionManagement/PermissionDefinitionSerializer_Tests.cs b/modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo/Abp/PermissionManagement/PermissionDefinitionSerializer_Tests.cs index 1e7c04580c..4dca621a63 100644 --- a/modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo/Abp/PermissionManagement/PermissionDefinitionSerializer_Tests.cs +++ b/modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo/Abp/PermissionManagement/PermissionDefinitionSerializer_Tests.cs @@ -16,7 +16,7 @@ namespace Volo.Abp.PermissionManagement; public class PermissionDefinitionSerializer_Tests : PermissionTestBase { private readonly IPermissionDefinitionSerializer _serializer; - + public PermissionDefinitionSerializer_Tests() { _serializer = GetRequiredService(); @@ -26,26 +26,26 @@ public class PermissionDefinitionSerializer_Tests : PermissionTestBase public async Task Serialize_Permission_Group_Definition() { // Arrange - + var context = new PermissionDefinitionContext(null); var group1 = CreatePermissionGroup1(context); - + // Act var permissionGroupRecord = await _serializer.SerializeAsync(group1); - + //Assert permissionGroupRecord.Name.ShouldBe("Group1"); permissionGroupRecord.DisplayName.ShouldBe("F:Group one"); permissionGroupRecord.GetProperty("CustomProperty1").ShouldBe("CustomValue1"); } - + [Fact] public async Task Serialize_Complex_Permission_Definition() { // Arrange - + var context = new PermissionDefinitionContext(null); var group1 = CreatePermissionGroup1(context); var permission1 = group1.AddPermission( @@ -61,14 +61,14 @@ public class PermissionDefinitionSerializer_Tests : PermissionTestBase .RequirePermissions(requiresAll: false, batchCheck: false,"Permission2", "Permission3"); // Act - + var permissionRecord = await _serializer.SerializeAsync( permission1, group1 ); - + //Assert - + permissionRecord.Name.ShouldBe("Permission1"); permissionRecord.GroupName.ShouldBe("Group1"); permissionRecord.DisplayName.ShouldBe("L:AbpPermissionManagement,Permission1"); @@ -78,6 +78,48 @@ public class PermissionDefinitionSerializer_Tests : PermissionTestBase permissionRecord.StateCheckers.ShouldBe("[{\"T\":\"A\"},{\"T\":\"G\",\"A\":true,\"N\":[\"GlobalFeature1\",\"GlobalFeature2\"]},{\"T\":\"F\",\"A\":true,\"N\":[\"Feature1\",\"Feature2\"]},{\"T\":\"P\",\"A\":false,\"N\":[\"Permission2\",\"Permission3\"]}]"); } + + [Fact] + public async Task Serialize_Complex_Resource_Permission_Definition() + { + // Arrange + + var context = new PermissionDefinitionContext(null); + var resourcePermission1 = context.AddResourcePermission( + "ResourcePermission1", + TestEntityResource.ResourceName, + "Permission1", + new LocalizableString(typeof(AbpPermissionManagementResource), "ResourcePermission1"), + MultiTenancySides.Tenant + ) + .WithProviders("ProviderA", "ProviderB") + .WithProperty("CustomProperty2", "CustomValue2") + .RequireAuthenticated() //For for testing, not so meaningful + .RequireGlobalFeatures("GlobalFeature1", "GlobalFeature2") + .RequireFeatures("Feature1", "Feature2") + .RequirePermissions(requiresAll: false, batchCheck: false,"Permission2", "Permission3"); + + // Act + + var permissionRecord = await _serializer.SerializeAsync( + resourcePermission1, + null + ); + + //Assert + + permissionRecord.Name.ShouldBe("ResourcePermission1"); + permissionRecord.GroupName.ShouldBe(null); + permissionRecord.ResourceName.ShouldBe(TestEntityResource.ResourceName); + permissionRecord.ManagementPermissionName.ShouldBe("Permission1"); + permissionRecord.DisplayName.ShouldBe("L:AbpPermissionManagement,ResourcePermission1"); + permissionRecord.GetProperty("CustomProperty2").ShouldBe("CustomValue2"); + permissionRecord.Providers.ShouldBe("ProviderA,ProviderB"); + permissionRecord.MultiTenancySide.ShouldBe(MultiTenancySides.Tenant); + permissionRecord.StateCheckers.ShouldBe("[{\"T\":\"A\"},{\"T\":\"G\",\"A\":true,\"N\":[\"GlobalFeature1\",\"GlobalFeature2\"]},{\"T\":\"F\",\"A\":true,\"N\":[\"Feature1\",\"Feature2\"]},{\"T\":\"P\",\"A\":false,\"N\":[\"Permission2\",\"Permission3\"]}]"); + } + + private static PermissionGroupDefinition CreatePermissionGroup1( IPermissionDefinitionContext context) { @@ -85,9 +127,9 @@ public class PermissionDefinitionSerializer_Tests : PermissionTestBase "Group1", displayName: new FixedLocalizableString("Group one") ); - + group["CustomProperty1"] = "CustomValue1"; - + return group; } -} \ No newline at end of file +} diff --git a/modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo/Abp/PermissionManagement/ResourcePermissionChecker_Basic_Tests.cs b/modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo/Abp/PermissionManagement/ResourcePermissionChecker_Basic_Tests.cs new file mode 100644 index 0000000000..81ba38f7f9 --- /dev/null +++ b/modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo/Abp/PermissionManagement/ResourcePermissionChecker_Basic_Tests.cs @@ -0,0 +1,28 @@ +using System.Threading.Tasks; +using Shouldly; +using Volo.Abp.Authorization.Permissions.Resources; +using Xunit; + +namespace Volo.Abp.PermissionManagement; + +public class ResourcePermissionChecker_Basic_Tests : PermissionTestBase +{ + private readonly IResourcePermissionChecker _resourcePermissionChecker; + + public ResourcePermissionChecker_Basic_Tests() + { + _resourcePermissionChecker = GetRequiredService(); + } + + [Fact] + public async Task Should_Return_Prohibited_If_Permission_Is_Not_Defined() + { + (await _resourcePermissionChecker.IsGrantedAsync(TestEntityResource.ResourceName, TestEntityResource.ResourceKey1,"UndefinedResourcePermissionName")).ShouldBeFalse(); + } + + [Fact] + public async Task Should_Return_False_As_Default_For_Any_Permission() + { + (await _resourcePermissionChecker.IsGrantedAsync(TestEntityResource.ResourceName, TestEntityResource.ResourceKey1,"MyPermission1")).ShouldBeFalse(); + } +} diff --git a/modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo/Abp/PermissionManagement/ResourcePermissionChecker_User_Tests.cs b/modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo/Abp/PermissionManagement/ResourcePermissionChecker_User_Tests.cs new file mode 100644 index 0000000000..261b5136d2 --- /dev/null +++ b/modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo/Abp/PermissionManagement/ResourcePermissionChecker_User_Tests.cs @@ -0,0 +1,121 @@ +using System; +using System.Security.Claims; +using System.Threading.Tasks; +using Shouldly; +using Volo.Abp.Authorization.Permissions; +using Volo.Abp.Authorization.Permissions.Resources; +using Volo.Abp.Security.Claims; +using Xunit; + +namespace Volo.Abp.PermissionManagement; + +public class ResourcePermissionChecker_User_Tests : PermissionTestBase +{ + private readonly IResourcePermissionChecker _resourcePermissionChecker; + private readonly ICurrentPrincipalAccessor _currentPrincipalAccessor; + + public ResourcePermissionChecker_User_Tests() + { + _resourcePermissionChecker = GetRequiredService(); + _currentPrincipalAccessor = GetRequiredService(); + } + + [Fact] + public async Task Should_Return_True_For_Granted_Current_User() + { + (await _resourcePermissionChecker.IsGrantedAsync( + CreatePrincipal(PermissionTestDataBuilder.User1Id), + "MyResourcePermission1", + TestEntityResource.ResourceName, + TestEntityResource.ResourceKey1 + )).ShouldBeTrue(); + } + + [Fact] + public async Task Should_Return_False_For_Non_Granted_Current_User() + { + (await _resourcePermissionChecker.IsGrantedAsync( + CreatePrincipal(PermissionTestDataBuilder.User2Id), + "MyResourcePermission1", + TestEntityResource.ResourceName, + TestEntityResource.ResourceKey1 + )).ShouldBeFalse(); + } + + + [Fact] + public async Task Should_Return_False_For_Granted_Current_User_If_The_Permission_Is_Disabled() + { + //Disabled permissions always returns false! + (await _resourcePermissionChecker.IsGrantedAsync( + CreatePrincipal(PermissionTestDataBuilder.User1Id), + "MyDisabledPermission1", + TestEntityResource.ResourceName, + TestEntityResource.ResourceKey1 + )).ShouldBeFalse(); + } + + [Fact] + public async Task Should_Return_False_For_Current_User_If_Anonymous() + { + (await _resourcePermissionChecker.IsGrantedAsync( + CreatePrincipal(null), + "MyResourcePermission1", + TestEntityResource.ResourceName, + TestEntityResource.ResourceKey1 + )).ShouldBeFalse(); + } + + [Fact] + public async Task Should_Not_Allow_Host_Permission_To_Tenant_User_Even_Granted_Before() + { + (await _resourcePermissionChecker.IsGrantedAsync( + CreatePrincipal(PermissionTestDataBuilder.User1Id, Guid.NewGuid()), + "MyResourcePermission3", + TestEntityResource.ResourceName, + TestEntityResource.ResourceKey3 + )).ShouldBeFalse(); + } + + [Fact] + public async Task Should_Return_False_For_Granted_Current_User_If_The_Permission_State_Is_Disabled() + { + (await _resourcePermissionChecker.IsGrantedAsync( + CreatePrincipal(PermissionTestDataBuilder.User1Id, Guid.NewGuid()), + "MyResourcePermission5", + TestEntityResource.ResourceName, + TestEntityResource.ResourceKey5 + )).ShouldBeFalse(); + } + + [Fact] + public async Task Should_Return_True_For_Granted_Current_User_If_The_Permission_State_Is_Enabled() + { + using (_currentPrincipalAccessor.Change(new Claim(AbpClaimTypes.Role, "super-admin"))) + { + (await _resourcePermissionChecker.IsGrantedAsync( + CreatePrincipal(PermissionTestDataBuilder.User1Id, Guid.NewGuid()), + "MyResourcePermission5", + TestEntityResource.ResourceName, + TestEntityResource.ResourceKey5 + )).ShouldBeTrue(); + } + } + + private static ClaimsPrincipal CreatePrincipal(Guid? userId, Guid? tenantId = null) + { + var claimsIdentity = new ClaimsIdentity(); + + if (userId != null) + { + claimsIdentity.AddClaim(new Claim(AbpClaimTypes.UserId, userId.ToString())); + } + + if (tenantId != null) + { + claimsIdentity.AddClaim(new Claim(AbpClaimTypes.TenantId, tenantId.ToString())); + } + + return new ClaimsPrincipal(claimsIdentity); + } +} diff --git a/modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo/Abp/PermissionManagement/ResourcePermissionGrantCacheItemInvalidator_Tests.cs b/modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo/Abp/PermissionManagement/ResourcePermissionGrantCacheItemInvalidator_Tests.cs new file mode 100644 index 0000000000..3bdc4ee69e --- /dev/null +++ b/modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo/Abp/PermissionManagement/ResourcePermissionGrantCacheItemInvalidator_Tests.cs @@ -0,0 +1,69 @@ +using System.Threading.Tasks; +using Shouldly; +using Volo.Abp.Authorization.Permissions; +using Volo.Abp.Authorization.Permissions.Resources; +using Volo.Abp.Caching; +using Xunit; + +namespace Volo.Abp.PermissionManagement; + +public class ResourcePermissionGrantCacheItemInvalidator_Tests : PermissionTestBase +{ + private readonly IDistributedCache _cache; + private readonly IResourcePermissionStore _resourcePermissionStore; + private readonly IResourcePermissionGrantRepository _resourcePermissionGrantRepository; + + public ResourcePermissionGrantCacheItemInvalidator_Tests() + { + _cache = GetRequiredService>(); + _resourcePermissionStore = GetRequiredService(); + _resourcePermissionGrantRepository = GetRequiredService(); + } + + [Fact] + public async Task PermissionStore_IsGrantedAsync_Should_Cache_PermissionGrant() + { + (await _cache.GetAsync(ResourcePermissionGrantCacheItem.CalculateCacheKey("MyResourcePermission1", + TestEntityResource.ResourceName, + TestEntityResource.ResourceKey1, + UserPermissionValueProvider.ProviderName, + PermissionTestDataBuilder.User1Id.ToString()))).ShouldBeNull(); + + await _resourcePermissionStore.IsGrantedAsync("MyResourcePermission1", + TestEntityResource.ResourceName, + TestEntityResource.ResourceKey1, + UserPermissionValueProvider.ProviderName, + PermissionTestDataBuilder.User1Id.ToString()); + + (await _cache.GetAsync(ResourcePermissionGrantCacheItem.CalculateCacheKey("MyResourcePermission1", + TestEntityResource.ResourceName, + TestEntityResource.ResourceKey1, + UserPermissionValueProvider.ProviderName, + PermissionTestDataBuilder.User1Id.ToString()))).ShouldNotBeNull(); + } + + [Fact] + public async Task Cache_Should_Invalidator_WhenPermissionGrantChanged() + { + // IsGrantedAsync will cache ResourcePermissionGrant + await _resourcePermissionStore.IsGrantedAsync("MyResourcePermission1", + TestEntityResource.ResourceName, + TestEntityResource.ResourceKey1, + UserPermissionValueProvider.ProviderName, + PermissionTestDataBuilder.User1Id.ToString()); + + var resourcePermissionGrant = await _resourcePermissionGrantRepository.FindAsync("MyResourcePermission1", + TestEntityResource.ResourceName, + TestEntityResource.ResourceKey1, + UserPermissionValueProvider.ProviderName, + PermissionTestDataBuilder.User1Id.ToString()); + resourcePermissionGrant.ShouldNotBeNull(); + await _resourcePermissionGrantRepository.DeleteAsync(resourcePermissionGrant); + + (await _cache.GetAsync(ResourcePermissionGrantCacheItem.CalculateCacheKey("MyResourcePermission1", + TestEntityResource.ResourceName, + TestEntityResource.ResourceKey1, + UserPermissionValueProvider.ProviderName, + PermissionTestDataBuilder.User1Id.ToString()))).ShouldBeNull(); + } +} diff --git a/modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo/Abp/PermissionManagement/ResourcePermissionGrantCacheItem_Tests.cs b/modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo/Abp/PermissionManagement/ResourcePermissionGrantCacheItem_Tests.cs new file mode 100644 index 0000000000..1d7b4ea12d --- /dev/null +++ b/modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo/Abp/PermissionManagement/ResourcePermissionGrantCacheItem_Tests.cs @@ -0,0 +1,15 @@ +using Shouldly; +using Xunit; + +namespace Volo.Abp.PermissionManagement; + +public class ResourcePermissionGrantCacheItem_Tests +{ + [Fact] + public void GetPermissionNameFormCacheKeyOrNull() + { + var key = ResourcePermissionGrantCacheItem.CalculateCacheKey("aaa", TestEntityResource.ResourceName, TestEntityResource.ResourceKey1,"bbb", "ccc"); + ResourcePermissionGrantCacheItem.GetPermissionNameFormCacheKeyOrNull(key).ShouldBe("aaa"); + ResourcePermissionGrantCacheItem.GetPermissionNameFormCacheKeyOrNull("aaabbbccc").ShouldBeNull(); + } +} diff --git a/modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo/Abp/PermissionManagement/ResourcePermissionManagementProvider_Tests.cs b/modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo/Abp/PermissionManagement/ResourcePermissionManagementProvider_Tests.cs new file mode 100644 index 0000000000..9651c3f313 --- /dev/null +++ b/modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo/Abp/PermissionManagement/ResourcePermissionManagementProvider_Tests.cs @@ -0,0 +1,90 @@ +using System; +using System.Threading.Tasks; +using Shouldly; +using Volo.Abp.Authorization.Permissions; +using Xunit; + +namespace Volo.Abp.PermissionManagement; + +public class ResourcePermissionManagementProvider_Tests : PermissionTestBase +{ + private readonly IResourcePermissionManagementProvider _resourcePermissionManagementProvider; + private readonly IResourcePermissionGrantRepository _resourcePermissionGrantRepository; + + public ResourcePermissionManagementProvider_Tests() + { + _resourcePermissionManagementProvider = GetRequiredService(); + _resourcePermissionGrantRepository = GetRequiredService(); + } + + [Fact] + public async Task CheckAsync() + { + await _resourcePermissionGrantRepository.InsertAsync( + new ResourcePermissionGrant( + Guid.NewGuid(), + "MyPermission1", + TestEntityResource.ResourceName, + TestEntityResource.ResourceKey1, + "Test", + "Test" + ) + ); + + var permissionValueProviderGrantInfo = await _resourcePermissionManagementProvider.CheckAsync( + "MyPermission1", + TestEntityResource.ResourceName, + TestEntityResource.ResourceKey1, + "Test", + "Test"); + + permissionValueProviderGrantInfo.IsGranted.ShouldBeTrue(); + permissionValueProviderGrantInfo.ProviderKey.ShouldBe("Test"); + } + + [Fact] + public async Task Check_Should_Return_NonGranted_When_ProviderName_NotEquals_Name() + { + var permissionValueProviderGrantInfo = await _resourcePermissionManagementProvider.CheckAsync( + "MyPermission1", + TestEntityResource.ResourceName, + TestEntityResource.ResourceKey1, + "TestNotExist", + "Test"); + + permissionValueProviderGrantInfo.IsGranted.ShouldBeFalse(); + permissionValueProviderGrantInfo.ProviderKey.ShouldBeNull(); + } + + [Fact] + public async Task SetAsync() + { + await _resourcePermissionGrantRepository.InsertAsync( + new ResourcePermissionGrant( + Guid.NewGuid(), + "MyPermission1", + TestEntityResource.ResourceName, + TestEntityResource.ResourceKey1, + "Test", + "Test" + ) + ); + (await _resourcePermissionGrantRepository.FindAsync("MyPermission1", + TestEntityResource.ResourceName, + TestEntityResource.ResourceKey1, + "Test", + "Test")).ShouldNotBeNull(); + + await _resourcePermissionManagementProvider.SetAsync("MyPermission1", + TestEntityResource.ResourceName, + TestEntityResource.ResourceKey1, + "Test", + false); + + (await _resourcePermissionGrantRepository.FindAsync("MyPermission1", + TestEntityResource.ResourceName, + TestEntityResource.ResourceKey1, + "Test", + "Test")).ShouldBeNull(); + } +} diff --git a/modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo/Abp/PermissionManagement/ResourcePermissionManager_Tests.cs b/modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo/Abp/PermissionManagement/ResourcePermissionManager_Tests.cs new file mode 100644 index 0000000000..73d37a5ff3 --- /dev/null +++ b/modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo/Abp/PermissionManagement/ResourcePermissionManager_Tests.cs @@ -0,0 +1,337 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Shouldly; +using Volo.Abp.Authorization.Permissions; +using Volo.Abp.Authorization.Permissions.Resources; +using Xunit; + +namespace Volo.Abp.PermissionManagement; + +public class ResourcePermissionManager_Tests : PermissionTestBase +{ + private readonly IResourcePermissionManager _resourcePermissionManager; + private readonly IResourcePermissionGrantRepository _resourcePermissionGrantRepository; + + public ResourcePermissionManager_Tests() + { + _resourcePermissionManager = GetRequiredService(); + _resourcePermissionGrantRepository = GetRequiredService(); + } + + [Fact] + public async Task GetProviderKeyLookupServicesAsync() + { + var permissionProviderKeyLookupServices = await _resourcePermissionManager.GetProviderKeyLookupServicesAsync(); + + permissionProviderKeyLookupServices.ShouldNotBeNull(); + permissionProviderKeyLookupServices.First().Name.ShouldBe("Test"); + } + + [Fact] + public async Task GetProviderKeyLookupServiceAsync() + { + var testProviderKeyLookupService = await _resourcePermissionManager.GetProviderKeyLookupServiceAsync("Test"); + testProviderKeyLookupService.ShouldNotBeNull(); + testProviderKeyLookupService.Name.ShouldBe("Test"); + + var exception = await Assert.ThrowsAsync(async () => + { + await _resourcePermissionManager.GetProviderKeyLookupServiceAsync("UndefinedProvider"); + }); + exception.Message.ShouldBe("Unknown resource permission provider key lookup service: UndefinedProvider"); + } + + [Fact] + public async Task GetAvailablePermissionsAsync() + { + var availablePermissions = await _resourcePermissionManager.GetAvailablePermissionsAsync(TestEntityResource.ResourceName); + + availablePermissions.ShouldNotBeNull(); + availablePermissions.ShouldContain(p => p.Name == "MyResourcePermission1"); + availablePermissions.ShouldContain(p => p.Name == "MyResourcePermission2"); + availablePermissions.ShouldContain(p => p.Name == "MyResourcePermission3"); + availablePermissions.ShouldContain(p => p.Name == "MyResourcePermission4"); + availablePermissions.ShouldContain(p => p.Name == "MyResourcePermission6"); + availablePermissions.ShouldContain(p => p.Name == "MyResourcePermission7"); + + availablePermissions.ShouldNotContain(p => p.Name == "MyResourcePermission5"); + availablePermissions.ShouldNotContain(p => p.Name == "MyResourceDisabledPermission1"); + availablePermissions.ShouldNotContain(p => p.Name == "MyResourceDisabledPermission2"); + } + + [Fact] + public async Task GetAsync() + { + await _resourcePermissionGrantRepository.InsertAsync(new ResourcePermissionGrant( + Guid.NewGuid(), + "MyResourcePermission1", + TestEntityResource.ResourceName, + TestEntityResource.ResourceKey1, + "Test", + "Test") + ); + + var grantedProviders = await _resourcePermissionManager.GetAsync( + "MyResourcePermission1", + TestEntityResource.ResourceName, + TestEntityResource.ResourceKey1, + "Test", + "Test"); + + grantedProviders.ShouldNotBeNull(); + grantedProviders.IsGranted.ShouldBeTrue(); + grantedProviders.Name.ShouldBe("MyResourcePermission1"); + grantedProviders.Providers.ShouldContain(x => x.Key == "Test"); + } + + [Fact] + public async Task Multiple_GetAsync() + { + await _resourcePermissionGrantRepository.InsertAsync(new ResourcePermissionGrant( + Guid.NewGuid(), + "MyResourcePermission1", + TestEntityResource.ResourceName, + TestEntityResource.ResourceKey1, + "Test", + "Test") + ); + await _resourcePermissionGrantRepository.InsertAsync(new ResourcePermissionGrant( + Guid.NewGuid(), + "MyResourcePermission2", + TestEntityResource.ResourceName, + TestEntityResource.ResourceKey1, + "Test", + "Test") + ); + + var grantedProviders = await _resourcePermissionManager.GetAsync( + new[] { "MyResourcePermission1", "MyResourcePermission2" }, + TestEntityResource.ResourceName, + TestEntityResource.ResourceKey1, + "Test", + "Test"); + + grantedProviders.Result.Count.ShouldBe(2); + grantedProviders.Result.First().IsGranted.ShouldBeTrue(); + grantedProviders.Result.First().Name.ShouldBe("MyResourcePermission1"); + grantedProviders.Result.First().Providers.ShouldContain(x => x.Key == "Test"); + + grantedProviders.Result.Last().IsGranted.ShouldBeTrue(); + grantedProviders.Result.Last().Name.ShouldBe("MyResourcePermission2"); + grantedProviders.Result.Last().Providers.ShouldContain(x => x.Key == "Test"); + } + + [Fact] + public async Task Get_Should_Return_Not_Granted_When_Permission_Undefined() + { + var result = await _resourcePermissionManager.GetAsync( + "MyResourcePermission1NotExist", + TestEntityResource.ResourceName, + TestEntityResource.ResourceKey1,"Test", "Test"); + result.Name.ShouldBe("MyResourcePermission1NotExist"); + result.Providers.ShouldBeEmpty(); + result.IsGranted.ShouldBeFalse(); + } + + [Fact] + public async Task GetAllAsync() + { + await _resourcePermissionGrantRepository.InsertAsync(new ResourcePermissionGrant( + Guid.NewGuid(), + "MyResourcePermission1", + TestEntityResource.ResourceName, + TestEntityResource.ResourceKey1, + "Test", + "Test") + ); + + await _resourcePermissionGrantRepository.InsertAsync(new ResourcePermissionGrant( + Guid.NewGuid(), + "MyResourcePermission2", + TestEntityResource.ResourceName, + TestEntityResource.ResourceKey1, + "Test", + "Test") + ); + + var permissionWithGrantedProviders = await _resourcePermissionManager.GetAllAsync( + TestEntityResource.ResourceName, + TestEntityResource.ResourceKey1, + "Test", + "Test"); + + permissionWithGrantedProviders.ShouldNotBeNull(); + permissionWithGrantedProviders.ShouldContain(x => + x.IsGranted && x.Name == "MyResourcePermission1" && x.Providers.Any(p => p.Key == "Test")); + permissionWithGrantedProviders.ShouldContain(x => + x.IsGranted && x.Name == "MyResourcePermission2" && x.Providers.Any(p => p.Key == "Test")); + + + permissionWithGrantedProviders = await _resourcePermissionManager.GetAllAsync( + TestEntityResource.ResourceName, + TestEntityResource.ResourceKey1); + + permissionWithGrantedProviders.ShouldNotBeNull(); + permissionWithGrantedProviders.ShouldContain(x => x.IsGranted && x.Name == "MyResourcePermission1" && x.Providers.Any(p => p.Key == "Test")); + permissionWithGrantedProviders.ShouldContain(x => x.IsGranted && x.Name == "MyResourcePermission2" && x.Providers.Any(p => p.Key == "Test")); + + permissionWithGrantedProviders.ShouldNotContain(x => x.Name == "MyResourcePermission5"); // Not available permission + + permissionWithGrantedProviders.ShouldContain(x => !x.IsGranted && x.Name == "MyResourcePermission3" && x.Providers.Count == 0); + permissionWithGrantedProviders.ShouldContain(x => !x.IsGranted && x.Name == "MyResourcePermission4" && x.Providers.Count == 0); + permissionWithGrantedProviders.ShouldContain(x => !x.IsGranted && x.Name == "MyResourcePermission6" && x.Providers.Count == 0); + permissionWithGrantedProviders.ShouldContain(x => !x.IsGranted && x.Name == "MyResourcePermission7" && x.Providers.Count == 0); + permissionWithGrantedProviders.ShouldContain(x => !x.IsGranted && x.Name == "MyResourcePermission8" && x.Providers.Count == 0); + } + + [Fact] + public async Task GetAllGroupAsync() + { + var group = await _resourcePermissionManager.GetAllGroupAsync( + TestEntityResource.ResourceName, + TestEntityResource.ResourceKey1); + + group.ShouldNotBeNull(); + group.Count.ShouldBe(1); + group.First().ProviderName.ShouldBe(UserResourcePermissionValueProvider.ProviderName); + group.First().ProviderKey.ShouldBe(PermissionTestDataBuilder.User1Id.ToString()); + group.First().Permissions.Count.ShouldBe(1); + group.First().Permissions.ShouldContain(x => x == "MyResourcePermission1"); + + group = await _resourcePermissionManager.GetAllGroupAsync( + TestEntityResource.ResourceName, + TestEntityResource.ResourceKey3); + + group.ShouldNotBeNull(); + group.Count.ShouldBe(1); + group.First().ProviderName.ShouldBe(UserResourcePermissionValueProvider.ProviderName); + group.First().ProviderKey.ShouldBe(PermissionTestDataBuilder.User1Id.ToString()); + group.First().Permissions.Count.ShouldBe(2); + group.First().Permissions.ShouldContain(x => x == "MyResourcePermission3"); + group.First().Permissions.ShouldContain(x => x == "MyResourcePermission6"); + } + + [Fact] + public async Task Set_Should_Silently_Ignore_When_Permission_Undefined() + { + await _resourcePermissionManager.SetAsync( + "MyResourcePermission1NotExist", + TestEntityResource.ResourceName, + TestEntityResource.ResourceKey1, + "Test", + "Test", + true); + } + + [Fact] + public async Task Set_Should_Throw_Exception_If_Provider_Not_Found() + { + var exception = await Assert.ThrowsAsync(async () => + { + await _resourcePermissionManager.SetAsync( + "MyResourcePermission1", + TestEntityResource.ResourceName, + TestEntityResource.ResourceKey1, + "UndefinedProvider", + "Test", + true); + }); + + exception.Message.ShouldBe("Unknown resource permission management provider: UndefinedProvider"); + } + + [Fact] + public async Task UpdateProviderKey() + { + await _resourcePermissionGrantRepository.InsertAsync(new ResourcePermissionGrant( + Guid.NewGuid(), + "MyResourcePermission1", + TestEntityResource.ResourceName, + TestEntityResource.ResourceKey1, + "Test", + "Test") + ); + var permissionGrant = await _resourcePermissionGrantRepository.FindAsync( + "MyResourcePermission1", + TestEntityResource.ResourceName, + TestEntityResource.ResourceKey1, + "Test", + "Test"); + permissionGrant.ProviderKey.ShouldBe("Test"); + + await _resourcePermissionManager.UpdateProviderKeyAsync(permissionGrant, "NewProviderKey"); + (await _resourcePermissionGrantRepository.FindAsync( + "MyResourcePermission1", + TestEntityResource.ResourceName, + TestEntityResource.ResourceKey1, + "Test", + "NewProviderKey")).ShouldNotBeNull(); + } + + [Fact] + public async Task DeleteAsync() + { + await _resourcePermissionGrantRepository.InsertAsync(new ResourcePermissionGrant( + Guid.NewGuid(), + "MyResourcePermission1", + TestEntityResource.ResourceName, + TestEntityResource.ResourceKey1, + "Test", + "Test") + ); + var permissionGrant = await _resourcePermissionGrantRepository.FindAsync("MyResourcePermission1", + TestEntityResource.ResourceName, + TestEntityResource.ResourceKey1, + "Test", + "Test"); + permissionGrant.ProviderKey.ShouldBe("Test"); + + await _resourcePermissionManager.DeleteAsync( + TestEntityResource.ResourceName, + TestEntityResource.ResourceKey1, + "Test", + "Test"); + + (await _resourcePermissionGrantRepository.FindAsync( + "MyResourcePermission1", + TestEntityResource.ResourceName, + TestEntityResource.ResourceKey1, + "Test", + "Test")).ShouldBeNull(); + } + + [Fact] + public async Task DeleteByProviderAsync() + { + await _resourcePermissionGrantRepository.InsertAsync(new ResourcePermissionGrant( + Guid.NewGuid(), + "MyResourcePermission1", + TestEntityResource.ResourceName, + TestEntityResource.ResourceKey1, + "Test", + "Test") + ); + + var permissionGrant = await _resourcePermissionGrantRepository.FindAsync("MyResourcePermission1", + TestEntityResource.ResourceName, + TestEntityResource.ResourceKey1, + "Test", + "Test"); + + permissionGrant.ProviderKey.ShouldBe("Test"); + + await _resourcePermissionManager.DeleteAsync( + "Test", + "Test"); + + (await _resourcePermissionGrantRepository.FindAsync( + "MyResourcePermission1", + TestEntityResource.ResourceName, + TestEntityResource.ResourceKey1, + "Test", + "Test")).ShouldBeNull(); + } +} diff --git a/modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo/Abp/PermissionManagement/ResourcePermissionStore_Tests.cs b/modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo/Abp/PermissionManagement/ResourcePermissionStore_Tests.cs new file mode 100644 index 0000000000..34e6f89c0c --- /dev/null +++ b/modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo/Abp/PermissionManagement/ResourcePermissionStore_Tests.cs @@ -0,0 +1,119 @@ +using System.Linq; +using System.Threading.Tasks; +using Shouldly; +using Volo.Abp.Authorization.Permissions; +using Volo.Abp.Authorization.Permissions.Resources; +using Xunit; + +namespace Volo.Abp.PermissionManagement; + +public class ResourcePermissionStore_Tests : PermissionTestBase +{ + private readonly IResourcePermissionStore _resourcePermissionStore; + + public ResourcePermissionStore_Tests() + { + _resourcePermissionStore = GetRequiredService(); + } + + [Fact] + public async Task IsGrantedAsync() + { + (await _resourcePermissionStore.IsGrantedAsync( + "MyResourcePermission1", + TestEntityResource.ResourceName, + TestEntityResource.ResourceKey1, + UserPermissionValueProvider.ProviderName, + PermissionTestDataBuilder.User1Id.ToString())).ShouldBeTrue(); + + (await _resourcePermissionStore.IsGrantedAsync( + "MyPermission1NotExist", + TestEntityResource.ResourceName, + TestEntityResource.ResourceKey1, + UserPermissionValueProvider.ProviderName, + PermissionTestDataBuilder.User1Id.ToString())).ShouldBeFalse(); + } + + [Fact] + public async Task IsGranted_Multiple() + { + var result = await _resourcePermissionStore.IsGrantedAsync( + new[] { "MyResourcePermission1", "MyResourcePermission1NotExist" }, + TestEntityResource.ResourceName, + TestEntityResource.ResourceKey1, + UserPermissionValueProvider.ProviderName, + PermissionTestDataBuilder.User1Id.ToString()); + + result.Result.Count.ShouldBe(2); + + result.Result.FirstOrDefault(x => x.Key == "MyResourcePermission1").Value.ShouldBe(PermissionGrantResult.Granted); + result.Result.FirstOrDefault(x => x.Key == "MyResourcePermission1NotExist").Value.ShouldBe(PermissionGrantResult.Undefined); + } + + + [Fact] + public async Task GetPermissionsAsync() + { + var permissions = await _resourcePermissionStore.GetPermissionsAsync( + TestEntityResource.ResourceName, + TestEntityResource.ResourceKey1); + + permissions.Result.Count.ShouldBe(10); + permissions.Result.ShouldContain(p => p.Key == "MyResourcePermission1" && p.Value == PermissionGrantResult.Granted); + permissions.Result.ShouldContain(p => p.Key == "MyResourceDisabledPermission1" && p.Value == PermissionGrantResult.Undefined); + permissions.Result.ShouldContain(p => p.Key == "MyResourcePermission2" && p.Value == PermissionGrantResult.Undefined); + permissions.Result.ShouldContain(p => p.Key == "MyResourcePermission3" && p.Value == PermissionGrantResult.Undefined); + permissions.Result.ShouldContain(p => p.Key == "MyResourcePermission4" && p.Value == PermissionGrantResult.Undefined); + permissions.Result.ShouldContain(p => p.Key == "MyResourcePermission5" && p.Value == PermissionGrantResult.Undefined); + permissions.Result.ShouldContain(p => p.Key == "MyResourcePermission6" && p.Value == PermissionGrantResult.Undefined); + permissions.Result.ShouldContain(p => p.Key == "MyResourceDisabledPermission2" && p.Value == PermissionGrantResult.Undefined); + permissions.Result.ShouldContain(p => p.Key == "MyResourcePermission7" && p.Value == PermissionGrantResult.Undefined); + permissions.Result.ShouldContain(p => p.Key == "MyResourcePermission8" && p.Value == PermissionGrantResult.Undefined); + + permissions = await _resourcePermissionStore.GetPermissionsAsync( + TestEntityResource.ResourceName, + TestEntityResource.ResourceKey2); + permissions.Result.ShouldAllBe(x => x.Value == PermissionGrantResult.Undefined); + } + + [Fact] + public async Task GetGrantedPermissionsAsync() + { + var grantedPermissions = await _resourcePermissionStore.GetGrantedPermissionsAsync( + TestEntityResource.ResourceName, + TestEntityResource.ResourceKey1); + + grantedPermissions.Length.ShouldBe(1); + grantedPermissions.ShouldContain("MyResourcePermission1"); + + grantedPermissions = await _resourcePermissionStore.GetGrantedPermissionsAsync( + TestEntityResource.ResourceName, + TestEntityResource.ResourceKey3); + + grantedPermissions.Length.ShouldBe(3); + grantedPermissions.ShouldContain("MyResourcePermission3"); + grantedPermissions.ShouldContain("MyResourcePermission5"); + grantedPermissions.ShouldContain("MyResourcePermission6"); + + } + + + [Fact] + public async Task GetGrantedResourceKeysAsync() + { + var grantedResourceKeys = await _resourcePermissionStore.GetGrantedResourceKeysAsync( + TestEntityResource.ResourceName, + "MyResourcePermission1"); + + grantedResourceKeys.Length.ShouldBe(1); + grantedResourceKeys.ShouldContain(TestEntityResource.ResourceKey1); + + grantedResourceKeys = await _resourcePermissionStore.GetGrantedResourceKeysAsync( + TestEntityResource.ResourceName, + "MyResourcePermission5"); + + grantedResourceKeys.Length.ShouldBe(2); + grantedResourceKeys.ShouldContain(TestEntityResource.ResourceKey3); + grantedResourceKeys.ShouldContain(TestEntityResource.ResourceKey5); + } +} diff --git a/modules/permission-management/test/Volo.Abp.PermissionManagement.EntityFrameworkCore.Tests/Volo.Abp.PermissionManagement.EntityFrameworkCore.Tests.csproj b/modules/permission-management/test/Volo.Abp.PermissionManagement.EntityFrameworkCore.Tests/Volo.Abp.PermissionManagement.EntityFrameworkCore.Tests.csproj index f99bf791dd..45c157641b 100644 --- a/modules/permission-management/test/Volo.Abp.PermissionManagement.EntityFrameworkCore.Tests/Volo.Abp.PermissionManagement.EntityFrameworkCore.Tests.csproj +++ b/modules/permission-management/test/Volo.Abp.PermissionManagement.EntityFrameworkCore.Tests/Volo.Abp.PermissionManagement.EntityFrameworkCore.Tests.csproj @@ -15,10 +15,10 @@ + - diff --git a/modules/permission-management/test/Volo.Abp.PermissionManagement.EntityFrameworkCore.Tests/Volo/Abp/PermissionManagement/EntityFrameworkCore/AbpPermissionManagementEntityFrameworkCoreTestModule.cs b/modules/permission-management/test/Volo.Abp.PermissionManagement.EntityFrameworkCore.Tests/Volo/Abp/PermissionManagement/EntityFrameworkCore/AbpPermissionManagementEntityFrameworkCoreTestModule.cs index a73e5afb86..7db8f8fe7e 100644 --- a/modules/permission-management/test/Volo.Abp.PermissionManagement.EntityFrameworkCore.Tests/Volo/Abp/PermissionManagement/EntityFrameworkCore/AbpPermissionManagementEntityFrameworkCoreTestModule.cs +++ b/modules/permission-management/test/Volo.Abp.PermissionManagement.EntityFrameworkCore.Tests/Volo/Abp/PermissionManagement/EntityFrameworkCore/AbpPermissionManagementEntityFrameworkCoreTestModule.cs @@ -1,47 +1,66 @@ -using System; -using System.Threading.Tasks; +using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage; using Microsoft.Extensions.DependencyInjection; using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore.Sqlite; using Volo.Abp.Modularity; using Volo.Abp.Threading; using Volo.Abp.Uow; +using Microsoft.Data.Sqlite; +using Volo.Abp.DependencyInjection; namespace Volo.Abp.PermissionManagement.EntityFrameworkCore; [DependsOn( typeof(AbpPermissionManagementEntityFrameworkCoreModule), - typeof(AbpPermissionManagementTestBaseModule))] + typeof(AbpPermissionManagementTestBaseModule), + typeof(AbpEntityFrameworkCoreSqliteModule) +)] public class AbpPermissionManagementEntityFrameworkCoreTestModule : AbpModule { - public override void ConfigureServices(ServiceConfigurationContext context) + public override void PreConfigureServices(ServiceConfigurationContext context) { - context.Services.AddEntityFrameworkInMemoryDatabase(); + PreConfigure(x => x.BusyTimeout = null); + } - var databaseName = Guid.NewGuid().ToString(); + public override void ConfigureServices(ServiceConfigurationContext context) + { + var sqliteConnection = CreateDatabaseAndGetConnection(); Configure(options => { options.Configure(abpDbContextConfigurationContext => { - abpDbContextConfigurationContext.DbContextOptions.UseInMemoryDatabase(databaseName); + abpDbContextConfigurationContext.DbContextOptions.UseSqlite(sqliteConnection); }); }); + Configure(options => + { + options.TransactionBehavior = UnitOfWorkTransactionBehavior.Disabled; //EF in-memory database does not support transactions + }); + context.Services.AddAlwaysDisableUnitOfWorkTransaction(); } - public override void OnApplicationInitialization(ApplicationInitializationContext context) + private static SqliteConnection CreateDatabaseAndGetConnection() { - var task = context.ServiceProvider.GetRequiredService().GetInitializeDynamicPermissionsTask(); - if (!task.IsCompleted) - { - AsyncHelper.RunSync(() => Awaited(task)); - } + var connection = new AbpUnitTestSqliteConnection("Data Source=:memory:"); + connection.Open(); + + new PermissionManagementDbContext( + new DbContextOptionsBuilder().UseSqlite(connection).Options + ).GetService().CreateTables(); + + return connection; } - private async static Task Awaited(Task task) + public override void OnApplicationInitialization(ApplicationInitializationContext context) { - await task; + var rootServiceProvider = context.ServiceProvider.GetRequiredService(); + var initializer = rootServiceProvider.GetRequiredService(); + AsyncHelper.RunSync(() => initializer.InitializeAsync(false)); } } diff --git a/modules/permission-management/test/Volo.Abp.PermissionManagement.EntityFrameworkCore.Tests/Volo/Abp/PermissionManagement/EntityFrameworkCore/ResourcePermissionGrantRepository_Tests.cs b/modules/permission-management/test/Volo.Abp.PermissionManagement.EntityFrameworkCore.Tests/Volo/Abp/PermissionManagement/EntityFrameworkCore/ResourcePermissionGrantRepository_Tests.cs new file mode 100644 index 0000000000..89fdb9a9d4 --- /dev/null +++ b/modules/permission-management/test/Volo.Abp.PermissionManagement.EntityFrameworkCore.Tests/Volo/Abp/PermissionManagement/EntityFrameworkCore/ResourcePermissionGrantRepository_Tests.cs @@ -0,0 +1,6 @@ +namespace Volo.Abp.PermissionManagement.EntityFrameworkCore; + +public class ResourcePermissionGrantRepository_Tests : ResourcePermissionGrantRepository_Tests +{ + +} diff --git a/modules/permission-management/test/Volo.Abp.PermissionManagement.MongoDB.Tests/Volo/Abp/PermissionManagement/MongoDb/MongoDbPermissionDefinitionRecordRepository_Tests.cs b/modules/permission-management/test/Volo.Abp.PermissionManagement.MongoDB.Tests/Volo/Abp/PermissionManagement/MongoDb/MongoDbPermissionDefinitionRecordRepository_Tests.cs index d43634cdb9..30f09c5720 100644 --- a/modules/permission-management/test/Volo.Abp.PermissionManagement.MongoDB.Tests/Volo/Abp/PermissionManagement/MongoDb/MongoDbPermissionDefinitionRecordRepository_Tests.cs +++ b/modules/permission-management/test/Volo.Abp.PermissionManagement.MongoDB.Tests/Volo/Abp/PermissionManagement/MongoDb/MongoDbPermissionDefinitionRecordRepository_Tests.cs @@ -3,7 +3,7 @@ namespace Volo.Abp.PermissionManagement.MongoDB; [Collection(MongoTestCollection.Name)] -public class MongoDbPermissionDefinitionRecordRepository_Tests : PermissionGrantRepository_Tests +public class MongoDbPermissionDefinitionRecordRepository_Tests : PermissionDefinitionRecordRepository_Tests { } diff --git a/modules/permission-management/test/Volo.Abp.PermissionManagement.MongoDB.Tests/Volo/Abp/PermissionManagement/MongoDb/ResourcePermissionGrantRepository_Tests.cs b/modules/permission-management/test/Volo.Abp.PermissionManagement.MongoDB.Tests/Volo/Abp/PermissionManagement/MongoDb/ResourcePermissionGrantRepository_Tests.cs new file mode 100644 index 0000000000..32240f998c --- /dev/null +++ b/modules/permission-management/test/Volo.Abp.PermissionManagement.MongoDB.Tests/Volo/Abp/PermissionManagement/MongoDb/ResourcePermissionGrantRepository_Tests.cs @@ -0,0 +1,9 @@ +using Xunit; + +namespace Volo.Abp.PermissionManagement.MongoDB; + +[Collection(MongoTestCollection.Name)] +public class ResourcePermissionGrantRepository_Tests : ResourcePermissionGrantRepository_Tests +{ + +} diff --git a/modules/permission-management/test/Volo.Abp.PermissionManagement.TestBase/Volo/Abp/PermissionManagement/AbpPermissionManagementTestBaseModule.cs b/modules/permission-management/test/Volo.Abp.PermissionManagement.TestBase/Volo/Abp/PermissionManagement/AbpPermissionManagementTestBaseModule.cs index a6102a318e..88c6ea1d69 100644 --- a/modules/permission-management/test/Volo.Abp.PermissionManagement.TestBase/Volo/Abp/PermissionManagement/AbpPermissionManagementTestBaseModule.cs +++ b/modules/permission-management/test/Volo.Abp.PermissionManagement.TestBase/Volo/Abp/PermissionManagement/AbpPermissionManagementTestBaseModule.cs @@ -18,6 +18,8 @@ public class AbpPermissionManagementTestBaseModule : AbpModule context.Services.Configure(options => { options.ManagementProviders.Add(); + options.ResourceManagementProviders.Add(); + options.ResourcePermissionProviderKeyLookupServices.Add(); }); } diff --git a/modules/permission-management/test/Volo.Abp.PermissionManagement.TestBase/Volo/Abp/PermissionManagement/PermissionTestDataBuilder.cs b/modules/permission-management/test/Volo.Abp.PermissionManagement.TestBase/Volo/Abp/PermissionManagement/PermissionTestDataBuilder.cs index c60dff997a..d0448ea5bd 100644 --- a/modules/permission-management/test/Volo.Abp.PermissionManagement.TestBase/Volo/Abp/PermissionManagement/PermissionTestDataBuilder.cs +++ b/modules/permission-management/test/Volo.Abp.PermissionManagement.TestBase/Volo/Abp/PermissionManagement/PermissionTestDataBuilder.cs @@ -12,12 +12,17 @@ public class PermissionTestDataBuilder : ITransientDependency public static Guid User2Id { get; } = Guid.NewGuid(); private readonly IPermissionGrantRepository _permissionGrantRepository; + private readonly IResourcePermissionGrantRepository _resourcePermissionGrantRepository; private readonly IGuidGenerator _guidGenerator; - public PermissionTestDataBuilder(IGuidGenerator guidGenerator, IPermissionGrantRepository permissionGrantRepository) + public PermissionTestDataBuilder( + IGuidGenerator guidGenerator, + IPermissionGrantRepository permissionGrantRepository, + IResourcePermissionGrantRepository resourcePermissionGrantRepository) { _guidGenerator = guidGenerator; _permissionGrantRepository = permissionGrantRepository; + _resourcePermissionGrantRepository = resourcePermissionGrantRepository; } public async Task BuildAsync() @@ -31,6 +36,15 @@ public class PermissionTestDataBuilder : ITransientDependency ) ); + await _permissionGrantRepository.InsertAsync( + new PermissionGrant( + _guidGenerator.Create(), + "TestEntityManagementPermission", + UserPermissionValueProvider.ProviderName, + User1Id.ToString() + ) + ); + await _permissionGrantRepository.InsertAsync( new PermissionGrant( _guidGenerator.Create(), @@ -57,5 +71,71 @@ public class PermissionTestDataBuilder : ITransientDependency User1Id.ToString() ) ); + + await _resourcePermissionGrantRepository.InsertAsync( + new ResourcePermissionGrant( + _guidGenerator.Create(), + "MyResourcePermission1", + TestEntityResource.ResourceName, + TestEntityResource.ResourceKey1, + UserPermissionValueProvider.ProviderName, + User1Id.ToString() + ) + ); + + await _resourcePermissionGrantRepository.InsertAsync( + new ResourcePermissionGrant( + _guidGenerator.Create(), + "MyDisabledResourcePermission1", + TestEntityResource.ResourceName, + TestEntityResource.ResourceKey1, + UserPermissionValueProvider.ProviderName, + User1Id.ToString() + ) + ); + + await _resourcePermissionGrantRepository.InsertAsync( + new ResourcePermissionGrant( + _guidGenerator.Create(), + "MyResourcePermission3", + TestEntityResource.ResourceName, + TestEntityResource.ResourceKey3, + UserPermissionValueProvider.ProviderName, + User1Id.ToString() + ) + ); + + await _resourcePermissionGrantRepository.InsertAsync( + new ResourcePermissionGrant( + _guidGenerator.Create(), + "MyResourcePermission5", + TestEntityResource.ResourceName, + TestEntityResource.ResourceKey3, + UserPermissionValueProvider.ProviderName, + User1Id.ToString() + ) + ); + + await _resourcePermissionGrantRepository.InsertAsync( + new ResourcePermissionGrant( + _guidGenerator.Create(), + "MyResourcePermission6", + TestEntityResource.ResourceName, + TestEntityResource.ResourceKey3, + UserPermissionValueProvider.ProviderName, + User1Id.ToString() + ) + ); + + await _resourcePermissionGrantRepository.InsertAsync( + new ResourcePermissionGrant( + _guidGenerator.Create(), + "MyResourcePermission5", + TestEntityResource.ResourceName, + TestEntityResource.ResourceKey5, + UserPermissionValueProvider.ProviderName, + User1Id.ToString() + ) + ); } } diff --git a/modules/permission-management/test/Volo.Abp.PermissionManagement.TestBase/Volo/Abp/PermissionManagement/ResourcePermissionGrantRepository_Tests.cs b/modules/permission-management/test/Volo.Abp.PermissionManagement.TestBase/Volo/Abp/PermissionManagement/ResourcePermissionGrantRepository_Tests.cs new file mode 100644 index 0000000000..90652683b1 --- /dev/null +++ b/modules/permission-management/test/Volo.Abp.PermissionManagement.TestBase/Volo/Abp/PermissionManagement/ResourcePermissionGrantRepository_Tests.cs @@ -0,0 +1,121 @@ +using System.Threading.Tasks; +using Shouldly; +using Volo.Abp.Authorization.Permissions; +using Volo.Abp.Modularity; +using Xunit; + +namespace Volo.Abp.PermissionManagement; + +public abstract class ResourcePermissionGrantRepository_Tests : PermissionManagementTestBase + where TStartupModule : IAbpModule +{ + protected IResourcePermissionGrantRepository ResourcePermissionGrantRepository { get; } + + protected ResourcePermissionGrantRepository_Tests() + { + ResourcePermissionGrantRepository = GetRequiredService(); + } + + [Fact] + public async Task FindAsync() + { + (await ResourcePermissionGrantRepository.FindAsync( + "MyResourcePermission1", + TestEntityResource.ResourceName, + TestEntityResource.ResourceKey1, + UserPermissionValueProvider.ProviderName, + PermissionTestDataBuilder.User1Id.ToString())).ShouldNotBeNull(); + + (await ResourcePermissionGrantRepository.FindAsync( + "Undefined-Permission", + TestEntityResource.ResourceName, + TestEntityResource.ResourceKey1, + UserPermissionValueProvider.ProviderName, + PermissionTestDataBuilder.User1Id.ToString())).ShouldBeNull(); + + (await ResourcePermissionGrantRepository.FindAsync( + "MyResourcePermission1", + TestEntityResource.ResourceName, + TestEntityResource.ResourceKey1, + "Undefined-Provider", + "Unknown-Id")).ShouldBeNull(); + } + + [Fact] + public async Task GetList4Async() + { + var permissionGrants = + await ResourcePermissionGrantRepository.GetListAsync( + TestEntityResource.ResourceName, + TestEntityResource.ResourceKey3, + UserPermissionValueProvider.ProviderName, + PermissionTestDataBuilder.User1Id.ToString()); + + permissionGrants.ShouldContain(p => p.Name == "MyResourcePermission3"); + permissionGrants.ShouldContain(p => p.Name == "MyResourcePermission5"); + } + + [Fact] + public async Task GetList5Async() + { + var permissionGrants = + await ResourcePermissionGrantRepository.GetListAsync( + new[] { "MyResourcePermission1", "MyResourcePermission3", "MyResourcePermission5" }, + TestEntityResource.ResourceName, + TestEntityResource.ResourceKey3, + UserPermissionValueProvider.ProviderName, + PermissionTestDataBuilder.User1Id.ToString()); + + permissionGrants.ShouldNotContain(p => p.Name == "MyResourcePermission1"); + permissionGrants.ShouldContain(p => p.Name == "MyResourcePermission3"); + permissionGrants.ShouldContain(p => p.Name == "MyResourcePermission5"); + } + + [Fact] + public async Task GetList2Async() + { + var permissionGrants = + await ResourcePermissionGrantRepository.GetListAsync( + UserPermissionValueProvider.ProviderName, + PermissionTestDataBuilder.User1Id.ToString()); + + permissionGrants.ShouldContain(p => p.Name == "MyResourcePermission1" && p.ResourceKey == TestEntityResource.ResourceKey1 && p.ResourceName == TestEntityResource.ResourceName); + permissionGrants.ShouldContain(p => p.Name == "MyDisabledResourcePermission1" && p.ResourceKey == TestEntityResource.ResourceKey1 && p.ResourceName == TestEntityResource.ResourceName); + permissionGrants.ShouldContain(p => p.Name == "MyResourcePermission3" && p.ResourceKey == TestEntityResource.ResourceKey3 && p.ResourceName == TestEntityResource.ResourceName); + permissionGrants.ShouldContain(p => p.Name == "MyResourcePermission5" && p.ResourceKey == TestEntityResource.ResourceKey3 && p.ResourceName == TestEntityResource.ResourceName); + permissionGrants.ShouldContain(p => p.Name == "MyResourcePermission5" && p.ResourceKey == TestEntityResource.ResourceKey5 && p.ResourceName == TestEntityResource.ResourceName); + + permissionGrants = + await ResourcePermissionGrantRepository.GetListAsync( + UserPermissionValueProvider.ProviderName, + PermissionTestDataBuilder.User2Id.ToString()); + + permissionGrants.ShouldBeEmpty(); + } + + [Fact] + public async Task GetPermissionsAsync() + { + var permissionGrants = + await ResourcePermissionGrantRepository.GetPermissionsAsync( + TestEntityResource.ResourceName, + TestEntityResource.ResourceKey1); + + permissionGrants.Count.ShouldBe(2); + permissionGrants.ShouldContain(p => p.Name == "MyResourcePermission1"); + permissionGrants.ShouldContain(p => p.Name == "MyDisabledResourcePermission1"); + } + + [Fact] + public async Task GetResourceKeys() + { + var permissionGrants = + await ResourcePermissionGrantRepository.GetResourceKeys( + TestEntityResource.ResourceName, + "MyResourcePermission5"); + + permissionGrants.Count.ShouldBe(2); + permissionGrants.ShouldContain(p => p.ResourceKey == TestEntityResource.ResourceKey3); + permissionGrants.ShouldContain(p => p.ResourceKey == TestEntityResource.ResourceKey5); + } +} diff --git a/modules/permission-management/test/Volo.Abp.PermissionManagement.TestBase/Volo/Abp/PermissionManagement/TestEntityResource.cs b/modules/permission-management/test/Volo.Abp.PermissionManagement.TestBase/Volo/Abp/PermissionManagement/TestEntityResource.cs new file mode 100644 index 0000000000..9f631b123d --- /dev/null +++ b/modules/permission-management/test/Volo.Abp.PermissionManagement.TestBase/Volo/Abp/PermissionManagement/TestEntityResource.cs @@ -0,0 +1,16 @@ +using System; + +namespace Volo.Abp.PermissionManagement; + +public class TestEntityResource +{ + public static readonly string ResourceName = typeof(TestEntityResource).FullName; + + public static readonly string ResourceKey1 = Guid.NewGuid().ToString(); + public static readonly string ResourceKey2 = Guid.NewGuid().ToString(); + public static readonly string ResourceKey3 = Guid.NewGuid().ToString(); + public static readonly string ResourceKey4 = Guid.NewGuid().ToString(); + public static readonly string ResourceKey5 = Guid.NewGuid().ToString(); + public static readonly string ResourceKey6 = Guid.NewGuid().ToString(); + public static readonly string ResourceKey7 = Guid.NewGuid().ToString(); +} diff --git a/modules/permission-management/test/Volo.Abp.PermissionManagement.TestBase/Volo/Abp/PermissionManagement/TestResourcePermissionDefinitionProvider.cs b/modules/permission-management/test/Volo.Abp.PermissionManagement.TestBase/Volo/Abp/PermissionManagement/TestResourcePermissionDefinitionProvider.cs new file mode 100644 index 0000000000..2d5a4d5d99 --- /dev/null +++ b/modules/permission-management/test/Volo.Abp.PermissionManagement.TestBase/Volo/Abp/PermissionManagement/TestResourcePermissionDefinitionProvider.cs @@ -0,0 +1,28 @@ +using Volo.Abp.Authorization.Permissions; +using Volo.Abp.MultiTenancy; + +namespace Volo.Abp.PermissionManagement; + +public class TestResourcePermissionDefinitionProvider : PermissionDefinitionProvider +{ + public override void Define(IPermissionDefinitionContext context) + { + context.AddGroup("TestEntityManagementPermissionGroup").AddPermission("TestEntityManagementPermission"); + + context.AddResourcePermission("MyResourcePermission1", TestEntityResource.ResourceName, "TestEntityManagementPermission"); + context.AddResourcePermission("MyResourceDisabledPermission1", TestEntityResource.ResourceName, "TestEntityManagementPermission", isEnabled: false); + context.AddResourcePermission("MyResourcePermission2", TestEntityResource.ResourceName, "TestEntityManagementPermission"); + context.AddResourcePermission("MyResourcePermission3", TestEntityResource.ResourceName, "TestEntityManagementPermission", multiTenancySide: MultiTenancySides.Host); + context.AddResourcePermission("MyResourcePermission4", TestEntityResource.ResourceName, "TestEntityManagementPermission", multiTenancySide: MultiTenancySides.Host).WithProviders(UserPermissionValueProvider.ProviderName); + + var myPermission5 = context.AddResourcePermission("MyResourcePermission5", TestEntityResource.ResourceName, "TestEntityManagementPermission"); + myPermission5.StateCheckers.Add(new TestRequireRolePermissionStateProvider("super-admin")); + + context.AddResourcePermission("MyResourcePermission6", TestEntityResource.ResourceName, "TestEntityManagementPermission"); + + context.AddResourcePermission("MyResourceDisabledPermission2", TestEntityResource.ResourceName, "TestEntityManagementPermission", isEnabled: false); + + context.AddResourcePermission("MyResourcePermission7", TestEntityResource.ResourceName, "TestEntityManagementPermission"); + context.AddResourcePermission("MyResourcePermission8", TestEntityResource.ResourceName, "TestEntityManagementPermission"); + } +} diff --git a/modules/permission-management/test/Volo.Abp.PermissionManagement.TestBase/Volo/Abp/PermissionManagement/TestResourcePermissionManagementProvider.cs b/modules/permission-management/test/Volo.Abp.PermissionManagement.TestBase/Volo/Abp/PermissionManagement/TestResourcePermissionManagementProvider.cs new file mode 100644 index 0000000000..e555c6133d --- /dev/null +++ b/modules/permission-management/test/Volo.Abp.PermissionManagement.TestBase/Volo/Abp/PermissionManagement/TestResourcePermissionManagementProvider.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Volo.Abp.Guids; +using Volo.Abp.MultiTenancy; + +namespace Volo.Abp.PermissionManagement; + +public class TestResourcePermissionManagementProvider : ResourcePermissionManagementProvider +{ + public override string Name => "Test"; + + public TestResourcePermissionManagementProvider( + IResourcePermissionGrantRepository resourcePermissionGrantRepository, + IGuidGenerator guidGenerator, + ICurrentTenant currentTenant) + : base( + resourcePermissionGrantRepository, + guidGenerator, + currentTenant) + { + + } +} diff --git a/modules/permission-management/test/Volo.Abp.PermissionManagement.TestBase/Volo/Abp/PermissionManagement/TestResourcePermissionProviderKeyLookupService.cs b/modules/permission-management/test/Volo.Abp.PermissionManagement.TestBase/Volo/Abp/PermissionManagement/TestResourcePermissionProviderKeyLookupService.cs new file mode 100644 index 0000000000..f42a204f7a --- /dev/null +++ b/modules/permission-management/test/Volo.Abp.PermissionManagement.TestBase/Volo/Abp/PermissionManagement/TestResourcePermissionProviderKeyLookupService.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Localization; + +namespace Volo.Abp.PermissionManagement; + +public class TestResourcePermissionProviderKeyLookupService : IResourcePermissionProviderKeyLookupService, ITransientDependency +{ + public string Name => "Test"; + + public ILocalizableString DisplayName => new LocalizableString("Test", "TestResource"); + + public Task> SearchAsync(string filter = null, int page = 1, CancellationToken cancellationToken = default) + { + throw new System.NotImplementedException(); + } + + public Task> SearchAsync(string[] keys, CancellationToken cancellationToken = default) + { + throw new System.NotImplementedException(); + } +} diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain/Volo/Abp/SettingManagement/AbpSettingManagementDomainModule.cs b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain/Volo/Abp/SettingManagement/AbpSettingManagementDomainModule.cs index 3f73342d0a..a5d2ba832e 100644 --- a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain/Volo/Abp/SettingManagement/AbpSettingManagementDomainModule.cs +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain/Volo/Abp/SettingManagement/AbpSettingManagementDomainModule.cs @@ -25,7 +25,6 @@ namespace Volo.Abp.SettingManagement; public class AbpSettingManagementDomainModule : AbpModule { private readonly CancellationTokenSource _cancellationTokenSource = new(); - private Task _initializeDynamicSettingsTask; public override void ConfigureServices(ServiceConfigurationContext context) { @@ -53,10 +52,11 @@ public class AbpSettingManagementDomainModule : AbpModule AsyncHelper.RunSync(() => OnApplicationInitializationAsync(context)); } - public override Task OnApplicationInitializationAsync(ApplicationInitializationContext context) + public override async Task OnApplicationInitializationAsync(ApplicationInitializationContext context) { - InitializeDynamicSettings(context); - return Task.CompletedTask; + var rootServiceProvider = context.ServiceProvider.GetRequiredService(); + var initializer = rootServiceProvider.GetRequiredService(); + await initializer.InitializeAsync(true, _cancellationTokenSource.Token); } public override Task OnApplicationShutdownAsync(ApplicationShutdownContext context) @@ -64,116 +64,4 @@ public class AbpSettingManagementDomainModule : AbpModule _cancellationTokenSource.Cancel(); return Task.CompletedTask; } - - public Task GetInitializeDynamicSettingsTask() - { - return _initializeDynamicSettingsTask ?? Task.CompletedTask; - } - - private void InitializeDynamicSettings(ApplicationInitializationContext context) - { - var options = context - .ServiceProvider - .GetRequiredService>() - .Value; - - if (!options.SaveStaticSettingsToDatabase && !options.IsDynamicSettingStoreEnabled) - { - return; - } - - var rootServiceProvider = context.ServiceProvider.GetRequiredService(); - - _initializeDynamicSettingsTask = Task.Run(async () => - { - using var scope = rootServiceProvider.CreateScope(); - var applicationLifetime = scope.ServiceProvider.GetService(); - var cancellationTokenProvider = scope.ServiceProvider.GetRequiredService(); - var cancellationToken = applicationLifetime?.ApplicationStopping ?? _cancellationTokenSource.Token; - - try - { - using (cancellationTokenProvider.Use(cancellationToken)) - { - if (cancellationTokenProvider.Token.IsCancellationRequested) - { - return; - } - - await SaveStaticSettingsToDatabaseAsync(options, scope, cancellationTokenProvider); - - if (cancellationTokenProvider.Token.IsCancellationRequested) - { - return; - } - - await PreCacheDynamicSettingsAsync(options, scope); - } - } - // ReSharper disable once EmptyGeneralCatchClause (No need to log since it is logged above) - catch { } - }); - } - - private async static Task SaveStaticSettingsToDatabaseAsync( - SettingManagementOptions options, - IServiceScope scope, - ICancellationTokenProvider cancellationTokenProvider) - { - if (!options.SaveStaticSettingsToDatabase) - { - return; - } - - await Policy - .Handle() - .WaitAndRetryAsync(8, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt) * 10)) - .ExecuteAsync(async _ => - { - try - { - // ReSharper disable once AccessToDisposedClosure - await scope - .ServiceProvider - .GetRequiredService() - .SaveAsync(); - } - catch (Exception ex) - { - // ReSharper disable once AccessToDisposedClosure - scope.ServiceProvider - .GetService>()? - .LogException(ex); - - throw; // Polly will catch it - } - }, cancellationTokenProvider.Token); - } - - private async static Task PreCacheDynamicSettingsAsync(SettingManagementOptions options, IServiceScope scope) - { - if (!options.IsDynamicSettingStoreEnabled) - { - return; - } - - try - { - // Pre-cache settings, so first request doesn't wait - await scope - .ServiceProvider - .GetRequiredService() - .GetAllAsync(); - } - catch (Exception ex) - { - // ReSharper disable once AccessToDisposedClosure - scope - .ServiceProvider - .GetService>()? - .LogException(ex); - - throw; // It will be cached in InitializeDynamicSettings - } - } } diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain/Volo/Abp/SettingManagement/SettingDynamicInitializer.cs b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain/Volo/Abp/SettingManagement/SettingDynamicInitializer.cs new file mode 100644 index 0000000000..41db669d9b --- /dev/null +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain/Volo/Abp/SettingManagement/SettingDynamicInitializer.cs @@ -0,0 +1,150 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.Options; +using Polly; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Settings; +using Volo.Abp.Threading; + +namespace Volo.Abp.SettingManagement; + +public class SettingDynamicInitializer : ITransientDependency +{ + public ILogger Logger { get; set; } + + protected IServiceProvider ServiceProvider { get; } + protected IOptions Options { get; } + [CanBeNull] + protected IHostApplicationLifetime ApplicationLifetime { get; } + protected ICancellationTokenProvider CancellationTokenProvider { get; } + protected IDynamicSettingDefinitionStore DynamicSettingDefinitionStore { get; } + protected IStaticSettingSaver StaticSettingSaver { get; } + + public SettingDynamicInitializer( + IServiceProvider serviceProvider, + IOptions options, + ICancellationTokenProvider cancellationTokenProvider, + IDynamicSettingDefinitionStore dynamicSettingDefinitionStore, + IStaticSettingSaver staticSettingSaver) + { + Logger = NullLogger.Instance; + + ServiceProvider = serviceProvider; + Options = options; + ApplicationLifetime = ServiceProvider.GetService(); + CancellationTokenProvider = cancellationTokenProvider; + DynamicSettingDefinitionStore = dynamicSettingDefinitionStore; + StaticSettingSaver = staticSettingSaver; + } + + public virtual Task InitializeAsync(bool runInBackground, CancellationToken cancellationToken = default) + { + var options = Options.Value; + + if (!options.SaveStaticSettingsToDatabase && !options.IsDynamicSettingStoreEnabled) + { + return Task.CompletedTask; + } + + if (runInBackground) + { + Task.Run(async () => + { + if (cancellationToken == default && ApplicationLifetime?.ApplicationStopping != null) + { + cancellationToken = ApplicationLifetime.ApplicationStopping; + } + await ExecuteInitializationAsync(options, cancellationToken); + }, cancellationToken); + + return Task.CompletedTask; + } + + return ExecuteInitializationAsync(options, cancellationToken); + } + + protected virtual async Task ExecuteInitializationAsync(SettingManagementOptions options, CancellationToken cancellationToken) + { + try + { + using (CancellationTokenProvider.Use(cancellationToken)) + { + if (CancellationTokenProvider.Token.IsCancellationRequested) + { + return; + } + + await SaveStaticSettingsToDatabaseAsync(options, cancellationToken); + + if (CancellationTokenProvider.Token.IsCancellationRequested) + { + return; + } + + await PreCacheDynamicSettingsAsync(options); + } + } + catch + { + // No need to log here since inner calls log + } + } + + protected virtual async Task SaveStaticSettingsToDatabaseAsync( + SettingManagementOptions options, + CancellationToken cancellationToken) + { + if (!options.SaveStaticSettingsToDatabase) + { + return; + } + + await Policy + .Handle() + .WaitAndRetryAsync( + 8, + retryAttempt => TimeSpan.FromSeconds( + Volo.Abp.RandomHelper.GetRandom( + (int)Math.Pow(2, retryAttempt) * 8, + (int)Math.Pow(2, retryAttempt) * 12) + ) + ) + .ExecuteAsync(async _ => + { + try + { + await StaticSettingSaver.SaveAsync(); + } + catch (Exception ex) + { + Logger.LogException(ex); + throw; // Polly will catch it + } + }, cancellationToken); + } + + protected virtual async Task PreCacheDynamicSettingsAsync(SettingManagementOptions options) + { + if (!options.IsDynamicSettingStoreEnabled) + { + return; + } + + try + { + // Pre-cache settings, so first request doesn't wait + await DynamicSettingDefinitionStore.GetAllAsync(); + } + catch (Exception ex) + { + Logger.LogException(ex); + throw; // It will be cached in Initialize() + } + } +} diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain/Volo/Abp/SettingManagement/StaticSettingDefinitionChangedEventHandler.cs b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain/Volo/Abp/SettingManagement/StaticSettingDefinitionChangedEventHandler.cs new file mode 100644 index 0000000000..cfc1058bb1 --- /dev/null +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain/Volo/Abp/SettingManagement/StaticSettingDefinitionChangedEventHandler.cs @@ -0,0 +1,32 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.EventBus; +using Volo.Abp.Settings; +using Volo.Abp.StaticDefinitions; +using Volo.Abp.Threading; + +namespace Volo.Abp.SettingManagement; + +public class StaticSettingDefinitionChangedEventHandler : ILocalEventHandler, ITransientDependency +{ + protected IStaticDefinitionCache> DefinitionCache { get; } + protected SettingDynamicInitializer SettingDynamicInitializer { get; } + protected ICancellationTokenProvider CancellationTokenProvider { get; } + + public StaticSettingDefinitionChangedEventHandler( + IStaticDefinitionCache> definitionCache, + SettingDynamicInitializer settingDynamicInitializer, + ICancellationTokenProvider cancellationTokenProvider) + { + DefinitionCache = definitionCache; + SettingDynamicInitializer = settingDynamicInitializer; + CancellationTokenProvider = cancellationTokenProvider; + } + + public virtual async Task HandleEventAsync(StaticSettingDefinitionChangedEvent eventData) + { + await DefinitionCache.ClearAsync(); + await SettingDynamicInitializer.InitializeAsync(false, CancellationTokenProvider.Token); + } +} diff --git a/modules/setting-management/test/Volo.Abp.SettingManagement.Tests/Volo/Abp/SettingManagement/SettingManager_Basic_Tests.cs b/modules/setting-management/test/Volo.Abp.SettingManagement.Tests/Volo/Abp/SettingManagement/SettingManager_Basic_Tests.cs index ca9db64c70..95691f53a9 100644 --- a/modules/setting-management/test/Volo.Abp.SettingManagement.Tests/Volo/Abp/SettingManagement/SettingManager_Basic_Tests.cs +++ b/modules/setting-management/test/Volo.Abp.SettingManagement.Tests/Volo/Abp/SettingManagement/SettingManager_Basic_Tests.cs @@ -18,11 +18,10 @@ public class SettingManager_Basic_Tests : SettingsTestBase } [Fact] - public async Task Should_Throw_Exception_When_Try_To_Get_An_Undefined_Setting() + public async Task Should_Return_Null_When_Try_To_Get_An_Undefined_Setting() { - await Assert.ThrowsAsync( - async () => await _settingProvider.GetOrNullAsync("UndefinedSetting") - ); + var value = await _settingProvider.GetOrNullAsync("UndefinedSetting"); + value.ShouldBeNull(); } [Fact] @@ -64,7 +63,7 @@ public class SettingManager_Basic_Tests : SettingsTestBase (await _settingManager.GetOrNullGlobalAsync("MySetting1")).ShouldBe("43"); (await _settingProvider.GetOrNullAsync("MySetting1")).ShouldBe("43"); } - + [Fact] public async Task Set_Should_Throw_Exception_If_Provider_Not_Found() { @@ -72,7 +71,7 @@ public class SettingManager_Basic_Tests : SettingsTestBase { await _settingManager.SetAsync("MySetting1", "43", "UndefinedProvider", "Test"); }); - + exception.Message.ShouldBe("Unknown setting value provider: UndefinedProvider"); } -} \ No newline at end of file +} diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/AbpTenantManagementDomainModule.cs b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/AbpTenantManagementDomainModule.cs index afce2e0809..9aad8753f6 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/AbpTenantManagementDomainModule.cs +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/AbpTenantManagementDomainModule.cs @@ -28,7 +28,7 @@ public class AbpTenantManagementDomainModule : AbpModule Configure(options => { - options.EtoMappings.Add(); + options.EtoMappings.Add(typeof(AbpTenantManagementDomainModule)); }); } diff --git a/modules/users/src/Volo.Abp.Users.Abstractions/Volo/Abp/Users/IRoleData.cs b/modules/users/src/Volo.Abp.Users.Abstractions/Volo/Abp/Users/IRoleData.cs new file mode 100644 index 0000000000..37656f6588 --- /dev/null +++ b/modules/users/src/Volo.Abp.Users.Abstractions/Volo/Abp/Users/IRoleData.cs @@ -0,0 +1,19 @@ +using System; +using Volo.Abp.Data; + +namespace Volo.Abp.Users; + +public interface IRoleData : IHasExtraProperties +{ + Guid Id { get; } + + Guid? TenantId { get; } + + string Name { get; } + + bool IsDefault { get; } + + bool IsStatic { get; } + + bool IsPublic { get; } +} diff --git a/modules/users/src/Volo.Abp.Users.Abstractions/Volo/Abp/Users/RoleData.cs b/modules/users/src/Volo.Abp.Users.Abstractions/Volo/Abp/Users/RoleData.cs new file mode 100644 index 0000000000..960a3dfcc7 --- /dev/null +++ b/modules/users/src/Volo.Abp.Users.Abstractions/Volo/Abp/Users/RoleData.cs @@ -0,0 +1,56 @@ +using System; +using JetBrains.Annotations; +using Volo.Abp.Data; + +namespace Volo.Abp.Users; + +public class RoleData : IRoleData +{ + public Guid Id { get; set; } + + public Guid? TenantId { get; set; } + + public string Name { get; set; } + + public bool IsDefault { get; set; } + + public bool IsStatic { get; set; } + + public bool IsPublic { get; set; } + + public ExtraPropertyDictionary ExtraProperties { get; } + + public RoleData() + { + + } + + public RoleData(IRoleData roleData) + { + Id = roleData.Id; + Name = roleData.Name; + IsDefault = roleData.IsDefault; + IsStatic = roleData.IsStatic; + IsPublic = roleData.IsPublic; + TenantId = roleData.TenantId; + ExtraProperties = roleData.ExtraProperties; + } + + public RoleData( + Guid id, + [NotNull] string name, + bool isDefault = false, + bool isStatic = false, + bool isPublic = false, + Guid? tenantId = null, + ExtraPropertyDictionary extraProperties = null) + { + Id = id; + Name = name; + IsDefault = isDefault; + IsStatic = isStatic; + IsPublic = isPublic; + TenantId = tenantId; + ExtraProperties = extraProperties; + } +} diff --git a/npm/ng-packs/apps/dev-app/project.json b/npm/ng-packs/apps/dev-app/project.json index 37f0502dde..bea3bf6612 100644 --- a/npm/ng-packs/apps/dev-app/project.json +++ b/npm/ng-packs/apps/dev-app/project.json @@ -176,9 +176,7 @@ }, "test": { "executor": "@nx/jest:jest", - "outputs": [ - "{workspaceRoot}/coverage/apps/dev-app" - ], + "outputs": ["{workspaceRoot}/coverage/apps/dev-app"], "options": { "jestConfig": "apps/dev-app/jest.config.ts" } diff --git a/npm/ng-packs/apps/dev-app/src/main.server.ts b/npm/ng-packs/apps/dev-app/src/main.server.ts index 4b9d4d1545..fbd3e9dbd7 100644 --- a/npm/ng-packs/apps/dev-app/src/main.server.ts +++ b/npm/ng-packs/apps/dev-app/src/main.server.ts @@ -1,7 +1,7 @@ -import { bootstrapApplication } from '@angular/platform-browser'; +import { bootstrapApplication, BootstrapContext } from '@angular/platform-browser'; import { AppComponent } from './app/app.component'; import { config } from './app/app.config.server'; -const bootstrap = () => bootstrapApplication(AppComponent, config); +const bootstrap = (context: BootstrapContext) => bootstrapApplication(AppComponent, config, context); export default bootstrap; diff --git a/npm/ng-packs/migrations.json b/npm/ng-packs/migrations.json index 49df20928b..3615f35e50 100644 --- a/npm/ng-packs/migrations.json +++ b/npm/ng-packs/migrations.json @@ -1,45 +1,118 @@ { "migrations": [ + { + "version": "22.0.0-beta.1", + "description": "Updates release version config based on the breaking changes in Nx v22", + "implementation": "./src/migrations/update-22-0-0/release-version-config-changes", + "package": "nx", + "name": "22-0-0-release-version-config-changes" + }, + { + "version": "22.0.0-beta.2", + "description": "Consolidates releaseTag* options into nested releaseTag object structure", + "implementation": "./src/migrations/update-22-0-0/consolidate-release-tag-config", + "package": "nx", + "name": "22-0-0-consolidate-release-tag-config" + }, { "cli": "nx", - "version": "21.2.0-beta.3", - "requires": { "@angular/core": ">=20.0.0" }, - "description": "Update the @angular/cli package version to ~20.0.0.", - "factory": "./src/migrations/update-21-2-0/update-angular-cli", + "version": "22.1.0-beta.5", + "description": "Updates the nx wrapper.", + "implementation": "./src/migrations/update-22-1-0/update-nx-wrapper", + "package": "nx", + "name": "22-1-0-update-nx-wrapper" + }, + { + "version": "21.5.0-beta.2", + "description": "Migrate the legacy 'development' custom condition to a workspace-unique custom condition name.", + "factory": "./src/migrations/update-21-5-0/migrate-development-custom-condition", + "package": "@nx/js", + "name": "migrate-development-custom-condition" + }, + { + "version": "22.0.0-beta.0", + "description": "Remove the deprecated `external` and `externalBuildTargets` options from the `@nx/js:swc` and `@nx/js:tsc` executors.", + "factory": "./src/migrations/update-22-0-0/remove-external-options-from-js-executors", + "package": "@nx/js", + "name": "remove-external-options-from-js-executors" + }, + { + "version": "22.1.0-rc.1", + "description": "Removes redundant TypeScript project references from project's tsconfig.json files when runtime tsconfig files (e.g., tsconfig.lib.json, tsconfig.app.json) exist.", + "factory": "./src/migrations/update-22-1-0/remove-redundant-ts-project-references", + "package": "@nx/js", + "name": "remove-redundant-ts-project-references" + }, + { + "version": "21.3.0-beta.3", + "description": "Rename the CLI option `testPathPattern` to `testPathPatterns`.", + "implementation": "./src/migrations/update-21-3-0/rename-test-path-pattern", + "package": "@nx/jest", + "name": "rename-test-path-pattern" + }, + { + "version": "22.2.0-beta.2", + "description": "Convert jest.config.ts files from ESM to CJS syntax (export default -> module.exports, import -> require) for projects using CommonJS resolution to ensure correct loading under Node.js type-stripping.", + "implementation": "./src/migrations/update-22-2-0/convert-jest-config-to-cjs", + "package": "@nx/jest", + "name": "convert-jest-config-to-cjs" + }, + { + "cli": "nx", + "version": "21.3.0-beta.4", + "requires": { "@angular/core": ">=20.1.0" }, + "description": "Update the @angular/cli package version to ~20.1.0.", + "factory": "./src/migrations/update-21-3-0/update-angular-cli", "package": "@nx/angular", - "name": "update-angular-cli-version-20-0-0" + "name": "update-angular-cli-version-20-1-0" }, { - "version": "21.2.0-beta.3", - "requires": { "@angular/core": ">=20.0.0" }, - "description": "Migrate imports of `provideServerRendering` from `@angular/platform-server` to `@angular/ssr`.", - "factory": "./src/migrations/update-21-2-0/migrate-provide-server-rendering-import", + "version": "21.5.0-beta.0", + "description": "Set the 'tsConfig' option to build and test targets to help with Angular migration issues.", + "factory": "./src/migrations/update-21-5-0/set-tsconfig-option", "package": "@nx/angular", - "name": "migrate-provide-server-rendering-import" + "name": "set-tsconfig-option" }, { - "version": "21.2.0-beta.3", - "requires": { "@angular/core": ">=20.0.0" }, - "description": "Replace `provideServerRouting` and `provideServerRoutesConfig` with `provideServerRendering` using `withRoutes`.", - "factory": "./src/migrations/update-21-2-0/replace-provide-server-routing", + "cli": "nx", + "version": "21.5.0-beta.2", + "requires": { "@angular/core": ">=20.2.0" }, + "description": "Update the @angular/cli package version to ~20.2.0.", + "factory": "./src/migrations/update-21-5-0/update-angular-cli", "package": "@nx/angular", - "name": "replace-provide-server-routing" + "name": "update-angular-cli-version-20-2-0" }, { - "version": "21.2.0-beta.3", - "requires": { "@angular/core": ">=20.0.0" }, - "description": "Update the generator defaults to maintain the previous style guide behavior.", - "factory": "./src/migrations/update-21-2-0/set-generator-defaults-for-previous-style-guide", + "version": "21.5.0-beta.2", + "requires": { "@angular/core": ">=20.2.0" }, + "description": "Remove any Karma configuration files that only contain the default content. The default configuration is automatically available without a specific project configurationfile.", + "factory": "./src/migrations/update-21-5-0/remove-default-karma-configuration-files", "package": "@nx/angular", - "name": "set-generator-defaults-for-previous-style-guide" + "name": "remove-default-karma-configuration-files" }, { - "version": "21.2.0-beta.3", - "requires": { "@angular/core": ">=20.0.0" }, - "description": "Update 'moduleResolution' to 'bundler' in TypeScript configurations. You can read more about this here: https://www.typescriptlang.org/tsconfig/#moduleResolution.", - "factory": "./src/migrations/update-21-2-0/update-module-resolution", + "cli": "nx", + "version": "21.6.1-beta.2", + "requires": { "@angular/core": ">=20.3.0" }, + "description": "Update the @angular/cli package version to ~20.3.0.", + "factory": "./src/migrations/update-21-6-1/update-angular-cli", "package": "@nx/angular", - "name": "update-module-resolution" + "name": "update-angular-cli-version-20-3-0" + }, + { + "version": "20.2.0", + "description": "Replaces usages of the deprecated Router.getCurrentNavigation method with the Router.currentNavigation signal", + "factory": "./bundles/router-current-navigation.cjs#migrate", + "optional": true, + "package": "@angular/core", + "name": "router-current-navigation" + }, + { + "version": "20.3.0", + "description": "Adds `BootstrapContext` to `bootstrapApplication` calls in `main.server.ts` to support server rendering.", + "factory": "./bundles/add-bootstrap-context-to-server-main.cjs#migrate", + "package": "@angular/core", + "name": "add-bootstrap-context-to-server-main" } ] } diff --git a/npm/ng-packs/package.json b/npm/ng-packs/package.json index f0019eb726..80b1ea7f33 100644 --- a/npm/ng-packs/package.json +++ b/npm/ng-packs/package.json @@ -48,49 +48,49 @@ "devDependencies": { "@abp/ng.theme.lepton-x": "~5.0.1", "@abp/utils": "~10.0.1", - "@angular-devkit/build-angular": "~20.0.0", - "@angular-devkit/core": "~20.0.0", - "@angular-devkit/schematics": "~20.0.0", - "@angular-devkit/schematics-cli": "~20.0.0", - "@angular-eslint/eslint-plugin": "~20.0.0", - "@angular-eslint/eslint-plugin-template": "~20.0.0", - "@angular-eslint/template-parser": "~20.0.0", - "@angular/animations": "~20.0.0", - "@angular/build": "~20.0.0", - "@angular/cli": "~20.0.0", - "@angular/common": "~20.0.0", - "@angular/compiler": "~20.0.0", - "@angular/compiler-cli": "~20.0.0", - "@angular/core": "~20.0.0", - "@angular/forms": "~20.0.0", - "@angular/language-service": "~20.0.0", - "@angular/localize": "~20.0.0", - "@angular/platform-browser": "~20.0.0", - "@angular/platform-browser-dynamic": "~20.0.0", - "@angular/platform-server": "~20.0.0", - "@angular/router": "~20.0.0", - "@angular/ssr": "~20.0.0", + "@angular-devkit/build-angular": "~21.0.0", + "@angular-devkit/core": "~21.0.0", + "@angular-devkit/schematics": "~21.0.0", + "@angular-devkit/schematics-cli": "~21.0.0", + "@angular-eslint/eslint-plugin": "~21.0.0", + "@angular-eslint/eslint-plugin-template": "~21.0.0", + "@angular-eslint/template-parser": "~21.0.0", + "@angular/animations": "21.0.0", + "@angular/build": "~21.0.0", + "@angular/cli": "~21.0.0", + "@angular/common": "~21.0.0", + "@angular/compiler": "~21.0.0", + "@angular/compiler-cli": "~21.0.0", + "@angular/core": "~21.0.0", + "@angular/forms": "~21.0.0", + "@angular/language-service": "~21.0.0", + "@angular/localize": "~21.0.0", + "@angular/platform-browser": "~21.0.0", + "@angular/platform-browser-dynamic": "~21.0.0", + "@angular/platform-server": "~21.0.0", + "@angular/router": "~21.0.0", + "@angular/ssr": "~21.0.0", "@fortawesome/fontawesome-free": "^6.0.0", - "@ng-bootstrap/ng-bootstrap": "~19.0.0", + "@ng-bootstrap/ng-bootstrap": "~20.0.0", "@ngneat/spectator": "~19.6.2", "@ngx-validate/core": "^0.2.0", - "@nx/angular": "~21.2.0", - "@nx/cypress": "~21.2.0", - "@nx/devkit": "~21.2.0", - "@nx/eslint": "~21.2.0", - "@nx/eslint-plugin": "~21.2.0", - "@nx/jest": "~21.2.0", - "@nx/js": "~21.2.0", - "@nx/plugin": "~21.2.0", - "@nx/web": "~21.2.0", - "@nx/workspace": "~21.2.0", + "@nx/angular": "~22.2.0", + "@nx/cypress": "~22.2.0", + "@nx/devkit": "~22.2.0", + "@nx/eslint": "~22.2.0", + "@nx/eslint-plugin": "~22.2.0", + "@nx/jest": "~22.2.0", + "@nx/js": "~22.2.0", + "@nx/plugin": "~22.2.0", + "@nx/web": "~22.2.0", + "@nx/workspace": "~22.2.0", "@popperjs/core": "~2.11.0", - "@schematics/angular": "~20.0.0", + "@schematics/angular": "~21.0.0", "@swc-node/register": "1.9.2", "@swc/cli": "0.6.0", "@swc/core": "~1.5.0", "@swc/helpers": "~0.5.0", - "@swimlane/ngx-datatable": "~21.1.0", + "@swimlane/ngx-datatable": "~22.0.0", "@types/express": "~5.0.0", "@types/jest": "29.5.14", "@types/node": "~20.11.0", @@ -120,9 +120,9 @@ "just-compare": "^2.0.0", "lerna": "^4.0.0", "lint-staged": "^13.0.0", - "ng-packagr": "~20.0.0", - "ng-zorro-antd": "~20.0.0", - "nx": "~21.2.0", + "ng-packagr": "~21.0.0", + "ng-zorro-antd": "~21.0.0-next.1", + "nx": "~22.2.0", "postcss": "^8.0.0", "postcss-import": "14.1.0", "postcss-preset-env": "7.5.0", @@ -131,12 +131,12 @@ "protractor": "~7.0.0", "rxjs": "~7.8.0", "should-quote": "^1.0.0", - "ts-jest": "29.1.0", + "ts-jest": "29.4.6", "ts-node": "10.9.1", "ts-toolbelt": "^9.0.0", "tslib": "^2.3.0", "tslint": "~6.1.0", - "typescript": "~5.8.0", + "typescript": "~5.9.0", "zone.js": "~0.15.0" }, "lint-staged": { diff --git a/npm/ng-packs/packages/account-core/project.json b/npm/ng-packs/packages/account-core/project.json index 3e60410088..312bdd30e9 100644 --- a/npm/ng-packs/packages/account-core/project.json +++ b/npm/ng-packs/packages/account-core/project.json @@ -4,6 +4,8 @@ "projectType": "library", "sourceRoot": "packages/account-core/src", "prefix": "abp", + "tags": [], + "implicitDependencies": ["core", "theme-shared"], "targets": { "build": { "executor": "@nx/angular:package", @@ -32,7 +34,5 @@ "executor": "@nx/eslint:lint", "outputs": ["{options.outputFile}"] } - }, - "tags": [], - "implicitDependencies": ["core", "theme-shared"] + } } diff --git a/npm/ng-packs/packages/account/project.json b/npm/ng-packs/packages/account/project.json index 9514a59dc6..41c6597e2a 100644 --- a/npm/ng-packs/packages/account/project.json +++ b/npm/ng-packs/packages/account/project.json @@ -4,6 +4,8 @@ "projectType": "library", "sourceRoot": "packages/account/src", "prefix": "abp", + "tags": [], + "implicitDependencies": ["core", "theme-shared", "account-core"], "targets": { "build": { "executor": "@nx/angular:package", @@ -32,7 +34,5 @@ "executor": "@nx/eslint:lint", "outputs": ["{options.outputFile}"] } - }, - "tags": [], - "implicitDependencies": ["core", "theme-shared", "account-core"] + } } diff --git a/npm/ng-packs/packages/components/lookup/ng-package.json b/npm/ng-packs/packages/components/lookup/ng-package.json new file mode 100644 index 0000000000..665ad2add2 --- /dev/null +++ b/npm/ng-packs/packages/components/lookup/ng-package.json @@ -0,0 +1,6 @@ +{ + "$schema": "../../../node_modules/ng-packagr/ng-entrypoint.schema.json", + "lib": { + "entryFile": "src/public-api.ts" + } +} \ No newline at end of file diff --git a/npm/ng-packs/packages/components/lookup/src/lib/lookup-search.component.html b/npm/ng-packs/packages/components/lookup/src/lib/lookup-search.component.html new file mode 100644 index 0000000000..5e09e264e8 --- /dev/null +++ b/npm/ng-packs/packages/components/lookup/src/lib/lookup-search.component.html @@ -0,0 +1,61 @@ +
+ @if (label()) { + + } + +
+ + @if (displayValue() && !disabled()) { + + } +
+ + @if (showDropdown() && !disabled()) { +
+ @if (isLoading()) { +
+ + {{ 'AbpUi::Loading' | abpLocalization }} +
+ } @else if (searchResults().length > 0) { + @for (item of searchResults(); track item.key) { + + } + } @else if (displayValue()) { + @if (noResultsTemplate()) { + + } @else { +
+ {{ 'AbpUi::NoDataAvailableInDatatable' | abpLocalization }} +
+ } + } +
+ } +
diff --git a/npm/ng-packs/packages/components/lookup/src/lib/lookup-search.component.scss b/npm/ng-packs/packages/components/lookup/src/lib/lookup-search.component.scss new file mode 100644 index 0000000000..4b9a9ab12e --- /dev/null +++ b/npm/ng-packs/packages/components/lookup/src/lib/lookup-search.component.scss @@ -0,0 +1,8 @@ +.abp-lookup-dropdown { + z-index: 1060; + max-height: 200px; + overflow-y: auto; + top: 100%; + margin-top: 0.25rem; + background-color: var(--lpx-content-bg); +} diff --git a/npm/ng-packs/packages/components/lookup/src/lib/lookup-search.component.ts b/npm/ng-packs/packages/components/lookup/src/lib/lookup-search.component.ts new file mode 100644 index 0000000000..b78bfa19ce --- /dev/null +++ b/npm/ng-packs/packages/components/lookup/src/lib/lookup-search.component.ts @@ -0,0 +1,140 @@ +import { + Component, + input, + output, + model, + signal, + OnInit, + ChangeDetectionStrategy, + TemplateRef, + contentChild, + DestroyRef, + inject, +} from '@angular/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { CommonModule } from '@angular/common'; +import { FormsModule } from '@angular/forms'; +import { LocalizationPipe } from '@abp/ng.core'; +import { Subject, Observable, debounceTime, distinctUntilChanged, of, finalize } from 'rxjs'; + +export interface LookupItem { + key: string; + displayName: string; + [key: string]: unknown; +} + +export type LookupSearchFn = (filter: string) => Observable; + +@Component({ + selector: 'abp-lookup-search', + templateUrl: './lookup-search.component.html', + styleUrl: './lookup-search.component.scss', + imports: [CommonModule, FormsModule, LocalizationPipe], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class LookupSearchComponent implements OnInit { + private readonly destroyRef = inject(DestroyRef); + + readonly label = input(); + readonly placeholder = input(''); + readonly debounceTime = input(300); + readonly minSearchLength = input(0); + readonly displayKey = input('displayName' as keyof T); + readonly valueKey = input('key' as keyof T); + readonly disabled = input(false); + + readonly searchFn = input>(() => of([])); + + readonly selectedValue = model(''); + readonly displayValue = model(''); + + readonly itemSelected = output(); + readonly searchChanged = output(); + + readonly itemTemplate = contentChild>('itemTemplate'); + readonly noResultsTemplate = contentChild>('noResultsTemplate'); + + readonly searchResults = signal([]); + readonly showDropdown = signal(true); + readonly isLoading = signal(false); + + private readonly searchSubject = new Subject(); + + ngOnInit() { + this.searchSubject + .pipe( + debounceTime(this.debounceTime()), + distinctUntilChanged(), + takeUntilDestroyed(this.destroyRef), + ) + .subscribe(filter => { + this.performSearch(filter); + }); + } + + onSearchInput(filter: string) { + this.displayValue.set(filter); + this.showDropdown.set(true); + this.searchChanged.emit(filter); + + if (filter.length >= this.minSearchLength()) { + this.searchSubject.next(filter); + } else { + this.searchResults.set([]); + } + } + + onSearchFocus() { + this.showDropdown.set(true); + const currentFilter = this.displayValue() || ''; + if (currentFilter.length >= this.minSearchLength()) { + this.performSearch(currentFilter); + } + } + + onSearchBlur(event: FocusEvent) { + const relatedTarget = event.relatedTarget as HTMLElement; + if (!relatedTarget?.closest('.abp-lookup-dropdown')) { + this.showDropdown.set(false); + } + } + + selectItem(item: T) { + const displayKeyValue = String(item[this.displayKey()] ?? ''); + const valueKeyValue = String(item[this.valueKey()] ?? ''); + + this.displayValue.set(displayKeyValue); + this.selectedValue.set(valueKeyValue); + this.searchResults.set([]); + this.showDropdown.set(false); + this.itemSelected.emit(item); + } + + clearSelection() { + this.displayValue.set(''); + this.selectedValue.set(''); + this.searchResults.set([]); + } + + private performSearch(filter: string) { + this.isLoading.set(true); + + this.searchFn()(filter) + .pipe( + takeUntilDestroyed(this.destroyRef), + finalize(() => this.isLoading.set(false)), + ) + .subscribe({ + next: results => { + this.searchResults.set(results); + }, + error: () => { + this.searchResults.set([]); + }, + }); + } + + getDisplayValue(item: T): string { + return String(item[this.displayKey()] ?? item[this.valueKey()] ?? ''); + } +} diff --git a/npm/ng-packs/packages/components/lookup/src/public-api.ts b/npm/ng-packs/packages/components/lookup/src/public-api.ts new file mode 100644 index 0000000000..b1232ef08f --- /dev/null +++ b/npm/ng-packs/packages/components/lookup/src/public-api.ts @@ -0,0 +1 @@ +export * from './lib/lookup-search.component'; diff --git a/npm/ng-packs/packages/components/package.json b/npm/ng-packs/packages/components/package.json index df305d27ae..1fb6de5e0c 100644 --- a/npm/ng-packs/packages/components/package.json +++ b/npm/ng-packs/packages/components/package.json @@ -12,7 +12,7 @@ }, "dependencies": { "chart.js": "^3.5.1", - "ng-zorro-antd": "~20.0.0", + "ng-zorro-antd": "~21.0.0-next.1", "@ctrl/tinycolor": "^4.0.0", "tslib": "^2.0.0" }, diff --git a/npm/ng-packs/packages/components/project.json b/npm/ng-packs/packages/components/project.json index 4a61b72ab4..671fabe461 100644 --- a/npm/ng-packs/packages/components/project.json +++ b/npm/ng-packs/packages/components/project.json @@ -4,6 +4,8 @@ "projectType": "library", "sourceRoot": "packages/components/src", "prefix": "abp", + "tags": [], + "implicitDependencies": ["core", "theme-shared"], "targets": { "build": { "executor": "@nx/angular:package", @@ -32,7 +34,5 @@ "executor": "@nx/eslint:lint", "outputs": ["{options.outputFile}"] } - }, - "tags": [], - "implicitDependencies": ["core", "theme-shared"] + } } diff --git a/npm/ng-packs/packages/components/tree/src/lib/components/tree.component.ts b/npm/ng-packs/packages/components/tree/src/lib/components/tree.component.ts index 316200704b..5c320a539b 100644 --- a/npm/ng-packs/packages/components/tree/src/lib/components/tree.component.ts +++ b/npm/ng-packs/packages/components/tree/src/lib/components/tree.component.ts @@ -29,7 +29,7 @@ import { DISABLE_TREE_STYLE_LOADING_TOKEN } from '../disable-tree-style-loading. import { TreeNodeTemplateDirective } from '../templates/tree-node-template.directive'; import { ExpandedIconTemplateDirective } from '../templates/expanded-icon-template.directive'; import { NgTemplateOutlet } from '@angular/common'; -import { NzNoAnimationDirective } from 'ng-zorro-antd/core/no-animation'; +import { NzNoAnimationDirective } from 'ng-zorro-antd/core/animation'; export type DropEvent = NzFormatEmitEvent & { pos: number }; diff --git a/npm/ng-packs/packages/components/tsconfig.lib.json b/npm/ng-packs/packages/components/tsconfig.lib.json index 22d2695db8..7dde5f04bf 100644 --- a/npm/ng-packs/packages/components/tsconfig.lib.json +++ b/npm/ng-packs/packages/components/tsconfig.lib.json @@ -2,14 +2,18 @@ "extends": "./tsconfig.json", "compilerOptions": { "outDir": "../../dist/out-tsc", - "target": "ES2022", "declaration": true, "declarationMap": true, "inlineSources": true, "types": [], - "lib": ["dom", "es2020"], + "target": "ES2022", + "lib": ["ES2020", "dom"], "useDefineForClassFields": false }, - "exclude": ["src/test-setup.ts", "**/*.spec.ts", "jest.config.ts"], - "include": ["**/*.ts"] + "exclude": [ + "src/test-setup.ts", + "src/**/*.spec.ts", + "jest.config.ts" + ], + "include": ["src/**/*.ts"] } diff --git a/npm/ng-packs/packages/components/tsconfig.lib.prod.json b/npm/ng-packs/packages/components/tsconfig.lib.prod.json index 0e06848ce5..8bfa43c12b 100644 --- a/npm/ng-packs/packages/components/tsconfig.lib.prod.json +++ b/npm/ng-packs/packages/components/tsconfig.lib.prod.json @@ -3,7 +3,8 @@ "compilerOptions": { "declarationMap": false, "target": "ES2022", - "useDefineForClassFields": false + "useDefineForClassFields": false, + "skipLibCheck": true }, "angularCompilerOptions": { "compilationMode": "partial" diff --git a/npm/ng-packs/packages/core/project.json b/npm/ng-packs/packages/core/project.json index 9efdfd9d83..a6b7789814 100644 --- a/npm/ng-packs/packages/core/project.json +++ b/npm/ng-packs/packages/core/project.json @@ -4,6 +4,7 @@ "projectType": "library", "sourceRoot": "packages/core/src", "prefix": "abp", + "tags": [], "targets": { "build": { "executor": "@nx/angular:package", @@ -32,6 +33,5 @@ "executor": "@nx/eslint:lint", "outputs": ["{options.outputFile}"] } - }, - "tags": [] + } } diff --git a/npm/ng-packs/packages/core/src/lib/utils/initial-utils.ts b/npm/ng-packs/packages/core/src/lib/utils/initial-utils.ts index ece0404dcb..414f4d0a31 100644 --- a/npm/ng-packs/packages/core/src/lib/utils/initial-utils.ts +++ b/npm/ng-packs/packages/core/src/lib/utils/initial-utils.ts @@ -1,7 +1,7 @@ import { registerLocaleData } from '@angular/common'; import { inject, Injector } from '@angular/core'; import { tap, catchError } from 'rxjs/operators'; -import { lastValueFrom, throwError } from 'rxjs'; +import { firstValueFrom, lastValueFrom, of, throwError, timeout } from 'rxjs'; import { ABP } from '../models/common'; import { Environment } from '../models/environment'; import { CurrentTenantDto } from '../proxy/volo/abp/asp-net-core/mvc/multi-tenancy/models'; @@ -10,10 +10,11 @@ import { EnvironmentService } from '../services/environment.service'; import { SessionStateService } from '../services/session-state.service'; import { CORE_OPTIONS } from '../tokens/options.token'; import { APP_INIT_ERROR_HANDLERS } from '../tokens/app-config.token'; +import { CHECK_AUTHENTICATION_STATE_FN_KEY } from '../tokens/check-authentication-state'; +import { APP_STARTED_WITH_SSR } from '../tokens/ssr-state.token'; import { getRemoteEnv } from './environment-utils'; import { parseTenantFromUrl } from './multi-tenancy-utils'; import { AuthService } from '../abstracts'; -import { CHECK_AUTHENTICATION_STATE_FN_KEY } from '../tokens/check-authentication-state'; import { noop } from './common-utils'; export async function getInitialData() { @@ -21,6 +22,7 @@ export async function getInitialData() { const environmentService = injector.get(EnvironmentService); const configState = injector.get(ConfigStateService); const options = injector.get(CORE_OPTIONS) as ABP.Root; + const appStartedWithSSR = injector.get(APP_STARTED_WITH_SSR); environmentService.setState(options.environment as Environment); await getRemoteEnv(injector, options.environment); @@ -49,8 +51,18 @@ export async function getInitialData() { return throwError(() => error); }), ); - // TODO: Not working with SSR - // await lastValueFrom(result$); + + if (appStartedWithSSR) { + await firstValueFrom( + result$.pipe( + timeout(0), + catchError(() => of(null)), + ), + ); + } else { + await lastValueFrom(result$); + } + await localeInitializer(injector); } diff --git a/npm/ng-packs/packages/core/tsconfig.lib.prod.json b/npm/ng-packs/packages/core/tsconfig.lib.prod.json index 0e06848ce5..8bfa43c12b 100644 --- a/npm/ng-packs/packages/core/tsconfig.lib.prod.json +++ b/npm/ng-packs/packages/core/tsconfig.lib.prod.json @@ -3,7 +3,8 @@ "compilerOptions": { "declarationMap": false, "target": "ES2022", - "useDefineForClassFields": false + "useDefineForClassFields": false, + "skipLibCheck": true }, "angularCompilerOptions": { "compilationMode": "partial" diff --git a/npm/ng-packs/packages/feature-management/project.json b/npm/ng-packs/packages/feature-management/project.json index b2f0e61b08..7712d1b67b 100644 --- a/npm/ng-packs/packages/feature-management/project.json +++ b/npm/ng-packs/packages/feature-management/project.json @@ -4,6 +4,8 @@ "projectType": "library", "sourceRoot": "packages/feature-management/src", "prefix": "abp", + "tags": [], + "implicitDependencies": ["core", "theme-shared"], "targets": { "build": { "executor": "@nx/angular:package", @@ -32,7 +34,5 @@ "executor": "@nx/eslint:lint", "outputs": ["{options.outputFile}"] } - }, - "tags": [], - "implicitDependencies": ["core", "theme-shared"] + } } diff --git a/npm/ng-packs/packages/generators/project.json b/npm/ng-packs/packages/generators/project.json index 06244af3d0..5924f7caac 100644 --- a/npm/ng-packs/packages/generators/project.json +++ b/npm/ng-packs/packages/generators/project.json @@ -3,6 +3,7 @@ "$schema": "../../node_modules/nx/schemas/project-schema.json", "sourceRoot": "packages/generators/src", "projectType": "library", + "tags": [], "targets": { "build": { "executor": "@nx/js:tsc", @@ -51,6 +52,5 @@ "jestConfig": "packages/generators/jest.config.ts" } } - }, - "tags": [] + } } diff --git a/npm/ng-packs/packages/generators/tsconfig.json b/npm/ng-packs/packages/generators/tsconfig.json index f765bc1b6a..8d1999ff05 100644 --- a/npm/ng-packs/packages/generators/tsconfig.json +++ b/npm/ng-packs/packages/generators/tsconfig.json @@ -2,7 +2,8 @@ "extends": "../../tsconfig.base.json", "compilerOptions": { "module": "commonjs", - "skipLibCheck": true + "skipLibCheck": true, + "moduleResolution": "node" }, "files": [], "include": [], diff --git a/npm/ng-packs/packages/identity/project.json b/npm/ng-packs/packages/identity/project.json index d51409ac2a..e67a92ba2a 100644 --- a/npm/ng-packs/packages/identity/project.json +++ b/npm/ng-packs/packages/identity/project.json @@ -4,6 +4,8 @@ "projectType": "library", "sourceRoot": "packages/identity/src", "prefix": "abp", + "tags": [], + "implicitDependencies": ["core", "theme-shared", "permission-management"], "targets": { "build": { "executor": "@nx/angular:package", @@ -32,7 +34,5 @@ "executor": "@nx/eslint:lint", "outputs": ["{options.outputFile}"] } - }, - "tags": [], - "implicitDependencies": ["core", "theme-shared", "permission-management"] + } } diff --git a/npm/ng-packs/packages/oauth/project.json b/npm/ng-packs/packages/oauth/project.json index 61662f4811..858ec5947c 100644 --- a/npm/ng-packs/packages/oauth/project.json +++ b/npm/ng-packs/packages/oauth/project.json @@ -4,6 +4,8 @@ "projectType": "library", "sourceRoot": "packages/oauth/src", "prefix": "abp", + "tags": [], + "implicitDependencies": ["core"], "targets": { "build": { "executor": "@nx/angular:package", @@ -31,7 +33,5 @@ "lint": { "executor": "@nx/eslint:lint" } - }, - "tags": [], - "implicitDependencies": ["core"] + } } diff --git a/npm/ng-packs/packages/oauth/src/lib/services/index.ts b/npm/ng-packs/packages/oauth/src/lib/services/index.ts index 32909341a1..9e8637a59a 100644 --- a/npm/ng-packs/packages/oauth/src/lib/services/index.ts +++ b/npm/ng-packs/packages/oauth/src/lib/services/index.ts @@ -3,3 +3,4 @@ export * from './oauth-error-filter.service'; export * from './remember-me.service'; export * from './browser-token-storage.service'; export * from './server-token-storage.service'; +export * from './memory-token-storage.service'; diff --git a/npm/ng-packs/packages/oauth/src/lib/services/memory-token-storage.service.ts b/npm/ng-packs/packages/oauth/src/lib/services/memory-token-storage.service.ts new file mode 100644 index 0000000000..7d531dfefd --- /dev/null +++ b/npm/ng-packs/packages/oauth/src/lib/services/memory-token-storage.service.ts @@ -0,0 +1,243 @@ +import { DestroyRef, DOCUMENT, inject, Injectable } from '@angular/core'; +import { OAuthStorage } from 'angular-oauth2-oidc'; +import { AbpLocalStorageService } from '@abp/ng.core'; +import { fromEvent } from 'rxjs'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; + +@Injectable({ + providedIn: 'root', +}) +export class MemoryTokenStorageService implements OAuthStorage { + private static workerUrl: string | null = null; + + private keysShouldStoreInMemory = [ + 'access_token', + 'id_token', + 'expires_at', + 'id_token_claims_obj', + 'id_token_expires_at', + 'id_token_stored_at', + 'access_token_stored_at', + 'abpOAuthClientId', + 'granted_scopes', + ]; + + private worker?: any; + private port?: MessagePort; + private cache = new Map(); + private localStorageService = inject(AbpLocalStorageService); + private _document = inject(DOCUMENT); + private destroyRef = inject(DestroyRef); + private useSharedWorker = false; + + constructor() { + this.initializeStorage(); + this.setupCleanup(); + } + + private initializeStorage(): void { + // @ts-ignore + if (typeof SharedWorker !== 'undefined') { + try { + // Create worker from data URL to avoid path resolution issues in consuming apps + // Data URLs are deterministic - same content produces same URL across all tabs + if (!MemoryTokenStorageService.workerUrl) { + MemoryTokenStorageService.workerUrl = this.createWorkerDataUrl(); + } + // @ts-ignore + this.worker = new SharedWorker(MemoryTokenStorageService.workerUrl, { + name: 'oauth-token-storage', + }); + this.port = this.worker.port; + this.port.start(); + this.useSharedWorker = true; + + this.port.onmessage = event => { + const { action, key, value } = event.data; + + switch (action) { + case 'set': + this.checkAuthStateChanges(key); + this.cache.set(key, value); + break; + case 'remove': + this.cache.delete(key); + this.refreshDocument(); + break; + case 'clear': + this.cache.clear(); + this.refreshDocument(); + break; + case 'get': + if (value !== null) { + this.cache.set(key, value); + } + break; + } + }; + + // Load all tokens from SharedWorker on initialization + this.keysShouldStoreInMemory.forEach(key => { + this.port?.postMessage({ action: 'get', key }); + }); + } catch (error) { + this.useSharedWorker = false; + } + } else { + this.useSharedWorker = false; + } + } + + getItem(key: string): string | null { + if (!this.keysShouldStoreInMemory.includes(key)) { + return this.localStorageService.getItem(key); + } + return this.cache.get(key) || null; + } + + setItem(key: string, value: string): void { + if (!this.keysShouldStoreInMemory.includes(key)) { + this.localStorageService.setItem(key, value); + return; + } + + if (this.useSharedWorker && this.port) { + this.cache.set(key, value); + this.port.postMessage({ action: 'set', key, value }); + } else { + this.cache.set(key, value); + } + } + + removeItem(key: string): void { + if (!this.keysShouldStoreInMemory.includes(key)) { + this.localStorageService.removeItem(key); + return; + } + + if (this.useSharedWorker && this.port) { + this.cache.delete(key); + this.port.postMessage({ action: 'remove', key }); + } else { + this.cache.delete(key); + } + } + + clear(): void { + if (this.useSharedWorker && this.port) { + this.port.postMessage({ action: 'clear' }); + } + this.cache.clear(); + } + + private cleanupPort(): void { + if (this.useSharedWorker && this.port) { + try { + this.port.postMessage({ action: 'disconnect' }); + } catch (error) { + // + } + } + } + + private setupCleanup(): void { + if (this._document.defaultView) { + fromEvent(this._document.defaultView, 'beforeunload') + .pipe(takeUntilDestroyed(this.destroyRef)) + .subscribe(() => this.cleanupPort()); + + fromEvent(this._document.defaultView, 'pagehide') + .pipe(takeUntilDestroyed(this.destroyRef)) + .subscribe(() => this.cleanupPort()); + } + } + + private checkAuthStateChanges = (key: string) => { + if (key === 'access_token' && !this.cache.get('access_token')) { + this.refreshDocument(); + } + }; + + private refreshDocument(): void { + this.cleanupPort(); + setTimeout(() => { + this._document.defaultView?.location.reload(); + }, 100); + } + + private createWorkerDataUrl(): string { + const workerScript = `const tokenStore = new Map(); +const ports = new Set(); + +function broadcastToOtherPorts(senderPort, message) { + const deadPorts = []; + ports.forEach(p => { + if (p !== senderPort) { + try { + p.postMessage(message); + } catch (error) { + deadPorts.push(p); + } + } + }); + deadPorts.forEach(p => ports.delete(p)); +} + +function removePort(port) { + if (ports.has(port)) { + ports.delete(port); + } +} + +self.onconnect = (event) => { + const port = event.ports[0]; + ports.add(port); + + port.addEventListener('messageerror', () => { + removePort(port); + }); + + port.onmessage = (e) => { + const { action, key, value } = e.data; + + switch (action) { + case 'set': + if (key && value !== undefined) { + tokenStore.set(key, value); + broadcastToOtherPorts(port, { action: 'set', key, value }); + } + break; + + case 'remove': + if (key) { + tokenStore.delete(key); + broadcastToOtherPorts(port, { action: 'remove', key }); + } + break; + + case 'clear': + tokenStore.clear(); + broadcastToOtherPorts(port, { action: 'clear' }); + break; + + case 'get': + if (key) { + const value = tokenStore.get(key) ?? null; + port.postMessage({ action: 'get', key, value }); + } + break; + + case 'disconnect': + removePort(port); + break; + + default: + // + } + }; + + port.start(); +};`; + return 'data:application/javascript;base64,' + btoa(workerScript); + } +} diff --git a/npm/ng-packs/packages/oauth/src/lib/services/oauth.service.ts b/npm/ng-packs/packages/oauth/src/lib/services/oauth.service.ts index a25c3f6138..8a30b098e1 100644 --- a/npm/ng-packs/packages/oauth/src/lib/services/oauth.service.ts +++ b/npm/ng-packs/packages/oauth/src/lib/services/oauth.service.ts @@ -110,7 +110,12 @@ export class AbpOAuthService implements IAuthService { this.document.defaultView?.location.replace('/authorize'); return Promise.resolve(); } - return this.oAuthService.refreshToken(); + try { + return this.oAuthService.refreshToken(); + } catch (error) { + console.log("Error while refreshing token: ", error); + return Promise.reject(); + } } getAccessTokenExpiration(): number { diff --git a/npm/ng-packs/packages/oauth/src/lib/utils/storage.factory.ts b/npm/ng-packs/packages/oauth/src/lib/utils/storage.factory.ts index 97cd86682b..8a2f7e04c5 100644 --- a/npm/ng-packs/packages/oauth/src/lib/utils/storage.factory.ts +++ b/npm/ng-packs/packages/oauth/src/lib/utils/storage.factory.ts @@ -1,9 +1,9 @@ import { inject, PLATFORM_ID } from '@angular/core'; import { isPlatformBrowser } from '@angular/common'; import { ServerTokenStorageService } from '../services/server-token-storage.service'; -import { BrowserTokenStorageService } from '../services'; +import { BrowserTokenStorageService, MemoryTokenStorageService } from '../services'; import { OAuthStorage } from 'angular-oauth2-oidc'; -import { AbpLocalStorageService, APP_STARTED_WITH_SSR } from '@abp/ng.core'; +import { APP_STARTED_WITH_SSR } from '@abp/ng.core'; export class MockStorage implements Storage { private data = new Map(); @@ -35,5 +35,5 @@ export function oAuthStorageFactory(): OAuthStorage { ? inject(BrowserTokenStorageService) : inject(ServerTokenStorageService); } - return inject(AbpLocalStorageService); + return inject(MemoryTokenStorageService); } diff --git a/npm/ng-packs/packages/permission-management/project.json b/npm/ng-packs/packages/permission-management/project.json index eb09ef6178..3e3287256d 100644 --- a/npm/ng-packs/packages/permission-management/project.json +++ b/npm/ng-packs/packages/permission-management/project.json @@ -4,6 +4,8 @@ "projectType": "library", "sourceRoot": "packages/permission-management/src", "prefix": "abp", + "tags": [], + "implicitDependencies": ["core", "theme-shared"], "targets": { "build": { "executor": "@nx/angular:package", @@ -32,7 +34,5 @@ "executor": "@nx/eslint:lint", "outputs": ["{options.outputFile}"] } - }, - "tags": [], - "implicitDependencies": ["core", "theme-shared"] + } } diff --git a/npm/ng-packs/packages/permission-management/proxy/src/lib/proxy/generate-proxy.json b/npm/ng-packs/packages/permission-management/proxy/src/lib/proxy/generate-proxy.json index dbdc934ed9..05e7c1c92d 100644 --- a/npm/ng-packs/packages/permission-management/proxy/src/lib/proxy/generate-proxy.json +++ b/npm/ng-packs/packages/permission-management/proxy/src/lib/proxy/generate-proxy.json @@ -3,37 +3,69 @@ "permissionManagement" ], "modules": { - "featureManagement": { - "rootPath": "featureManagement", - "remoteServiceName": "AbpFeatureManagement", + "abp": { + "rootPath": "abp", + "remoteServiceName": "abp", "controllers": { - "Volo.Abp.FeatureManagement.FeaturesController": { - "controllerName": "Features", - "controllerGroupName": "Features", - "type": "Volo.Abp.FeatureManagement.FeaturesController", + "Pages.Abp.MultiTenancy.AbpTenantController": { + "controllerName": "AbpTenant", + "controllerGroupName": "AbpTenant", + "isRemoteService": true, + "isIntegrationService": false, + "apiVersion": null, + "type": "Pages.Abp.MultiTenancy.AbpTenantController", "interfaces": [ { - "type": "Volo.Abp.FeatureManagement.IFeatureAppService" + "type": "Volo.Abp.AspNetCore.Mvc.MultiTenancy.IAbpTenantAppService", + "name": "IAbpTenantAppService", + "methods": [ + { + "name": "FindTenantByNameAsync", + "parametersOnMethod": [ + { + "name": "name", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + } + ], + "returnValue": { + "type": "Volo.Abp.AspNetCore.Mvc.MultiTenancy.FindTenantResultDto", + "typeSimple": "Volo.Abp.AspNetCore.Mvc.MultiTenancy.FindTenantResultDto" + } + }, + { + "name": "FindTenantByIdAsync", + "parametersOnMethod": [ + { + "name": "id", + "typeAsString": "System.Guid, System.Private.CoreLib", + "type": "System.Guid", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + } + ], + "returnValue": { + "type": "Volo.Abp.AspNetCore.Mvc.MultiTenancy.FindTenantResultDto", + "typeSimple": "Volo.Abp.AspNetCore.Mvc.MultiTenancy.FindTenantResultDto" + } + } + ] } ], "actions": { - "GetAsyncByProviderNameAndProviderKey": { - "uniqueName": "GetAsyncByProviderNameAndProviderKey", - "name": "GetAsync", + "FindTenantByNameAsyncByName": { + "uniqueName": "FindTenantByNameAsyncByName", + "name": "FindTenantByNameAsync", "httpMethod": "GET", - "url": "api/feature-management/features", + "url": "api/abp/multi-tenancy/tenants/by-name/{name}", "supportedVersions": [], "parametersOnMethod": [ { - "name": "providerName", - "typeAsString": "System.String, System.Private.CoreLib", - "type": "System.String", - "typeSimple": "string", - "isOptional": false, - "defaultValue": null - }, - { - "name": "providerKey", + "name": "name", "typeAsString": "System.String, System.Private.CoreLib", "type": "System.String", "typeSimple": "string", @@ -43,181 +75,229 @@ ], "parameters": [ { - "nameOnMethod": "providerName", - "name": "providerName", - "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isOptional": false, - "defaultValue": null, - "constraintTypes": null, - "bindingSourceId": "ModelBinding", - "descriptorName": "" - }, - { - "nameOnMethod": "providerKey", - "name": "providerKey", + "nameOnMethod": "name", + "name": "name", "jsonName": null, "type": "System.String", "typeSimple": "string", "isOptional": false, "defaultValue": null, - "constraintTypes": null, - "bindingSourceId": "ModelBinding", + "constraintTypes": [], + "bindingSourceId": "Path", "descriptorName": "" } ], "returnValue": { - "type": "Volo.Abp.FeatureManagement.GetFeatureListResultDto", - "typeSimple": "Volo.Abp.FeatureManagement.GetFeatureListResultDto" + "type": "Volo.Abp.AspNetCore.Mvc.MultiTenancy.FindTenantResultDto", + "typeSimple": "Volo.Abp.AspNetCore.Mvc.MultiTenancy.FindTenantResultDto" }, "allowAnonymous": null, - "implementFrom": "Volo.Abp.FeatureManagement.IFeatureAppService" + "implementFrom": "Volo.Abp.AspNetCore.Mvc.MultiTenancy.IAbpTenantAppService" }, - "UpdateAsyncByProviderNameAndProviderKeyAndInput": { - "uniqueName": "UpdateAsyncByProviderNameAndProviderKeyAndInput", - "name": "UpdateAsync", - "httpMethod": "PUT", - "url": "api/feature-management/features", + "FindTenantByIdAsyncById": { + "uniqueName": "FindTenantByIdAsyncById", + "name": "FindTenantByIdAsync", + "httpMethod": "GET", + "url": "api/abp/multi-tenancy/tenants/by-id/{id}", "supportedVersions": [], "parametersOnMethod": [ { - "name": "providerName", - "typeAsString": "System.String, System.Private.CoreLib", - "type": "System.String", - "typeSimple": "string", - "isOptional": false, - "defaultValue": null - }, - { - "name": "providerKey", - "typeAsString": "System.String, System.Private.CoreLib", - "type": "System.String", + "name": "id", + "typeAsString": "System.Guid, System.Private.CoreLib", + "type": "System.Guid", "typeSimple": "string", "isOptional": false, "defaultValue": null - }, - { - "name": "input", - "typeAsString": "Volo.Abp.FeatureManagement.UpdateFeaturesDto, Volo.Abp.FeatureManagement.Application.Contracts", - "type": "Volo.Abp.FeatureManagement.UpdateFeaturesDto", - "typeSimple": "Volo.Abp.FeatureManagement.UpdateFeaturesDto", - "isOptional": false, - "defaultValue": null } ], "parameters": [ { - "nameOnMethod": "providerName", - "name": "providerName", + "nameOnMethod": "id", + "name": "id", "jsonName": null, - "type": "System.String", + "type": "System.Guid", "typeSimple": "string", "isOptional": false, "defaultValue": null, - "constraintTypes": null, - "bindingSourceId": "ModelBinding", + "constraintTypes": [], + "bindingSourceId": "Path", "descriptorName": "" - }, + } + ], + "returnValue": { + "type": "Volo.Abp.AspNetCore.Mvc.MultiTenancy.FindTenantResultDto", + "typeSimple": "Volo.Abp.AspNetCore.Mvc.MultiTenancy.FindTenantResultDto" + }, + "allowAnonymous": null, + "implementFrom": "Volo.Abp.AspNetCore.Mvc.MultiTenancy.IAbpTenantAppService" + } + } + }, + "Volo.Abp.AspNetCore.Mvc.ApiExploring.AbpApiDefinitionController": { + "controllerName": "AbpApiDefinition", + "controllerGroupName": "AbpApiDefinition", + "isRemoteService": true, + "isIntegrationService": false, + "apiVersion": null, + "type": "Volo.Abp.AspNetCore.Mvc.ApiExploring.AbpApiDefinitionController", + "interfaces": [], + "actions": { + "GetByModel": { + "uniqueName": "GetByModel", + "name": "Get", + "httpMethod": "GET", + "url": "api/abp/api-definition", + "supportedVersions": [], + "parametersOnMethod": [ { - "nameOnMethod": "providerKey", - "name": "providerKey", - "jsonName": null, - "type": "System.String", - "typeSimple": "string", + "name": "model", + "typeAsString": "Volo.Abp.Http.Modeling.ApplicationApiDescriptionModelRequestDto, Volo.Abp.Http", + "type": "Volo.Abp.Http.Modeling.ApplicationApiDescriptionModelRequestDto", + "typeSimple": "Volo.Abp.Http.Modeling.ApplicationApiDescriptionModelRequestDto", "isOptional": false, - "defaultValue": null, - "constraintTypes": null, - "bindingSourceId": "ModelBinding", - "descriptorName": "" - }, + "defaultValue": null + } + ], + "parameters": [ { - "nameOnMethod": "input", - "name": "input", + "nameOnMethod": "model", + "name": "IncludeTypes", "jsonName": null, - "type": "Volo.Abp.FeatureManagement.UpdateFeaturesDto", - "typeSimple": "Volo.Abp.FeatureManagement.UpdateFeaturesDto", + "type": "System.Boolean", + "typeSimple": "boolean", "isOptional": false, "defaultValue": null, "constraintTypes": null, - "bindingSourceId": "Body", - "descriptorName": "" + "bindingSourceId": "ModelBinding", + "descriptorName": "model" } ], "returnValue": { - "type": "System.Void", - "typeSimple": "System.Void" + "type": "Volo.Abp.Http.Modeling.ApplicationApiDescriptionModel", + "typeSimple": "Volo.Abp.Http.Modeling.ApplicationApiDescriptionModel" }, "allowAnonymous": null, - "implementFrom": "Volo.Abp.FeatureManagement.IFeatureAppService" + "implementFrom": "Volo.Abp.AspNetCore.Mvc.ApiExploring.AbpApiDefinitionController" } } - } - } - }, - "multi-tenancy": { - "rootPath": "multi-tenancy", - "remoteServiceName": "AbpTenantManagement", - "controllers": { - "Volo.Abp.TenantManagement.TenantController": { - "controllerName": "Tenant", - "controllerGroupName": "Tenant", - "type": "Volo.Abp.TenantManagement.TenantController", + }, + "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.AbpApplicationConfigurationController": { + "controllerName": "AbpApplicationConfiguration", + "controllerGroupName": "AbpApplicationConfiguration", + "isRemoteService": true, + "isIntegrationService": false, + "apiVersion": null, + "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.AbpApplicationConfigurationController", "interfaces": [ { - "type": "Volo.Abp.TenantManagement.ITenantAppService" + "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.IAbpApplicationConfigurationAppService", + "name": "IAbpApplicationConfigurationAppService", + "methods": [ + { + "name": "GetAsync", + "parametersOnMethod": [ + { + "name": "options", + "typeAsString": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationConfigurationRequestOptions, Volo.Abp.AspNetCore.Mvc.Contracts", + "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationConfigurationRequestOptions", + "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationConfigurationRequestOptions", + "isOptional": false, + "defaultValue": null + } + ], + "returnValue": { + "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationConfigurationDto", + "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationConfigurationDto" + } + } + ] } ], "actions": { - "GetAsyncById": { - "uniqueName": "GetAsyncById", + "GetAsyncByOptions": { + "uniqueName": "GetAsyncByOptions", "name": "GetAsync", "httpMethod": "GET", - "url": "api/multi-tenancy/tenants/{id}", + "url": "api/abp/application-configuration", "supportedVersions": [], "parametersOnMethod": [ { - "name": "id", - "typeAsString": "System.Guid, System.Private.CoreLib", - "type": "System.Guid", - "typeSimple": "string", + "name": "options", + "typeAsString": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationConfigurationRequestOptions, Volo.Abp.AspNetCore.Mvc.Contracts", + "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationConfigurationRequestOptions", + "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationConfigurationRequestOptions", "isOptional": false, "defaultValue": null } ], "parameters": [ { - "nameOnMethod": "id", - "name": "id", + "nameOnMethod": "options", + "name": "IncludeLocalizationResources", "jsonName": null, - "type": "System.Guid", - "typeSimple": "string", + "type": "System.Boolean", + "typeSimple": "boolean", "isOptional": false, "defaultValue": null, - "constraintTypes": [], - "bindingSourceId": "Path", - "descriptorName": "" + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "options" } ], "returnValue": { - "type": "Volo.Abp.TenantManagement.TenantDto", - "typeSimple": "Volo.Abp.TenantManagement.TenantDto" + "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationConfigurationDto", + "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationConfigurationDto" }, "allowAnonymous": null, - "implementFrom": "Volo.Abp.Application.Services.IReadOnlyAppService" - }, - "GetListAsyncByInput": { - "uniqueName": "GetListAsyncByInput", - "name": "GetListAsync", + "implementFrom": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.IAbpApplicationConfigurationAppService" + } + } + }, + "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.AbpApplicationLocalizationController": { + "controllerName": "AbpApplicationLocalization", + "controllerGroupName": "AbpApplicationLocalization", + "isRemoteService": true, + "isIntegrationService": false, + "apiVersion": null, + "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.AbpApplicationLocalizationController", + "interfaces": [ + { + "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.IAbpApplicationLocalizationAppService", + "name": "IAbpApplicationLocalizationAppService", + "methods": [ + { + "name": "GetAsync", + "parametersOnMethod": [ + { + "name": "input", + "typeAsString": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationLocalizationRequestDto, Volo.Abp.AspNetCore.Mvc.Contracts", + "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationLocalizationRequestDto", + "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationLocalizationRequestDto", + "isOptional": false, + "defaultValue": null + } + ], + "returnValue": { + "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationLocalizationDto", + "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationLocalizationDto" + } + } + ] + } + ], + "actions": { + "GetAsyncByInput": { + "uniqueName": "GetAsyncByInput", + "name": "GetAsync", "httpMethod": "GET", - "url": "api/multi-tenancy/tenants", + "url": "api/abp/application-localization", "supportedVersions": [], "parametersOnMethod": [ { "name": "input", - "typeAsString": "Volo.Abp.TenantManagement.GetTenantsInput, Volo.Abp.TenantManagement.Application.Contracts", - "type": "Volo.Abp.TenantManagement.GetTenantsInput", - "typeSimple": "Volo.Abp.TenantManagement.GetTenantsInput", + "typeAsString": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationLocalizationRequestDto, Volo.Abp.AspNetCore.Mvc.Contracts", + "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationLocalizationRequestDto", + "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationLocalizationRequestDto", "isOptional": false, "defaultValue": null } @@ -225,7 +305,7 @@ "parameters": [ { "nameOnMethod": "input", - "name": "Filter", + "name": "CultureName", "jsonName": null, "type": "System.String", "typeSimple": "string", @@ -237,60 +317,165 @@ }, { "nameOnMethod": "input", - "name": "Sorting", + "name": "OnlyDynamics", "jsonName": null, - "type": "System.String", - "typeSimple": "string", + "type": "System.Boolean", + "typeSimple": "boolean", "isOptional": false, "defaultValue": null, "constraintTypes": null, "bindingSourceId": "ModelBinding", "descriptorName": "input" + } + ], + "returnValue": { + "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationLocalizationDto", + "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationLocalizationDto" + }, + "allowAnonymous": null, + "implementFrom": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.IAbpApplicationLocalizationAppService" + } + } + } + } + }, + "account": { + "rootPath": "account", + "remoteServiceName": "AbpAccount", + "controllers": { + "Volo.Abp.Account.AccountController": { + "controllerName": "Account", + "controllerGroupName": "Account", + "isRemoteService": true, + "isIntegrationService": false, + "apiVersion": null, + "type": "Volo.Abp.Account.AccountController", + "interfaces": [ + { + "type": "Volo.Abp.Account.IAccountAppService", + "name": "IAccountAppService", + "methods": [ + { + "name": "RegisterAsync", + "parametersOnMethod": [ + { + "name": "input", + "typeAsString": "Volo.Abp.Account.RegisterDto, Volo.Abp.Account.Application.Contracts", + "type": "Volo.Abp.Account.RegisterDto", + "typeSimple": "Volo.Abp.Account.RegisterDto", + "isOptional": false, + "defaultValue": null + } + ], + "returnValue": { + "type": "Volo.Abp.Identity.IdentityUserDto", + "typeSimple": "Volo.Abp.Identity.IdentityUserDto" + } }, { - "nameOnMethod": "input", - "name": "SkipCount", - "jsonName": null, - "type": "System.Int32", - "typeSimple": "number", - "isOptional": false, - "defaultValue": null, - "constraintTypes": null, - "bindingSourceId": "ModelBinding", - "descriptorName": "input" + "name": "SendPasswordResetCodeAsync", + "parametersOnMethod": [ + { + "name": "input", + "typeAsString": "Volo.Abp.Account.SendPasswordResetCodeDto, Volo.Abp.Account.Application.Contracts", + "type": "Volo.Abp.Account.SendPasswordResetCodeDto", + "typeSimple": "Volo.Abp.Account.SendPasswordResetCodeDto", + "isOptional": false, + "defaultValue": null + } + ], + "returnValue": { + "type": "System.Void", + "typeSimple": "System.Void" + } }, + { + "name": "VerifyPasswordResetTokenAsync", + "parametersOnMethod": [ + { + "name": "input", + "typeAsString": "Volo.Abp.Account.VerifyPasswordResetTokenInput, Volo.Abp.Account.Application.Contracts", + "type": "Volo.Abp.Account.VerifyPasswordResetTokenInput", + "typeSimple": "Volo.Abp.Account.VerifyPasswordResetTokenInput", + "isOptional": false, + "defaultValue": null + } + ], + "returnValue": { + "type": "System.Boolean", + "typeSimple": "boolean" + } + }, + { + "name": "ResetPasswordAsync", + "parametersOnMethod": [ + { + "name": "input", + "typeAsString": "Volo.Abp.Account.ResetPasswordDto, Volo.Abp.Account.Application.Contracts", + "type": "Volo.Abp.Account.ResetPasswordDto", + "typeSimple": "Volo.Abp.Account.ResetPasswordDto", + "isOptional": false, + "defaultValue": null + } + ], + "returnValue": { + "type": "System.Void", + "typeSimple": "System.Void" + } + } + ] + } + ], + "actions": { + "RegisterAsyncByInput": { + "uniqueName": "RegisterAsyncByInput", + "name": "RegisterAsync", + "httpMethod": "POST", + "url": "api/account/register", + "supportedVersions": [], + "parametersOnMethod": [ + { + "name": "input", + "typeAsString": "Volo.Abp.Account.RegisterDto, Volo.Abp.Account.Application.Contracts", + "type": "Volo.Abp.Account.RegisterDto", + "typeSimple": "Volo.Abp.Account.RegisterDto", + "isOptional": false, + "defaultValue": null + } + ], + "parameters": [ { "nameOnMethod": "input", - "name": "MaxResultCount", + "name": "input", "jsonName": null, - "type": "System.Int32", - "typeSimple": "number", + "type": "Volo.Abp.Account.RegisterDto", + "typeSimple": "Volo.Abp.Account.RegisterDto", "isOptional": false, "defaultValue": null, "constraintTypes": null, - "bindingSourceId": "ModelBinding", - "descriptorName": "input" + "bindingSourceId": "Body", + "descriptorName": "" } ], "returnValue": { - "type": "Volo.Abp.Application.Dtos.PagedResultDto", - "typeSimple": "Volo.Abp.Application.Dtos.PagedResultDto" + "type": "Volo.Abp.Identity.IdentityUserDto", + "typeSimple": "Volo.Abp.Identity.IdentityUserDto" }, "allowAnonymous": null, - "implementFrom": "Volo.Abp.Application.Services.IReadOnlyAppService" + "implementFrom": "Volo.Abp.Account.IAccountAppService" }, - "CreateAsyncByInput": { - "uniqueName": "CreateAsyncByInput", - "name": "CreateAsync", + "SendPasswordResetCodeAsyncByInput": { + "uniqueName": "SendPasswordResetCodeAsyncByInput", + "name": "SendPasswordResetCodeAsync", "httpMethod": "POST", - "url": "api/multi-tenancy/tenants", + "url": "api/account/send-password-reset-code", "supportedVersions": [], "parametersOnMethod": [ { "name": "input", - "typeAsString": "Volo.Abp.TenantManagement.TenantCreateDto, Volo.Abp.TenantManagement.Application.Contracts", - "type": "Volo.Abp.TenantManagement.TenantCreateDto", - "typeSimple": "Volo.Abp.TenantManagement.TenantCreateDto", + "typeAsString": "Volo.Abp.Account.SendPasswordResetCodeDto, Volo.Abp.Account.Application.Contracts", + "type": "Volo.Abp.Account.SendPasswordResetCodeDto", + "typeSimple": "Volo.Abp.Account.SendPasswordResetCodeDto", "isOptional": false, "defaultValue": null } @@ -300,8 +485,8 @@ "nameOnMethod": "input", "name": "input", "jsonName": null, - "type": "Volo.Abp.TenantManagement.TenantCreateDto", - "typeSimple": "Volo.Abp.TenantManagement.TenantCreateDto", + "type": "Volo.Abp.Account.SendPasswordResetCodeDto", + "typeSimple": "Volo.Abp.Account.SendPasswordResetCodeDto", "isOptional": false, "defaultValue": null, "constraintTypes": null, @@ -310,55 +495,35 @@ } ], "returnValue": { - "type": "Volo.Abp.TenantManagement.TenantDto", - "typeSimple": "Volo.Abp.TenantManagement.TenantDto" + "type": "System.Void", + "typeSimple": "System.Void" }, "allowAnonymous": null, - "implementFrom": "Volo.Abp.Application.Services.ICreateAppService" + "implementFrom": "Volo.Abp.Account.IAccountAppService" }, - "UpdateAsyncByIdAndInput": { - "uniqueName": "UpdateAsyncByIdAndInput", - "name": "UpdateAsync", - "httpMethod": "PUT", - "url": "api/multi-tenancy/tenants/{id}", + "VerifyPasswordResetTokenAsyncByInput": { + "uniqueName": "VerifyPasswordResetTokenAsyncByInput", + "name": "VerifyPasswordResetTokenAsync", + "httpMethod": "POST", + "url": "api/account/verify-password-reset-token", "supportedVersions": [], "parametersOnMethod": [ - { - "name": "id", - "typeAsString": "System.Guid, System.Private.CoreLib", - "type": "System.Guid", - "typeSimple": "string", - "isOptional": false, - "defaultValue": null - }, { "name": "input", - "typeAsString": "Volo.Abp.TenantManagement.TenantUpdateDto, Volo.Abp.TenantManagement.Application.Contracts", - "type": "Volo.Abp.TenantManagement.TenantUpdateDto", - "typeSimple": "Volo.Abp.TenantManagement.TenantUpdateDto", + "typeAsString": "Volo.Abp.Account.VerifyPasswordResetTokenInput, Volo.Abp.Account.Application.Contracts", + "type": "Volo.Abp.Account.VerifyPasswordResetTokenInput", + "typeSimple": "Volo.Abp.Account.VerifyPasswordResetTokenInput", "isOptional": false, "defaultValue": null } ], "parameters": [ - { - "nameOnMethod": "id", - "name": "id", - "jsonName": null, - "type": "System.Guid", - "typeSimple": "string", - "isOptional": false, - "defaultValue": null, - "constraintTypes": [], - "bindingSourceId": "Path", - "descriptorName": "" - }, { "nameOnMethod": "input", "name": "input", "jsonName": null, - "type": "Volo.Abp.TenantManagement.TenantUpdateDto", - "typeSimple": "Volo.Abp.TenantManagement.TenantUpdateDto", + "type": "Volo.Abp.Account.VerifyPasswordResetTokenInput", + "typeSimple": "Volo.Abp.Account.VerifyPasswordResetTokenInput", "isOptional": false, "defaultValue": null, "constraintTypes": null, @@ -367,39 +532,39 @@ } ], "returnValue": { - "type": "Volo.Abp.TenantManagement.TenantDto", - "typeSimple": "Volo.Abp.TenantManagement.TenantDto" + "type": "System.Boolean", + "typeSimple": "boolean" }, "allowAnonymous": null, - "implementFrom": "Volo.Abp.Application.Services.IUpdateAppService" + "implementFrom": "Volo.Abp.Account.IAccountAppService" }, - "DeleteAsyncById": { - "uniqueName": "DeleteAsyncById", - "name": "DeleteAsync", - "httpMethod": "DELETE", - "url": "api/multi-tenancy/tenants/{id}", + "ResetPasswordAsyncByInput": { + "uniqueName": "ResetPasswordAsyncByInput", + "name": "ResetPasswordAsync", + "httpMethod": "POST", + "url": "api/account/reset-password", "supportedVersions": [], "parametersOnMethod": [ { - "name": "id", - "typeAsString": "System.Guid, System.Private.CoreLib", - "type": "System.Guid", - "typeSimple": "string", + "name": "input", + "typeAsString": "Volo.Abp.Account.ResetPasswordDto, Volo.Abp.Account.Application.Contracts", + "type": "Volo.Abp.Account.ResetPasswordDto", + "typeSimple": "Volo.Abp.Account.ResetPasswordDto", "isOptional": false, "defaultValue": null } ], "parameters": [ { - "nameOnMethod": "id", - "name": "id", + "nameOnMethod": "input", + "name": "input", "jsonName": null, - "type": "System.Guid", - "typeSimple": "string", + "type": "Volo.Abp.Account.ResetPasswordDto", + "typeSimple": "Volo.Abp.Account.ResetPasswordDto", "isOptional": false, "defaultValue": null, - "constraintTypes": [], - "bindingSourceId": "Path", + "constraintTypes": null, + "bindingSourceId": "Body", "descriptorName": "" } ], @@ -408,92 +573,188 @@ "typeSimple": "System.Void" }, "allowAnonymous": null, - "implementFrom": "Volo.Abp.Application.Services.IDeleteAppService" - }, - "GetDefaultConnectionStringAsyncById": { - "uniqueName": "GetDefaultConnectionStringAsyncById", - "name": "GetDefaultConnectionStringAsync", + "implementFrom": "Volo.Abp.Account.IAccountAppService" + } + } + }, + "Volo.Abp.Account.DynamicClaimsController": { + "controllerName": "DynamicClaims", + "controllerGroupName": "DynamicClaims", + "isRemoteService": true, + "isIntegrationService": false, + "apiVersion": null, + "type": "Volo.Abp.Account.DynamicClaimsController", + "interfaces": [ + { + "type": "Volo.Abp.Account.IDynamicClaimsAppService", + "name": "IDynamicClaimsAppService", + "methods": [ + { + "name": "RefreshAsync", + "parametersOnMethod": [], + "returnValue": { + "type": "System.Void", + "typeSimple": "System.Void" + } + } + ] + } + ], + "actions": { + "RefreshAsync": { + "uniqueName": "RefreshAsync", + "name": "RefreshAsync", + "httpMethod": "POST", + "url": "api/account/dynamic-claims/refresh", + "supportedVersions": [], + "parametersOnMethod": [], + "parameters": [], + "returnValue": { + "type": "System.Void", + "typeSimple": "System.Void" + }, + "allowAnonymous": null, + "implementFrom": "Volo.Abp.Account.IDynamicClaimsAppService" + } + } + }, + "Volo.Abp.Account.ProfileController": { + "controllerName": "Profile", + "controllerGroupName": "Profile", + "isRemoteService": true, + "isIntegrationService": false, + "apiVersion": null, + "type": "Volo.Abp.Account.ProfileController", + "interfaces": [ + { + "type": "Volo.Abp.Account.IProfileAppService", + "name": "IProfileAppService", + "methods": [ + { + "name": "GetAsync", + "parametersOnMethod": [], + "returnValue": { + "type": "Volo.Abp.Account.ProfileDto", + "typeSimple": "Volo.Abp.Account.ProfileDto" + } + }, + { + "name": "UpdateAsync", + "parametersOnMethod": [ + { + "name": "input", + "typeAsString": "Volo.Abp.Account.UpdateProfileDto, Volo.Abp.Account.Application.Contracts", + "type": "Volo.Abp.Account.UpdateProfileDto", + "typeSimple": "Volo.Abp.Account.UpdateProfileDto", + "isOptional": false, + "defaultValue": null + } + ], + "returnValue": { + "type": "Volo.Abp.Account.ProfileDto", + "typeSimple": "Volo.Abp.Account.ProfileDto" + } + }, + { + "name": "ChangePasswordAsync", + "parametersOnMethod": [ + { + "name": "input", + "typeAsString": "Volo.Abp.Account.ChangePasswordInput, Volo.Abp.Account.Application.Contracts", + "type": "Volo.Abp.Account.ChangePasswordInput", + "typeSimple": "Volo.Abp.Account.ChangePasswordInput", + "isOptional": false, + "defaultValue": null + } + ], + "returnValue": { + "type": "System.Void", + "typeSimple": "System.Void" + } + } + ] + } + ], + "actions": { + "GetAsync": { + "uniqueName": "GetAsync", + "name": "GetAsync", "httpMethod": "GET", - "url": "api/multi-tenancy/tenants/{id}/default-connection-string", + "url": "api/account/my-profile", + "supportedVersions": [], + "parametersOnMethod": [], + "parameters": [], + "returnValue": { + "type": "Volo.Abp.Account.ProfileDto", + "typeSimple": "Volo.Abp.Account.ProfileDto" + }, + "allowAnonymous": null, + "implementFrom": "Volo.Abp.Account.IProfileAppService" + }, + "UpdateAsyncByInput": { + "uniqueName": "UpdateAsyncByInput", + "name": "UpdateAsync", + "httpMethod": "PUT", + "url": "api/account/my-profile", "supportedVersions": [], "parametersOnMethod": [ { - "name": "id", - "typeAsString": "System.Guid, System.Private.CoreLib", - "type": "System.Guid", - "typeSimple": "string", + "name": "input", + "typeAsString": "Volo.Abp.Account.UpdateProfileDto, Volo.Abp.Account.Application.Contracts", + "type": "Volo.Abp.Account.UpdateProfileDto", + "typeSimple": "Volo.Abp.Account.UpdateProfileDto", "isOptional": false, "defaultValue": null } ], "parameters": [ { - "nameOnMethod": "id", - "name": "id", + "nameOnMethod": "input", + "name": "input", "jsonName": null, - "type": "System.Guid", - "typeSimple": "string", + "type": "Volo.Abp.Account.UpdateProfileDto", + "typeSimple": "Volo.Abp.Account.UpdateProfileDto", "isOptional": false, "defaultValue": null, - "constraintTypes": [], - "bindingSourceId": "Path", + "constraintTypes": null, + "bindingSourceId": "Body", "descriptorName": "" } ], "returnValue": { - "type": "System.String", - "typeSimple": "string" + "type": "Volo.Abp.Account.ProfileDto", + "typeSimple": "Volo.Abp.Account.ProfileDto" }, "allowAnonymous": null, - "implementFrom": "Volo.Abp.TenantManagement.ITenantAppService" + "implementFrom": "Volo.Abp.Account.IProfileAppService" }, - "UpdateDefaultConnectionStringAsyncByIdAndDefaultConnectionString": { - "uniqueName": "UpdateDefaultConnectionStringAsyncByIdAndDefaultConnectionString", - "name": "UpdateDefaultConnectionStringAsync", - "httpMethod": "PUT", - "url": "api/multi-tenancy/tenants/{id}/default-connection-string", + "ChangePasswordAsyncByInput": { + "uniqueName": "ChangePasswordAsyncByInput", + "name": "ChangePasswordAsync", + "httpMethod": "POST", + "url": "api/account/my-profile/change-password", "supportedVersions": [], "parametersOnMethod": [ { - "name": "id", - "typeAsString": "System.Guid, System.Private.CoreLib", - "type": "System.Guid", - "typeSimple": "string", - "isOptional": false, - "defaultValue": null - }, - { - "name": "defaultConnectionString", - "typeAsString": "System.String, System.Private.CoreLib", - "type": "System.String", - "typeSimple": "string", + "name": "input", + "typeAsString": "Volo.Abp.Account.ChangePasswordInput, Volo.Abp.Account.Application.Contracts", + "type": "Volo.Abp.Account.ChangePasswordInput", + "typeSimple": "Volo.Abp.Account.ChangePasswordInput", "isOptional": false, "defaultValue": null } ], "parameters": [ { - "nameOnMethod": "id", - "name": "id", - "jsonName": null, - "type": "System.Guid", - "typeSimple": "string", - "isOptional": false, - "defaultValue": null, - "constraintTypes": [], - "bindingSourceId": "Path", - "descriptorName": "" - }, - { - "nameOnMethod": "defaultConnectionString", - "name": "defaultConnectionString", + "nameOnMethod": "input", + "name": "input", "jsonName": null, - "type": "System.String", - "typeSimple": "string", + "type": "Volo.Abp.Account.ChangePasswordInput", + "typeSimple": "Volo.Abp.Account.ChangePasswordInput", "isOptional": false, "defaultValue": null, "constraintTypes": null, - "bindingSourceId": "ModelBinding", + "bindingSourceId": "Body", "descriptorName": "" } ], @@ -502,179 +763,16 @@ "typeSimple": "System.Void" }, "allowAnonymous": null, - "implementFrom": "Volo.Abp.TenantManagement.ITenantAppService" - }, - "DeleteDefaultConnectionStringAsyncById": { - "uniqueName": "DeleteDefaultConnectionStringAsyncById", - "name": "DeleteDefaultConnectionStringAsync", - "httpMethod": "DELETE", - "url": "api/multi-tenancy/tenants/{id}/default-connection-string", - "supportedVersions": [], - "parametersOnMethod": [ - { - "name": "id", - "typeAsString": "System.Guid, System.Private.CoreLib", - "type": "System.Guid", - "typeSimple": "string", - "isOptional": false, - "defaultValue": null - } - ], - "parameters": [ - { - "nameOnMethod": "id", - "name": "id", - "jsonName": null, - "type": "System.Guid", - "typeSimple": "string", - "isOptional": false, - "defaultValue": null, - "constraintTypes": [], - "bindingSourceId": "Path", - "descriptorName": "" - } - ], - "returnValue": { - "type": "System.Void", - "typeSimple": "System.Void" - }, - "allowAnonymous": null, - "implementFrom": "Volo.Abp.TenantManagement.ITenantAppService" - } - } - } - } - }, - "account": { - "rootPath": "account", - "remoteServiceName": "AbpAccount", - "controllers": { - "Volo.Abp.Account.AccountController": { - "controllerName": "Account", - "controllerGroupName": "Account", - "type": "Volo.Abp.Account.AccountController", - "interfaces": [ - { - "type": "Volo.Abp.Account.IAccountAppService" - } - ], - "actions": { - "RegisterAsyncByInput": { - "uniqueName": "RegisterAsyncByInput", - "name": "RegisterAsync", - "httpMethod": "POST", - "url": "api/account/register", - "supportedVersions": [], - "parametersOnMethod": [ - { - "name": "input", - "typeAsString": "Volo.Abp.Account.RegisterDto, Volo.Abp.Account.Application.Contracts", - "type": "Volo.Abp.Account.RegisterDto", - "typeSimple": "Volo.Abp.Account.RegisterDto", - "isOptional": false, - "defaultValue": null - } - ], - "parameters": [ - { - "nameOnMethod": "input", - "name": "input", - "jsonName": null, - "type": "Volo.Abp.Account.RegisterDto", - "typeSimple": "Volo.Abp.Account.RegisterDto", - "isOptional": false, - "defaultValue": null, - "constraintTypes": null, - "bindingSourceId": "Body", - "descriptorName": "" - } - ], - "returnValue": { - "type": "Volo.Abp.Identity.IdentityUserDto", - "typeSimple": "Volo.Abp.Identity.IdentityUserDto" - }, - "allowAnonymous": null, - "implementFrom": "Volo.Abp.Account.IAccountAppService" - }, - "SendPasswordResetCodeAsyncByInput": { - "uniqueName": "SendPasswordResetCodeAsyncByInput", - "name": "SendPasswordResetCodeAsync", - "httpMethod": "POST", - "url": "api/account/send-password-reset-code", - "supportedVersions": [], - "parametersOnMethod": [ - { - "name": "input", - "typeAsString": "Volo.Abp.Account.SendPasswordResetCodeDto, Volo.Abp.Account.Application.Contracts", - "type": "Volo.Abp.Account.SendPasswordResetCodeDto", - "typeSimple": "Volo.Abp.Account.SendPasswordResetCodeDto", - "isOptional": false, - "defaultValue": null - } - ], - "parameters": [ - { - "nameOnMethod": "input", - "name": "input", - "jsonName": null, - "type": "Volo.Abp.Account.SendPasswordResetCodeDto", - "typeSimple": "Volo.Abp.Account.SendPasswordResetCodeDto", - "isOptional": false, - "defaultValue": null, - "constraintTypes": null, - "bindingSourceId": "Body", - "descriptorName": "" - } - ], - "returnValue": { - "type": "System.Void", - "typeSimple": "System.Void" - }, - "allowAnonymous": null, - "implementFrom": "Volo.Abp.Account.IAccountAppService" - }, - "ResetPasswordAsyncByInput": { - "uniqueName": "ResetPasswordAsyncByInput", - "name": "ResetPasswordAsync", - "httpMethod": "POST", - "url": "api/account/reset-password", - "supportedVersions": [], - "parametersOnMethod": [ - { - "name": "input", - "typeAsString": "Volo.Abp.Account.ResetPasswordDto, Volo.Abp.Account.Application.Contracts", - "type": "Volo.Abp.Account.ResetPasswordDto", - "typeSimple": "Volo.Abp.Account.ResetPasswordDto", - "isOptional": false, - "defaultValue": null - } - ], - "parameters": [ - { - "nameOnMethod": "input", - "name": "input", - "jsonName": null, - "type": "Volo.Abp.Account.ResetPasswordDto", - "typeSimple": "Volo.Abp.Account.ResetPasswordDto", - "isOptional": false, - "defaultValue": null, - "constraintTypes": null, - "bindingSourceId": "Body", - "descriptorName": "" - } - ], - "returnValue": { - "type": "System.Void", - "typeSimple": "System.Void" - }, - "allowAnonymous": null, - "implementFrom": "Volo.Abp.Account.IAccountAppService" + "implementFrom": "Volo.Abp.Account.IProfileAppService" } } }, "Volo.Abp.Account.Web.Areas.Account.Controllers.AccountController": { "controllerName": "Account", "controllerGroupName": "Login", + "isRemoteService": true, + "isIntegrationService": false, + "apiVersion": null, "type": "Volo.Abp.Account.Web.Areas.Account.Controllers.AccountController", "interfaces": [], "actions": { @@ -771,87 +869,106 @@ } } }, - "settingManagement": { - "rootPath": "settingManagement", - "remoteServiceName": "SettingManagement", + "featureManagement": { + "rootPath": "featureManagement", + "remoteServiceName": "AbpFeatureManagement", "controllers": { - "Volo.Abp.SettingManagement.EmailSettingsController": { - "controllerName": "EmailSettings", - "controllerGroupName": "EmailSettings", - "type": "Volo.Abp.SettingManagement.EmailSettingsController", + "Volo.Abp.FeatureManagement.FeaturesController": { + "controllerName": "Features", + "controllerGroupName": "Features", + "isRemoteService": true, + "isIntegrationService": false, + "apiVersion": null, + "type": "Volo.Abp.FeatureManagement.FeaturesController", "interfaces": [ { - "type": "Volo.Abp.SettingManagement.IEmailSettingsAppService" - } - ], - "actions": { - "GetAsync": { - "uniqueName": "GetAsync", - "name": "GetAsync", - "httpMethod": "GET", - "url": "api/setting-management/emailing", - "supportedVersions": [], - "parametersOnMethod": [], - "parameters": [], - "returnValue": { - "type": "Volo.Abp.SettingManagement.EmailSettingsDto", - "typeSimple": "Volo.Abp.SettingManagement.EmailSettingsDto" - }, - "allowAnonymous": null, - "implementFrom": "Volo.Abp.SettingManagement.IEmailSettingsAppService" - }, - "UpdateAsyncByInput": { - "uniqueName": "UpdateAsyncByInput", - "name": "UpdateAsync", - "httpMethod": "POST", - "url": "api/setting-management/emailing", - "supportedVersions": [], - "parametersOnMethod": [ + "type": "Volo.Abp.FeatureManagement.IFeatureAppService", + "name": "IFeatureAppService", + "methods": [ { - "name": "input", - "typeAsString": "Volo.Abp.SettingManagement.UpdateEmailSettingsDto, Volo.Abp.SettingManagement.Application.Contracts", - "type": "Volo.Abp.SettingManagement.UpdateEmailSettingsDto", - "typeSimple": "Volo.Abp.SettingManagement.UpdateEmailSettingsDto", - "isOptional": false, - "defaultValue": null - } - ], - "parameters": [ + "name": "GetAsync", + "parametersOnMethod": [ + { + "name": "providerName", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "providerKey", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + } + ], + "returnValue": { + "type": "Volo.Abp.FeatureManagement.GetFeatureListResultDto", + "typeSimple": "Volo.Abp.FeatureManagement.GetFeatureListResultDto" + } + }, { - "nameOnMethod": "input", - "name": "input", - "jsonName": null, - "type": "Volo.Abp.SettingManagement.UpdateEmailSettingsDto", - "typeSimple": "Volo.Abp.SettingManagement.UpdateEmailSettingsDto", - "isOptional": false, - "defaultValue": null, - "constraintTypes": null, - "bindingSourceId": "Body", - "descriptorName": "" + "name": "UpdateAsync", + "parametersOnMethod": [ + { + "name": "providerName", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "providerKey", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "input", + "typeAsString": "Volo.Abp.FeatureManagement.UpdateFeaturesDto, Volo.Abp.FeatureManagement.Application.Contracts", + "type": "Volo.Abp.FeatureManagement.UpdateFeaturesDto", + "typeSimple": "Volo.Abp.FeatureManagement.UpdateFeaturesDto", + "isOptional": false, + "defaultValue": null + } + ], + "returnValue": { + "type": "System.Void", + "typeSimple": "System.Void" + } + }, + { + "name": "DeleteAsync", + "parametersOnMethod": [ + { + "name": "providerName", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "providerKey", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + } + ], + "returnValue": { + "type": "System.Void", + "typeSimple": "System.Void" + } } - ], - "returnValue": { - "type": "System.Void", - "typeSimple": "System.Void" - }, - "allowAnonymous": null, - "implementFrom": "Volo.Abp.SettingManagement.IEmailSettingsAppService" - } - } - } - } - }, - "permissionManagement": { - "rootPath": "permissionManagement", - "remoteServiceName": "AbpPermissionManagement", - "controllers": { - "Volo.Abp.PermissionManagement.PermissionsController": { - "controllerName": "Permissions", - "controllerGroupName": "Permissions", - "type": "Volo.Abp.PermissionManagement.PermissionsController", - "interfaces": [ - { - "type": "Volo.Abp.PermissionManagement.IPermissionAppService" + ] } ], "actions": { @@ -859,7 +976,7 @@ "uniqueName": "GetAsyncByProviderNameAndProviderKey", "name": "GetAsync", "httpMethod": "GET", - "url": "api/permission-management/permissions", + "url": "api/feature-management/features", "supportedVersions": [], "parametersOnMethod": [ { @@ -906,17 +1023,17 @@ } ], "returnValue": { - "type": "Volo.Abp.PermissionManagement.GetPermissionListResultDto", - "typeSimple": "Volo.Abp.PermissionManagement.GetPermissionListResultDto" + "type": "Volo.Abp.FeatureManagement.GetFeatureListResultDto", + "typeSimple": "Volo.Abp.FeatureManagement.GetFeatureListResultDto" }, "allowAnonymous": null, - "implementFrom": "Volo.Abp.PermissionManagement.IPermissionAppService" + "implementFrom": "Volo.Abp.FeatureManagement.IFeatureAppService" }, "UpdateAsyncByProviderNameAndProviderKeyAndInput": { "uniqueName": "UpdateAsyncByProviderNameAndProviderKeyAndInput", "name": "UpdateAsync", "httpMethod": "PUT", - "url": "api/permission-management/permissions", + "url": "api/feature-management/features", "supportedVersions": [], "parametersOnMethod": [ { @@ -937,9 +1054,9 @@ }, { "name": "input", - "typeAsString": "Volo.Abp.PermissionManagement.UpdatePermissionsDto, Volo.Abp.PermissionManagement.Application.Contracts", - "type": "Volo.Abp.PermissionManagement.UpdatePermissionsDto", - "typeSimple": "Volo.Abp.PermissionManagement.UpdatePermissionsDto", + "typeAsString": "Volo.Abp.FeatureManagement.UpdateFeaturesDto, Volo.Abp.FeatureManagement.Application.Contracts", + "type": "Volo.Abp.FeatureManagement.UpdateFeaturesDto", + "typeSimple": "Volo.Abp.FeatureManagement.UpdateFeaturesDto", "isOptional": false, "defaultValue": null } @@ -973,8 +1090,8 @@ "nameOnMethod": "input", "name": "input", "jsonName": null, - "type": "Volo.Abp.PermissionManagement.UpdatePermissionsDto", - "typeSimple": "Volo.Abp.PermissionManagement.UpdatePermissionsDto", + "type": "Volo.Abp.FeatureManagement.UpdateFeaturesDto", + "typeSimple": "Volo.Abp.FeatureManagement.UpdateFeaturesDto", "isOptional": false, "defaultValue": null, "constraintTypes": null, @@ -987,171 +1104,64 @@ "typeSimple": "System.Void" }, "allowAnonymous": null, - "implementFrom": "Volo.Abp.PermissionManagement.IPermissionAppService" - } - } - } - } - }, - "abp": { - "rootPath": "abp", - "remoteServiceName": "abp", - "controllers": { - "Pages.Abp.MultiTenancy.AbpTenantController": { - "controllerName": "AbpTenant", - "controllerGroupName": "AbpTenant", - "type": "Pages.Abp.MultiTenancy.AbpTenantController", - "interfaces": [ - { - "type": "Volo.Abp.AspNetCore.Mvc.MultiTenancy.IAbpTenantAppService" - } - ], - "actions": { - "FindTenantByNameAsyncByName": { - "uniqueName": "FindTenantByNameAsyncByName", - "name": "FindTenantByNameAsync", - "httpMethod": "GET", - "url": "api/abp/multi-tenancy/tenants/by-name/{name}", + "implementFrom": "Volo.Abp.FeatureManagement.IFeatureAppService" + }, + "DeleteAsyncByProviderNameAndProviderKey": { + "uniqueName": "DeleteAsyncByProviderNameAndProviderKey", + "name": "DeleteAsync", + "httpMethod": "DELETE", + "url": "api/feature-management/features", "supportedVersions": [], "parametersOnMethod": [ { - "name": "name", + "name": "providerName", "typeAsString": "System.String, System.Private.CoreLib", "type": "System.String", "typeSimple": "string", "isOptional": false, "defaultValue": null - } - ], - "parameters": [ + }, { - "nameOnMethod": "name", - "name": "name", - "jsonName": null, + "name": "providerKey", + "typeAsString": "System.String, System.Private.CoreLib", "type": "System.String", "typeSimple": "string", "isOptional": false, - "defaultValue": null, - "constraintTypes": [], - "bindingSourceId": "Path", - "descriptorName": "" - } - ], - "returnValue": { - "type": "Volo.Abp.AspNetCore.Mvc.MultiTenancy.FindTenantResultDto", - "typeSimple": "Volo.Abp.AspNetCore.Mvc.MultiTenancy.FindTenantResultDto" - }, - "allowAnonymous": null, - "implementFrom": "Volo.Abp.AspNetCore.Mvc.MultiTenancy.IAbpTenantAppService" - }, - "FindTenantByIdAsyncById": { - "uniqueName": "FindTenantByIdAsyncById", - "name": "FindTenantByIdAsync", - "httpMethod": "GET", - "url": "api/abp/multi-tenancy/tenants/by-id/{id}", - "supportedVersions": [], - "parametersOnMethod": [ - { - "name": "id", - "typeAsString": "System.Guid, System.Private.CoreLib", - "type": "System.Guid", - "typeSimple": "string", - "isOptional": false, "defaultValue": null } ], "parameters": [ { - "nameOnMethod": "id", - "name": "id", + "nameOnMethod": "providerName", + "name": "providerName", "jsonName": null, - "type": "System.Guid", + "type": "System.String", "typeSimple": "string", "isOptional": false, "defaultValue": null, - "constraintTypes": [], - "bindingSourceId": "Path", + "constraintTypes": null, + "bindingSourceId": "ModelBinding", "descriptorName": "" - } - ], - "returnValue": { - "type": "Volo.Abp.AspNetCore.Mvc.MultiTenancy.FindTenantResultDto", - "typeSimple": "Volo.Abp.AspNetCore.Mvc.MultiTenancy.FindTenantResultDto" - }, - "allowAnonymous": null, - "implementFrom": "Volo.Abp.AspNetCore.Mvc.MultiTenancy.IAbpTenantAppService" - } - } - }, - "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.AbpApplicationConfigurationController": { - "controllerName": "AbpApplicationConfiguration", - "controllerGroupName": "AbpApplicationConfiguration", - "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.AbpApplicationConfigurationController", - "interfaces": [ - { - "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.IAbpApplicationConfigurationAppService" - } - ], - "actions": { - "GetAsync": { - "uniqueName": "GetAsync", - "name": "GetAsync", - "httpMethod": "GET", - "url": "api/abp/application-configuration", - "supportedVersions": [], - "parametersOnMethod": [], - "parameters": [], - "returnValue": { - "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationConfigurationDto", - "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationConfigurationDto" - }, - "allowAnonymous": null, - "implementFrom": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.IAbpApplicationConfigurationAppService" - } - } - }, - "Volo.Abp.AspNetCore.Mvc.ApiExploring.AbpApiDefinitionController": { - "controllerName": "AbpApiDefinition", - "controllerGroupName": "AbpApiDefinition", - "type": "Volo.Abp.AspNetCore.Mvc.ApiExploring.AbpApiDefinitionController", - "interfaces": [], - "actions": { - "GetByModel": { - "uniqueName": "GetByModel", - "name": "Get", - "httpMethod": "GET", - "url": "api/abp/api-definition", - "supportedVersions": [], - "parametersOnMethod": [ - { - "name": "model", - "typeAsString": "Volo.Abp.Http.Modeling.ApplicationApiDescriptionModelRequestDto, Volo.Abp.Http", - "type": "Volo.Abp.Http.Modeling.ApplicationApiDescriptionModelRequestDto", - "typeSimple": "Volo.Abp.Http.Modeling.ApplicationApiDescriptionModelRequestDto", - "isOptional": false, - "defaultValue": null - } - ], - "parameters": [ + }, { - "nameOnMethod": "model", - "name": "IncludeTypes", + "nameOnMethod": "providerKey", + "name": "providerKey", "jsonName": null, - "type": "System.Boolean", - "typeSimple": "boolean", + "type": "System.String", + "typeSimple": "string", "isOptional": false, "defaultValue": null, "constraintTypes": null, "bindingSourceId": "ModelBinding", - "descriptorName": "model" + "descriptorName": "" } ], "returnValue": { - "type": "Volo.Abp.Http.Modeling.ApplicationApiDescriptionModel", - "typeSimple": "Volo.Abp.Http.Modeling.ApplicationApiDescriptionModel" + "type": "System.Void", + "typeSimple": "System.Void" }, "allowAnonymous": null, - "implementFrom": "Volo.Abp.AspNetCore.Mvc.ApiExploring.AbpApiDefinitionController" + "implementFrom": "Volo.Abp.FeatureManagement.IFeatureAppService" } } } @@ -1164,10 +1174,117 @@ "Volo.Abp.Identity.IdentityRoleController": { "controllerName": "IdentityRole", "controllerGroupName": "Role", + "isRemoteService": true, + "isIntegrationService": false, + "apiVersion": null, "type": "Volo.Abp.Identity.IdentityRoleController", "interfaces": [ { - "type": "Volo.Abp.Identity.IIdentityRoleAppService" + "type": "Volo.Abp.Identity.IIdentityRoleAppService", + "name": "IIdentityRoleAppService", + "methods": [ + { + "name": "GetAllListAsync", + "parametersOnMethod": [], + "returnValue": { + "type": "Volo.Abp.Application.Dtos.ListResultDto", + "typeSimple": "Volo.Abp.Application.Dtos.ListResultDto" + } + }, + { + "name": "GetAsync", + "parametersOnMethod": [ + { + "name": "id", + "typeAsString": "System.Guid, System.Private.CoreLib", + "type": "System.Guid", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + } + ], + "returnValue": { + "type": "Volo.Abp.Identity.IdentityRoleDto", + "typeSimple": "Volo.Abp.Identity.IdentityRoleDto" + } + }, + { + "name": "GetListAsync", + "parametersOnMethod": [ + { + "name": "input", + "typeAsString": "Volo.Abp.Identity.GetIdentityRolesInput, Volo.Abp.Identity.Application.Contracts", + "type": "Volo.Abp.Identity.GetIdentityRolesInput", + "typeSimple": "Volo.Abp.Identity.GetIdentityRolesInput", + "isOptional": false, + "defaultValue": null + } + ], + "returnValue": { + "type": "Volo.Abp.Application.Dtos.PagedResultDto", + "typeSimple": "Volo.Abp.Application.Dtos.PagedResultDto" + } + }, + { + "name": "CreateAsync", + "parametersOnMethod": [ + { + "name": "input", + "typeAsString": "Volo.Abp.Identity.IdentityRoleCreateDto, Volo.Abp.Identity.Application.Contracts", + "type": "Volo.Abp.Identity.IdentityRoleCreateDto", + "typeSimple": "Volo.Abp.Identity.IdentityRoleCreateDto", + "isOptional": false, + "defaultValue": null + } + ], + "returnValue": { + "type": "Volo.Abp.Identity.IdentityRoleDto", + "typeSimple": "Volo.Abp.Identity.IdentityRoleDto" + } + }, + { + "name": "UpdateAsync", + "parametersOnMethod": [ + { + "name": "id", + "typeAsString": "System.Guid, System.Private.CoreLib", + "type": "System.Guid", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "input", + "typeAsString": "Volo.Abp.Identity.IdentityRoleUpdateDto, Volo.Abp.Identity.Application.Contracts", + "type": "Volo.Abp.Identity.IdentityRoleUpdateDto", + "typeSimple": "Volo.Abp.Identity.IdentityRoleUpdateDto", + "isOptional": false, + "defaultValue": null + } + ], + "returnValue": { + "type": "Volo.Abp.Identity.IdentityRoleDto", + "typeSimple": "Volo.Abp.Identity.IdentityRoleDto" + } + }, + { + "name": "DeleteAsync", + "parametersOnMethod": [ + { + "name": "id", + "typeAsString": "System.Guid, System.Private.CoreLib", + "type": "System.Guid", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + } + ], + "returnValue": { + "type": "System.Void", + "typeSimple": "System.Void" + } + } + ] } ], "actions": { @@ -1250,6 +1367,18 @@ "constraintTypes": null, "bindingSourceId": "ModelBinding", "descriptorName": "input" + }, + { + "nameOnMethod": "input", + "name": "ExtraProperties", + "jsonName": null, + "type": "Volo.Abp.Data.ExtraPropertyDictionary", + "typeSimple": "{string:object}", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "input" } ], "returnValue": { @@ -1432,10 +1561,193 @@ "Volo.Abp.Identity.IdentityUserController": { "controllerName": "IdentityUser", "controllerGroupName": "User", + "isRemoteService": true, + "isIntegrationService": false, + "apiVersion": null, "type": "Volo.Abp.Identity.IdentityUserController", "interfaces": [ { - "type": "Volo.Abp.Identity.IIdentityUserAppService" + "type": "Volo.Abp.Identity.IIdentityUserAppService", + "name": "IIdentityUserAppService", + "methods": [ + { + "name": "GetRolesAsync", + "parametersOnMethod": [ + { + "name": "id", + "typeAsString": "System.Guid, System.Private.CoreLib", + "type": "System.Guid", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + } + ], + "returnValue": { + "type": "Volo.Abp.Application.Dtos.ListResultDto", + "typeSimple": "Volo.Abp.Application.Dtos.ListResultDto" + } + }, + { + "name": "GetAssignableRolesAsync", + "parametersOnMethod": [], + "returnValue": { + "type": "Volo.Abp.Application.Dtos.ListResultDto", + "typeSimple": "Volo.Abp.Application.Dtos.ListResultDto" + } + }, + { + "name": "UpdateRolesAsync", + "parametersOnMethod": [ + { + "name": "id", + "typeAsString": "System.Guid, System.Private.CoreLib", + "type": "System.Guid", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "input", + "typeAsString": "Volo.Abp.Identity.IdentityUserUpdateRolesDto, Volo.Abp.Identity.Application.Contracts", + "type": "Volo.Abp.Identity.IdentityUserUpdateRolesDto", + "typeSimple": "Volo.Abp.Identity.IdentityUserUpdateRolesDto", + "isOptional": false, + "defaultValue": null + } + ], + "returnValue": { + "type": "System.Void", + "typeSimple": "System.Void" + } + }, + { + "name": "FindByUsernameAsync", + "parametersOnMethod": [ + { + "name": "userName", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + } + ], + "returnValue": { + "type": "Volo.Abp.Identity.IdentityUserDto", + "typeSimple": "Volo.Abp.Identity.IdentityUserDto" + } + }, + { + "name": "FindByEmailAsync", + "parametersOnMethod": [ + { + "name": "email", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + } + ], + "returnValue": { + "type": "Volo.Abp.Identity.IdentityUserDto", + "typeSimple": "Volo.Abp.Identity.IdentityUserDto" + } + }, + { + "name": "GetAsync", + "parametersOnMethod": [ + { + "name": "id", + "typeAsString": "System.Guid, System.Private.CoreLib", + "type": "System.Guid", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + } + ], + "returnValue": { + "type": "Volo.Abp.Identity.IdentityUserDto", + "typeSimple": "Volo.Abp.Identity.IdentityUserDto" + } + }, + { + "name": "GetListAsync", + "parametersOnMethod": [ + { + "name": "input", + "typeAsString": "Volo.Abp.Identity.GetIdentityUsersInput, Volo.Abp.Identity.Application.Contracts", + "type": "Volo.Abp.Identity.GetIdentityUsersInput", + "typeSimple": "Volo.Abp.Identity.GetIdentityUsersInput", + "isOptional": false, + "defaultValue": null + } + ], + "returnValue": { + "type": "Volo.Abp.Application.Dtos.PagedResultDto", + "typeSimple": "Volo.Abp.Application.Dtos.PagedResultDto" + } + }, + { + "name": "CreateAsync", + "parametersOnMethod": [ + { + "name": "input", + "typeAsString": "Volo.Abp.Identity.IdentityUserCreateDto, Volo.Abp.Identity.Application.Contracts", + "type": "Volo.Abp.Identity.IdentityUserCreateDto", + "typeSimple": "Volo.Abp.Identity.IdentityUserCreateDto", + "isOptional": false, + "defaultValue": null + } + ], + "returnValue": { + "type": "Volo.Abp.Identity.IdentityUserDto", + "typeSimple": "Volo.Abp.Identity.IdentityUserDto" + } + }, + { + "name": "UpdateAsync", + "parametersOnMethod": [ + { + "name": "id", + "typeAsString": "System.Guid, System.Private.CoreLib", + "type": "System.Guid", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "input", + "typeAsString": "Volo.Abp.Identity.IdentityUserUpdateDto, Volo.Abp.Identity.Application.Contracts", + "type": "Volo.Abp.Identity.IdentityUserUpdateDto", + "typeSimple": "Volo.Abp.Identity.IdentityUserUpdateDto", + "isOptional": false, + "defaultValue": null + } + ], + "returnValue": { + "type": "Volo.Abp.Identity.IdentityUserDto", + "typeSimple": "Volo.Abp.Identity.IdentityUserDto" + } + }, + { + "name": "DeleteAsync", + "parametersOnMethod": [ + { + "name": "id", + "typeAsString": "System.Guid, System.Private.CoreLib", + "type": "System.Guid", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + } + ], + "returnValue": { + "type": "System.Void", + "typeSimple": "System.Void" + } + } + ] } ], "actions": { @@ -1540,6 +1852,18 @@ "constraintTypes": null, "bindingSourceId": "ModelBinding", "descriptorName": "input" + }, + { + "nameOnMethod": "input", + "name": "ExtraProperties", + "jsonName": null, + "type": "Volo.Abp.Data.ExtraPropertyDictionary", + "typeSimple": "{string:object}", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "input" } ], "returnValue": { @@ -1868,10 +2192,84 @@ "Volo.Abp.Identity.IdentityUserLookupController": { "controllerName": "IdentityUserLookup", "controllerGroupName": "UserLookup", + "isRemoteService": true, + "isIntegrationService": false, + "apiVersion": null, "type": "Volo.Abp.Identity.IdentityUserLookupController", "interfaces": [ { - "type": "Volo.Abp.Identity.IIdentityUserLookupAppService" + "type": "Volo.Abp.Identity.IIdentityUserLookupAppService", + "name": "IIdentityUserLookupAppService", + "methods": [ + { + "name": "FindByIdAsync", + "parametersOnMethod": [ + { + "name": "id", + "typeAsString": "System.Guid, System.Private.CoreLib", + "type": "System.Guid", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + } + ], + "returnValue": { + "type": "Volo.Abp.Users.UserData", + "typeSimple": "Volo.Abp.Users.UserData" + } + }, + { + "name": "FindByUserNameAsync", + "parametersOnMethod": [ + { + "name": "userName", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + } + ], + "returnValue": { + "type": "Volo.Abp.Users.UserData", + "typeSimple": "Volo.Abp.Users.UserData" + } + }, + { + "name": "SearchAsync", + "parametersOnMethod": [ + { + "name": "input", + "typeAsString": "Volo.Abp.Identity.UserLookupSearchInputDto, Volo.Abp.Identity.Application.Contracts", + "type": "Volo.Abp.Identity.UserLookupSearchInputDto", + "typeSimple": "Volo.Abp.Identity.UserLookupSearchInputDto", + "isOptional": false, + "defaultValue": null + } + ], + "returnValue": { + "type": "Volo.Abp.Application.Dtos.ListResultDto", + "typeSimple": "Volo.Abp.Application.Dtos.ListResultDto" + } + }, + { + "name": "GetCountAsync", + "parametersOnMethod": [ + { + "name": "input", + "typeAsString": "Volo.Abp.Identity.UserLookupCountInputDto, Volo.Abp.Identity.Application.Contracts", + "type": "Volo.Abp.Identity.UserLookupCountInputDto", + "typeSimple": "Volo.Abp.Identity.UserLookupCountInputDto", + "isOptional": false, + "defaultValue": null + } + ], + "returnValue": { + "type": "System.Int64", + "typeSimple": "number" + } + } + ] } ], "actions": { @@ -2013,6 +2411,18 @@ "constraintTypes": null, "bindingSourceId": "ModelBinding", "descriptorName": "input" + }, + { + "nameOnMethod": "input", + "name": "ExtraProperties", + "jsonName": null, + "type": "Volo.Abp.Data.ExtraPropertyDictionary", + "typeSimple": "{string:object}", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "input" } ], "returnValue": { @@ -2060,44 +2470,303 @@ "implementFrom": "Volo.Abp.Identity.IIdentityUserLookupAppService" } } - }, - "Volo.Abp.Identity.ProfileController": { - "controllerName": "Profile", - "controllerGroupName": "Profile", - "type": "Volo.Abp.Identity.ProfileController", + } + } + }, + "multi-tenancy": { + "rootPath": "multi-tenancy", + "remoteServiceName": "AbpTenantManagement", + "controllers": { + "Volo.Abp.TenantManagement.TenantController": { + "controllerName": "Tenant", + "controllerGroupName": "Tenant", + "isRemoteService": true, + "isIntegrationService": false, + "apiVersion": null, + "type": "Volo.Abp.TenantManagement.TenantController", "interfaces": [ { - "type": "Volo.Abp.Identity.IProfileAppService" - } - ], - "actions": { - "GetAsync": { - "uniqueName": "GetAsync", - "name": "GetAsync", - "httpMethod": "GET", - "url": "api/identity/my-profile", - "supportedVersions": [], - "parametersOnMethod": [], - "parameters": [], - "returnValue": { - "type": "Volo.Abp.Identity.ProfileDto", - "typeSimple": "Volo.Abp.Identity.ProfileDto" - }, + "type": "Volo.Abp.TenantManagement.ITenantAppService", + "name": "ITenantAppService", + "methods": [ + { + "name": "GetDefaultConnectionStringAsync", + "parametersOnMethod": [ + { + "name": "id", + "typeAsString": "System.Guid, System.Private.CoreLib", + "type": "System.Guid", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + } + ], + "returnValue": { + "type": "System.String", + "typeSimple": "string" + } + }, + { + "name": "UpdateDefaultConnectionStringAsync", + "parametersOnMethod": [ + { + "name": "id", + "typeAsString": "System.Guid, System.Private.CoreLib", + "type": "System.Guid", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "defaultConnectionString", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + } + ], + "returnValue": { + "type": "System.Void", + "typeSimple": "System.Void" + } + }, + { + "name": "DeleteDefaultConnectionStringAsync", + "parametersOnMethod": [ + { + "name": "id", + "typeAsString": "System.Guid, System.Private.CoreLib", + "type": "System.Guid", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + } + ], + "returnValue": { + "type": "System.Void", + "typeSimple": "System.Void" + } + }, + { + "name": "GetAsync", + "parametersOnMethod": [ + { + "name": "id", + "typeAsString": "System.Guid, System.Private.CoreLib", + "type": "System.Guid", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + } + ], + "returnValue": { + "type": "Volo.Abp.TenantManagement.TenantDto", + "typeSimple": "Volo.Abp.TenantManagement.TenantDto" + } + }, + { + "name": "GetListAsync", + "parametersOnMethod": [ + { + "name": "input", + "typeAsString": "Volo.Abp.TenantManagement.GetTenantsInput, Volo.Abp.TenantManagement.Application.Contracts", + "type": "Volo.Abp.TenantManagement.GetTenantsInput", + "typeSimple": "Volo.Abp.TenantManagement.GetTenantsInput", + "isOptional": false, + "defaultValue": null + } + ], + "returnValue": { + "type": "Volo.Abp.Application.Dtos.PagedResultDto", + "typeSimple": "Volo.Abp.Application.Dtos.PagedResultDto" + } + }, + { + "name": "CreateAsync", + "parametersOnMethod": [ + { + "name": "input", + "typeAsString": "Volo.Abp.TenantManagement.TenantCreateDto, Volo.Abp.TenantManagement.Application.Contracts", + "type": "Volo.Abp.TenantManagement.TenantCreateDto", + "typeSimple": "Volo.Abp.TenantManagement.TenantCreateDto", + "isOptional": false, + "defaultValue": null + } + ], + "returnValue": { + "type": "Volo.Abp.TenantManagement.TenantDto", + "typeSimple": "Volo.Abp.TenantManagement.TenantDto" + } + }, + { + "name": "UpdateAsync", + "parametersOnMethod": [ + { + "name": "id", + "typeAsString": "System.Guid, System.Private.CoreLib", + "type": "System.Guid", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "input", + "typeAsString": "Volo.Abp.TenantManagement.TenantUpdateDto, Volo.Abp.TenantManagement.Application.Contracts", + "type": "Volo.Abp.TenantManagement.TenantUpdateDto", + "typeSimple": "Volo.Abp.TenantManagement.TenantUpdateDto", + "isOptional": false, + "defaultValue": null + } + ], + "returnValue": { + "type": "Volo.Abp.TenantManagement.TenantDto", + "typeSimple": "Volo.Abp.TenantManagement.TenantDto" + } + }, + { + "name": "DeleteAsync", + "parametersOnMethod": [ + { + "name": "id", + "typeAsString": "System.Guid, System.Private.CoreLib", + "type": "System.Guid", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + } + ], + "returnValue": { + "type": "System.Void", + "typeSimple": "System.Void" + } + } + ] + } + ], + "actions": { + "GetAsyncById": { + "uniqueName": "GetAsyncById", + "name": "GetAsync", + "httpMethod": "GET", + "url": "api/multi-tenancy/tenants/{id}", + "supportedVersions": [], + "parametersOnMethod": [ + { + "name": "id", + "typeAsString": "System.Guid, System.Private.CoreLib", + "type": "System.Guid", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + } + ], + "parameters": [ + { + "nameOnMethod": "id", + "name": "id", + "jsonName": null, + "type": "System.Guid", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null, + "constraintTypes": [], + "bindingSourceId": "Path", + "descriptorName": "" + } + ], + "returnValue": { + "type": "Volo.Abp.TenantManagement.TenantDto", + "typeSimple": "Volo.Abp.TenantManagement.TenantDto" + }, "allowAnonymous": null, - "implementFrom": "Volo.Abp.Identity.IProfileAppService" + "implementFrom": "Volo.Abp.Application.Services.IReadOnlyAppService" }, - "UpdateAsyncByInput": { - "uniqueName": "UpdateAsyncByInput", - "name": "UpdateAsync", - "httpMethod": "PUT", - "url": "api/identity/my-profile", + "GetListAsyncByInput": { + "uniqueName": "GetListAsyncByInput", + "name": "GetListAsync", + "httpMethod": "GET", + "url": "api/multi-tenancy/tenants", + "supportedVersions": [], + "parametersOnMethod": [ + { + "name": "input", + "typeAsString": "Volo.Abp.TenantManagement.GetTenantsInput, Volo.Abp.TenantManagement.Application.Contracts", + "type": "Volo.Abp.TenantManagement.GetTenantsInput", + "typeSimple": "Volo.Abp.TenantManagement.GetTenantsInput", + "isOptional": false, + "defaultValue": null + } + ], + "parameters": [ + { + "nameOnMethod": "input", + "name": "Filter", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "input" + }, + { + "nameOnMethod": "input", + "name": "Sorting", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "input" + }, + { + "nameOnMethod": "input", + "name": "SkipCount", + "jsonName": null, + "type": "System.Int32", + "typeSimple": "number", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "input" + }, + { + "nameOnMethod": "input", + "name": "MaxResultCount", + "jsonName": null, + "type": "System.Int32", + "typeSimple": "number", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "input" + } + ], + "returnValue": { + "type": "Volo.Abp.Application.Dtos.PagedResultDto", + "typeSimple": "Volo.Abp.Application.Dtos.PagedResultDto" + }, + "allowAnonymous": null, + "implementFrom": "Volo.Abp.Application.Services.IReadOnlyAppService" + }, + "CreateAsyncByInput": { + "uniqueName": "CreateAsyncByInput", + "name": "CreateAsync", + "httpMethod": "POST", + "url": "api/multi-tenancy/tenants", "supportedVersions": [], "parametersOnMethod": [ { "name": "input", - "typeAsString": "Volo.Abp.Identity.UpdateProfileDto, Volo.Abp.Identity.Application.Contracts", - "type": "Volo.Abp.Identity.UpdateProfileDto", - "typeSimple": "Volo.Abp.Identity.UpdateProfileDto", + "typeAsString": "Volo.Abp.TenantManagement.TenantCreateDto, Volo.Abp.TenantManagement.Application.Contracts", + "type": "Volo.Abp.TenantManagement.TenantCreateDto", + "typeSimple": "Volo.Abp.TenantManagement.TenantCreateDto", "isOptional": false, "defaultValue": null } @@ -2107,8 +2776,8 @@ "nameOnMethod": "input", "name": "input", "jsonName": null, - "type": "Volo.Abp.Identity.UpdateProfileDto", - "typeSimple": "Volo.Abp.Identity.UpdateProfileDto", + "type": "Volo.Abp.TenantManagement.TenantCreateDto", + "typeSimple": "Volo.Abp.TenantManagement.TenantCreateDto", "isOptional": false, "defaultValue": null, "constraintTypes": null, @@ -2117,35 +2786,55 @@ } ], "returnValue": { - "type": "Volo.Abp.Identity.ProfileDto", - "typeSimple": "Volo.Abp.Identity.ProfileDto" + "type": "Volo.Abp.TenantManagement.TenantDto", + "typeSimple": "Volo.Abp.TenantManagement.TenantDto" }, "allowAnonymous": null, - "implementFrom": "Volo.Abp.Identity.IProfileAppService" + "implementFrom": "Volo.Abp.Application.Services.ICreateAppService" }, - "ChangePasswordAsyncByInput": { - "uniqueName": "ChangePasswordAsyncByInput", - "name": "ChangePasswordAsync", - "httpMethod": "POST", - "url": "api/identity/my-profile/change-password", + "UpdateAsyncByIdAndInput": { + "uniqueName": "UpdateAsyncByIdAndInput", + "name": "UpdateAsync", + "httpMethod": "PUT", + "url": "api/multi-tenancy/tenants/{id}", "supportedVersions": [], "parametersOnMethod": [ + { + "name": "id", + "typeAsString": "System.Guid, System.Private.CoreLib", + "type": "System.Guid", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, { "name": "input", - "typeAsString": "Volo.Abp.Identity.ChangePasswordInput, Volo.Abp.Identity.Application.Contracts", - "type": "Volo.Abp.Identity.ChangePasswordInput", - "typeSimple": "Volo.Abp.Identity.ChangePasswordInput", + "typeAsString": "Volo.Abp.TenantManagement.TenantUpdateDto, Volo.Abp.TenantManagement.Application.Contracts", + "type": "Volo.Abp.TenantManagement.TenantUpdateDto", + "typeSimple": "Volo.Abp.TenantManagement.TenantUpdateDto", "isOptional": false, "defaultValue": null } ], "parameters": [ + { + "nameOnMethod": "id", + "name": "id", + "jsonName": null, + "type": "System.Guid", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null, + "constraintTypes": [], + "bindingSourceId": "Path", + "descriptorName": "" + }, { "nameOnMethod": "input", "name": "input", "jsonName": null, - "type": "Volo.Abp.Identity.ChangePasswordInput", - "typeSimple": "Volo.Abp.Identity.ChangePasswordInput", + "type": "Volo.Abp.TenantManagement.TenantUpdateDto", + "typeSimple": "Volo.Abp.TenantManagement.TenantUpdateDto", "isOptional": false, "defaultValue": null, "constraintTypes": null, @@ -2153,259 +2842,2882 @@ "descriptorName": "" } ], + "returnValue": { + "type": "Volo.Abp.TenantManagement.TenantDto", + "typeSimple": "Volo.Abp.TenantManagement.TenantDto" + }, + "allowAnonymous": null, + "implementFrom": "Volo.Abp.Application.Services.IUpdateAppService" + }, + "DeleteAsyncById": { + "uniqueName": "DeleteAsyncById", + "name": "DeleteAsync", + "httpMethod": "DELETE", + "url": "api/multi-tenancy/tenants/{id}", + "supportedVersions": [], + "parametersOnMethod": [ + { + "name": "id", + "typeAsString": "System.Guid, System.Private.CoreLib", + "type": "System.Guid", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + } + ], + "parameters": [ + { + "nameOnMethod": "id", + "name": "id", + "jsonName": null, + "type": "System.Guid", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null, + "constraintTypes": [], + "bindingSourceId": "Path", + "descriptorName": "" + } + ], "returnValue": { "type": "System.Void", "typeSimple": "System.Void" }, "allowAnonymous": null, - "implementFrom": "Volo.Abp.Identity.IProfileAppService" - } - } + "implementFrom": "Volo.Abp.Application.Services.IDeleteAppService" + }, + "GetDefaultConnectionStringAsyncById": { + "uniqueName": "GetDefaultConnectionStringAsyncById", + "name": "GetDefaultConnectionStringAsync", + "httpMethod": "GET", + "url": "api/multi-tenancy/tenants/{id}/default-connection-string", + "supportedVersions": [], + "parametersOnMethod": [ + { + "name": "id", + "typeAsString": "System.Guid, System.Private.CoreLib", + "type": "System.Guid", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + } + ], + "parameters": [ + { + "nameOnMethod": "id", + "name": "id", + "jsonName": null, + "type": "System.Guid", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null, + "constraintTypes": [], + "bindingSourceId": "Path", + "descriptorName": "" + } + ], + "returnValue": { + "type": "System.String", + "typeSimple": "string" + }, + "allowAnonymous": null, + "implementFrom": "Volo.Abp.TenantManagement.ITenantAppService" + }, + "UpdateDefaultConnectionStringAsyncByIdAndDefaultConnectionString": { + "uniqueName": "UpdateDefaultConnectionStringAsyncByIdAndDefaultConnectionString", + "name": "UpdateDefaultConnectionStringAsync", + "httpMethod": "PUT", + "url": "api/multi-tenancy/tenants/{id}/default-connection-string", + "supportedVersions": [], + "parametersOnMethod": [ + { + "name": "id", + "typeAsString": "System.Guid, System.Private.CoreLib", + "type": "System.Guid", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "defaultConnectionString", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + } + ], + "parameters": [ + { + "nameOnMethod": "id", + "name": "id", + "jsonName": null, + "type": "System.Guid", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null, + "constraintTypes": [], + "bindingSourceId": "Path", + "descriptorName": "" + }, + { + "nameOnMethod": "defaultConnectionString", + "name": "defaultConnectionString", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "" + } + ], + "returnValue": { + "type": "System.Void", + "typeSimple": "System.Void" + }, + "allowAnonymous": null, + "implementFrom": "Volo.Abp.TenantManagement.ITenantAppService" + }, + "DeleteDefaultConnectionStringAsyncById": { + "uniqueName": "DeleteDefaultConnectionStringAsyncById", + "name": "DeleteDefaultConnectionStringAsync", + "httpMethod": "DELETE", + "url": "api/multi-tenancy/tenants/{id}/default-connection-string", + "supportedVersions": [], + "parametersOnMethod": [ + { + "name": "id", + "typeAsString": "System.Guid, System.Private.CoreLib", + "type": "System.Guid", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + } + ], + "parameters": [ + { + "nameOnMethod": "id", + "name": "id", + "jsonName": null, + "type": "System.Guid", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null, + "constraintTypes": [], + "bindingSourceId": "Path", + "descriptorName": "" + } + ], + "returnValue": { + "type": "System.Void", + "typeSimple": "System.Void" + }, + "allowAnonymous": null, + "implementFrom": "Volo.Abp.TenantManagement.ITenantAppService" + } + } + } + } + }, + "permissionManagement": { + "rootPath": "permissionManagement", + "remoteServiceName": "AbpPermissionManagement", + "controllers": { + "Volo.Abp.PermissionManagement.PermissionsController": { + "controllerName": "Permissions", + "controllerGroupName": "Permissions", + "isRemoteService": true, + "isIntegrationService": false, + "apiVersion": null, + "type": "Volo.Abp.PermissionManagement.PermissionsController", + "interfaces": [ + { + "type": "Volo.Abp.PermissionManagement.IPermissionAppService", + "name": "IPermissionAppService", + "methods": [ + { + "name": "GetAsync", + "parametersOnMethod": [ + { + "name": "providerName", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "providerKey", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + } + ], + "returnValue": { + "type": "Volo.Abp.PermissionManagement.GetPermissionListResultDto", + "typeSimple": "Volo.Abp.PermissionManagement.GetPermissionListResultDto" + } + }, + { + "name": "GetByGroupAsync", + "parametersOnMethod": [ + { + "name": "groupName", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "providerName", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "providerKey", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + } + ], + "returnValue": { + "type": "Volo.Abp.PermissionManagement.GetPermissionListResultDto", + "typeSimple": "Volo.Abp.PermissionManagement.GetPermissionListResultDto" + } + }, + { + "name": "UpdateAsync", + "parametersOnMethod": [ + { + "name": "providerName", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "providerKey", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "input", + "typeAsString": "Volo.Abp.PermissionManagement.UpdatePermissionsDto, Volo.Abp.PermissionManagement.Application.Contracts", + "type": "Volo.Abp.PermissionManagement.UpdatePermissionsDto", + "typeSimple": "Volo.Abp.PermissionManagement.UpdatePermissionsDto", + "isOptional": false, + "defaultValue": null + } + ], + "returnValue": { + "type": "System.Void", + "typeSimple": "System.Void" + } + }, + { + "name": "GetResourceProviderKeyLookupServicesAsync", + "parametersOnMethod": [ + { + "name": "resourceName", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + } + ], + "returnValue": { + "type": "Volo.Abp.PermissionManagement.GetResourceProviderListResultDto", + "typeSimple": "Volo.Abp.PermissionManagement.GetResourceProviderListResultDto" + } + }, + { + "name": "SearchResourceProviderKeyAsync", + "parametersOnMethod": [ + { + "name": "resourceName", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "serviceName", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "filter", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "page", + "typeAsString": "System.Int32, System.Private.CoreLib", + "type": "System.Int32", + "typeSimple": "number", + "isOptional": false, + "defaultValue": null + } + ], + "returnValue": { + "type": "Volo.Abp.PermissionManagement.SearchProviderKeyListResultDto", + "typeSimple": "Volo.Abp.PermissionManagement.SearchProviderKeyListResultDto" + } + }, + { + "name": "GetResourceDefinitionsAsync", + "parametersOnMethod": [ + { + "name": "resourceName", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + } + ], + "returnValue": { + "type": "Volo.Abp.PermissionManagement.GetResourcePermissionDefinitionListResultDto", + "typeSimple": "Volo.Abp.PermissionManagement.GetResourcePermissionDefinitionListResultDto" + } + }, + { + "name": "GetResourceAsync", + "parametersOnMethod": [ + { + "name": "resourceName", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "resourceKey", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + } + ], + "returnValue": { + "type": "Volo.Abp.PermissionManagement.GetResourcePermissionListResultDto", + "typeSimple": "Volo.Abp.PermissionManagement.GetResourcePermissionListResultDto" + } + }, + { + "name": "GetResourceByProviderAsync", + "parametersOnMethod": [ + { + "name": "resourceName", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "resourceKey", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "providerName", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "providerKey", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + } + ], + "returnValue": { + "type": "Volo.Abp.PermissionManagement.GetResourcePermissionWithProviderListResultDto", + "typeSimple": "Volo.Abp.PermissionManagement.GetResourcePermissionWithProviderListResultDto" + } + }, + { + "name": "UpdateResourceAsync", + "parametersOnMethod": [ + { + "name": "resourceName", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "resourceKey", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "input", + "typeAsString": "Volo.Abp.PermissionManagement.UpdateResourcePermissionsDto, Volo.Abp.PermissionManagement.Application.Contracts", + "type": "Volo.Abp.PermissionManagement.UpdateResourcePermissionsDto", + "typeSimple": "Volo.Abp.PermissionManagement.UpdateResourcePermissionsDto", + "isOptional": false, + "defaultValue": null + } + ], + "returnValue": { + "type": "System.Void", + "typeSimple": "System.Void" + } + }, + { + "name": "DeleteResourceAsync", + "parametersOnMethod": [ + { + "name": "resourceName", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "resourceKey", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "providerName", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "providerKey", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + } + ], + "returnValue": { + "type": "System.Void", + "typeSimple": "System.Void" + } + } + ] + } + ], + "actions": { + "GetAsyncByProviderNameAndProviderKey": { + "uniqueName": "GetAsyncByProviderNameAndProviderKey", + "name": "GetAsync", + "httpMethod": "GET", + "url": "api/permission-management/permissions", + "supportedVersions": [], + "parametersOnMethod": [ + { + "name": "providerName", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "providerKey", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + } + ], + "parameters": [ + { + "nameOnMethod": "providerName", + "name": "providerName", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "" + }, + { + "nameOnMethod": "providerKey", + "name": "providerKey", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "" + } + ], + "returnValue": { + "type": "Volo.Abp.PermissionManagement.GetPermissionListResultDto", + "typeSimple": "Volo.Abp.PermissionManagement.GetPermissionListResultDto" + }, + "allowAnonymous": null, + "implementFrom": "Volo.Abp.PermissionManagement.IPermissionAppService" + }, + "GetByGroupAsyncByGroupNameAndProviderNameAndProviderKey": { + "uniqueName": "GetByGroupAsyncByGroupNameAndProviderNameAndProviderKey", + "name": "GetByGroupAsync", + "httpMethod": "GET", + "url": "api/permission-management/permissions/by-group", + "supportedVersions": [], + "parametersOnMethod": [ + { + "name": "groupName", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "providerName", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "providerKey", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + } + ], + "parameters": [ + { + "nameOnMethod": "groupName", + "name": "groupName", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "" + }, + { + "nameOnMethod": "providerName", + "name": "providerName", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "" + }, + { + "nameOnMethod": "providerKey", + "name": "providerKey", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "" + } + ], + "returnValue": { + "type": "Volo.Abp.PermissionManagement.GetPermissionListResultDto", + "typeSimple": "Volo.Abp.PermissionManagement.GetPermissionListResultDto" + }, + "allowAnonymous": null, + "implementFrom": "Volo.Abp.PermissionManagement.IPermissionAppService" + }, + "UpdateAsyncByProviderNameAndProviderKeyAndInput": { + "uniqueName": "UpdateAsyncByProviderNameAndProviderKeyAndInput", + "name": "UpdateAsync", + "httpMethod": "PUT", + "url": "api/permission-management/permissions", + "supportedVersions": [], + "parametersOnMethod": [ + { + "name": "providerName", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "providerKey", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "input", + "typeAsString": "Volo.Abp.PermissionManagement.UpdatePermissionsDto, Volo.Abp.PermissionManagement.Application.Contracts", + "type": "Volo.Abp.PermissionManagement.UpdatePermissionsDto", + "typeSimple": "Volo.Abp.PermissionManagement.UpdatePermissionsDto", + "isOptional": false, + "defaultValue": null + } + ], + "parameters": [ + { + "nameOnMethod": "providerName", + "name": "providerName", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "" + }, + { + "nameOnMethod": "providerKey", + "name": "providerKey", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "" + }, + { + "nameOnMethod": "input", + "name": "input", + "jsonName": null, + "type": "Volo.Abp.PermissionManagement.UpdatePermissionsDto", + "typeSimple": "Volo.Abp.PermissionManagement.UpdatePermissionsDto", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "Body", + "descriptorName": "" + } + ], + "returnValue": { + "type": "System.Void", + "typeSimple": "System.Void" + }, + "allowAnonymous": null, + "implementFrom": "Volo.Abp.PermissionManagement.IPermissionAppService" + }, + "GetResourceProviderKeyLookupServicesAsyncByResourceName": { + "uniqueName": "GetResourceProviderKeyLookupServicesAsyncByResourceName", + "name": "GetResourceProviderKeyLookupServicesAsync", + "httpMethod": "GET", + "url": "api/permission-management/permissions/resource-provider-key-lookup-services", + "supportedVersions": [], + "parametersOnMethod": [ + { + "name": "resourceName", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + } + ], + "parameters": [ + { + "nameOnMethod": "resourceName", + "name": "resourceName", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "" + } + ], + "returnValue": { + "type": "Volo.Abp.PermissionManagement.GetResourceProviderListResultDto", + "typeSimple": "Volo.Abp.PermissionManagement.GetResourceProviderListResultDto" + }, + "allowAnonymous": null, + "implementFrom": "Volo.Abp.PermissionManagement.IPermissionAppService" + }, + "SearchResourceProviderKeyAsyncByResourceNameAndServiceNameAndFilterAndPage": { + "uniqueName": "SearchResourceProviderKeyAsyncByResourceNameAndServiceNameAndFilterAndPage", + "name": "SearchResourceProviderKeyAsync", + "httpMethod": "GET", + "url": "api/permission-management/permissions/search-resource-provider-keys", + "supportedVersions": [], + "parametersOnMethod": [ + { + "name": "resourceName", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "serviceName", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "filter", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "page", + "typeAsString": "System.Int32, System.Private.CoreLib", + "type": "System.Int32", + "typeSimple": "number", + "isOptional": false, + "defaultValue": null + } + ], + "parameters": [ + { + "nameOnMethod": "resourceName", + "name": "resourceName", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "" + }, + { + "nameOnMethod": "serviceName", + "name": "serviceName", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "" + }, + { + "nameOnMethod": "filter", + "name": "filter", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "" + }, + { + "nameOnMethod": "page", + "name": "page", + "jsonName": null, + "type": "System.Int32", + "typeSimple": "number", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "" + } + ], + "returnValue": { + "type": "Volo.Abp.PermissionManagement.SearchProviderKeyListResultDto", + "typeSimple": "Volo.Abp.PermissionManagement.SearchProviderKeyListResultDto" + }, + "allowAnonymous": null, + "implementFrom": "Volo.Abp.PermissionManagement.IPermissionAppService" + }, + "GetResourceDefinitionsAsyncByResourceName": { + "uniqueName": "GetResourceDefinitionsAsyncByResourceName", + "name": "GetResourceDefinitionsAsync", + "httpMethod": "GET", + "url": "api/permission-management/permissions/resource-definitions", + "supportedVersions": [], + "parametersOnMethod": [ + { + "name": "resourceName", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + } + ], + "parameters": [ + { + "nameOnMethod": "resourceName", + "name": "resourceName", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "" + } + ], + "returnValue": { + "type": "Volo.Abp.PermissionManagement.GetResourcePermissionDefinitionListResultDto", + "typeSimple": "Volo.Abp.PermissionManagement.GetResourcePermissionDefinitionListResultDto" + }, + "allowAnonymous": null, + "implementFrom": "Volo.Abp.PermissionManagement.IPermissionAppService" + }, + "GetResourceAsyncByResourceNameAndResourceKey": { + "uniqueName": "GetResourceAsyncByResourceNameAndResourceKey", + "name": "GetResourceAsync", + "httpMethod": "GET", + "url": "api/permission-management/permissions/resource", + "supportedVersions": [], + "parametersOnMethod": [ + { + "name": "resourceName", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "resourceKey", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + } + ], + "parameters": [ + { + "nameOnMethod": "resourceName", + "name": "resourceName", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "" + }, + { + "nameOnMethod": "resourceKey", + "name": "resourceKey", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "" + } + ], + "returnValue": { + "type": "Volo.Abp.PermissionManagement.GetResourcePermissionListResultDto", + "typeSimple": "Volo.Abp.PermissionManagement.GetResourcePermissionListResultDto" + }, + "allowAnonymous": null, + "implementFrom": "Volo.Abp.PermissionManagement.IPermissionAppService" + }, + "GetResourceByProviderAsyncByResourceNameAndResourceKeyAndProviderNameAndProviderKey": { + "uniqueName": "GetResourceByProviderAsyncByResourceNameAndResourceKeyAndProviderNameAndProviderKey", + "name": "GetResourceByProviderAsync", + "httpMethod": "GET", + "url": "api/permission-management/permissions/resource/by-provider", + "supportedVersions": [], + "parametersOnMethod": [ + { + "name": "resourceName", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "resourceKey", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "providerName", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "providerKey", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + } + ], + "parameters": [ + { + "nameOnMethod": "resourceName", + "name": "resourceName", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "" + }, + { + "nameOnMethod": "resourceKey", + "name": "resourceKey", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "" + }, + { + "nameOnMethod": "providerName", + "name": "providerName", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "" + }, + { + "nameOnMethod": "providerKey", + "name": "providerKey", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "" + } + ], + "returnValue": { + "type": "Volo.Abp.PermissionManagement.GetResourcePermissionWithProviderListResultDto", + "typeSimple": "Volo.Abp.PermissionManagement.GetResourcePermissionWithProviderListResultDto" + }, + "allowAnonymous": null, + "implementFrom": "Volo.Abp.PermissionManagement.IPermissionAppService" + }, + "UpdateResourceAsyncByResourceNameAndResourceKeyAndInput": { + "uniqueName": "UpdateResourceAsyncByResourceNameAndResourceKeyAndInput", + "name": "UpdateResourceAsync", + "httpMethod": "PUT", + "url": "api/permission-management/permissions/resource", + "supportedVersions": [], + "parametersOnMethod": [ + { + "name": "resourceName", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "resourceKey", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "input", + "typeAsString": "Volo.Abp.PermissionManagement.UpdateResourcePermissionsDto, Volo.Abp.PermissionManagement.Application.Contracts", + "type": "Volo.Abp.PermissionManagement.UpdateResourcePermissionsDto", + "typeSimple": "Volo.Abp.PermissionManagement.UpdateResourcePermissionsDto", + "isOptional": false, + "defaultValue": null + } + ], + "parameters": [ + { + "nameOnMethod": "resourceName", + "name": "resourceName", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "" + }, + { + "nameOnMethod": "resourceKey", + "name": "resourceKey", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "" + }, + { + "nameOnMethod": "input", + "name": "input", + "jsonName": null, + "type": "Volo.Abp.PermissionManagement.UpdateResourcePermissionsDto", + "typeSimple": "Volo.Abp.PermissionManagement.UpdateResourcePermissionsDto", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "Body", + "descriptorName": "" + } + ], + "returnValue": { + "type": "System.Void", + "typeSimple": "System.Void" + }, + "allowAnonymous": null, + "implementFrom": "Volo.Abp.PermissionManagement.IPermissionAppService" + }, + "DeleteResourceAsyncByResourceNameAndResourceKeyAndProviderNameAndProviderKey": { + "uniqueName": "DeleteResourceAsyncByResourceNameAndResourceKeyAndProviderNameAndProviderKey", + "name": "DeleteResourceAsync", + "httpMethod": "DELETE", + "url": "api/permission-management/permissions/resource", + "supportedVersions": [], + "parametersOnMethod": [ + { + "name": "resourceName", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "resourceKey", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "providerName", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + }, + { + "name": "providerKey", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + } + ], + "parameters": [ + { + "nameOnMethod": "resourceName", + "name": "resourceName", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "" + }, + { + "nameOnMethod": "resourceKey", + "name": "resourceKey", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "" + }, + { + "nameOnMethod": "providerName", + "name": "providerName", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "" + }, + { + "nameOnMethod": "providerKey", + "name": "providerKey", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "" + } + ], + "returnValue": { + "type": "System.Void", + "typeSimple": "System.Void" + }, + "allowAnonymous": null, + "implementFrom": "Volo.Abp.PermissionManagement.IPermissionAppService" + } + } + } + } + }, + "settingManagement": { + "rootPath": "settingManagement", + "remoteServiceName": "SettingManagement", + "controllers": { + "Volo.Abp.SettingManagement.EmailSettingsController": { + "controllerName": "EmailSettings", + "controllerGroupName": "EmailSettings", + "isRemoteService": true, + "isIntegrationService": false, + "apiVersion": null, + "type": "Volo.Abp.SettingManagement.EmailSettingsController", + "interfaces": [ + { + "type": "Volo.Abp.SettingManagement.IEmailSettingsAppService", + "name": "IEmailSettingsAppService", + "methods": [ + { + "name": "GetAsync", + "parametersOnMethod": [], + "returnValue": { + "type": "Volo.Abp.SettingManagement.EmailSettingsDto", + "typeSimple": "Volo.Abp.SettingManagement.EmailSettingsDto" + } + }, + { + "name": "UpdateAsync", + "parametersOnMethod": [ + { + "name": "input", + "typeAsString": "Volo.Abp.SettingManagement.UpdateEmailSettingsDto, Volo.Abp.SettingManagement.Application.Contracts", + "type": "Volo.Abp.SettingManagement.UpdateEmailSettingsDto", + "typeSimple": "Volo.Abp.SettingManagement.UpdateEmailSettingsDto", + "isOptional": false, + "defaultValue": null + } + ], + "returnValue": { + "type": "System.Void", + "typeSimple": "System.Void" + } + }, + { + "name": "SendTestEmailAsync", + "parametersOnMethod": [ + { + "name": "input", + "typeAsString": "Volo.Abp.SettingManagement.SendTestEmailInput, Volo.Abp.SettingManagement.Application.Contracts", + "type": "Volo.Abp.SettingManagement.SendTestEmailInput", + "typeSimple": "Volo.Abp.SettingManagement.SendTestEmailInput", + "isOptional": false, + "defaultValue": null + } + ], + "returnValue": { + "type": "System.Void", + "typeSimple": "System.Void" + } + } + ] + } + ], + "actions": { + "GetAsync": { + "uniqueName": "GetAsync", + "name": "GetAsync", + "httpMethod": "GET", + "url": "api/setting-management/emailing", + "supportedVersions": [], + "parametersOnMethod": [], + "parameters": [], + "returnValue": { + "type": "Volo.Abp.SettingManagement.EmailSettingsDto", + "typeSimple": "Volo.Abp.SettingManagement.EmailSettingsDto" + }, + "allowAnonymous": null, + "implementFrom": "Volo.Abp.SettingManagement.IEmailSettingsAppService" + }, + "UpdateAsyncByInput": { + "uniqueName": "UpdateAsyncByInput", + "name": "UpdateAsync", + "httpMethod": "POST", + "url": "api/setting-management/emailing", + "supportedVersions": [], + "parametersOnMethod": [ + { + "name": "input", + "typeAsString": "Volo.Abp.SettingManagement.UpdateEmailSettingsDto, Volo.Abp.SettingManagement.Application.Contracts", + "type": "Volo.Abp.SettingManagement.UpdateEmailSettingsDto", + "typeSimple": "Volo.Abp.SettingManagement.UpdateEmailSettingsDto", + "isOptional": false, + "defaultValue": null + } + ], + "parameters": [ + { + "nameOnMethod": "input", + "name": "input", + "jsonName": null, + "type": "Volo.Abp.SettingManagement.UpdateEmailSettingsDto", + "typeSimple": "Volo.Abp.SettingManagement.UpdateEmailSettingsDto", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "Body", + "descriptorName": "" + } + ], + "returnValue": { + "type": "System.Void", + "typeSimple": "System.Void" + }, + "allowAnonymous": null, + "implementFrom": "Volo.Abp.SettingManagement.IEmailSettingsAppService" + }, + "SendTestEmailAsyncByInput": { + "uniqueName": "SendTestEmailAsyncByInput", + "name": "SendTestEmailAsync", + "httpMethod": "POST", + "url": "api/setting-management/emailing/send-test-email", + "supportedVersions": [], + "parametersOnMethod": [ + { + "name": "input", + "typeAsString": "Volo.Abp.SettingManagement.SendTestEmailInput, Volo.Abp.SettingManagement.Application.Contracts", + "type": "Volo.Abp.SettingManagement.SendTestEmailInput", + "typeSimple": "Volo.Abp.SettingManagement.SendTestEmailInput", + "isOptional": false, + "defaultValue": null + } + ], + "parameters": [ + { + "nameOnMethod": "input", + "name": "input", + "jsonName": null, + "type": "Volo.Abp.SettingManagement.SendTestEmailInput", + "typeSimple": "Volo.Abp.SettingManagement.SendTestEmailInput", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "Body", + "descriptorName": "" + } + ], + "returnValue": { + "type": "System.Void", + "typeSimple": "System.Void" + }, + "allowAnonymous": null, + "implementFrom": "Volo.Abp.SettingManagement.IEmailSettingsAppService" + } + } + }, + "Volo.Abp.SettingManagement.TimeZoneSettingsController": { + "controllerName": "TimeZoneSettings", + "controllerGroupName": "TimeZoneSettings", + "isRemoteService": true, + "isIntegrationService": false, + "apiVersion": null, + "type": "Volo.Abp.SettingManagement.TimeZoneSettingsController", + "interfaces": [ + { + "type": "Volo.Abp.SettingManagement.ITimeZoneSettingsAppService", + "name": "ITimeZoneSettingsAppService", + "methods": [ + { + "name": "GetAsync", + "parametersOnMethod": [], + "returnValue": { + "type": "System.String", + "typeSimple": "string" + } + }, + { + "name": "GetTimezonesAsync", + "parametersOnMethod": [], + "returnValue": { + "type": "System.Collections.Generic.List", + "typeSimple": "[Volo.Abp.NameValue]" + } + }, + { + "name": "UpdateAsync", + "parametersOnMethod": [ + { + "name": "timezone", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + } + ], + "returnValue": { + "type": "System.Void", + "typeSimple": "System.Void" + } + } + ] + } + ], + "actions": { + "GetAsync": { + "uniqueName": "GetAsync", + "name": "GetAsync", + "httpMethod": "GET", + "url": "api/setting-management/timezone", + "supportedVersions": [], + "parametersOnMethod": [], + "parameters": [], + "returnValue": { + "type": "System.String", + "typeSimple": "string" + }, + "allowAnonymous": null, + "implementFrom": "Volo.Abp.SettingManagement.ITimeZoneSettingsAppService" + }, + "GetTimezonesAsync": { + "uniqueName": "GetTimezonesAsync", + "name": "GetTimezonesAsync", + "httpMethod": "GET", + "url": "api/setting-management/timezone/timezones", + "supportedVersions": [], + "parametersOnMethod": [], + "parameters": [], + "returnValue": { + "type": "System.Collections.Generic.List", + "typeSimple": "[Volo.Abp.NameValue]" + }, + "allowAnonymous": null, + "implementFrom": "Volo.Abp.SettingManagement.ITimeZoneSettingsAppService" + }, + "UpdateAsyncByTimezone": { + "uniqueName": "UpdateAsyncByTimezone", + "name": "UpdateAsync", + "httpMethod": "POST", + "url": "api/setting-management/timezone", + "supportedVersions": [], + "parametersOnMethod": [ + { + "name": "timezone", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + } + ], + "parameters": [ + { + "nameOnMethod": "timezone", + "name": "timezone", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "" + } + ], + "returnValue": { + "type": "System.Void", + "typeSimple": "System.Void" + }, + "allowAnonymous": null, + "implementFrom": "Volo.Abp.SettingManagement.ITimeZoneSettingsAppService" + } + } + } + } + } + }, + "types": { + "Volo.Abp.Account.ChangePasswordInput": { + "baseType": null, + "isEnum": false, + "enumNames": null, + "enumValues": null, + "genericArguments": null, + "properties": [ + { + "name": "CurrentPassword", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": 0, + "maxLength": 128, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "NewPassword", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isRequired": true, + "minLength": 0, + "maxLength": 128, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + } + ] + }, + "Volo.Abp.Account.ProfileDto": { + "baseType": "Volo.Abp.ObjectExtending.ExtensibleObject", + "isEnum": false, + "enumNames": null, + "enumValues": null, + "genericArguments": null, + "properties": [ + { + "name": "UserName", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "Email", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "Name", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "Surname", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "PhoneNumber", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "IsExternal", + "jsonName": null, + "type": "System.Boolean", + "typeSimple": "boolean", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "HasPassword", + "jsonName": null, + "type": "System.Boolean", + "typeSimple": "boolean", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "ConcurrencyStamp", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + } + ] + }, + "Volo.Abp.Account.RegisterDto": { + "baseType": "Volo.Abp.ObjectExtending.ExtensibleObject", + "isEnum": false, + "enumNames": null, + "enumValues": null, + "genericArguments": null, + "properties": [ + { + "name": "UserName", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isRequired": true, + "minLength": 0, + "maxLength": 256, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "EmailAddress", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isRequired": true, + "minLength": 0, + "maxLength": 256, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "Password", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isRequired": true, + "minLength": 0, + "maxLength": 128, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "AppName", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isRequired": true, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + } + ] + }, + "Volo.Abp.Account.ResetPasswordDto": { + "baseType": null, + "isEnum": false, + "enumNames": null, + "enumValues": null, + "genericArguments": null, + "properties": [ + { + "name": "UserId", + "jsonName": null, + "type": "System.Guid", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "ResetToken", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isRequired": true, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "Password", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isRequired": true, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + } + ] + }, + "Volo.Abp.Account.SendPasswordResetCodeDto": { + "baseType": null, + "isEnum": false, + "enumNames": null, + "enumValues": null, + "genericArguments": null, + "properties": [ + { + "name": "Email", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isRequired": true, + "minLength": 0, + "maxLength": 256, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "AppName", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isRequired": true, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "ReturnUrl", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "ReturnUrlHash", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + } + ] + }, + "Volo.Abp.Account.UpdateProfileDto": { + "baseType": "Volo.Abp.ObjectExtending.ExtensibleObject", + "isEnum": false, + "enumNames": null, + "enumValues": null, + "genericArguments": null, + "properties": [ + { + "name": "UserName", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": 0, + "maxLength": 256, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "Email", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": 0, + "maxLength": 256, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "Name", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": 0, + "maxLength": 64, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "Surname", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": 0, + "maxLength": 64, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "PhoneNumber", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": 0, + "maxLength": 16, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "ConcurrencyStamp", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + } + ] + }, + "Volo.Abp.Account.VerifyPasswordResetTokenInput": { + "baseType": null, + "isEnum": false, + "enumNames": null, + "enumValues": null, + "genericArguments": null, + "properties": [ + { + "name": "UserId", + "jsonName": null, + "type": "System.Guid", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "ResetToken", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isRequired": true, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + } + ] + }, + "Volo.Abp.Account.Web.Areas.Account.Controllers.Models.AbpLoginResult": { + "baseType": null, + "isEnum": false, + "enumNames": null, + "enumValues": null, + "genericArguments": null, + "properties": [ + { + "name": "Result", + "jsonName": null, + "type": "Volo.Abp.Account.Web.Areas.Account.Controllers.Models.LoginResultType", + "typeSimple": "enum", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "Description", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + } + ] + }, + "Volo.Abp.Account.Web.Areas.Account.Controllers.Models.LoginResultType": { + "baseType": "System.Enum", + "isEnum": true, + "enumNames": [ + "Success", + "InvalidUserNameOrPassword", + "NotAllowed", + "LockedOut", + "RequiresTwoFactor" + ], + "enumValues": [ + 1, + 2, + 3, + 4, + 5 + ], + "genericArguments": null, + "properties": null + }, + "Volo.Abp.Account.Web.Areas.Account.Controllers.Models.UserLoginInfo": { + "baseType": null, + "isEnum": false, + "enumNames": null, + "enumValues": null, + "genericArguments": null, + "properties": [ + { + "name": "UserNameOrEmailAddress", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isRequired": true, + "minLength": 0, + "maxLength": 255, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "Password", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isRequired": true, + "minLength": 0, + "maxLength": 32, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "RememberMe", + "jsonName": null, + "type": "System.Boolean", + "typeSimple": "boolean", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + } + ] + }, + "Volo.Abp.Application.Dtos.ExtensibleAuditedEntityDto": { + "baseType": "Volo.Abp.Application.Dtos.ExtensibleCreationAuditedEntityDto", + "isEnum": false, + "enumNames": null, + "enumValues": null, + "genericArguments": [ + "TPrimaryKey" + ], + "properties": [ + { + "name": "LastModificationTime", + "jsonName": null, + "type": "System.DateTime?", + "typeSimple": "string?", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true + }, + { + "name": "LastModifierId", + "jsonName": null, + "type": "System.Guid?", + "typeSimple": "string?", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true + } + ] + }, + "Volo.Abp.Application.Dtos.ExtensibleCreationAuditedEntityDto": { + "baseType": "Volo.Abp.Application.Dtos.ExtensibleEntityDto", + "isEnum": false, + "enumNames": null, + "enumValues": null, + "genericArguments": [ + "TPrimaryKey" + ], + "properties": [ + { + "name": "CreationTime", + "jsonName": null, + "type": "System.DateTime", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "CreatorId", + "jsonName": null, + "type": "System.Guid?", + "typeSimple": "string?", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true + } + ] + }, + "Volo.Abp.Application.Dtos.ExtensibleEntityDto": { + "baseType": "Volo.Abp.ObjectExtending.ExtensibleObject", + "isEnum": false, + "enumNames": null, + "enumValues": null, + "genericArguments": null, + "properties": [] + }, + "Volo.Abp.Application.Dtos.ExtensibleEntityDto": { + "baseType": "Volo.Abp.ObjectExtending.ExtensibleObject", + "isEnum": false, + "enumNames": null, + "enumValues": null, + "genericArguments": [ + "TKey" + ], + "properties": [ + { + "name": "Id", + "jsonName": null, + "type": "TKey", + "typeSimple": "TKey", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true + } + ] + }, + "Volo.Abp.Application.Dtos.ExtensibleFullAuditedEntityDto": { + "baseType": "Volo.Abp.Application.Dtos.ExtensibleAuditedEntityDto", + "isEnum": false, + "enumNames": null, + "enumValues": null, + "genericArguments": [ + "TPrimaryKey" + ], + "properties": [ + { + "name": "IsDeleted", + "jsonName": null, + "type": "System.Boolean", + "typeSimple": "boolean", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "DeleterId", + "jsonName": null, + "type": "System.Guid?", + "typeSimple": "string?", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true + }, + { + "name": "DeletionTime", + "jsonName": null, + "type": "System.DateTime?", + "typeSimple": "string?", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true + } + ] + }, + "Volo.Abp.Application.Dtos.ExtensibleLimitedResultRequestDto": { + "baseType": "Volo.Abp.Application.Dtos.ExtensibleEntityDto", + "isEnum": false, + "enumNames": null, + "enumValues": null, + "genericArguments": null, + "properties": [ + { + "name": "MaxResultCount", + "jsonName": null, + "type": "System.Int32", + "typeSimple": "number", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": "1", + "maximum": "2147483647", + "regex": null, + "isNullable": false } - } - } - }, - "types": { - "Volo.Abp.Account.RegisterDto": { - "baseType": "Volo.Abp.ObjectExtending.ExtensibleObject", + ] + }, + "Volo.Abp.Application.Dtos.ExtensiblePagedAndSortedResultRequestDto": { + "baseType": "Volo.Abp.Application.Dtos.ExtensiblePagedResultRequestDto", "isEnum": false, "enumNames": null, "enumValues": null, "genericArguments": null, "properties": [ { - "name": "UserName", + "name": "Sorting", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": true - }, + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true + } + ] + }, + "Volo.Abp.Application.Dtos.ExtensiblePagedResultRequestDto": { + "baseType": "Volo.Abp.Application.Dtos.ExtensibleLimitedResultRequestDto", + "isEnum": false, + "enumNames": null, + "enumValues": null, + "genericArguments": null, + "properties": [ { - "name": "EmailAddress", + "name": "SkipCount", "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": true - }, + "type": "System.Int32", + "typeSimple": "number", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": "0", + "maximum": "2147483647", + "regex": null, + "isNullable": false + } + ] + }, + "Volo.Abp.Application.Dtos.LimitedResultRequestDto": { + "baseType": null, + "isEnum": false, + "enumNames": null, + "enumValues": null, + "genericArguments": null, + "properties": [ { - "name": "Password", + "name": "MaxResultCount", "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": true - }, + "type": "System.Int32", + "typeSimple": "number", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": "1", + "maximum": "2147483647", + "regex": null, + "isNullable": false + } + ] + }, + "Volo.Abp.Application.Dtos.ListResultDto": { + "baseType": null, + "isEnum": false, + "enumNames": null, + "enumValues": null, + "genericArguments": [ + "T" + ], + "properties": [ { - "name": "AppName", + "name": "Items", + "jsonName": null, + "type": "[T]", + "typeSimple": "[T]", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + } + ] + }, + "Volo.Abp.Application.Dtos.PagedAndSortedResultRequestDto": { + "baseType": "Volo.Abp.Application.Dtos.PagedResultRequestDto", + "isEnum": false, + "enumNames": null, + "enumValues": null, + "genericArguments": null, + "properties": [ + { + "name": "Sorting", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": true + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true } ] }, - "Volo.Abp.ObjectExtending.ExtensibleObject": { + "Volo.Abp.Application.Dtos.PagedResultDto": { + "baseType": "Volo.Abp.Application.Dtos.ListResultDto", + "isEnum": false, + "enumNames": null, + "enumValues": null, + "genericArguments": [ + "T" + ], + "properties": [ + { + "name": "TotalCount", + "jsonName": null, + "type": "System.Int64", + "typeSimple": "number", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + } + ] + }, + "Volo.Abp.Application.Dtos.PagedResultRequestDto": { + "baseType": "Volo.Abp.Application.Dtos.LimitedResultRequestDto", + "isEnum": false, + "enumNames": null, + "enumValues": null, + "genericArguments": null, + "properties": [ + { + "name": "SkipCount", + "jsonName": null, + "type": "System.Int32", + "typeSimple": "number", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": "0", + "maximum": "2147483647", + "regex": null, + "isNullable": false + } + ] + }, + "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationAuthConfigurationDto": { + "baseType": null, + "isEnum": false, + "enumNames": null, + "enumValues": null, + "genericArguments": null, + "properties": [ + { + "name": "GrantedPolicies", + "jsonName": null, + "type": "{System.String:System.Boolean}", + "typeSimple": "{string:boolean}", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + } + ] + }, + "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationConfigurationDto": { "baseType": null, "isEnum": false, "enumNames": null, "enumValues": null, "genericArguments": null, "properties": [ + { + "name": "Localization", + "jsonName": null, + "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationLocalizationConfigurationDto", + "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationLocalizationConfigurationDto", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "Auth", + "jsonName": null, + "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationAuthConfigurationDto", + "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationAuthConfigurationDto", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "Setting", + "jsonName": null, + "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationSettingConfigurationDto", + "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationSettingConfigurationDto", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "CurrentUser", + "jsonName": null, + "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.CurrentUserDto", + "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.CurrentUserDto", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "Features", + "jsonName": null, + "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationFeatureConfigurationDto", + "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationFeatureConfigurationDto", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "GlobalFeatures", + "jsonName": null, + "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationGlobalFeatureConfigurationDto", + "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationGlobalFeatureConfigurationDto", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "MultiTenancy", + "jsonName": null, + "type": "Volo.Abp.AspNetCore.Mvc.MultiTenancy.MultiTenancyInfoDto", + "typeSimple": "Volo.Abp.AspNetCore.Mvc.MultiTenancy.MultiTenancyInfoDto", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "CurrentTenant", + "jsonName": null, + "type": "Volo.Abp.AspNetCore.Mvc.MultiTenancy.CurrentTenantDto", + "typeSimple": "Volo.Abp.AspNetCore.Mvc.MultiTenancy.CurrentTenantDto", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "Timing", + "jsonName": null, + "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.TimingDto", + "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.TimingDto", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "Clock", + "jsonName": null, + "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ClockDto", + "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ClockDto", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "ObjectExtensions", + "jsonName": null, + "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ObjectExtensionsDto", + "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ObjectExtensionsDto", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, { "name": "ExtraProperties", "jsonName": null, "type": "{System.String:System.Object}", "typeSimple": "{string:object}", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.Identity.IdentityUserDto": { - "baseType": "Volo.Abp.Application.Dtos.ExtensibleFullAuditedEntityDto", + "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationConfigurationRequestOptions": { + "baseType": null, "isEnum": false, "enumNames": null, "enumValues": null, "genericArguments": null, "properties": [ { - "name": "TenantId", + "name": "IncludeLocalizationResources", "jsonName": null, - "type": "System.Guid?", - "typeSimple": "string?", - "isRequired": false - }, + "type": "System.Boolean", + "typeSimple": "boolean", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + } + ] + }, + "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationFeatureConfigurationDto": { + "baseType": null, + "isEnum": false, + "enumNames": null, + "enumValues": null, + "genericArguments": null, + "properties": [ { - "name": "UserName", + "name": "Values", "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": false - }, + "type": "{System.String:System.String}", + "typeSimple": "{string:string}", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + } + ] + }, + "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationGlobalFeatureConfigurationDto": { + "baseType": null, + "isEnum": false, + "enumNames": null, + "enumValues": null, + "genericArguments": null, + "properties": [ { - "name": "Name", + "name": "EnabledFeatures", "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": false + "type": "[System.String]", + "typeSimple": "[string]", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + } + ] + }, + "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationLocalizationConfigurationDto": { + "baseType": null, + "isEnum": false, + "enumNames": null, + "enumValues": null, + "genericArguments": null, + "properties": [ + { + "name": "Values", + "jsonName": null, + "type": "{System.String:System.Collections.Generic.Dictionary}", + "typeSimple": "{string:System.Collections.Generic.Dictionary}", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "Surname", + "name": "Resources", "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": false + "type": "{System.String:Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationLocalizationResourceDto}", + "typeSimple": "{string:Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationLocalizationResourceDto}", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "Email", + "name": "Languages", "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": false + "type": "[Volo.Abp.Localization.LanguageInfo]", + "typeSimple": "[Volo.Abp.Localization.LanguageInfo]", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "EmailConfirmed", + "name": "CurrentCulture", "jsonName": null, - "type": "System.Boolean", - "typeSimple": "boolean", - "isRequired": false + "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.CurrentCultureDto", + "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.CurrentCultureDto", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "PhoneNumber", + "name": "DefaultResourceName", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true }, { - "name": "PhoneNumberConfirmed", + "name": "LanguagesMap", "jsonName": null, - "type": "System.Boolean", - "typeSimple": "boolean", - "isRequired": false + "type": "{System.String:[Volo.Abp.NameValue]}", + "typeSimple": "{string:[Volo.Abp.NameValue]}", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "LockoutEnabled", + "name": "LanguageFilesMap", "jsonName": null, - "type": "System.Boolean", - "typeSimple": "boolean", - "isRequired": false - }, + "type": "{System.String:[Volo.Abp.NameValue]}", + "typeSimple": "{string:[Volo.Abp.NameValue]}", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + } + ] + }, + "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationLocalizationDto": { + "baseType": null, + "isEnum": false, + "enumNames": null, + "enumValues": null, + "genericArguments": null, + "properties": [ { - "name": "LockoutEnd", + "name": "Resources", "jsonName": null, - "type": "System.DateTimeOffset?", - "typeSimple": "string?", - "isRequired": false + "type": "{System.String:Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationLocalizationResourceDto}", + "typeSimple": "{string:Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationLocalizationResourceDto}", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "ConcurrencyStamp", + "name": "CurrentCulture", "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": false + "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.CurrentCultureDto", + "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.CurrentCultureDto", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.Application.Dtos.ExtensibleFullAuditedEntityDto": { - "baseType": "Volo.Abp.Application.Dtos.ExtensibleAuditedEntityDto", + "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationLocalizationRequestDto": { + "baseType": null, "isEnum": false, "enumNames": null, "enumValues": null, - "genericArguments": [ - "TPrimaryKey" - ], + "genericArguments": null, "properties": [ { - "name": "IsDeleted", - "jsonName": null, - "type": "System.Boolean", - "typeSimple": "boolean", - "isRequired": false - }, - { - "name": "DeleterId", + "name": "CultureName", "jsonName": null, - "type": "System.Guid?", - "typeSimple": "string?", - "isRequired": false + "type": "System.String", + "typeSimple": "string", + "isRequired": true, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "DeletionTime", + "name": "OnlyDynamics", "jsonName": null, - "type": "System.DateTime?", - "typeSimple": "string?", - "isRequired": false + "type": "System.Boolean", + "typeSimple": "boolean", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.Application.Dtos.ExtensibleAuditedEntityDto": { - "baseType": "Volo.Abp.Application.Dtos.ExtensibleCreationAuditedEntityDto", + "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationLocalizationResourceDto": { + "baseType": null, "isEnum": false, "enumNames": null, "enumValues": null, - "genericArguments": [ - "TPrimaryKey" - ], + "genericArguments": null, "properties": [ { - "name": "LastModificationTime", + "name": "Texts", "jsonName": null, - "type": "System.DateTime?", - "typeSimple": "string?", - "isRequired": false + "type": "{System.String:System.String}", + "typeSimple": "{string:string}", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "LastModifierId", + "name": "BaseResources", "jsonName": null, - "type": "System.Guid?", - "typeSimple": "string?", - "isRequired": false + "type": "[System.String]", + "typeSimple": "[string]", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.Application.Dtos.ExtensibleCreationAuditedEntityDto": { - "baseType": "Volo.Abp.Application.Dtos.ExtensibleEntityDto", + "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationSettingConfigurationDto": { + "baseType": null, "isEnum": false, "enumNames": null, "enumValues": null, - "genericArguments": [ - "TPrimaryKey" - ], + "genericArguments": null, "properties": [ { - "name": "CreationTime", - "jsonName": null, - "type": "System.DateTime", - "typeSimple": "string", - "isRequired": false - }, - { - "name": "CreatorId", + "name": "Values", "jsonName": null, - "type": "System.Guid?", - "typeSimple": "string?", - "isRequired": false + "type": "{System.String:System.String}", + "typeSimple": "{string:string}", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.Application.Dtos.ExtensibleEntityDto": { - "baseType": "Volo.Abp.ObjectExtending.ExtensibleObject", + "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ClockDto": { + "baseType": null, "isEnum": false, "enumNames": null, "enumValues": null, - "genericArguments": [ - "TKey" - ], + "genericArguments": null, "properties": [ { - "name": "Id", + "name": "Kind", "jsonName": null, - "type": "TKey", - "typeSimple": "TKey", - "isRequired": false + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.Account.SendPasswordResetCodeDto": { + "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.CurrentCultureDto": { "baseType": null, "isEnum": false, "enumNames": null, @@ -2413,66 +5725,125 @@ "genericArguments": null, "properties": [ { - "name": "Email", + "name": "DisplayName", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": true + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "AppName", + "name": "EnglishName", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": true + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "ReturnUrl", + "name": "ThreeLetterIsoLanguageName", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "ReturnUrlHash", + "name": "TwoLetterIsoLanguageName", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false - } - ] - }, - "Volo.Abp.Account.ResetPasswordDto": { - "baseType": null, - "isEnum": false, - "enumNames": null, - "enumValues": null, - "genericArguments": null, - "properties": [ + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, { - "name": "UserId", + "name": "IsRightToLeft", "jsonName": null, - "type": "System.Guid", + "type": "System.Boolean", + "typeSimple": "boolean", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "CultureName", + "jsonName": null, + "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "ResetToken", + "name": "Name", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": true + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "Password", + "name": "NativeName", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": true + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "DateTimeFormat", + "jsonName": null, + "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.DateTimeFormatDto", + "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.DateTimeFormatDto", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.Account.Web.Areas.Account.Controllers.Models.UserLoginInfo": { + "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.CurrentUserDto": { "baseType": null, "isEnum": false, "enumNames": null, @@ -2480,219 +5851,338 @@ "genericArguments": null, "properties": [ { - "name": "UserNameOrEmailAddress", + "name": "IsAuthenticated", + "jsonName": null, + "type": "System.Boolean", + "typeSimple": "boolean", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "Id", + "jsonName": null, + "type": "System.Guid?", + "typeSimple": "string?", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true + }, + { + "name": "TenantId", + "jsonName": null, + "type": "System.Guid?", + "typeSimple": "string?", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true + }, + { + "name": "ImpersonatorUserId", + "jsonName": null, + "type": "System.Guid?", + "typeSimple": "string?", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true + }, + { + "name": "ImpersonatorTenantId", + "jsonName": null, + "type": "System.Guid?", + "typeSimple": "string?", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true + }, + { + "name": "ImpersonatorUserName", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": true + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true }, { - "name": "Password", + "name": "ImpersonatorTenantName", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": true + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true }, { - "name": "RememberMe", + "name": "UserName", "jsonName": null, - "type": "System.Boolean", - "typeSimple": "boolean", - "isRequired": false - } - ] - }, - "Volo.Abp.Account.Web.Areas.Account.Controllers.Models.AbpLoginResult": { - "baseType": null, - "isEnum": false, - "enumNames": null, - "enumValues": null, - "genericArguments": null, - "properties": [ + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true + }, { - "name": "Result", + "name": "Name", "jsonName": null, - "type": "Volo.Abp.Account.Web.Areas.Account.Controllers.Models.LoginResultType", - "typeSimple": "Volo.Abp.Account.Web.Areas.Account.Controllers.Models.LoginResultType", - "isRequired": false + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true }, { - "name": "Description", + "name": "SurName", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false - } - ] - }, - "Volo.Abp.Account.Web.Areas.Account.Controllers.Models.LoginResultType": { - "baseType": "System.Enum", - "isEnum": true, - "enumNames": [ - "Success", - "InvalidUserNameOrPassword", - "NotAllowed", - "LockedOut", - "RequiresTwoFactor" - ], - "enumValues": [ - 1, - 2, - 3, - 4, - 5 - ], - "genericArguments": null, - "properties": null - }, - "Volo.Abp.AspNetCore.Mvc.MultiTenancy.FindTenantResultDto": { - "baseType": null, - "isEnum": false, - "enumNames": null, - "enumValues": null, - "genericArguments": null, - "properties": [ + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true + }, { - "name": "Success", + "name": "Email", "jsonName": null, - "type": "System.Boolean", - "typeSimple": "boolean", - "isRequired": false + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true }, { - "name": "TenantId", + "name": "EmailVerified", "jsonName": null, - "type": "System.Guid?", - "typeSimple": "string?", - "isRequired": false + "type": "System.Boolean", + "typeSimple": "boolean", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "Name", + "name": "PhoneNumber", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true }, { - "name": "IsActive", + "name": "PhoneNumberVerified", "jsonName": null, "type": "System.Boolean", "typeSimple": "boolean", - "isRequired": false - } - ] - }, - "Volo.Abp.Application.Dtos.ListResultDto": { - "baseType": null, - "isEnum": false, - "enumNames": null, - "enumValues": null, - "genericArguments": [ - "T" - ], - "properties": [ + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, { - "name": "Items", + "name": "Roles", "jsonName": null, - "type": "[T]", - "typeSimple": "[T]", - "isRequired": false + "type": "[System.String]", + "typeSimple": "[string]", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "SessionId", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true } ] }, - "Volo.Abp.Identity.IdentityRoleDto": { - "baseType": "Volo.Abp.Application.Dtos.ExtensibleEntityDto", + "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.DateTimeFormatDto": { + "baseType": null, "isEnum": false, "enumNames": null, "enumValues": null, "genericArguments": null, "properties": [ { - "name": "Name", + "name": "CalendarAlgorithmType", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "IsDefault", + "name": "DateTimeFormatLong", "jsonName": null, - "type": "System.Boolean", - "typeSimple": "boolean", - "isRequired": false + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "IsStatic", + "name": "ShortDatePattern", "jsonName": null, - "type": "System.Boolean", - "typeSimple": "boolean", - "isRequired": false + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "IsPublic", + "name": "FullDateTimePattern", "jsonName": null, - "type": "System.Boolean", - "typeSimple": "boolean", - "isRequired": false + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "ConcurrencyStamp", + "name": "DateSeparator", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false - } - ] - }, - "Volo.Abp.Identity.GetIdentityRolesInput": { - "baseType": "Volo.Abp.Application.Dtos.PagedAndSortedResultRequestDto", - "isEnum": false, - "enumNames": null, - "enumValues": null, - "genericArguments": null, - "properties": [ + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, { - "name": "Filter", + "name": "ShortTimePattern", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false - } - ] - }, - "Volo.Abp.Application.Dtos.PagedAndSortedResultRequestDto": { - "baseType": "Volo.Abp.Application.Dtos.PagedResultRequestDto", - "isEnum": false, - "enumNames": null, - "enumValues": null, - "genericArguments": null, - "properties": [ + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, { - "name": "Sorting", + "name": "LongTimePattern", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.Application.Dtos.PagedResultRequestDto": { - "baseType": "Volo.Abp.Application.Dtos.LimitedResultRequestDto", + "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.IanaTimeZone": { + "baseType": null, "isEnum": false, "enumNames": null, "enumValues": null, "genericArguments": null, "properties": [ { - "name": "SkipCount", + "name": "TimeZoneName", "jsonName": null, - "type": "System.Int32", - "typeSimple": "number", - "isRequired": false + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true } ] }, - "Volo.Abp.Application.Dtos.LimitedResultRequestDto": { + "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.EntityExtensionDto": { "baseType": null, "isEnum": false, "enumNames": null, @@ -2700,56 +6190,70 @@ "genericArguments": null, "properties": [ { - "name": "DefaultMaxResultCount", - "jsonName": null, - "type": "System.Int32", - "typeSimple": "number", - "isRequired": false - }, - { - "name": "MaxMaxResultCount", + "name": "Properties", "jsonName": null, - "type": "System.Int32", - "typeSimple": "number", - "isRequired": false + "type": "{System.String:Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyDto}", + "typeSimple": "{string:Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyDto}", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "MaxResultCount", + "name": "Configuration", "jsonName": null, - "type": "System.Int32", - "typeSimple": "number", - "isRequired": false + "type": "{System.String:System.Object}", + "typeSimple": "{string:object}", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.Application.Dtos.PagedResultDto": { - "baseType": "Volo.Abp.Application.Dtos.ListResultDto", + "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionEnumDto": { + "baseType": null, "isEnum": false, "enumNames": null, "enumValues": null, - "genericArguments": [ - "T" - ], + "genericArguments": null, "properties": [ { - "name": "TotalCount", + "name": "Fields", "jsonName": null, - "type": "System.Int64", - "typeSimple": "number", - "isRequired": false + "type": "[Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionEnumFieldDto]", + "typeSimple": "[Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionEnumFieldDto]", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "LocalizationResource", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true } ] }, - "Volo.Abp.Identity.IdentityRoleCreateDto": { - "baseType": "Volo.Abp.Identity.IdentityRoleCreateOrUpdateDtoBase", - "isEnum": false, - "enumNames": null, - "enumValues": null, - "genericArguments": null, - "properties": [] - }, - "Volo.Abp.Identity.IdentityRoleCreateOrUpdateDtoBase": { - "baseType": "Volo.Abp.ObjectExtending.ExtensibleObject", + "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionEnumFieldDto": { + "baseType": null, "isEnum": false, "enumNames": null, "enumValues": null, @@ -2760,154 +6264,144 @@ "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": true - }, - { - "name": "IsDefault", - "jsonName": null, - "type": "System.Boolean", - "typeSimple": "boolean", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true }, { - "name": "IsPublic", + "name": "Value", "jsonName": null, - "type": "System.Boolean", - "typeSimple": "boolean", - "isRequired": false + "type": "System.Object", + "typeSimple": "object", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true } ] }, - "Volo.Abp.Identity.IdentityRoleUpdateDto": { - "baseType": "Volo.Abp.Identity.IdentityRoleCreateOrUpdateDtoBase", + "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyApiCreateDto": { + "baseType": null, "isEnum": false, "enumNames": null, "enumValues": null, "genericArguments": null, "properties": [ { - "name": "ConcurrencyStamp", + "name": "IsAvailable", "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": false + "type": "System.Boolean", + "typeSimple": "boolean", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.Identity.GetIdentityUsersInput": { - "baseType": "Volo.Abp.Application.Dtos.PagedAndSortedResultRequestDto", + "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyApiDto": { + "baseType": null, "isEnum": false, "enumNames": null, "enumValues": null, "genericArguments": null, "properties": [ { - "name": "Filter", + "name": "OnGet", "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": false - } - ] - }, - "Volo.Abp.Identity.IdentityUserCreateDto": { - "baseType": "Volo.Abp.Identity.IdentityUserCreateOrUpdateDtoBase", - "isEnum": false, - "enumNames": null, - "enumValues": null, - "genericArguments": null, - "properties": [ + "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyApiGetDto", + "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyApiGetDto", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, { - "name": "Password", + "name": "OnCreate", "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": true + "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyApiCreateDto", + "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyApiCreateDto", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "OnUpdate", + "jsonName": null, + "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyApiUpdateDto", + "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyApiUpdateDto", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.Identity.IdentityUserCreateOrUpdateDtoBase": { - "baseType": "Volo.Abp.ObjectExtending.ExtensibleObject", + "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyApiGetDto": { + "baseType": null, "isEnum": false, "enumNames": null, "enumValues": null, "genericArguments": null, "properties": [ { - "name": "UserName", - "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": true - }, - { - "name": "Name", - "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": false - }, - { - "name": "Surname", - "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": false - }, - { - "name": "Email", - "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": true - }, - { - "name": "PhoneNumber", - "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": false - }, - { - "name": "LockoutEnabled", + "name": "IsAvailable", "jsonName": null, "type": "System.Boolean", "typeSimple": "boolean", - "isRequired": false - }, - { - "name": "RoleNames", - "jsonName": null, - "type": "[System.String]", - "typeSimple": "[string]", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.Identity.IdentityUserUpdateDto": { - "baseType": "Volo.Abp.Identity.IdentityUserCreateOrUpdateDtoBase", + "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyApiUpdateDto": { + "baseType": null, "isEnum": false, "enumNames": null, "enumValues": null, "genericArguments": null, "properties": [ { - "name": "Password", - "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": false - }, - { - "name": "ConcurrencyStamp", + "name": "IsAvailable", "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": false + "type": "System.Boolean", + "typeSimple": "boolean", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.Identity.IdentityUserUpdateRolesDto": { + "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyAttributeDto": { "baseType": null, "isEnum": false, "enumNames": null, @@ -2915,15 +6409,34 @@ "genericArguments": null, "properties": [ { - "name": "RoleNames", + "name": "TypeSimple", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "Config", "jsonName": null, - "type": "[System.String]", - "typeSimple": "[string]", - "isRequired": true + "type": "{System.String:System.Object}", + "typeSimple": "{string:object}", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.Users.UserData": { + "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyDto": { "baseType": null, "isEnum": false, "enumNames": null, @@ -2931,87 +6444,160 @@ "genericArguments": null, "properties": [ { - "name": "Id", + "name": "Type", "jsonName": null, - "type": "System.Guid", + "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "TenantId", + "name": "TypeSimple", "jsonName": null, - "type": "System.Guid?", - "typeSimple": "string?", - "isRequired": false + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "UserName", + "name": "DisplayName", "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": false + "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.LocalizableStringDto", + "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.LocalizableStringDto", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true }, { - "name": "Name", + "name": "Api", "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": false + "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyApiDto", + "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyApiDto", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "Surname", + "name": "Ui", "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": false + "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyUiDto", + "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyUiDto", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "Email", + "name": "Policy", "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": false + "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyPolicyDto", + "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyPolicyDto", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "EmailConfirmed", + "name": "Attributes", "jsonName": null, - "type": "System.Boolean", - "typeSimple": "boolean", - "isRequired": false + "type": "[Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyAttributeDto]", + "typeSimple": "[Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyAttributeDto]", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "PhoneNumber", + "name": "Configuration", "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": false + "type": "{System.String:System.Object}", + "typeSimple": "{string:object}", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "PhoneNumberConfirmed", + "name": "DefaultValue", "jsonName": null, - "type": "System.Boolean", - "typeSimple": "boolean", - "isRequired": false + "type": "System.Object", + "typeSimple": "object", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true } ] }, - "Volo.Abp.Identity.UserLookupSearchInputDto": { - "baseType": "Volo.Abp.Application.Dtos.PagedAndSortedResultRequestDto", + "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyFeaturePolicyDto": { + "baseType": null, "isEnum": false, "enumNames": null, "enumValues": null, "genericArguments": null, "properties": [ { - "name": "Filter", + "name": "Features", "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": false + "type": "[System.String]", + "typeSimple": "[string]", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "RequiresAll", + "jsonName": null, + "type": "System.Boolean", + "typeSimple": "boolean", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.Identity.UserLookupCountInputDto": { + "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyGlobalFeaturePolicyDto": { "baseType": null, "isEnum": false, "enumNames": null, @@ -3019,131 +6605,274 @@ "genericArguments": null, "properties": [ { - "name": "Filter", + "name": "Features", "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": false + "type": "[System.String]", + "typeSimple": "[string]", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "RequiresAll", + "jsonName": null, + "type": "System.Boolean", + "typeSimple": "boolean", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.Identity.ProfileDto": { - "baseType": "Volo.Abp.ObjectExtending.ExtensibleObject", + "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyPermissionPolicyDto": { + "baseType": null, "isEnum": false, "enumNames": null, "enumValues": null, "genericArguments": null, "properties": [ { - "name": "UserName", + "name": "PermissionNames", "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": false + "type": "[System.String]", + "typeSimple": "[string]", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "Email", + "name": "RequiresAll", "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": false - }, + "type": "System.Boolean", + "typeSimple": "boolean", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + } + ] + }, + "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyPolicyDto": { + "baseType": null, + "isEnum": false, + "enumNames": null, + "enumValues": null, + "genericArguments": null, + "properties": [ { - "name": "Name", + "name": "GlobalFeatures", "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": false + "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyGlobalFeaturePolicyDto", + "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyGlobalFeaturePolicyDto", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "Surname", + "name": "Features", "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": false + "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyFeaturePolicyDto", + "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyFeaturePolicyDto", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "PhoneNumber", + "name": "Permissions", "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": false + "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyPermissionPolicyDto", + "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyPermissionPolicyDto", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + } + ] + }, + "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyUiDto": { + "baseType": null, + "isEnum": false, + "enumNames": null, + "enumValues": null, + "genericArguments": null, + "properties": [ + { + "name": "OnTable", + "jsonName": null, + "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyUiTableDto", + "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyUiTableDto", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "IsExternal", + "name": "OnCreateForm", "jsonName": null, - "type": "System.Boolean", - "typeSimple": "boolean", - "isRequired": false + "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyUiFormDto", + "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyUiFormDto", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "HasPassword", + "name": "OnEditForm", "jsonName": null, - "type": "System.Boolean", - "typeSimple": "boolean", - "isRequired": false + "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyUiFormDto", + "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyUiFormDto", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "ConcurrencyStamp", + "name": "Lookup", "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": false + "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyUiLookupDto", + "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyUiLookupDto", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.Identity.UpdateProfileDto": { - "baseType": "Volo.Abp.ObjectExtending.ExtensibleObject", + "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyUiFormDto": { + "baseType": null, "isEnum": false, "enumNames": null, "enumValues": null, "genericArguments": null, "properties": [ { - "name": "UserName", + "name": "IsVisible", "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": false - }, + "type": "System.Boolean", + "typeSimple": "boolean", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + } + ] + }, + "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyUiLookupDto": { + "baseType": null, + "isEnum": false, + "enumNames": null, + "enumValues": null, + "genericArguments": null, + "properties": [ { - "name": "Email", + "name": "Url", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "Name", + "name": "ResultListPropertyName", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "Surname", + "name": "DisplayPropertyName", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "PhoneNumber", + "name": "ValuePropertyName", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "ConcurrencyStamp", + "name": "FilterParamName", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.Identity.ChangePasswordInput": { + "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyUiTableDto": { "baseType": null, "isEnum": false, "enumNames": null, @@ -3151,22 +6880,21 @@ "genericArguments": null, "properties": [ { - "name": "CurrentPassword", - "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": false - }, - { - "name": "NewPassword", + "name": "IsVisible", "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": true + "type": "System.Boolean", + "typeSimple": "boolean", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.PermissionManagement.GetPermissionListResultDto": { + "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.LocalizableStringDto": { "baseType": null, "isEnum": false, "enumNames": null, @@ -3174,22 +6902,34 @@ "genericArguments": null, "properties": [ { - "name": "EntityDisplayName", + "name": "Name", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "Groups", + "name": "Resource", "jsonName": null, - "type": "[Volo.Abp.PermissionManagement.PermissionGroupDto]", - "typeSimple": "[Volo.Abp.PermissionManagement.PermissionGroupDto]", - "isRequired": false + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true } ] }, - "Volo.Abp.PermissionManagement.PermissionGroupDto": { + "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ModuleExtensionDto": { "baseType": null, "isEnum": false, "enumNames": null, @@ -3197,29 +6937,34 @@ "genericArguments": null, "properties": [ { - "name": "Name", - "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": false - }, - { - "name": "DisplayName", + "name": "Entities", "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": false + "type": "{System.String:Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.EntityExtensionDto}", + "typeSimple": "{string:Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.EntityExtensionDto}", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "Permissions", + "name": "Configuration", "jsonName": null, - "type": "[Volo.Abp.PermissionManagement.PermissionGrantInfoDto]", - "typeSimple": "[Volo.Abp.PermissionManagement.PermissionGrantInfoDto]", - "isRequired": false + "type": "{System.String:System.Object}", + "typeSimple": "{string:object}", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.PermissionManagement.PermissionGrantInfoDto": { + "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ObjectExtensionsDto": { "baseType": null, "isEnum": false, "enumNames": null, @@ -3227,50 +6972,34 @@ "genericArguments": null, "properties": [ { - "name": "Name", - "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": false - }, - { - "name": "DisplayName", - "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": false - }, - { - "name": "ParentName", - "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": false - }, - { - "name": "IsGranted", - "jsonName": null, - "type": "System.Boolean", - "typeSimple": "boolean", - "isRequired": false - }, - { - "name": "AllowedProviders", + "name": "Modules", "jsonName": null, - "type": "[System.String]", - "typeSimple": "[string]", - "isRequired": false + "type": "{System.String:Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ModuleExtensionDto}", + "typeSimple": "{string:Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ModuleExtensionDto}", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "GrantedProviders", + "name": "Enums", "jsonName": null, - "type": "[Volo.Abp.PermissionManagement.ProviderInfoDto]", - "typeSimple": "[Volo.Abp.PermissionManagement.ProviderInfoDto]", - "isRequired": false + "type": "{System.String:Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionEnumDto}", + "typeSimple": "{string:Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionEnumDto}", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.PermissionManagement.ProviderInfoDto": { + "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.TimeZone": { "baseType": null, "isEnum": false, "enumNames": null, @@ -3278,22 +7007,34 @@ "genericArguments": null, "properties": [ { - "name": "ProviderName", + "name": "Iana", "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": false + "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.IanaTimeZone", + "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.IanaTimeZone", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "ProviderKey", + "name": "Windows", "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": false + "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.WindowsTimeZone", + "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.WindowsTimeZone", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.PermissionManagement.UpdatePermissionsDto": { + "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.TimingDto": { "baseType": null, "isEnum": false, "enumNames": null, @@ -3301,38 +7042,91 @@ "genericArguments": null, "properties": [ { - "name": "Permissions", + "name": "TimeZone", "jsonName": null, - "type": "[Volo.Abp.PermissionManagement.UpdatePermissionDto]", - "typeSimple": "[Volo.Abp.PermissionManagement.UpdatePermissionDto]", - "isRequired": false + "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.TimeZone", + "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.TimeZone", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.PermissionManagement.UpdatePermissionDto": { + "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.WindowsTimeZone": { "baseType": null, "isEnum": false, "enumNames": null, "enumValues": null, "genericArguments": null, "properties": [ + { + "name": "TimeZoneId", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true + } + ] + }, + "Volo.Abp.AspNetCore.Mvc.MultiTenancy.CurrentTenantDto": { + "baseType": null, + "isEnum": false, + "enumNames": null, + "enumValues": null, + "genericArguments": null, + "properties": [ + { + "name": "Id", + "jsonName": null, + "type": "System.Guid?", + "typeSimple": "string?", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true + }, { "name": "Name", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true }, { - "name": "IsGranted", + "name": "IsAvailable", "jsonName": null, "type": "System.Boolean", "typeSimple": "boolean", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.SettingManagement.EmailSettingsDto": { + "Volo.Abp.AspNetCore.Mvc.MultiTenancy.FindTenantResultDto": { "baseType": null, "isEnum": false, "enumNames": null, @@ -3340,71 +7134,95 @@ "genericArguments": null, "properties": [ { - "name": "SmtpHost", - "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": false - }, - { - "name": "SmtpPort", + "name": "Success", "jsonName": null, - "type": "System.Int32", - "typeSimple": "number", - "isRequired": false + "type": "System.Boolean", + "typeSimple": "boolean", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "SmtpUserName", + "name": "TenantId", "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": false + "type": "System.Guid?", + "typeSimple": "string?", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true }, { - "name": "SmtpPassword", + "name": "Name", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true }, { - "name": "SmtpDomain", + "name": "NormalizedName", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true }, { - "name": "SmtpEnableSsl", + "name": "IsActive", "jsonName": null, "type": "System.Boolean", "typeSimple": "boolean", - "isRequired": false - }, + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + } + ] + }, + "Volo.Abp.AspNetCore.Mvc.MultiTenancy.MultiTenancyInfoDto": { + "baseType": null, + "isEnum": false, + "enumNames": null, + "enumValues": null, + "genericArguments": null, + "properties": [ { - "name": "SmtpUseDefaultCredentials", + "name": "IsEnabled", "jsonName": null, "type": "System.Boolean", "typeSimple": "boolean", - "isRequired": false - }, - { - "name": "DefaultFromAddress", - "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": false - }, - { - "name": "DefaultFromDisplayName", - "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.SettingManagement.UpdateEmailSettingsDto": { + "Volo.Abp.FeatureManagement.FeatureDto": { "baseType": null, "isEnum": false, "enumNames": null, @@ -3412,72 +7230,161 @@ "genericArguments": null, "properties": [ { - "name": "SmtpHost", + "name": "Name", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "SmtpPort", + "name": "DisplayName", "jsonName": null, - "type": "System.Int32", - "typeSimple": "number", - "isRequired": false + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "SmtpUserName", + "name": "Value", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "SmtpPassword", + "name": "Provider", "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": false + "type": "Volo.Abp.FeatureManagement.FeatureProviderDto", + "typeSimple": "Volo.Abp.FeatureManagement.FeatureProviderDto", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "SmtpDomain", + "name": "Description", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "SmtpEnableSsl", + "name": "ValueType", "jsonName": null, - "type": "System.Boolean", - "typeSimple": "boolean", - "isRequired": false + "type": "Volo.Abp.Validation.StringValues.IStringValueType", + "typeSimple": "Volo.Abp.Validation.StringValues.IStringValueType", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "SmtpUseDefaultCredentials", + "name": "Depth", "jsonName": null, - "type": "System.Boolean", - "typeSimple": "boolean", - "isRequired": false + "type": "System.Int32", + "typeSimple": "number", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "DefaultFromAddress", + "name": "ParentName", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + } + ] + }, + "Volo.Abp.FeatureManagement.FeatureGroupDto": { + "baseType": null, + "isEnum": false, + "enumNames": null, + "enumValues": null, + "genericArguments": null, + "properties": [ + { + "name": "Name", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": true + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "DefaultFromDisplayName", + "name": "DisplayName", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": true + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "Features", + "jsonName": null, + "type": "[Volo.Abp.FeatureManagement.FeatureDto]", + "typeSimple": "[Volo.Abp.FeatureManagement.FeatureDto]", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.TenantManagement.TenantDto": { - "baseType": "Volo.Abp.Application.Dtos.ExtensibleEntityDto", + "Volo.Abp.FeatureManagement.FeatureProviderDto": { + "baseType": null, "isEnum": false, "enumNames": null, "enumValues": null, @@ -3488,89 +7395,248 @@ "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "ConcurrencyStamp", + "name": "Key", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.TenantManagement.GetTenantsInput": { - "baseType": "Volo.Abp.Application.Dtos.PagedAndSortedResultRequestDto", + "Volo.Abp.FeatureManagement.GetFeatureListResultDto": { + "baseType": null, "isEnum": false, "enumNames": null, "enumValues": null, "genericArguments": null, "properties": [ { - "name": "Filter", + "name": "Groups", "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": false + "type": "[Volo.Abp.FeatureManagement.FeatureGroupDto]", + "typeSimple": "[Volo.Abp.FeatureManagement.FeatureGroupDto]", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.TenantManagement.TenantCreateDto": { - "baseType": "Volo.Abp.TenantManagement.TenantCreateOrUpdateDtoBase", + "Volo.Abp.FeatureManagement.UpdateFeatureDto": { + "baseType": null, "isEnum": false, "enumNames": null, "enumValues": null, "genericArguments": null, "properties": [ { - "name": "AdminEmailAddress", + "name": "Name", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": true + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "AdminPassword", + "name": "Value", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": true + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.TenantManagement.TenantCreateOrUpdateDtoBase": { - "baseType": "Volo.Abp.ObjectExtending.ExtensibleObject", + "Volo.Abp.FeatureManagement.UpdateFeaturesDto": { + "baseType": null, "isEnum": false, "enumNames": null, "enumValues": null, "genericArguments": null, "properties": [ { - "name": "Name", + "name": "Features", "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": true + "type": "[Volo.Abp.FeatureManagement.UpdateFeatureDto]", + "typeSimple": "[Volo.Abp.FeatureManagement.UpdateFeatureDto]", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.TenantManagement.TenantUpdateDto": { - "baseType": "Volo.Abp.TenantManagement.TenantCreateOrUpdateDtoBase", + "Volo.Abp.Http.Modeling.ActionApiDescriptionModel": { + "baseType": null, "isEnum": false, "enumNames": null, "enumValues": null, "genericArguments": null, "properties": [ { - "name": "ConcurrencyStamp", + "name": "UniqueName", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "Name", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "HttpMethod", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true + }, + { + "name": "Url", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "SupportedVersions", + "jsonName": null, + "type": "[System.String]", + "typeSimple": "[string]", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true + }, + { + "name": "ParametersOnMethod", + "jsonName": null, + "type": "[Volo.Abp.Http.Modeling.MethodParameterApiDescriptionModel]", + "typeSimple": "[Volo.Abp.Http.Modeling.MethodParameterApiDescriptionModel]", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "Parameters", + "jsonName": null, + "type": "[Volo.Abp.Http.Modeling.ParameterApiDescriptionModel]", + "typeSimple": "[Volo.Abp.Http.Modeling.ParameterApiDescriptionModel]", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "ReturnValue", + "jsonName": null, + "type": "Volo.Abp.Http.Modeling.ReturnValueApiDescriptionModel", + "typeSimple": "Volo.Abp.Http.Modeling.ReturnValueApiDescriptionModel", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "AllowAnonymous", + "jsonName": null, + "type": "System.Boolean?", + "typeSimple": "boolean?", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true + }, + { + "name": "ImplementFrom", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true } ] }, - "Volo.Abp.FeatureManagement.GetFeatureListResultDto": { + "Volo.Abp.Http.Modeling.ApplicationApiDescriptionModel": { "baseType": null, "isEnum": false, "enumNames": null, @@ -3578,15 +7644,34 @@ "genericArguments": null, "properties": [ { - "name": "Groups", + "name": "Modules", "jsonName": null, - "type": "[Volo.Abp.FeatureManagement.FeatureGroupDto]", - "typeSimple": "[Volo.Abp.FeatureManagement.FeatureGroupDto]", - "isRequired": false + "type": "{System.String:Volo.Abp.Http.Modeling.ModuleApiDescriptionModel}", + "typeSimple": "{string:Volo.Abp.Http.Modeling.ModuleApiDescriptionModel}", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "Types", + "jsonName": null, + "type": "{System.String:Volo.Abp.Http.Modeling.TypeApiDescriptionModel}", + "typeSimple": "{string:Volo.Abp.Http.Modeling.TypeApiDescriptionModel}", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.FeatureManagement.FeatureGroupDto": { + "Volo.Abp.Http.Modeling.ApplicationApiDescriptionModelRequestDto": { "baseType": null, "isEnum": false, "enumNames": null, @@ -3594,29 +7679,21 @@ "genericArguments": null, "properties": [ { - "name": "Name", - "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": false - }, - { - "name": "DisplayName", - "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": false - }, - { - "name": "Features", + "name": "IncludeTypes", "jsonName": null, - "type": "[Volo.Abp.FeatureManagement.FeatureDto]", - "typeSimple": "[Volo.Abp.FeatureManagement.FeatureDto]", - "isRequired": false + "type": "System.Boolean", + "typeSimple": "boolean", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.FeatureManagement.FeatureDto": { + "Volo.Abp.Http.Modeling.ControllerApiDescriptionModel": { "baseType": null, "isEnum": false, "enumNames": null, @@ -3624,64 +7701,112 @@ "genericArguments": null, "properties": [ { - "name": "Name", + "name": "ControllerName", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "DisplayName", + "name": "ControllerGroupName", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true }, { - "name": "Value", + "name": "IsRemoteService", "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": false + "type": "System.Boolean", + "typeSimple": "boolean", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "Provider", + "name": "IsIntegrationService", "jsonName": null, - "type": "Volo.Abp.FeatureManagement.FeatureProviderDto", - "typeSimple": "Volo.Abp.FeatureManagement.FeatureProviderDto", - "isRequired": false + "type": "System.Boolean", + "typeSimple": "boolean", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "Description", + "name": "ApiVersion", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true }, { - "name": "ValueType", + "name": "Type", "jsonName": null, - "type": "Volo.Abp.Validation.StringValues.IStringValueType", - "typeSimple": "Volo.Abp.Validation.StringValues.IStringValueType", - "isRequired": false + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "Depth", + "name": "Interfaces", "jsonName": null, - "type": "System.Int32", - "typeSimple": "number", - "isRequired": false + "type": "[Volo.Abp.Http.Modeling.ControllerInterfaceApiDescriptionModel]", + "typeSimple": "[Volo.Abp.Http.Modeling.ControllerInterfaceApiDescriptionModel]", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "ParentName", + "name": "Actions", "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": false + "type": "{System.String:Volo.Abp.Http.Modeling.ActionApiDescriptionModel}", + "typeSimple": "{string:Volo.Abp.Http.Modeling.ActionApiDescriptionModel}", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.FeatureManagement.FeatureProviderDto": { + "Volo.Abp.Http.Modeling.ControllerInterfaceApiDescriptionModel": { "baseType": null, "isEnum": false, "enumNames": null, @@ -3689,22 +7814,47 @@ "genericArguments": null, "properties": [ { - "name": "Name", + "name": "Type", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "Key", + "name": "Name", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "Methods", + "jsonName": null, + "type": "[Volo.Abp.Http.Modeling.InterfaceMethodApiDescriptionModel]", + "typeSimple": "[Volo.Abp.Http.Modeling.InterfaceMethodApiDescriptionModel]", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.Validation.StringValues.IStringValueType": { + "Volo.Abp.Http.Modeling.InterfaceMethodApiDescriptionModel": { "baseType": null, "isEnum": false, "enumNames": null, @@ -3716,32 +7866,43 @@ "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false - }, - { - "name": "Item", - "jsonName": null, - "type": "System.Object", - "typeSimple": "object", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "Properties", + "name": "ParametersOnMethod", "jsonName": null, - "type": "{System.String:System.Object}", - "typeSimple": "{string:object}", - "isRequired": false + "type": "[Volo.Abp.Http.Modeling.MethodParameterApiDescriptionModel]", + "typeSimple": "[Volo.Abp.Http.Modeling.MethodParameterApiDescriptionModel]", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "Validator", + "name": "ReturnValue", "jsonName": null, - "type": "Volo.Abp.Validation.StringValues.IValueValidator", - "typeSimple": "Volo.Abp.Validation.StringValues.IValueValidator", - "isRequired": false + "type": "Volo.Abp.Http.Modeling.ReturnValueApiDescriptionModel", + "typeSimple": "Volo.Abp.Http.Modeling.ReturnValueApiDescriptionModel", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.Validation.StringValues.IValueValidator": { + "Volo.Abp.Http.Modeling.MethodParameterApiDescriptionModel": { "baseType": null, "isEnum": false, "enumNames": null, @@ -3753,64 +7914,82 @@ "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "Item", + "name": "TypeAsString", "jsonName": null, - "type": "System.Object", - "typeSimple": "object", - "isRequired": false + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "Properties", - "jsonName": null, - "type": "{System.String:System.Object}", - "typeSimple": "{string:object}", - "isRequired": false - } - ] - }, - "Volo.Abp.FeatureManagement.UpdateFeaturesDto": { - "baseType": null, - "isEnum": false, - "enumNames": null, - "enumValues": null, - "genericArguments": null, - "properties": [ - { - "name": "Features", - "jsonName": null, - "type": "[Volo.Abp.FeatureManagement.UpdateFeatureDto]", - "typeSimple": "[Volo.Abp.FeatureManagement.UpdateFeatureDto]", - "isRequired": false - } - ] - }, - "Volo.Abp.FeatureManagement.UpdateFeatureDto": { - "baseType": null, - "isEnum": false, - "enumNames": null, - "enumValues": null, - "genericArguments": null, - "properties": [ - { - "name": "Name", + "name": "Type", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "Value", + "name": "TypeSimple", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "IsOptional", + "jsonName": null, + "type": "System.Boolean", + "typeSimple": "boolean", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "DefaultValue", + "jsonName": null, + "type": "System.Object", + "typeSimple": "object", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true } ] }, - "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationConfigurationDto": { + "Volo.Abp.Http.Modeling.ModuleApiDescriptionModel": { "baseType": null, "isEnum": false, "enumNames": null, @@ -3818,78 +7997,47 @@ "genericArguments": null, "properties": [ { - "name": "Localization", - "jsonName": null, - "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationLocalizationConfigurationDto", - "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationLocalizationConfigurationDto", - "isRequired": false - }, - { - "name": "Auth", - "jsonName": null, - "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationAuthConfigurationDto", - "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationAuthConfigurationDto", - "isRequired": false - }, - { - "name": "Setting", - "jsonName": null, - "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationSettingConfigurationDto", - "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationSettingConfigurationDto", - "isRequired": false - }, - { - "name": "CurrentUser", - "jsonName": null, - "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.CurrentUserDto", - "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.CurrentUserDto", - "isRequired": false - }, - { - "name": "Features", - "jsonName": null, - "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationFeatureConfigurationDto", - "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationFeatureConfigurationDto", - "isRequired": false - }, - { - "name": "MultiTenancy", - "jsonName": null, - "type": "Volo.Abp.AspNetCore.Mvc.MultiTenancy.MultiTenancyInfoDto", - "typeSimple": "Volo.Abp.AspNetCore.Mvc.MultiTenancy.MultiTenancyInfoDto", - "isRequired": false - }, - { - "name": "CurrentTenant", - "jsonName": null, - "type": "Volo.Abp.AspNetCore.Mvc.MultiTenancy.CurrentTenantDto", - "typeSimple": "Volo.Abp.AspNetCore.Mvc.MultiTenancy.CurrentTenantDto", - "isRequired": false - }, - { - "name": "Timing", + "name": "RootPath", "jsonName": null, - "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.TimingDto", - "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.TimingDto", - "isRequired": false + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "Clock", + "name": "RemoteServiceName", "jsonName": null, - "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ClockDto", - "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ClockDto", - "isRequired": false + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "ObjectExtensions", + "name": "Controllers", "jsonName": null, - "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ObjectExtensionsDto", - "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ObjectExtensionsDto", - "isRequired": false + "type": "{System.String:Volo.Abp.Http.Modeling.ControllerApiDescriptionModel}", + "typeSimple": "{string:Volo.Abp.Http.Modeling.ControllerApiDescriptionModel}", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationLocalizationConfigurationDto": { + "Volo.Abp.Http.Modeling.ParameterApiDescriptionModel": { "baseType": null, "isEnum": false, "enumNames": null, @@ -3897,87 +8045,138 @@ "genericArguments": null, "properties": [ { - "name": "Values", + "name": "NameOnMethod", "jsonName": null, - "type": "{System.String:System.Collections.Generic.Dictionary}", - "typeSimple": "{string:System.Collections.Generic.Dictionary}", - "isRequired": false + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "Languages", + "name": "Name", "jsonName": null, - "type": "[Volo.Abp.Localization.LanguageInfo]", - "typeSimple": "[Volo.Abp.Localization.LanguageInfo]", - "isRequired": false + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "CurrentCulture", + "name": "JsonName", "jsonName": null, - "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.CurrentCultureDto", - "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.CurrentCultureDto", - "isRequired": false + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true }, { - "name": "DefaultResourceName", + "name": "Type", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true }, { - "name": "LanguagesMap", + "name": "TypeSimple", "jsonName": null, - "type": "{System.String:[Volo.Abp.NameValue]}", - "typeSimple": "{string:[Volo.Abp.NameValue]}", - "isRequired": false + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true }, { - "name": "LanguageFilesMap", + "name": "IsOptional", "jsonName": null, - "type": "{System.String:[Volo.Abp.NameValue]}", - "typeSimple": "{string:[Volo.Abp.NameValue]}", - "isRequired": false - } - ] - }, - "Volo.Abp.Localization.LanguageInfo": { - "baseType": null, - "isEnum": false, - "enumNames": null, - "enumValues": null, - "genericArguments": null, - "properties": [ + "type": "System.Boolean", + "typeSimple": "boolean", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, { - "name": "CultureName", + "name": "DefaultValue", "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": false + "type": "System.Object", + "typeSimple": "object", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true }, { - "name": "UiCultureName", + "name": "ConstraintTypes", "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": false + "type": "[System.String]", + "typeSimple": "[string]", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true }, { - "name": "DisplayName", + "name": "BindingSourceId", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true }, { - "name": "FlagIcon", + "name": "DescriptorName", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true } ] }, - "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.CurrentCultureDto": { + "Volo.Abp.Http.Modeling.PropertyApiDescriptionModel": { "baseType": null, "isEnum": false, "enumNames": null, @@ -3985,71 +8184,151 @@ "genericArguments": null, "properties": [ { - "name": "DisplayName", + "name": "Name", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "EnglishName", + "name": "JsonName", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true }, { - "name": "ThreeLetterIsoLanguageName", + "name": "Type", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "TwoLetterIsoLanguageName", + "name": "TypeSimple", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "IsRightToLeft", + "name": "IsRequired", "jsonName": null, "type": "System.Boolean", "typeSimple": "boolean", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "CultureName", + "name": "MinLength", + "jsonName": null, + "type": "System.Int32?", + "typeSimple": "number?", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true + }, + { + "name": "MaxLength", + "jsonName": null, + "type": "System.Int32?", + "typeSimple": "number?", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true + }, + { + "name": "Minimum", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true }, { - "name": "Name", + "name": "Maximum", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true }, { - "name": "NativeName", + "name": "Regex", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true }, { - "name": "DateTimeFormat", + "name": "IsNullable", "jsonName": null, - "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.DateTimeFormatDto", - "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.DateTimeFormatDto", - "isRequired": false + "type": "System.Boolean", + "typeSimple": "boolean", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.DateTimeFormatDto": { + "Volo.Abp.Http.Modeling.ReturnValueApiDescriptionModel": { "baseType": null, "isEnum": false, "enumNames": null, @@ -4057,330 +8336,704 @@ "genericArguments": null, "properties": [ { - "name": "CalendarAlgorithmType", + "name": "Type", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "DateTimeFormatLong", + "name": "TypeSimple", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false - }, + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + } + ] + }, + "Volo.Abp.Http.Modeling.TypeApiDescriptionModel": { + "baseType": null, + "isEnum": false, + "enumNames": null, + "enumValues": null, + "genericArguments": null, + "properties": [ { - "name": "ShortDatePattern", + "name": "BaseType", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true }, { - "name": "FullDateTimePattern", + "name": "IsEnum", "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": false + "type": "System.Boolean", + "typeSimple": "boolean", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "DateSeparator", + "name": "EnumNames", "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": false + "type": "[System.String]", + "typeSimple": "[string]", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true }, { - "name": "ShortTimePattern", + "name": "EnumValues", + "jsonName": null, + "type": "[System.Object]", + "typeSimple": "[object]", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true + }, + { + "name": "GenericArguments", + "jsonName": null, + "type": "[System.String]", + "typeSimple": "[string]", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true + }, + { + "name": "Properties", + "jsonName": null, + "type": "[Volo.Abp.Http.Modeling.PropertyApiDescriptionModel]", + "typeSimple": "[Volo.Abp.Http.Modeling.PropertyApiDescriptionModel]", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true + } + ] + }, + "Volo.Abp.Identity.GetIdentityRolesInput": { + "baseType": "Volo.Abp.Application.Dtos.ExtensiblePagedAndSortedResultRequestDto", + "isEnum": false, + "enumNames": null, + "enumValues": null, + "genericArguments": null, + "properties": [ + { + "name": "Filter", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false - }, + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + } + ] + }, + "Volo.Abp.Identity.GetIdentityUsersInput": { + "baseType": "Volo.Abp.Application.Dtos.ExtensiblePagedAndSortedResultRequestDto", + "isEnum": false, + "enumNames": null, + "enumValues": null, + "genericArguments": null, + "properties": [ { - "name": "LongTimePattern", + "name": "Filter", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.NameValue": { - "baseType": "Volo.Abp.NameValue", + "Volo.Abp.Identity.IdentityRoleCreateDto": { + "baseType": "Volo.Abp.Identity.IdentityRoleCreateOrUpdateDtoBase", "isEnum": false, "enumNames": null, "enumValues": null, "genericArguments": null, "properties": [] }, - "Volo.Abp.NameValue": { - "baseType": null, + "Volo.Abp.Identity.IdentityRoleCreateOrUpdateDtoBase": { + "baseType": "Volo.Abp.ObjectExtending.ExtensibleObject", "isEnum": false, "enumNames": null, "enumValues": null, - "genericArguments": [ - "T" - ], + "genericArguments": null, "properties": [ { "name": "Name", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": true, + "minLength": 0, + "maxLength": 256, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "Value", + "name": "IsDefault", "jsonName": null, - "type": "T", - "typeSimple": "T", - "isRequired": false + "type": "System.Boolean", + "typeSimple": "boolean", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "IsPublic", + "jsonName": null, + "type": "System.Boolean", + "typeSimple": "boolean", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationAuthConfigurationDto": { - "baseType": null, + "Volo.Abp.Identity.IdentityRoleDto": { + "baseType": "Volo.Abp.Application.Dtos.ExtensibleEntityDto", "isEnum": false, "enumNames": null, "enumValues": null, "genericArguments": null, "properties": [ { - "name": "Policies", + "name": "Name", "jsonName": null, - "type": "{System.String:System.Boolean}", - "typeSimple": "{string:boolean}", - "isRequired": false + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "GrantedPolicies", + "name": "IsDefault", "jsonName": null, - "type": "{System.String:System.Boolean}", - "typeSimple": "{string:boolean}", - "isRequired": false + "type": "System.Boolean", + "typeSimple": "boolean", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "IsStatic", + "jsonName": null, + "type": "System.Boolean", + "typeSimple": "boolean", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "IsPublic", + "jsonName": null, + "type": "System.Boolean", + "typeSimple": "boolean", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "ConcurrencyStamp", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "CreationTime", + "jsonName": null, + "type": "System.DateTime", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationSettingConfigurationDto": { - "baseType": null, + "Volo.Abp.Identity.IdentityRoleUpdateDto": { + "baseType": "Volo.Abp.Identity.IdentityRoleCreateOrUpdateDtoBase", "isEnum": false, "enumNames": null, "enumValues": null, "genericArguments": null, "properties": [ { - "name": "Values", + "name": "ConcurrencyStamp", "jsonName": null, - "type": "{System.String:System.String}", - "typeSimple": "{string:string}", - "isRequired": false + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.CurrentUserDto": { - "baseType": null, + "Volo.Abp.Identity.IdentityUserCreateDto": { + "baseType": "Volo.Abp.Identity.IdentityUserCreateOrUpdateDtoBase", "isEnum": false, "enumNames": null, "enumValues": null, "genericArguments": null, "properties": [ { - "name": "IsAuthenticated", + "name": "Password", "jsonName": null, - "type": "System.Boolean", - "typeSimple": "boolean", - "isRequired": false + "type": "System.String", + "typeSimple": "string", + "isRequired": true, + "minLength": 0, + "maxLength": 128, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + } + ] + }, + "Volo.Abp.Identity.IdentityUserCreateOrUpdateDtoBase": { + "baseType": "Volo.Abp.ObjectExtending.ExtensibleObject", + "isEnum": false, + "enumNames": null, + "enumValues": null, + "genericArguments": null, + "properties": [ + { + "name": "UserName", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isRequired": true, + "minLength": 0, + "maxLength": 256, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "Id", + "name": "Name", "jsonName": null, - "type": "System.Guid?", - "typeSimple": "string?", - "isRequired": false + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": 0, + "maxLength": 64, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "TenantId", + "name": "Surname", "jsonName": null, - "type": "System.Guid?", - "typeSimple": "string?", - "isRequired": false + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": 0, + "maxLength": 64, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "ImpersonatorUserId", + "name": "Email", "jsonName": null, - "type": "System.Guid?", - "typeSimple": "string?", - "isRequired": false + "type": "System.String", + "typeSimple": "string", + "isRequired": true, + "minLength": 0, + "maxLength": 256, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "PhoneNumber", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": 0, + "maxLength": 16, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "IsActive", + "jsonName": null, + "type": "System.Boolean", + "typeSimple": "boolean", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "LockoutEnabled", + "jsonName": null, + "type": "System.Boolean", + "typeSimple": "boolean", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "ImpersonatorTenantId", + "name": "RoleNames", + "jsonName": null, + "type": "[System.String]", + "typeSimple": "[string]", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + } + ] + }, + "Volo.Abp.Identity.IdentityUserDto": { + "baseType": "Volo.Abp.Application.Dtos.ExtensibleFullAuditedEntityDto", + "isEnum": false, + "enumNames": null, + "enumValues": null, + "genericArguments": null, + "properties": [ + { + "name": "TenantId", "jsonName": null, "type": "System.Guid?", "typeSimple": "string?", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true }, { "name": "UserName", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { "name": "Name", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "SurName", + "name": "Surname", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { "name": "Email", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "EmailVerified", + "name": "EmailConfirmed", "jsonName": null, "type": "System.Boolean", "typeSimple": "boolean", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { "name": "PhoneNumber", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "PhoneNumberVerified", + "name": "PhoneNumberConfirmed", "jsonName": null, "type": "System.Boolean", "typeSimple": "boolean", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "Roles", - "jsonName": null, - "type": "[System.String]", - "typeSimple": "[string]", - "isRequired": false - } - ] - }, - "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationFeatureConfigurationDto": { - "baseType": null, - "isEnum": false, - "enumNames": null, - "enumValues": null, - "genericArguments": null, - "properties": [ - { - "name": "Values", + "name": "IsActive", "jsonName": null, - "type": "{System.String:System.String}", - "typeSimple": "{string:string}", - "isRequired": false - } - ] - }, - "Volo.Abp.AspNetCore.Mvc.MultiTenancy.MultiTenancyInfoDto": { - "baseType": null, - "isEnum": false, - "enumNames": null, - "enumValues": null, - "genericArguments": null, - "properties": [ + "type": "System.Boolean", + "typeSimple": "boolean", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, { - "name": "IsEnabled", + "name": "LockoutEnabled", "jsonName": null, "type": "System.Boolean", "typeSimple": "boolean", - "isRequired": false - } - ] - }, - "Volo.Abp.AspNetCore.Mvc.MultiTenancy.CurrentTenantDto": { - "baseType": null, - "isEnum": false, - "enumNames": null, - "enumValues": null, - "genericArguments": null, - "properties": [ + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, { - "name": "Id", + "name": "AccessFailedCount", "jsonName": null, - "type": "System.Guid?", + "type": "System.Int32", + "typeSimple": "number", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "LockoutEnd", + "jsonName": null, + "type": "System.DateTimeOffset?", "typeSimple": "string?", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true }, { - "name": "Name", + "name": "ConcurrencyStamp", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "IsAvailable", + "name": "EntityVersion", "jsonName": null, - "type": "System.Boolean", - "typeSimple": "boolean", - "isRequired": false - } - ] - }, - "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.TimingDto": { - "baseType": null, - "isEnum": false, - "enumNames": null, - "enumValues": null, - "genericArguments": null, - "properties": [ + "type": "System.Int32", + "typeSimple": "number", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, { - "name": "TimeZone", + "name": "LastPasswordChangeTime", "jsonName": null, - "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.TimeZone", - "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.TimeZone", - "isRequired": false + "type": "System.DateTimeOffset?", + "typeSimple": "string?", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true } ] }, - "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.TimeZone": { - "baseType": null, + "Volo.Abp.Identity.IdentityUserUpdateDto": { + "baseType": "Volo.Abp.Identity.IdentityUserCreateOrUpdateDtoBase", "isEnum": false, "enumNames": null, "enumValues": null, "genericArguments": null, "properties": [ { - "name": "Iana", + "name": "Password", "jsonName": null, - "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.IanaTimeZone", - "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.IanaTimeZone", - "isRequired": false + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": 0, + "maxLength": 128, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "Windows", + "name": "ConcurrencyStamp", "jsonName": null, - "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.WindowsTimeZone", - "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.WindowsTimeZone", - "isRequired": false + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.IanaTimeZone": { + "Volo.Abp.Identity.IdentityUserUpdateRolesDto": { "baseType": null, "isEnum": false, "enumNames": null, @@ -4388,15 +9041,21 @@ "genericArguments": null, "properties": [ { - "name": "TimeZoneName", + "name": "RoleNames", "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": false + "type": "[System.String]", + "typeSimple": "[string]", + "isRequired": true, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.WindowsTimeZone": { + "Volo.Abp.Identity.UserLookupCountInputDto": { "baseType": null, "isEnum": false, "enumNames": null, @@ -4404,31 +9063,43 @@ "genericArguments": null, "properties": [ { - "name": "TimeZoneId", + "name": "Filter", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ClockDto": { - "baseType": null, + "Volo.Abp.Identity.UserLookupSearchInputDto": { + "baseType": "Volo.Abp.Application.Dtos.ExtensiblePagedAndSortedResultRequestDto", "isEnum": false, "enumNames": null, "enumValues": null, "genericArguments": null, "properties": [ { - "name": "Kind", + "name": "Filter", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ObjectExtensionsDto": { + "Volo.Abp.Localization.LanguageInfo": { "baseType": null, "isEnum": false, "enumNames": null, @@ -4436,45 +9107,105 @@ "genericArguments": null, "properties": [ { - "name": "Modules", + "name": "CultureName", "jsonName": null, - "type": "{System.String:Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ModuleExtensionDto}", - "typeSimple": "{string:Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ModuleExtensionDto}", - "isRequired": false + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "Enums", + "name": "UiCultureName", "jsonName": null, - "type": "{System.String:Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionEnumDto}", - "typeSimple": "{string:Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionEnumDto}", - "isRequired": false + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "DisplayName", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "TwoLetterISOLanguageName", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ModuleExtensionDto": { - "baseType": null, + "Volo.Abp.NameValue": { + "baseType": "Volo.Abp.NameValue", "isEnum": false, "enumNames": null, "enumValues": null, "genericArguments": null, + "properties": [] + }, + "Volo.Abp.NameValue": { + "baseType": null, + "isEnum": false, + "enumNames": null, + "enumValues": null, + "genericArguments": [ + "T" + ], "properties": [ { - "name": "Entities", + "name": "Name", "jsonName": null, - "type": "{System.String:Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.EntityExtensionDto}", - "typeSimple": "{string:Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.EntityExtensionDto}", - "isRequired": false + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "Configuration", + "name": "Value", "jsonName": null, - "type": "{System.String:System.Object}", - "typeSimple": "{string:object}", - "isRequired": false + "type": "T", + "typeSimple": "T", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true } ] }, - "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.EntityExtensionDto": { + "Volo.Abp.ObjectExtending.ExtensibleObject": { "baseType": null, "isEnum": false, "enumNames": null, @@ -4482,22 +9213,21 @@ "genericArguments": null, "properties": [ { - "name": "Properties", - "jsonName": null, - "type": "{System.String:Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyDto}", - "typeSimple": "{string:Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyDto}", - "isRequired": false - }, - { - "name": "Configuration", + "name": "ExtraProperties", "jsonName": null, "type": "{System.String:System.Object}", "typeSimple": "{string:object}", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyDto": { + "Volo.Abp.PermissionManagement.GetPermissionListResultDto": { "baseType": null, "isEnum": false, "enumNames": null, @@ -4505,64 +9235,34 @@ "genericArguments": null, "properties": [ { - "name": "Type", - "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": false - }, - { - "name": "TypeSimple", + "name": "EntityDisplayName", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false - }, - { - "name": "DisplayName", - "jsonName": null, - "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.LocalizableStringDto", - "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.LocalizableStringDto", - "isRequired": false - }, - { - "name": "Api", - "jsonName": null, - "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyApiDto", - "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyApiDto", - "isRequired": false - }, - { - "name": "Ui", - "jsonName": null, - "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyUiDto", - "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyUiDto", - "isRequired": false - }, - { - "name": "Attributes", - "jsonName": null, - "type": "[Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyAttributeDto]", - "typeSimple": "[Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyAttributeDto]", - "isRequired": false - }, - { - "name": "Configuration", - "jsonName": null, - "type": "{System.String:System.Object}", - "typeSimple": "{string:object}", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "DefaultValue", + "name": "Groups", "jsonName": null, - "type": "System.Object", - "typeSimple": "object", - "isRequired": false + "type": "[Volo.Abp.PermissionManagement.PermissionGroupDto]", + "typeSimple": "[Volo.Abp.PermissionManagement.PermissionGroupDto]", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.LocalizableStringDto": { + "Volo.Abp.PermissionManagement.GetResourcePermissionDefinitionListResultDto": { "baseType": null, "isEnum": false, "enumNames": null, @@ -4570,22 +9270,21 @@ "genericArguments": null, "properties": [ { - "name": "Name", - "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": false - }, - { - "name": "Resource", + "name": "Permissions", "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": false + "type": "[Volo.Abp.PermissionManagement.ResourcePermissionDefinitionDto]", + "typeSimple": "[Volo.Abp.PermissionManagement.ResourcePermissionDefinitionDto]", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyApiDto": { + "Volo.Abp.PermissionManagement.GetResourcePermissionListResultDto": { "baseType": null, "isEnum": false, "enumNames": null, @@ -4593,29 +9292,21 @@ "genericArguments": null, "properties": [ { - "name": "OnGet", - "jsonName": null, - "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyApiGetDto", - "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyApiGetDto", - "isRequired": false - }, - { - "name": "OnCreate", - "jsonName": null, - "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyApiCreateDto", - "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyApiCreateDto", - "isRequired": false - }, - { - "name": "OnUpdate", + "name": "Permissions", "jsonName": null, - "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyApiUpdateDto", - "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyApiUpdateDto", - "isRequired": false + "type": "[Volo.Abp.PermissionManagement.ResourcePermissionGrantInfoDto]", + "typeSimple": "[Volo.Abp.PermissionManagement.ResourcePermissionGrantInfoDto]", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyApiGetDto": { + "Volo.Abp.PermissionManagement.GetResourcePermissionWithProviderListResultDto": { "baseType": null, "isEnum": false, "enumNames": null, @@ -4623,15 +9314,21 @@ "genericArguments": null, "properties": [ { - "name": "IsAvailable", + "name": "Permissions", "jsonName": null, - "type": "System.Boolean", - "typeSimple": "boolean", - "isRequired": false + "type": "[Volo.Abp.PermissionManagement.ResourcePermissionWithProdiverGrantInfoDto]", + "typeSimple": "[Volo.Abp.PermissionManagement.ResourcePermissionWithProdiverGrantInfoDto]", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyApiCreateDto": { + "Volo.Abp.PermissionManagement.GetResourceProviderListResultDto": { "baseType": null, "isEnum": false, "enumNames": null, @@ -4639,15 +9336,56 @@ "genericArguments": null, "properties": [ { - "name": "IsAvailable", + "name": "Providers", + "jsonName": null, + "type": "[Volo.Abp.PermissionManagement.ResourceProviderDto]", + "typeSimple": "[Volo.Abp.PermissionManagement.ResourceProviderDto]", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + } + ] + }, + "Volo.Abp.PermissionManagement.GrantedResourcePermissionDto": { + "baseType": null, + "isEnum": false, + "enumNames": null, + "enumValues": null, + "genericArguments": null, + "properties": [ + { + "name": "Name", "jsonName": null, - "type": "System.Boolean", - "typeSimple": "boolean", - "isRequired": false + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "DisplayName", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyApiUpdateDto": { + "Volo.Abp.PermissionManagement.PermissionGrantInfoDto": { "baseType": null, "isEnum": false, "enumNames": null, @@ -4655,15 +9393,86 @@ "genericArguments": null, "properties": [ { - "name": "IsAvailable", + "name": "Name", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "DisplayName", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "ParentName", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "IsGranted", "jsonName": null, "type": "System.Boolean", "typeSimple": "boolean", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "AllowedProviders", + "jsonName": null, + "type": "[System.String]", + "typeSimple": "[string]", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "GrantedProviders", + "jsonName": null, + "type": "[Volo.Abp.PermissionManagement.ProviderInfoDto]", + "typeSimple": "[Volo.Abp.PermissionManagement.ProviderInfoDto]", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyUiDto": { + "Volo.Abp.PermissionManagement.PermissionGroupDto": { "baseType": null, "isEnum": false, "enumNames": null, @@ -4671,36 +9480,73 @@ "genericArguments": null, "properties": [ { - "name": "OnTable", + "name": "Name", "jsonName": null, - "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyUiTableDto", - "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyUiTableDto", - "isRequired": false + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "OnCreateForm", + "name": "DisplayName", "jsonName": null, - "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyUiFormDto", - "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyUiFormDto", - "isRequired": false + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "OnEditForm", + "name": "DisplayNameKey", "jsonName": null, - "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyUiFormDto", - "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyUiFormDto", - "isRequired": false + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "Lookup", + "name": "DisplayNameResource", "jsonName": null, - "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyUiLookupDto", - "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyUiLookupDto", - "isRequired": false + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "Permissions", + "jsonName": null, + "type": "[Volo.Abp.PermissionManagement.PermissionGrantInfoDto]", + "typeSimple": "[Volo.Abp.PermissionManagement.PermissionGrantInfoDto]", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyUiTableDto": { + "Volo.Abp.PermissionManagement.ProviderInfoDto": { "baseType": null, "isEnum": false, "enumNames": null, @@ -4708,15 +9554,34 @@ "genericArguments": null, "properties": [ { - "name": "IsVisible", + "name": "ProviderName", "jsonName": null, - "type": "System.Boolean", - "typeSimple": "boolean", - "isRequired": false + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "ProviderKey", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyUiFormDto": { + "Volo.Abp.PermissionManagement.ResourcePermissionDefinitionDto": { "baseType": null, "isEnum": false, "enumNames": null, @@ -4724,15 +9589,34 @@ "genericArguments": null, "properties": [ { - "name": "IsVisible", + "name": "Name", "jsonName": null, - "type": "System.Boolean", - "typeSimple": "boolean", - "isRequired": false + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "DisplayName", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyUiLookupDto": { + "Volo.Abp.PermissionManagement.ResourcePermissionGrantInfoDto": { "baseType": null, "isEnum": false, "enumNames": null, @@ -4740,43 +9624,73 @@ "genericArguments": null, "properties": [ { - "name": "Url", + "name": "ProviderName", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "ResultListPropertyName", + "name": "ProviderKey", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "DisplayPropertyName", + "name": "ProviderDisplayName", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "ValuePropertyName", + "name": "ProviderNameDisplayName", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "FilterParamName", + "name": "Permissions", "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": false + "type": "[Volo.Abp.PermissionManagement.GrantedResourcePermissionDto]", + "typeSimple": "[Volo.Abp.PermissionManagement.GrantedResourcePermissionDto]", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionPropertyAttributeDto": { + "Volo.Abp.PermissionManagement.ResourcePermissionWithProdiverGrantInfoDto": { "baseType": null, "isEnum": false, "enumNames": null, @@ -4784,22 +9698,60 @@ "genericArguments": null, "properties": [ { - "name": "TypeSimple", + "name": "Name", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "Config", + "name": "DisplayName", "jsonName": null, - "type": "{System.String:System.Object}", - "typeSimple": "{string:object}", - "isRequired": false + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "Providers", + "jsonName": null, + "type": "[System.String]", + "typeSimple": "[string]", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "IsGranted", + "jsonName": null, + "type": "System.Boolean", + "typeSimple": "boolean", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionEnumDto": { + "Volo.Abp.PermissionManagement.ResourceProviderDto": { "baseType": null, "isEnum": false, "enumNames": null, @@ -4807,22 +9759,34 @@ "genericArguments": null, "properties": [ { - "name": "Fields", + "name": "Name", "jsonName": null, - "type": "[Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionEnumFieldDto]", - "typeSimple": "[Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionEnumFieldDto]", - "isRequired": false + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "LocalizationResource", + "name": "DisplayName", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending.ExtensionEnumFieldDto": { + "Volo.Abp.PermissionManagement.SearchProviderKeyInfo": { "baseType": null, "isEnum": false, "enumNames": null, @@ -4830,22 +9794,34 @@ "genericArguments": null, "properties": [ { - "name": "Name", + "name": "ProviderKey", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "Value", + "name": "ProviderDisplayName", "jsonName": null, - "type": "System.Object", - "typeSimple": "object", - "isRequired": false + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.Http.Modeling.ApplicationApiDescriptionModelRequestDto": { + "Volo.Abp.PermissionManagement.SearchProviderKeyListResultDto": { "baseType": null, "isEnum": false, "enumNames": null, @@ -4853,15 +9829,21 @@ "genericArguments": null, "properties": [ { - "name": "IncludeTypes", - "jsonName": null, - "type": "System.Boolean", - "typeSimple": "boolean", - "isRequired": false + "name": "Keys", + "jsonName": null, + "type": "[Volo.Abp.PermissionManagement.SearchProviderKeyInfo]", + "typeSimple": "[Volo.Abp.PermissionManagement.SearchProviderKeyInfo]", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.Http.Modeling.ApplicationApiDescriptionModel": { + "Volo.Abp.PermissionManagement.UpdatePermissionDto": { "baseType": null, "isEnum": false, "enumNames": null, @@ -4869,22 +9851,34 @@ "genericArguments": null, "properties": [ { - "name": "Modules", + "name": "Name", "jsonName": null, - "type": "{System.String:Volo.Abp.Http.Modeling.ModuleApiDescriptionModel}", - "typeSimple": "{string:Volo.Abp.Http.Modeling.ModuleApiDescriptionModel}", - "isRequired": false + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "Types", + "name": "IsGranted", "jsonName": null, - "type": "{System.String:Volo.Abp.Http.Modeling.TypeApiDescriptionModel}", - "typeSimple": "{string:Volo.Abp.Http.Modeling.TypeApiDescriptionModel}", - "isRequired": false + "type": "System.Boolean", + "typeSimple": "boolean", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.Http.Modeling.ModuleApiDescriptionModel": { + "Volo.Abp.PermissionManagement.UpdatePermissionsDto": { "baseType": null, "isEnum": false, "enumNames": null, @@ -4892,29 +9886,69 @@ "genericArguments": null, "properties": [ { - "name": "RootPath", + "name": "Permissions", + "jsonName": null, + "type": "[Volo.Abp.PermissionManagement.UpdatePermissionDto]", + "typeSimple": "[Volo.Abp.PermissionManagement.UpdatePermissionDto]", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + } + ] + }, + "Volo.Abp.PermissionManagement.UpdateResourcePermissionsDto": { + "baseType": null, + "isEnum": false, + "enumNames": null, + "enumValues": null, + "genericArguments": null, + "properties": [ + { + "name": "ProviderName", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "RemoteServiceName", + "name": "ProviderKey", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "Controllers", + "name": "Permissions", "jsonName": null, - "type": "{System.String:Volo.Abp.Http.Modeling.ControllerApiDescriptionModel}", - "typeSimple": "{string:Volo.Abp.Http.Modeling.ControllerApiDescriptionModel}", - "isRequired": false + "type": "[System.String]", + "typeSimple": "[string]", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.Http.Modeling.ControllerApiDescriptionModel": { + "Volo.Abp.SettingManagement.EmailSettingsDto": { "baseType": null, "isEnum": false, "enumNames": null, @@ -4922,43 +9956,125 @@ "genericArguments": null, "properties": [ { - "name": "ControllerName", + "name": "SmtpHost", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "ControllerGroupName", + "name": "SmtpPort", + "jsonName": null, + "type": "System.Int32", + "typeSimple": "number", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "SmtpUserName", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "Type", + "name": "SmtpPassword", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "Interfaces", + "name": "SmtpDomain", "jsonName": null, - "type": "[Volo.Abp.Http.Modeling.ControllerInterfaceApiDescriptionModel]", - "typeSimple": "[Volo.Abp.Http.Modeling.ControllerInterfaceApiDescriptionModel]", - "isRequired": false + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "Actions", + "name": "SmtpEnableSsl", + "jsonName": null, + "type": "System.Boolean", + "typeSimple": "boolean", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "SmtpUseDefaultCredentials", + "jsonName": null, + "type": "System.Boolean", + "typeSimple": "boolean", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "DefaultFromAddress", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "DefaultFromDisplayName", "jsonName": null, - "type": "{System.String:Volo.Abp.Http.Modeling.ActionApiDescriptionModel}", - "typeSimple": "{string:Volo.Abp.Http.Modeling.ActionApiDescriptionModel}", - "isRequired": false + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.Http.Modeling.ControllerInterfaceApiDescriptionModel": { + "Volo.Abp.SettingManagement.SendTestEmailInput": { "baseType": null, "isEnum": false, "enumNames": null, @@ -4966,15 +10082,60 @@ "genericArguments": null, "properties": [ { - "name": "Type", + "name": "SenderEmailAddress", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isRequired": true, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "TargetEmailAddress", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isRequired": true, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "Subject", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": true, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + }, + { + "name": "Body", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.Http.Modeling.ActionApiDescriptionModel": { + "Volo.Abp.SettingManagement.UpdateEmailSettingsDto": { "baseType": null, "isEnum": false, "enumNames": null, @@ -4982,129 +10143,261 @@ "genericArguments": null, "properties": [ { - "name": "UniqueName", + "name": "SmtpHost", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": 256, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "Name", + "name": "SmtpPort", "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": false + "type": "System.Int32", + "typeSimple": "number", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": "1", + "maximum": "65535", + "regex": null, + "isNullable": false }, { - "name": "HttpMethod", + "name": "SmtpUserName", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": 1024, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "Url", + "name": "SmtpPassword", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": 1024, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "SupportedVersions", + "name": "SmtpDomain", "jsonName": null, - "type": "[System.String]", - "typeSimple": "[string]", - "isRequired": false + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": 1024, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "ParametersOnMethod", + "name": "SmtpEnableSsl", "jsonName": null, - "type": "[Volo.Abp.Http.Modeling.MethodParameterApiDescriptionModel]", - "typeSimple": "[Volo.Abp.Http.Modeling.MethodParameterApiDescriptionModel]", - "isRequired": false + "type": "System.Boolean", + "typeSimple": "boolean", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "Parameters", + "name": "SmtpUseDefaultCredentials", "jsonName": null, - "type": "[Volo.Abp.Http.Modeling.ParameterApiDescriptionModel]", - "typeSimple": "[Volo.Abp.Http.Modeling.ParameterApiDescriptionModel]", - "isRequired": false + "type": "System.Boolean", + "typeSimple": "boolean", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "ReturnValue", + "name": "DefaultFromAddress", "jsonName": null, - "type": "Volo.Abp.Http.Modeling.ReturnValueApiDescriptionModel", - "typeSimple": "Volo.Abp.Http.Modeling.ReturnValueApiDescriptionModel", - "isRequired": false + "type": "System.String", + "typeSimple": "string", + "isRequired": true, + "minLength": null, + "maxLength": 1024, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "AllowAnonymous", + "name": "DefaultFromDisplayName", "jsonName": null, - "type": "System.Boolean?", - "typeSimple": "boolean?", - "isRequired": false - }, + "type": "System.String", + "typeSimple": "string", + "isRequired": true, + "minLength": null, + "maxLength": 1024, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + } + ] + }, + "Volo.Abp.TenantManagement.GetTenantsInput": { + "baseType": "Volo.Abp.Application.Dtos.PagedAndSortedResultRequestDto", + "isEnum": false, + "enumNames": null, + "enumValues": null, + "genericArguments": null, + "properties": [ { - "name": "ImplementFrom", + "name": "Filter", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.Http.Modeling.MethodParameterApiDescriptionModel": { - "baseType": null, + "Volo.Abp.TenantManagement.TenantCreateDto": { + "baseType": "Volo.Abp.TenantManagement.TenantCreateOrUpdateDtoBase", "isEnum": false, "enumNames": null, "enumValues": null, "genericArguments": null, "properties": [ { - "name": "Name", + "name": "AdminEmailAddress", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": true, + "minLength": null, + "maxLength": 256, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "TypeAsString", + "name": "AdminPassword", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false - }, + "isRequired": true, + "minLength": null, + "maxLength": 128, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + } + ] + }, + "Volo.Abp.TenantManagement.TenantCreateOrUpdateDtoBase": { + "baseType": "Volo.Abp.ObjectExtending.ExtensibleObject", + "isEnum": false, + "enumNames": null, + "enumValues": null, + "genericArguments": null, + "properties": [ { - "name": "Type", + "name": "Name", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false - }, + "isRequired": true, + "minLength": 0, + "maxLength": 64, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + } + ] + }, + "Volo.Abp.TenantManagement.TenantDto": { + "baseType": "Volo.Abp.Application.Dtos.ExtensibleEntityDto", + "isEnum": false, + "enumNames": null, + "enumValues": null, + "genericArguments": null, + "properties": [ { - "name": "TypeSimple", + "name": "Name", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "IsOptional", + "name": "ConcurrencyStamp", "jsonName": null, - "type": "System.Boolean", - "typeSimple": "boolean", - "isRequired": false - }, + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false + } + ] + }, + "Volo.Abp.TenantManagement.TenantUpdateDto": { + "baseType": "Volo.Abp.TenantManagement.TenantCreateOrUpdateDtoBase", + "isEnum": false, + "enumNames": null, + "enumValues": null, + "genericArguments": null, + "properties": [ { - "name": "DefaultValue", + "name": "ConcurrencyStamp", "jsonName": null, - "type": "System.Object", - "typeSimple": "object", - "isRequired": false + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.Http.Modeling.ParameterApiDescriptionModel": { + "Volo.Abp.Users.UserData": { "baseType": null, "isEnum": false, "enumNames": null, @@ -5112,101 +10405,151 @@ "genericArguments": null, "properties": [ { - "name": "NameOnMethod", + "name": "Id", "jsonName": null, - "type": "System.String", + "type": "System.Guid", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "Name", + "name": "TenantId", "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": false + "type": "System.Guid?", + "typeSimple": "string?", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true }, { - "name": "JsonName", + "name": "UserName", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "Type", + "name": "Name", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "TypeSimple", + "name": "Surname", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "IsOptional", + "name": "IsActive", "jsonName": null, "type": "System.Boolean", "typeSimple": "boolean", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "DefaultValue", + "name": "Email", "jsonName": null, - "type": "System.Object", - "typeSimple": "object", - "isRequired": false + "type": "System.String", + "typeSimple": "string", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "ConstraintTypes", + "name": "EmailConfirmed", "jsonName": null, - "type": "[System.String]", - "typeSimple": "[string]", - "isRequired": false + "type": "System.Boolean", + "typeSimple": "boolean", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "BindingSourceId", + "name": "PhoneNumber", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "DescriptorName", - "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": false - } - ] - }, - "Volo.Abp.Http.Modeling.ReturnValueApiDescriptionModel": { - "baseType": null, - "isEnum": false, - "enumNames": null, - "enumValues": null, - "genericArguments": null, - "properties": [ - { - "name": "Type", + "name": "PhoneNumberConfirmed", "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": false + "type": "System.Boolean", + "typeSimple": "boolean", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "TypeSimple", + "name": "ExtraProperties", "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": false + "type": "{System.String:System.Object}", + "typeSimple": "{string:object}", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.Http.Modeling.TypeApiDescriptionModel": { + "Volo.Abp.Validation.StringValues.IStringValueType": { "baseType": null, "isEnum": false, "enumNames": null, @@ -5214,50 +10557,60 @@ "genericArguments": null, "properties": [ { - "name": "BaseType", + "name": "Name", "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false - }, - { - "name": "IsEnum", - "jsonName": null, - "type": "System.Boolean", - "typeSimple": "boolean", - "isRequired": false - }, - { - "name": "EnumNames", - "jsonName": null, - "type": "[System.String]", - "typeSimple": "[string]", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "EnumValues", + "name": "Item", "jsonName": null, - "type": "[System.Object]", - "typeSimple": "[object]", - "isRequired": false + "type": "System.Object", + "typeSimple": "object", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true }, { - "name": "GenericArguments", + "name": "Properties", "jsonName": null, - "type": "[System.String]", - "typeSimple": "[string]", - "isRequired": false + "type": "{System.String:System.Object}", + "typeSimple": "{string:object}", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "Properties", + "name": "Validator", "jsonName": null, - "type": "[Volo.Abp.Http.Modeling.PropertyApiDescriptionModel]", - "typeSimple": "[Volo.Abp.Http.Modeling.PropertyApiDescriptionModel]", - "isRequired": false + "type": "Volo.Abp.Validation.StringValues.IValueValidator", + "typeSimple": "Volo.Abp.Validation.StringValues.IValueValidator", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] }, - "Volo.Abp.Http.Modeling.PropertyApiDescriptionModel": { + "Volo.Abp.Validation.StringValues.IValueValidator": { "baseType": null, "isEnum": false, "enumNames": null, @@ -5269,35 +10622,39 @@ "jsonName": null, "type": "System.String", "typeSimple": "string", - "isRequired": false - }, - { - "name": "JsonName", - "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": false - }, - { - "name": "Type", - "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": false + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false }, { - "name": "TypeSimple", + "name": "Item", "jsonName": null, - "type": "System.String", - "typeSimple": "string", - "isRequired": false + "type": "System.Object", + "typeSimple": "object", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": true }, { - "name": "IsRequired", + "name": "Properties", "jsonName": null, - "type": "System.Boolean", - "typeSimple": "boolean", - "isRequired": false + "type": "{System.String:System.Object}", + "typeSimple": "{string:object}", + "isRequired": false, + "minLength": null, + "maxLength": null, + "minimum": null, + "maximum": null, + "regex": null, + "isNullable": false } ] } diff --git a/npm/ng-packs/packages/permission-management/proxy/src/lib/proxy/models.ts b/npm/ng-packs/packages/permission-management/proxy/src/lib/proxy/models.ts index cbba2214ec..49e67f7614 100644 --- a/npm/ng-packs/packages/permission-management/proxy/src/lib/proxy/models.ts +++ b/npm/ng-packs/packages/permission-management/proxy/src/lib/proxy/models.ts @@ -4,6 +4,27 @@ export interface GetPermissionListResultDto { groups: PermissionGroupDto[]; } +export interface GetResourcePermissionDefinitionListResultDto { + permissions?: ResourcePermissionDefinitionDto[]; +} + +export interface GetResourcePermissionListResultDto { + permissions?: ResourcePermissionGrantInfoDto[]; +} + +export interface GetResourcePermissionWithProviderListResultDto { + permissions?: ResourcePermissionWithProdiverGrantInfoDto[]; +} + +export interface GetResourceProviderListResultDto { + providers?: ResourceProviderDto[]; +} + +export interface GrantedResourcePermissionDto { + name?: string; + displayName?: string; +} + export interface PermissionGrantInfoDto { name?: string; displayName?: string; @@ -17,6 +38,8 @@ export interface PermissionGroupDto { name?: string; displayName?: string; permissions: PermissionGrantInfoDto[]; + displayNameKey?: string; + displayNameResource?: string; } export interface ProviderInfoDto { @@ -24,6 +47,40 @@ export interface ProviderInfoDto { providerKey?: string; } +export interface ResourcePermissionDefinitionDto { + name?: string; + displayName?: string; +} + +export interface ResourcePermissionGrantInfoDto { + providerName?: string; + providerKey?: string; + providerDisplayName?: string; + providerNameDisplayName?: string; + permissions?: GrantedResourcePermissionDto[]; +} + +export interface ResourcePermissionWithProdiverGrantInfoDto { + name?: string; + displayName?: string; + providers?: string[]; + isGranted?: boolean; +} + +export interface ResourceProviderDto { + name?: string; + displayName?: string; +} + +export interface SearchProviderKeyInfo { + providerKey?: string; + providerDisplayName?: string; +} + +export interface SearchProviderKeyListResultDto { + keys?: SearchProviderKeyInfo[]; +} + export interface UpdatePermissionDto { name?: string; isGranted: boolean; @@ -32,3 +89,9 @@ export interface UpdatePermissionDto { export interface UpdatePermissionsDto { permissions: UpdatePermissionDto[]; } + +export interface UpdateResourcePermissionsDto { + providerName?: string; + providerKey?: string; + permissions?: string[]; +} diff --git a/npm/ng-packs/packages/permission-management/proxy/src/lib/proxy/permissions.service.ts b/npm/ng-packs/packages/permission-management/proxy/src/lib/proxy/permissions.service.ts index dbd13ca778..8f28edf3fb 100644 --- a/npm/ng-packs/packages/permission-management/proxy/src/lib/proxy/permissions.service.ts +++ b/npm/ng-packs/packages/permission-management/proxy/src/lib/proxy/permissions.service.ts @@ -1,5 +1,5 @@ -import type { GetPermissionListResultDto, UpdatePermissionsDto } from './models'; -import { RestService } from '@abp/ng.core'; +import type { GetPermissionListResultDto, GetResourcePermissionDefinitionListResultDto, GetResourcePermissionListResultDto, GetResourcePermissionWithProviderListResultDto, GetResourceProviderListResultDto, SearchProviderKeyListResultDto, UpdatePermissionsDto, UpdateResourcePermissionsDto } from './models'; +import { RestService, Rest } from '@abp/ng.core'; import { Injectable, inject } from '@angular/core'; @Injectable({ @@ -7,23 +7,97 @@ import { Injectable, inject } from '@angular/core'; }) export class PermissionsService { private restService = inject(RestService); - apiName = 'AbpPermissionManagement'; - get = (providerName: string, providerKey: string) => + + deleteResource = (resourceName: string, resourceKey: string, providerName: string, providerKey: string, config?: Partial) => + this.restService.request({ + method: 'DELETE', + url: '/api/permission-management/permissions/resource', + params: { resourceName, resourceKey, providerName, providerKey }, + }, + { apiName: this.apiName, ...config }); + + + get = (providerName: string, providerKey: string, config?: Partial) => this.restService.request({ method: 'GET', url: '/api/permission-management/permissions', params: { providerName, providerKey }, }, - { apiName: this.apiName }); + { apiName: this.apiName, ...config }); + + + getByGroup = (groupName: string, providerName: string, providerKey: string, config?: Partial) => + this.restService.request({ + method: 'GET', + url: '/api/permission-management/permissions/by-group', + params: { groupName, providerName, providerKey }, + }, + { apiName: this.apiName, ...config }); + + + getResource = (resourceName: string, resourceKey: string, config?: Partial) => + this.restService.request({ + method: 'GET', + url: '/api/permission-management/permissions/resource', + params: { resourceName, resourceKey }, + }, + { apiName: this.apiName, ...config }); + + + getResourceByProvider = (resourceName: string, resourceKey: string, providerName: string, providerKey: string, config?: Partial) => + this.restService.request({ + method: 'GET', + url: '/api/permission-management/permissions/resource/by-provider', + params: { resourceName, resourceKey, providerName, providerKey }, + }, + { apiName: this.apiName, ...config }); - update = (providerName: string, providerKey: string, input: UpdatePermissionsDto) => + + getResourceDefinitions = (resourceName: string, config?: Partial) => + this.restService.request({ + method: 'GET', + url: '/api/permission-management/permissions/resource-definitions', + params: { resourceName }, + }, + { apiName: this.apiName, ...config }); + + + getResourceProviderKeyLookupServices = (resourceName: string, config?: Partial) => + this.restService.request({ + method: 'GET', + url: '/api/permission-management/permissions/resource-provider-key-lookup-services', + params: { resourceName }, + }, + { apiName: this.apiName, ...config }); + + + searchResourceProviderKey = (resourceName: string, serviceName: string, filter: string, page: number, config?: Partial) => + this.restService.request({ + method: 'GET', + url: '/api/permission-management/permissions/search-resource-provider-keys', + params: { resourceName, serviceName, filter, page }, + }, + { apiName: this.apiName, ...config }); + + + update = (providerName: string, providerKey: string, input: UpdatePermissionsDto, config?: Partial) => this.restService.request({ method: 'PUT', url: '/api/permission-management/permissions', params: { providerName, providerKey }, body: input, }, - { apiName: this.apiName }); + { apiName: this.apiName, ...config }); + + + updateResource = (resourceName: string, resourceKey: string, input: UpdateResourcePermissionsDto, config?: Partial) => + this.restService.request({ + method: 'PUT', + url: '/api/permission-management/permissions/resource', + params: { resourceName, resourceKey }, + body: input, + }, + { apiName: this.apiName, ...config }); } diff --git a/npm/ng-packs/packages/permission-management/src/lib/components/index.ts b/npm/ng-packs/packages/permission-management/src/lib/components/index.ts index efa91b45a2..074ddb880d 100644 --- a/npm/ng-packs/packages/permission-management/src/lib/components/index.ts +++ b/npm/ng-packs/packages/permission-management/src/lib/components/index.ts @@ -1 +1,6 @@ export * from './permission-management.component'; +export * from './resource-permission-management/resource-permission-management.component'; +export * from './resource-permission-management/provider-key-search/provider-key-search.component'; +export * from './resource-permission-management/permission-checkbox-list/permission-checkbox-list.component'; +export * from './resource-permission-management/resource-permission-list/resource-permission-list.component'; +export * from './resource-permission-management/resource-permission-form/resource-permission-form.component'; diff --git a/npm/ng-packs/packages/permission-management/src/lib/components/resource-permission-management/permission-checkbox-list/permission-checkbox-list.component.html b/npm/ng-packs/packages/permission-management/src/lib/components/resource-permission-management/permission-checkbox-list/permission-checkbox-list.component.html new file mode 100644 index 0000000000..aeaefdff20 --- /dev/null +++ b/npm/ng-packs/packages/permission-management/src/lib/components/resource-permission-management/permission-checkbox-list/permission-checkbox-list.component.html @@ -0,0 +1,33 @@ +
+ @if (showTitle()) { +
{{ title() | abpLocalization }}
+ } +
+ + +
+
+ @for (perm of permissions(); track perm.name) { +
+ + +
+ } +
+
diff --git a/npm/ng-packs/packages/permission-management/src/lib/components/resource-permission-management/permission-checkbox-list/permission-checkbox-list.component.scss b/npm/ng-packs/packages/permission-management/src/lib/components/resource-permission-management/permission-checkbox-list/permission-checkbox-list.component.scss new file mode 100644 index 0000000000..0562864716 --- /dev/null +++ b/npm/ng-packs/packages/permission-management/src/lib/components/resource-permission-management/permission-checkbox-list/permission-checkbox-list.component.scss @@ -0,0 +1,4 @@ +.abp-permission-list-container { + max-height: 300px; + overflow-y: auto; +} \ No newline at end of file diff --git a/npm/ng-packs/packages/permission-management/src/lib/components/resource-permission-management/permission-checkbox-list/permission-checkbox-list.component.ts b/npm/ng-packs/packages/permission-management/src/lib/components/resource-permission-management/permission-checkbox-list/permission-checkbox-list.component.ts new file mode 100644 index 0000000000..26c5a314f9 --- /dev/null +++ b/npm/ng-packs/packages/permission-management/src/lib/components/resource-permission-management/permission-checkbox-list/permission-checkbox-list.component.ts @@ -0,0 +1,24 @@ +import { Component, input, inject, ChangeDetectionStrategy } from '@angular/core'; +import { LocalizationPipe } from '@abp/ng.core'; +import { ResourcePermissionStateService } from '../../../services/resource-permission-state.service'; + +interface PermissionItem { + name?: string | null; + displayName?: string | null; +} + +@Component({ + selector: 'abp-permission-checkbox-list', + templateUrl: './permission-checkbox-list.component.html', + styleUrl: './permission-checkbox-list.component.scss', + imports: [LocalizationPipe], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class PermissionCheckboxListComponent { + readonly state = inject(ResourcePermissionStateService); + + readonly permissions = input.required(); + readonly idPrefix = input('default'); + readonly title = input('AbpPermissionManagement::ResourcePermissionPermissions'); + readonly showTitle = input(true); +} diff --git a/npm/ng-packs/packages/permission-management/src/lib/components/resource-permission-management/provider-key-search/provider-key-search.component.html b/npm/ng-packs/packages/permission-management/src/lib/components/resource-permission-management/provider-key-search/provider-key-search.component.html new file mode 100644 index 0000000000..b04e1552c9 --- /dev/null +++ b/npm/ng-packs/packages/permission-management/src/lib/components/resource-permission-management/provider-key-search/provider-key-search.component.html @@ -0,0 +1,10 @@ + diff --git a/npm/ng-packs/packages/permission-management/src/lib/components/resource-permission-management/provider-key-search/provider-key-search.component.ts b/npm/ng-packs/packages/permission-management/src/lib/components/resource-permission-management/provider-key-search/provider-key-search.component.ts new file mode 100644 index 0000000000..d8379890fb --- /dev/null +++ b/npm/ng-packs/packages/permission-management/src/lib/components/resource-permission-management/provider-key-search/provider-key-search.component.ts @@ -0,0 +1,67 @@ +import { + Component, + input, + inject, + OnInit, + ChangeDetectionStrategy, + DestroyRef, +} from '@angular/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { PermissionsService } from '@abp/ng.permission-management/proxy'; +import { LookupSearchComponent, LookupItem } from '@abp/ng.components/lookup'; +import { Observable, map } from 'rxjs'; +import { ResourcePermissionStateService } from '../../../services/resource-permission-state.service'; + +interface ProviderKeyLookupItem extends LookupItem { + providerKey: string; + providerDisplayName?: string; +} + +@Component({ + selector: 'abp-provider-key-search', + templateUrl: './provider-key-search.component.html', + imports: [LookupSearchComponent], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class ProviderKeySearchComponent implements OnInit { + readonly state = inject(ResourcePermissionStateService); + private readonly service = inject(PermissionsService); + private readonly destroyRef = inject(DestroyRef); + + readonly resourceName = input.required(); + + searchFn: (filter: string) => Observable = () => new Observable(); + + ngOnInit() { + this.searchFn = (filter: string) => this.loadProviderKeys(filter); + } + + onItemSelected(item: ProviderKeyLookupItem) { + // State is already updated via displayValue and selectedValue bindings + // This handler can be used for additional side effects if needed + } + + private loadProviderKeys(filter: string): Observable { + const providerName = this.state.selectedProviderName(); + if (!providerName) { + return new Observable(subscriber => { + subscriber.next([]); + subscriber.complete(); + }); + } + + return this.service + .searchResourceProviderKey(this.resourceName(), providerName, filter, 1) + .pipe( + map(res => + (res.keys || []).map(k => ({ + key: k.providerKey || '', + displayName: k.providerDisplayName || k.providerKey || '', + providerKey: k.providerKey || '', + providerDisplayName: k.providerDisplayName || undefined, + })), + ), + takeUntilDestroyed(this.destroyRef), + ); + } +} diff --git a/npm/ng-packs/packages/permission-management/src/lib/components/resource-permission-management/resource-permission-form/resource-permission-form.component.html b/npm/ng-packs/packages/permission-management/src/lib/components/resource-permission-management/resource-permission-form/resource-permission-form.component.html new file mode 100644 index 0000000000..50d6370185 --- /dev/null +++ b/npm/ng-packs/packages/permission-management/src/lib/components/resource-permission-management/resource-permission-form/resource-permission-form.component.html @@ -0,0 +1,37 @@ +@if (mode() === eResourcePermissionViewModes.Add) { +
+ +
+ @for (provider of state.providers(); track provider.name; let i = $index) { +
+ + +
+ } +
+ + +
+ + +} @else { +
+

{{ 'AbpPermissionManagement::Permissions' | abpLocalization }}

+ +
+} diff --git a/npm/ng-packs/packages/permission-management/src/lib/components/resource-permission-management/resource-permission-form/resource-permission-form.component.ts b/npm/ng-packs/packages/permission-management/src/lib/components/resource-permission-management/resource-permission-form/resource-permission-form.component.ts new file mode 100644 index 0000000000..8761da7f90 --- /dev/null +++ b/npm/ng-packs/packages/permission-management/src/lib/components/resource-permission-management/resource-permission-form/resource-permission-form.component.ts @@ -0,0 +1,31 @@ +import { Component, input, inject, output, ChangeDetectionStrategy } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { LocalizationPipe } from '@abp/ng.core'; +import { ResourcePermissionStateService } from '../../../services/resource-permission-state.service'; +import { ProviderKeySearchComponent } from '../provider-key-search/provider-key-search.component'; +import { PermissionCheckboxListComponent } from '../permission-checkbox-list/permission-checkbox-list.component'; + +import { eResourcePermissionViewModes } from '../../../enums/view-modes'; + +@Component({ + selector: 'abp-resource-permission-form', + templateUrl: './resource-permission-form.component.html', + imports: [ + FormsModule, + LocalizationPipe, + ProviderKeySearchComponent, + PermissionCheckboxListComponent, + ], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class ResourcePermissionFormComponent { + readonly state = inject(ResourcePermissionStateService); + readonly eResourcePermissionViewModes = eResourcePermissionViewModes; + + readonly mode = input.required(); + readonly resourceName = input.required(); + + readonly save = output(); + readonly cancel = output(); +} + diff --git a/npm/ng-packs/packages/permission-management/src/lib/components/resource-permission-management/resource-permission-list/resource-permission-list.component.html b/npm/ng-packs/packages/permission-management/src/lib/components/resource-permission-management/resource-permission-list/resource-permission-list.component.html new file mode 100644 index 0000000000..a00c909aff --- /dev/null +++ b/npm/ng-packs/packages/permission-management/src/lib/components/resource-permission-management/resource-permission-list/resource-permission-list.component.html @@ -0,0 +1,41 @@ +
+ +
+ + +
+ + +
+
+ +@if (state.resourcePermissions().length > 0) { + +} @else { +
+ {{ 'AbpPermissionManagement::NoPermissionsAssigned' | abpLocalization }} +
+} diff --git a/npm/ng-packs/packages/permission-management/src/lib/components/resource-permission-management/resource-permission-list/resource-permission-list.component.ts b/npm/ng-packs/packages/permission-management/src/lib/components/resource-permission-management/resource-permission-list/resource-permission-list.component.ts new file mode 100644 index 0000000000..777246af9a --- /dev/null +++ b/npm/ng-packs/packages/permission-management/src/lib/components/resource-permission-management/resource-permission-list/resource-permission-list.component.ts @@ -0,0 +1,33 @@ +import { Component, inject, output, ChangeDetectionStrategy } from '@angular/core'; +import { ListService, LocalizationPipe } from '@abp/ng.core'; +import { ExtensibleTableComponent, EXTENSIONS_IDENTIFIER } from '@abp/ng.components/extensible'; +import { ResourcePermissionGrantInfoDto } from '@abp/ng.permission-management/proxy'; +import { ResourcePermissionStateService } from '../../../services/resource-permission-state.service'; +import { ePermissionManagementComponents } from '../../../enums/components'; +import { configureResourcePermissionExtensions } from '../../../services/extensions.service'; + +@Component({ + selector: 'abp-resource-permission-list', + templateUrl: './resource-permission-list.component.html', + providers: [ + ListService, + { + provide: EXTENSIONS_IDENTIFIER, + useValue: ePermissionManagementComponents.ResourcePermissions, + }, + ], + imports: [LocalizationPipe, ExtensibleTableComponent], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class ResourcePermissionListComponent { + readonly state = inject(ResourcePermissionStateService); + readonly list = inject(ListService); + + readonly addClicked = output(); + readonly editClicked = output(); + readonly deleteClicked = output(); + + constructor() { + configureResourcePermissionExtensions(); + } +} diff --git a/npm/ng-packs/packages/permission-management/src/lib/components/resource-permission-management/resource-permission-management.component.html b/npm/ng-packs/packages/permission-management/src/lib/components/resource-permission-management/resource-permission-management.component.html new file mode 100644 index 0000000000..aa26f12bb3 --- /dev/null +++ b/npm/ng-packs/packages/permission-management/src/lib/components/resource-permission-management/resource-permission-management.component.html @@ -0,0 +1,67 @@ + + + + + + + @if (!state.hasResourcePermission() || !state.hasProviderKeyLookupService()) { + + } @else { + @switch (state.viewMode()) { + @case (eResourcePermissionViewModes.List) { + + } + @case (eResourcePermissionViewModes.Add) { + + } + @case (eResourcePermissionViewModes.Edit) { + + } + } + } + + + + @if (state.isListMode()) { + + } @else { + + + {{ 'AbpUi::Save' | abpLocalization }} + + } + + diff --git a/npm/ng-packs/packages/permission-management/src/lib/components/resource-permission-management/resource-permission-management.component.ts b/npm/ng-packs/packages/permission-management/src/lib/components/resource-permission-management/resource-permission-management.component.ts new file mode 100644 index 0000000000..49437e183e --- /dev/null +++ b/npm/ng-packs/packages/permission-management/src/lib/components/resource-permission-management/resource-permission-management.component.ts @@ -0,0 +1,216 @@ +import { ListService, LocalizationPipe } from '@abp/ng.core'; +import { + ButtonComponent, + Confirmation, + ConfirmationService, + ModalCloseDirective, + ModalComponent, + ToasterService, +} from '@abp/ng.theme.shared'; +import { + PermissionsService, + ResourcePermissionGrantInfoDto, +} from '@abp/ng.permission-management/proxy'; +import { Component, inject, input, model, OnInit, effect, untracked, signal } from '@angular/core'; +import { finalize, switchMap, of } from 'rxjs'; +import { ResourcePermissionStateService } from '../../services/resource-permission-state.service'; +import { ResourcePermissionListComponent } from './resource-permission-list/resource-permission-list.component'; +import { ResourcePermissionFormComponent } from './resource-permission-form/resource-permission-form.component'; + +import { eResourcePermissionViewModes } from '../../enums/view-modes'; + +const DEFAULT_MAX_RESULT_COUNT = 10; + +@Component({ + selector: 'abp-resource-permission-management', + templateUrl: './resource-permission-management.component.html', + exportAs: 'abpResourcePermissionManagement', + providers: [ResourcePermissionStateService, ListService], + imports: [ + ModalComponent, + LocalizationPipe, + ButtonComponent, + ModalCloseDirective, + ResourcePermissionListComponent, + ResourcePermissionFormComponent, + ], +}) +export class ResourcePermissionManagementComponent implements OnInit { + readonly eResourcePermissionViewModes = eResourcePermissionViewModes; + + protected readonly service = inject(PermissionsService); + protected readonly toasterService = inject(ToasterService); + protected readonly confirmationService = inject(ConfirmationService); + protected readonly state = inject(ResourcePermissionStateService); + private readonly list = inject(ListService); + + readonly resourceName = input.required(); + readonly resourceKey = input.required(); + readonly resourceDisplayName = input(); + + readonly visible = model(false); + + private readonly previousVisible = signal(false); + + constructor() { + effect(() => { + const resourceName = this.resourceName(); + const resourceKey = this.resourceKey(); + const resourceDisplayName = this.resourceDisplayName(); + + untracked(() => { + this.state.resourceName.set(resourceName); + this.state.resourceKey.set(resourceKey); + this.state.resourceDisplayName.set(resourceDisplayName); + }); + }); + + effect(() => { + const isVisible = this.visible(); + const wasVisible = this.previousVisible(); + if (isVisible && !wasVisible) { + this.openModal(); + } else if (!isVisible && wasVisible) { + this.state.reset(); + } + untracked(() => this.previousVisible.set(isVisible)); + }); + } + + ngOnInit() { + this.list.maxResultCount = DEFAULT_MAX_RESULT_COUNT; + + this.list + .hookToQuery(query => { + const allData = this.state.allResourcePermissions(); + const skipCount = query.skipCount || 0; + const maxResultCount = query.maxResultCount || DEFAULT_MAX_RESULT_COUNT; + + const paginatedData = allData.slice(skipCount, skipCount + maxResultCount); + + return of({ + items: paginatedData, + totalCount: allData.length, + }); + }) + .subscribe(result => { + this.state.resourcePermissions.set(result.items); + this.state.totalCount.set(result.totalCount); + }); + } + + openModal() { + this.state.modalBusy.set(true); + + this.service + .getResource(this.resourceName(), this.resourceKey()) + .pipe( + switchMap(permRes => { + this.state.setResourceData(permRes.permissions || []); + this.list.get(); + return this.service.getResourceProviderKeyLookupServices(this.resourceName()); + }), + switchMap(providerRes => { + this.state.setProviders(providerRes.providers || []); + return this.service.getResourceDefinitions(this.resourceName()); + }), + finalize(() => this.state.modalBusy.set(false)), + ) + .subscribe({ + next: defRes => { + this.state.setDefinitions(defRes.permissions || []); + }, + error: () => { + this.toasterService.error('AbpPermissionManagement::ErrorLoadingPermissions'); + }, + }); + } + + onAddClicked() { + this.state.goToAddMode(); + } + + onEditClicked(grant: ResourcePermissionGrantInfoDto) { + this.state.prepareEditMode(grant); + this.state.modalBusy.set(true); + + this.service + .getResourceByProvider( + this.resourceName(), + this.resourceKey(), + grant.providerName || '', + grant.providerKey || '', + ) + .pipe(finalize(() => this.state.modalBusy.set(false))) + .subscribe({ + next: res => { + this.state.setEditModePermissions(res.permissions || []); + }, + }); + } + + onDeleteClicked(grant: ResourcePermissionGrantInfoDto) { + this.confirmationService + .warn( + 'AbpPermissionManagement::ResourcePermissionDeletionConfirmationMessage', + 'AbpPermissionManagement::AreYouSure', + { + messageLocalizationParams: [grant.providerKey || ''], + }, + ) + .subscribe((status: Confirmation.Status) => { + if (status === Confirmation.Status.confirm) { + this.state.modalBusy.set(true); + this.service + .deleteResource( + this.resourceName(), + this.resourceKey(), + grant.providerName || '', + grant.providerKey || '', + ) + .pipe( + switchMap(() => this.service.getResource(this.resourceName(), this.resourceKey())), + finalize(() => this.state.modalBusy.set(false)), + ) + .subscribe({ + next: res => { + this.state.setResourceData(res.permissions || []); + this.list.get(); + this.toasterService.success('AbpUi::DeletedSuccessfully'); + }, + }); + } + }); + } + + savePermission() { + const isEdit = this.state.isEditMode(); + const providerName = isEdit ? this.state.editProviderName() : this.state.selectedProviderName(); + const providerKey = isEdit ? this.state.editProviderKey() : this.state.selectedProviderKey(); + + if (!isEdit && !this.state.canSave()) { + this.toasterService.warn('AbpPermissionManagement::PleaseSelectProviderAndPermissions'); + return; + } + + this.state.modalBusy.set(true); + this.service + .updateResource(this.resourceName(), this.resourceKey(), { + providerName, + providerKey, + permissions: this.state.selectedPermissions(), + }) + .pipe( + switchMap(() => this.service.getResource(this.resourceName(), this.resourceKey())), + finalize(() => this.state.modalBusy.set(false)), + ) + .subscribe({ + next: res => { + this.state.setResourceData(res.permissions || []); + this.list.get(); + this.toasterService.success('AbpUi::SavedSuccessfully'); + this.state.goToListMode(); + }, + }); + } +} diff --git a/npm/ng-packs/packages/permission-management/src/lib/defaults/default-resource-permission-entity-props.ts b/npm/ng-packs/packages/permission-management/src/lib/defaults/default-resource-permission-entity-props.ts new file mode 100644 index 0000000000..107410f73c --- /dev/null +++ b/npm/ng-packs/packages/permission-management/src/lib/defaults/default-resource-permission-entity-props.ts @@ -0,0 +1,34 @@ +import { EntityProp, ePropType } from '@abp/ng.components/extensible'; +import { ResourcePermissionGrantInfoDto } from '@abp/ng.permission-management/proxy'; +import { of } from 'rxjs'; + +export const DEFAULT_RESOURCE_PERMISSION_ENTITY_PROPS = EntityProp.createMany([ + { + type: ePropType.String, + name: 'providerWithKey', + displayName: 'AbpPermissionManagement::Provider', + sortable: false, + valueResolver: data => { + const providerName = data.record.providerName || ''; + const providerDisplayName = data.record.providerDisplayName || data.record.providerKey || ''; + // Get first letter of provider name for abbreviation + const abbr = providerName.charAt(0).toUpperCase(); + return of( + `${abbr}${providerDisplayName}` + ); + }, + }, + { + type: ePropType.String, + name: 'permissions', + displayName: 'AbpPermissionManagement::Permissions', + sortable: false, + valueResolver: data => { + const permissions = data.record.permissions || []; + const pills = permissions + .map(p => `${p.displayName}`) + .join(''); + return of(pills); + }, + }, +]); diff --git a/npm/ng-packs/packages/permission-management/src/lib/defaults/index.ts b/npm/ng-packs/packages/permission-management/src/lib/defaults/index.ts new file mode 100644 index 0000000000..930121e6f1 --- /dev/null +++ b/npm/ng-packs/packages/permission-management/src/lib/defaults/index.ts @@ -0,0 +1 @@ +export * from './default-resource-permission-entity-props'; diff --git a/npm/ng-packs/packages/permission-management/src/lib/enums/components.ts b/npm/ng-packs/packages/permission-management/src/lib/enums/components.ts index 175d39c999..5db553c4e9 100644 --- a/npm/ng-packs/packages/permission-management/src/lib/enums/components.ts +++ b/npm/ng-packs/packages/permission-management/src/lib/enums/components.ts @@ -1,3 +1,4 @@ export const enum ePermissionManagementComponents { PermissionManagement = 'PermissionManagement.PermissionManagementComponent', + ResourcePermissions = 'PermissionManagement.ResourcePermissionsComponent', } diff --git a/npm/ng-packs/packages/permission-management/src/lib/enums/index.ts b/npm/ng-packs/packages/permission-management/src/lib/enums/index.ts new file mode 100644 index 0000000000..df29235e5b --- /dev/null +++ b/npm/ng-packs/packages/permission-management/src/lib/enums/index.ts @@ -0,0 +1,2 @@ +export * from './view-modes'; +export * from './components'; diff --git a/npm/ng-packs/packages/permission-management/src/lib/enums/view-modes.ts b/npm/ng-packs/packages/permission-management/src/lib/enums/view-modes.ts new file mode 100644 index 0000000000..4611dda210 --- /dev/null +++ b/npm/ng-packs/packages/permission-management/src/lib/enums/view-modes.ts @@ -0,0 +1,5 @@ +export enum eResourcePermissionViewModes { + List = 'list', + Add = 'add', + Edit = 'edit', +} diff --git a/npm/ng-packs/packages/permission-management/src/lib/services/extensions.service.ts b/npm/ng-packs/packages/permission-management/src/lib/services/extensions.service.ts new file mode 100644 index 0000000000..ef9de4bc37 --- /dev/null +++ b/npm/ng-packs/packages/permission-management/src/lib/services/extensions.service.ts @@ -0,0 +1,23 @@ +import { + ExtensionsService, + mergeWithDefaultProps, +} from '@abp/ng.components/extensible'; +import { inject } from '@angular/core'; +import { + RESOURCE_PERMISSION_ENTITY_PROP_CONTRIBUTORS, + DEFAULT_RESOURCE_PERMISSION_ENTITY_PROPS_MAP, +} from '../tokens'; + +export function configureResourcePermissionExtensions() { + const extensions = inject(ExtensionsService); + + const config = { optional: true }; + + const propContributors = inject(RESOURCE_PERMISSION_ENTITY_PROP_CONTRIBUTORS, config) || {}; + + mergeWithDefaultProps( + extensions.entityProps, + DEFAULT_RESOURCE_PERMISSION_ENTITY_PROPS_MAP, + propContributors, + ); +} diff --git a/npm/ng-packs/packages/permission-management/src/lib/services/index.ts b/npm/ng-packs/packages/permission-management/src/lib/services/index.ts new file mode 100644 index 0000000000..f4dae3a571 --- /dev/null +++ b/npm/ng-packs/packages/permission-management/src/lib/services/index.ts @@ -0,0 +1,2 @@ +export * from './extensions.service'; +export * from './resource-permission-state.service'; diff --git a/npm/ng-packs/packages/permission-management/src/lib/services/resource-permission-state.service.ts b/npm/ng-packs/packages/permission-management/src/lib/services/resource-permission-state.service.ts new file mode 100644 index 0000000000..e27bcd10c4 --- /dev/null +++ b/npm/ng-packs/packages/permission-management/src/lib/services/resource-permission-state.service.ts @@ -0,0 +1,164 @@ +import { Injectable, signal, computed } from '@angular/core'; +import { + ResourcePermissionGrantInfoDto, + ResourceProviderDto, + ResourcePermissionDefinitionDto, + SearchProviderKeyInfo, + ResourcePermissionWithProdiverGrantInfoDto, +} from '@abp/ng.permission-management/proxy'; +import { eResourcePermissionViewModes } from '../enums/view-modes'; + +@Injectable() +export class ResourcePermissionStateService { + // View state + readonly viewMode = signal(eResourcePermissionViewModes.List); + readonly modalBusy = signal(false); + readonly hasResourcePermission = signal(false); + readonly hasProviderKeyLookupService = signal(false); + + // Resource data + readonly resourceName = signal(''); + readonly resourceKey = signal(''); + readonly resourceDisplayName = signal(undefined); + + // Permissions data + readonly allResourcePermissions = signal([]); + readonly resourcePermissions = signal([]); + readonly totalCount = signal(0); + readonly permissionDefinitions = signal([]); + readonly permissionsWithProvider = signal([]); + readonly selectedPermissions = signal([]); + + // Provider data + readonly providers = signal([]); + readonly selectedProviderName = signal(''); + readonly selectedProviderKey = signal(''); + + // Edit mode specific + readonly editProviderName = signal(''); + readonly editProviderKey = signal(''); + + // Search state + readonly searchFilter = signal(''); + readonly searchResults = signal([]); + readonly showDropdown = signal(false); + + // Computed properties + readonly isAddMode = computed(() => this.viewMode() === eResourcePermissionViewModes.Add); + readonly isEditMode = computed(() => this.viewMode() === eResourcePermissionViewModes.Edit); + readonly isListMode = computed(() => this.viewMode() === eResourcePermissionViewModes.List); + + readonly currentPermissionsList = computed(() => + this.isAddMode() ? this.permissionDefinitions() : this.permissionsWithProvider() + ); + + readonly allPermissionsSelected = computed(() => { + const definitions = this.currentPermissionsList(); + return definitions.length > 0 && + definitions.every(p => this.selectedPermissions().includes(p.name || '')); + }); + + readonly canSave = computed(() => { + if (this.isAddMode()) { + return !!this.selectedProviderKey() && this.selectedPermissions().length > 0; + } + return this.selectedPermissions().length >= 0; + }); + + // State transition methods + goToListMode() { + this.viewMode.set(eResourcePermissionViewModes.List); + this.selectedPermissions.set([]); + } + + goToAddMode() { + this.viewMode.set(eResourcePermissionViewModes.Add); + this.selectedPermissions.set([]); + this.selectedProviderKey.set(''); + this.searchResults.set([]); + this.searchFilter.set(''); + } + + prepareEditMode(grant: ResourcePermissionGrantInfoDto) { + this.editProviderName.set(grant.providerName || ''); + this.editProviderKey.set(grant.providerKey || ''); + } + + setEditModePermissions(permissions: ResourcePermissionWithProdiverGrantInfoDto[]) { + this.permissionsWithProvider.set(permissions); + this.selectedPermissions.set( + permissions.filter(p => p.isGranted).map(p => p.name || '') + ); + this.viewMode.set(eResourcePermissionViewModes.Edit); + } + + // Permission selection methods + togglePermission(permissionName: string) { + const current = this.selectedPermissions(); + if (current.includes(permissionName)) { + this.selectedPermissions.set(current.filter(p => p !== permissionName)); + } else { + this.selectedPermissions.set([...current, permissionName]); + } + } + + toggleAllPermissions(selectAll: boolean) { + const permissions = this.currentPermissionsList(); + this.selectedPermissions.set( + selectAll ? permissions.map(p => p.name || '') : [] + ); + } + + isPermissionSelected(permissionName: string): boolean { + return this.selectedPermissions().includes(permissionName); + } + + // Provider search methods + onProviderChange(providerName: string) { + this.selectedProviderName.set(providerName); + this.selectedProviderKey.set(''); + this.searchResults.set([]); + this.searchFilter.set(''); + } + + selectProviderKey(key: SearchProviderKeyInfo) { + this.selectedProviderKey.set(key.providerKey || ''); + this.searchFilter.set(key.providerDisplayName || key.providerKey || ''); + this.searchResults.set([]); + this.showDropdown.set(false); + } + + // Reset all state + reset() { + this.viewMode.set(eResourcePermissionViewModes.List); + this.allResourcePermissions.set([]); + this.resourcePermissions.set([]); + this.totalCount.set(0); + this.selectedProviderName.set(''); + this.selectedProviderKey.set(''); + this.searchFilter.set(''); + this.selectedPermissions.set([]); + this.searchResults.set([]); + this.editProviderName.set(''); + this.editProviderKey.set(''); + } + + // Data loading helpers + setResourceData(permissions: ResourcePermissionGrantInfoDto[]) { + this.allResourcePermissions.set(permissions); + this.totalCount.set(permissions.length); + } + + setProviders(providers: ResourceProviderDto[]) { + this.providers.set(providers); + this.hasProviderKeyLookupService.set(providers.length > 0); + if (providers.length) { + this.selectedProviderName.set(providers[0].name || ''); + } + } + + setDefinitions(permissions: ResourcePermissionDefinitionDto[]) { + this.permissionDefinitions.set(permissions); + this.hasResourcePermission.set(permissions.length > 0); + } +} diff --git a/npm/ng-packs/packages/permission-management/src/lib/tokens/extensions.token.ts b/npm/ng-packs/packages/permission-management/src/lib/tokens/extensions.token.ts new file mode 100644 index 0000000000..b87f253eb1 --- /dev/null +++ b/npm/ng-packs/packages/permission-management/src/lib/tokens/extensions.token.ts @@ -0,0 +1,17 @@ +import { ResourcePermissionGrantInfoDto } from '@abp/ng.permission-management/proxy'; +import { EntityPropContributorCallback } from '@abp/ng.components/extensible'; +import { InjectionToken } from '@angular/core'; +import { DEFAULT_RESOURCE_PERMISSION_ENTITY_PROPS } from '../defaults/default-resource-permission-entity-props'; +import { ePermissionManagementComponents } from '../enums/components'; + +export const DEFAULT_RESOURCE_PERMISSION_ENTITY_PROPS_MAP = { + [ePermissionManagementComponents.ResourcePermissions]: DEFAULT_RESOURCE_PERMISSION_ENTITY_PROPS, +}; + +export const RESOURCE_PERMISSION_ENTITY_PROP_CONTRIBUTORS = new InjectionToken( + 'RESOURCE_PERMISSION_ENTITY_PROP_CONTRIBUTORS', +); + +type EntityPropContributors = Partial<{ + [ePermissionManagementComponents.ResourcePermissions]: EntityPropContributorCallback[]; +}>; diff --git a/npm/ng-packs/packages/permission-management/src/lib/tokens/index.ts b/npm/ng-packs/packages/permission-management/src/lib/tokens/index.ts new file mode 100644 index 0000000000..33233400a2 --- /dev/null +++ b/npm/ng-packs/packages/permission-management/src/lib/tokens/index.ts @@ -0,0 +1 @@ +export * from './extensions.token'; diff --git a/npm/ng-packs/packages/schematics/package.json b/npm/ng-packs/packages/schematics/package.json index d878f17f3a..8a7836e8ba 100644 --- a/npm/ng-packs/packages/schematics/package.json +++ b/npm/ng-packs/packages/schematics/package.json @@ -4,16 +4,16 @@ "author": "", "schematics": "./collection.json", "dependencies": { - "@angular-devkit/core": "~20.0.0", - "@angular-devkit/schematics": "~20.0.0", - "@angular/cli": "~20.0.0", + "@angular-devkit/core": "~21.0.0", + "@angular-devkit/schematics": "~21.0.0", + "@angular/cli": "~21.0.0", "got": "^11.5.2", "jsonc-parser": "^2.3.0", "should-quote": "^1.0.0", - "typescript": "~5.8.0" + "typescript": "~5.9.0" }, "devDependencies": { - "@schematics/angular": "~20.0.0", + "@schematics/angular": "~21.0.0", "@types/jest": "29.4.4", "@types/node": "20.2.5", "jest": "29.4.3", diff --git a/npm/ng-packs/packages/schematics/project.json b/npm/ng-packs/packages/schematics/project.json index 7e3f6a8fd3..74a01e9f03 100644 --- a/npm/ng-packs/packages/schematics/project.json +++ b/npm/ng-packs/packages/schematics/project.json @@ -4,6 +4,7 @@ "projectType": "library", "sourceRoot": "packages/schematics/src", "prefix": "abp", + "tags": [], "targets": { "test": { "executor": "@nx/jest:jest", @@ -16,6 +17,5 @@ "executor": "@nx/eslint:lint", "outputs": ["{options.outputFile}"] } - }, - "tags": [] + } } diff --git a/npm/ng-packs/packages/schematics/src/models/api-definition.ts b/npm/ng-packs/packages/schematics/src/models/api-definition.ts index 513201f0a9..70dca68dd5 100644 --- a/npm/ng-packs/packages/schematics/src/models/api-definition.ts +++ b/npm/ng-packs/packages/schematics/src/models/api-definition.ts @@ -20,6 +20,7 @@ export interface PropertyDef { type: string; typeSimple: string; isRequired: boolean; + isNullable: boolean; } export interface Module { diff --git a/npm/ng-packs/packages/schematics/src/utils/model.ts b/npm/ng-packs/packages/schematics/src/utils/model.ts index 1ea9130ef2..4e5431da60 100644 --- a/npm/ng-packs/packages/schematics/src/utils/model.ts +++ b/npm/ng-packs/packages/schematics/src/utils/model.ts @@ -153,6 +153,10 @@ export function createImportRefToInterfaceReducerCreator(params: ModelGeneratorP type = simplifyType(prop.type); } + if (prop.isNullable) { + type = `${type} | null`; + } + const refs = parseType(prop.type).reduce( (acc: string[], r) => acc.concat(parseGenerics(r).toGenerics()), [], @@ -186,10 +190,7 @@ export function createRefToImportReducerCreator(params: ModelGeneratorParams) { } function isOptionalProperty(prop: PropertyDef) { - return ( - prop.typeSimple.endsWith('?') || - ((prop.typeSimple === 'string' || prop.typeSimple.includes('enum')) && !prop.isRequired) - ); + return !prop.isRequired; } export function parseBaseTypeWithGenericTypes(type: string): string[] { diff --git a/npm/ng-packs/packages/setting-management/project.json b/npm/ng-packs/packages/setting-management/project.json index 334e58be26..57f72234fb 100644 --- a/npm/ng-packs/packages/setting-management/project.json +++ b/npm/ng-packs/packages/setting-management/project.json @@ -4,6 +4,8 @@ "projectType": "library", "sourceRoot": "packages/setting-management/src", "prefix": "abp", + "tags": [], + "implicitDependencies": ["core", "theme-shared", "components"], "targets": { "build": { "executor": "@nx/angular:package", @@ -32,7 +34,5 @@ "executor": "@nx/eslint:lint", "outputs": ["{options.outputFile}"] } - }, - "tags": [], - "implicitDependencies": ["core", "theme-shared", "components"] + } } diff --git a/npm/ng-packs/packages/tenant-management/project.json b/npm/ng-packs/packages/tenant-management/project.json index c2f4c3bc8c..2394793380 100644 --- a/npm/ng-packs/packages/tenant-management/project.json +++ b/npm/ng-packs/packages/tenant-management/project.json @@ -4,6 +4,8 @@ "projectType": "library", "sourceRoot": "packages/tenant-management/src", "prefix": "abp", + "tags": [], + "implicitDependencies": ["core", "theme-shared", "feature-management"], "targets": { "build": { "executor": "@nx/angular:package", @@ -32,7 +34,5 @@ "executor": "@nx/eslint:lint", "outputs": ["{options.outputFile}"] } - }, - "tags": [], - "implicitDependencies": ["core", "theme-shared", "feature-management"] + } } diff --git a/npm/ng-packs/packages/theme-basic/project.json b/npm/ng-packs/packages/theme-basic/project.json index 5c2936576f..be0464cc18 100644 --- a/npm/ng-packs/packages/theme-basic/project.json +++ b/npm/ng-packs/packages/theme-basic/project.json @@ -4,6 +4,8 @@ "projectType": "library", "sourceRoot": "packages/theme-basic/src", "prefix": "abp", + "tags": [], + "implicitDependencies": ["core", "theme-shared", "account-core"], "targets": { "build": { "executor": "@nx/angular:package", @@ -32,7 +34,5 @@ "executor": "@nx/eslint:lint", "outputs": ["{options.outputFile}"] } - }, - "tags": [], - "implicitDependencies": ["core", "theme-shared", "account-core"] + } } diff --git a/npm/ng-packs/packages/theme-shared/package.json b/npm/ng-packs/packages/theme-shared/package.json index add2a3e0e1..8f4fe7888a 100644 --- a/npm/ng-packs/packages/theme-shared/package.json +++ b/npm/ng-packs/packages/theme-shared/package.json @@ -9,10 +9,10 @@ "dependencies": { "@abp/ng.core": "~10.0.1", "@fortawesome/fontawesome-free": "^6.0.0", - "@ng-bootstrap/ng-bootstrap": "~19.0.0", + "@ng-bootstrap/ng-bootstrap": "~20.0.0", "@ngx-validate/core": "^0.2.0", "@popperjs/core": "~2.11.0", - "@swimlane/ngx-datatable": "^21.0.0", + "@swimlane/ngx-datatable": "~22.0.0", "bootstrap": "^5.0.0", "tslib": "^2.0.0" }, diff --git a/npm/ng-packs/packages/theme-shared/project.json b/npm/ng-packs/packages/theme-shared/project.json index 6ad2a9db6d..a77551187c 100644 --- a/npm/ng-packs/packages/theme-shared/project.json +++ b/npm/ng-packs/packages/theme-shared/project.json @@ -4,6 +4,8 @@ "projectType": "library", "sourceRoot": "packages/theme-shared/src", "prefix": "abp", + "tags": [], + "implicitDependencies": ["core", "oauth"], "targets": { "build": { "executor": "@nx/angular:package", @@ -32,7 +34,5 @@ "executor": "@nx/eslint:lint", "outputs": ["{options.outputFile}"] } - }, - "tags": [], - "implicitDependencies": ["core", "oauth"] + } } diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/toast-container/toast-container.component.ts b/npm/ng-packs/packages/theme-shared/src/lib/components/toast-container/toast-container.component.ts index aca2149f8b..9c884cb937 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/components/toast-container/toast-container.component.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/toast-container/toast-container.component.ts @@ -46,7 +46,7 @@ export class ToastContainerComponent implements OnInit { }); } - @HostListener('window:resize', ['$event']) + @HostListener('window:resize') onWindowResize() { this.setDefaultRight(); } diff --git a/npm/ng-packs/packages/theme-shared/src/lib/directives/ngx-datatable-default.directive.ts b/npm/ng-packs/packages/theme-shared/src/lib/directives/ngx-datatable-default.directive.ts index b4c0d419c3..951de1a6f2 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/directives/ngx-datatable-default.directive.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/directives/ngx-datatable-default.directive.ts @@ -8,7 +8,7 @@ import { inject, PLATFORM_ID, } from '@angular/core'; -import { ColumnMode, DatatableComponent, ScrollerComponent } from '@swimlane/ngx-datatable'; +import { ColumnMode, DatatableComponent } from '@swimlane/ngx-datatable'; import { fromEvent, Subscription } from 'rxjs'; import { debounceTime } from 'rxjs/operators'; @@ -41,7 +41,7 @@ export class NgxDatatableDefaultDirective implements AfterViewInit, OnDestroy { this.table.virtualization = false; } - private fixHorizontalGap(scroller: ScrollerComponent) { + private fixHorizontalGap(scroller: any) { const { body, documentElement } = this.document; if (isPlatformBrowser(this.platformId)) { if (documentElement.scrollHeight !== documentElement.clientHeight) { diff --git a/npm/ng-packs/tsconfig.base.json b/npm/ng-packs/tsconfig.base.json index 0adc8db800..5e84e8105b 100644 --- a/npm/ng-packs/tsconfig.base.json +++ b/npm/ng-packs/tsconfig.base.json @@ -4,12 +4,12 @@ "strict": false, "sourceMap": true, "declaration": false, - "moduleResolution": "node", + "moduleResolution": "bundler", "emitDecoratorMetadata": true, "experimentalDecorators": true, "importHelpers": true, - "target": "es2020", - "module": "esnext", + "target": "ES2020", + "module": "ES2022", "lib": ["es2020", "dom"], "esModuleInterop": true, "baseUrl": "./", @@ -23,6 +23,7 @@ "@abp/ng.components/chart.js": ["packages/components/chart.js/src/public-api.ts"], "@abp/ng.components/dynamic-form": ["packages/components/dynamic-form/src/public-api.ts"], "@abp/ng.components/extensible": ["packages/components/extensible/src/public-api.ts"], + "@abp/ng.components/lookup": ["packages/components/lookup/src/public-api.ts"], "@abp/ng.components/page": ["packages/components/page/src/public-api.ts"], "@abp/ng.components/tree": ["packages/components/tree/src/public-api.ts"], "@abp/ng.core": ["packages/core/src/public-api.ts"], @@ -48,7 +49,10 @@ "@abp/ng.theme.basic/testing": ["packages/theme-basic/testing/src/public-api.ts"], "@abp/ng.theme.shared": ["packages/theme-shared/src/public-api.ts"], "@abp/ng.theme.shared/testing": ["packages/theme-shared/testing/src/public-api.ts"], - "@abp/nx.generators": ["packages/generators/src/index.ts"] + "@abp/nx.generators": ["packages/generators/src/index.ts"], + "ng-zorro-antd/core/no-animation": [ + "node_modules/ng-zorro-antd/core/no-animation" + ] } }, "exclude": ["node_modules", "tmp"] diff --git a/templates/app-nolayers/angular/package.json b/templates/app-nolayers/angular/package.json index 420ad7b2cf..6a8c89da4e 100644 --- a/templates/app-nolayers/angular/package.json +++ b/templates/app-nolayers/angular/package.json @@ -21,15 +21,15 @@ "@abp/ng.tenant-management": "~10.0.1", "@abp/ng.theme.lepton-x": "~5.0.1", "@abp/ng.theme.shared": "~10.0.1", - "@angular/animations": "~20.0.0", - "@angular/common": "~20.0.0", - "@angular/compiler": "~20.0.0", - "@angular/core": "~20.0.0", - "@angular/forms": "~20.0.0", - "@angular/localize": "~20.0.0", - "@angular/platform-browser": "~20.0.0", - "@angular/platform-browser-dynamic": "~20.0.0", - "@angular/router": "~20.0.0", + "@angular/animations": "~21.0.0", + "@angular/common": "~21.0.0", + "@angular/compiler": "~21.0.0", + "@angular/core": "~21.0.0", + "@angular/forms": "~21.0.0", + "@angular/localize": "~21.0.0", + "@angular/platform-browser": "~21.0.0", + "@angular/platform-browser-dynamic": "~21.0.0", + "@angular/router": "~21.0.0", "bootstrap-icons": "~1.8.0", "rxjs": "~7.8.0", "tslib": "^2.0.0", @@ -37,27 +37,26 @@ }, "devDependencies": { "@abp/ng.schematics": "~10.0.1", - "@angular-devkit/build-angular": "~20.0.0", - "@angular-eslint/builder": "~20.0.0", - "@angular-eslint/eslint-plugin": "~20.0.0", - "@angular-eslint/eslint-plugin-template": "~20.0.0", - "@angular-eslint/schematics": "~20.0.0", - "@angular-eslint/template-parser": "~20.0.0", - "@angular/cli": "~20.0.0", - "@angular/compiler-cli": "~20.0.0", - "@angular/language-service": "~20.0.0", - "@angular/build": "~20.0.0", + "@angular-eslint/builder": "~21.0.0", + "@angular-eslint/eslint-plugin": "~21.0.0", + "@angular-eslint/eslint-plugin-template": "~21.0.0", + "@angular-eslint/schematics": "~21.0.0", + "@angular-eslint/template-parser": "~21.0.0", + "@angular/build": "~21.0.0", + "@angular/cli": "~21.0.0", + "@angular/compiler-cli": "~21.0.0", + "@angular/language-service": "~21.0.0", "@types/jasmine": "~3.6.0", "@types/node": "^12.11.1", "@typescript-eslint/eslint-plugin": "7.16.0", "@typescript-eslint/parser": "7.16.0", "eslint": "^8.0.0", "jasmine-core": "~4.0.0", - "karma": "~6.3.0", + "karma": "~6.4.4", "karma-chrome-launcher": "~3.1.0", "karma-coverage": "~2.1.0", "karma-jasmine": "~4.0.0", "karma-jasmine-html-reporter": "^1.7.0", - "typescript": "~5.8.0" + "typescript": "~5.9.0" } } diff --git a/templates/app-nolayers/angular/src/main.ts b/templates/app-nolayers/angular/src/main.ts index 7180ec1a32..b7ef9f0bc3 100644 --- a/templates/app-nolayers/angular/src/main.ts +++ b/templates/app-nolayers/angular/src/main.ts @@ -1,5 +1,6 @@ +import { provideZoneChangeDetection } from "@angular/core"; import { bootstrapApplication } from '@angular/platform-browser'; import { appConfig } from './app/app.config'; import { AppComponent } from './app/app.component'; -bootstrapApplication(AppComponent, appConfig).catch(err => console.error(err)); +bootstrapApplication(AppComponent, {...appConfig, providers: [provideZoneChangeDetection(), ...appConfig.providers]}).catch(err => console.error(err)); diff --git a/templates/app-nolayers/angular/tsconfig.json b/templates/app-nolayers/angular/tsconfig.json index 0322d97e4d..9a18adaf46 100644 --- a/templates/app-nolayers/angular/tsconfig.json +++ b/templates/app-nolayers/angular/tsconfig.json @@ -10,13 +10,9 @@ "moduleResolution": "bundler", "importHelpers": true, "target": "ES2022", - "module": "esnext", + "module": "ES2022", "skipLibCheck": true, "esModuleInterop": true, - "lib": [ - "es2020", - "dom" - ], "paths": { "@proxy": [ "src/app/proxy/index.ts" diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/MyCompanyName.MyProjectName.Blazor.Server.Mongo.csproj b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/MyCompanyName.MyProjectName.Blazor.Server.Mongo.csproj index 037cf42aa2..c8898f2ecd 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/MyCompanyName.MyProjectName.Blazor.Server.Mongo.csproj +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/MyCompanyName.MyProjectName.Blazor.Server.Mongo.csproj @@ -8,8 +8,8 @@ - - + + diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Migrations/20251020063503_Initial.Designer.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Migrations/20251218020112_Initial.Designer.cs similarity index 94% rename from templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Migrations/20251020063503_Initial.Designer.cs rename to templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Migrations/20251218020112_Initial.Designer.cs index 6a2f32045a..decbedad69 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Migrations/20251020063503_Initial.Designer.cs +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Migrations/20251218020112_Initial.Designer.cs @@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore; namespace MyCompanyName.MyProjectName.Blazor.Server.Migrations { [DbContext(typeof(MyProjectNameDbContext))] - [Migration("20251020063503_Initial")] + [Migration("20251218020112_Initial")] partial class Initial { /// @@ -22,7 +22,7 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Migrations #pragma warning disable 612, 618 modelBuilder .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) - .HasAnnotation("ProductVersion", "10.0.0-rc.2.25502.107") + .HasAnnotation("ProductVersion", "10.0.0") .HasAnnotation("Relational:MaxIdentifierLength", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); @@ -821,6 +821,9 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Migrations b.Property("LastPasswordChangeTime") .HasColumnType("datetimeoffset"); + b.Property("LastSignInTime") + .HasColumnType("datetimeoffset"); + b.Property("LockoutEnabled") .ValueGeneratedOnAdd() .HasColumnType("bit") @@ -1017,6 +1020,26 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Migrations b.ToTable("AbpUserOrganizationUnits", (string)null); }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserPasskey", b => + { + b.Property("CredentialId") + .HasMaxLength(1024) + .HasColumnType("varbinary(1024)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("CredentialId"); + + b.HasIndex("UserId"); + + b.ToTable("AbpUserPasskeys", (string)null); + }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserPasswordHistory", b => { b.Property("UserId") @@ -1506,13 +1529,16 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Migrations .HasColumnName("ExtraProperties"); b.Property("GroupName") - .IsRequired() .HasMaxLength(128) .HasColumnType("nvarchar(128)"); b.Property("IsEnabled") .HasColumnType("bit"); + b.Property("ManagementPermissionName") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + b.Property("MultiTenancySide") .HasColumnType("tinyint"); @@ -1529,6 +1555,10 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Migrations .HasMaxLength(128) .HasColumnType("nvarchar(128)"); + b.Property("ResourceName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + b.Property("StateCheckers") .HasMaxLength(256) .HasColumnType("nvarchar(256)"); @@ -1537,8 +1567,9 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Migrations b.HasIndex("GroupName"); - b.HasIndex("Name") - .IsUnique(); + b.HasIndex("ResourceName", "Name") + .IsUnique() + .HasFilter("[ResourceName] IS NOT NULL"); b.ToTable("AbpPermissions", (string)null); }); @@ -1605,6 +1636,50 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Migrations b.ToTable("AbpPermissionGroups", (string)null); }); + modelBuilder.Entity("Volo.Abp.PermissionManagement.ResourcePermissionGrant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ProviderKey") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ProviderName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ResourceKey") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ResourceName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "Name", "ResourceName", "ResourceKey", "ProviderName", "ProviderKey") + .IsUnique() + .HasFilter("[TenantId] IS NOT NULL"); + + b.ToTable("AbpResourcePermissionGrants", (string)null); + }); + modelBuilder.Entity("Volo.Abp.SettingManagement.Setting", b => { b.Property("Id") @@ -1845,6 +1920,51 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Migrations .IsRequired(); }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserPasskey", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Passkeys") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("Volo.Abp.Identity.IdentityPasskeyData", "Data", b1 => + { + b1.Property("IdentityUserPasskeyCredentialId"); + + b1.Property("AttestationObject"); + + b1.Property("ClientDataJson"); + + b1.Property("CreatedAt"); + + b1.Property("IsBackedUp"); + + b1.Property("IsBackupEligible"); + + b1.Property("IsUserVerified"); + + b1.Property("Name"); + + b1.Property("PublicKey"); + + b1.Property("SignCount"); + + b1.PrimitiveCollection("Transports"); + + b1.HasKey("IdentityUserPasskeyCredentialId"); + + b1.ToTable("AbpUserPasskeys"); + + b1.ToJson("Data"); + + b1.WithOwner() + .HasForeignKey("IdentityUserPasskeyCredentialId"); + }); + + b.Navigation("Data"); + }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserPasswordHistory", b => { b.HasOne("Volo.Abp.Identity.IdentityUser", null) @@ -1952,6 +2072,8 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Migrations b.Navigation("OrganizationUnits"); + b.Navigation("Passkeys"); + b.Navigation("PasswordHistories"); b.Navigation("Roles"); diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Migrations/20251020063503_Initial.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Migrations/20251218020112_Initial.cs similarity index 94% rename from templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Migrations/20251020063503_Initial.cs rename to templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Migrations/20251218020112_Initial.cs index 8538b6a79a..15dc0c422b 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Migrations/20251020063503_Initial.cs +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Migrations/20251218020112_Initial.cs @@ -211,8 +211,10 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Migrations columns: table => new { Id = table.Column(type: "uniqueidentifier", nullable: false), - GroupName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + GroupName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: true), Name = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + ResourceName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + ManagementPermissionName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: true), ParentName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: true), DisplayName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), IsEnabled = table.Column(type: "bit", nullable: false), @@ -226,6 +228,23 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Migrations table.PrimaryKey("PK_AbpPermissions", x => x.Id); }); + migrationBuilder.CreateTable( + name: "AbpResourcePermissionGrants", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + TenantId = table.Column(type: "uniqueidentifier", nullable: true), + Name = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + ProviderName = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false), + ProviderKey = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false), + ResourceName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), + ResourceKey = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpResourcePermissionGrants", x => x.Id); + }); + migrationBuilder.CreateTable( name: "AbpRoles", columns: table => new @@ -393,6 +412,7 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Migrations ShouldChangePasswordOnNextLogin = table.Column(type: "bit", nullable: false), EntityVersion = table.Column(type: "int", nullable: false), LastPasswordChangeTime = table.Column(type: "datetimeoffset", nullable: true), + LastSignInTime = table.Column(type: "datetimeoffset", nullable: true), ExtraProperties = table.Column(type: "nvarchar(max)", nullable: false), ConcurrencyStamp = table.Column(type: "nvarchar(40)", maxLength: 40, nullable: false), CreationTime = table.Column(type: "datetime2", nullable: false), @@ -658,6 +678,26 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Migrations onDelete: ReferentialAction.Cascade); }); + migrationBuilder.CreateTable( + name: "AbpUserPasskeys", + columns: table => new + { + CredentialId = table.Column(type: "varbinary(1024)", maxLength: 1024, nullable: false), + TenantId = table.Column(type: "uniqueidentifier", nullable: true), + UserId = table.Column(type: "uniqueidentifier", nullable: false), + Data = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpUserPasskeys", x => x.CredentialId); + table.ForeignKey( + name: "FK_AbpUserPasskeys_AbpUsers_UserId", + column: x => x.UserId, + principalTable: "AbpUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + migrationBuilder.CreateTable( name: "AbpUserPasswordHistories", columns: table => new @@ -906,10 +946,18 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Migrations column: "GroupName"); migrationBuilder.CreateIndex( - name: "IX_AbpPermissions_Name", + name: "IX_AbpPermissions_ResourceName_Name", table: "AbpPermissions", - column: "Name", - unique: true); + columns: new[] { "ResourceName", "Name" }, + unique: true, + filter: "[ResourceName] IS NOT NULL"); + + migrationBuilder.CreateIndex( + name: "IX_AbpResourcePermissionGrants_TenantId_Name_ResourceName_ResourceKey_ProviderName_ProviderKey", + table: "AbpResourcePermissionGrants", + columns: new[] { "TenantId", "Name", "ResourceName", "ResourceKey", "ProviderName", "ProviderKey" }, + unique: true, + filter: "[TenantId] IS NOT NULL"); migrationBuilder.CreateIndex( name: "IX_AbpRoleClaims_RoleId", @@ -994,6 +1042,11 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Migrations table: "AbpUserOrganizationUnits", columns: new[] { "UserId", "OrganizationUnitId" }); + migrationBuilder.CreateIndex( + name: "IX_AbpUserPasskeys_UserId", + table: "AbpUserPasskeys", + column: "UserId"); + migrationBuilder.CreateIndex( name: "IX_AbpUserRoles_RoleId_UserId", table: "AbpUserRoles", @@ -1089,6 +1142,9 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Migrations migrationBuilder.DropTable( name: "AbpPermissions"); + migrationBuilder.DropTable( + name: "AbpResourcePermissionGrants"); + migrationBuilder.DropTable( name: "AbpRoleClaims"); @@ -1119,6 +1175,9 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Migrations migrationBuilder.DropTable( name: "AbpUserOrganizationUnits"); + migrationBuilder.DropTable( + name: "AbpUserPasskeys"); + migrationBuilder.DropTable( name: "AbpUserPasswordHistories"); diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Migrations/MyProjectNameDbContextModelSnapshot.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Migrations/MyProjectNameDbContextModelSnapshot.cs index 1925f69fb3..d6e1ddae45 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Migrations/MyProjectNameDbContextModelSnapshot.cs +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Migrations/MyProjectNameDbContextModelSnapshot.cs @@ -19,7 +19,7 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Migrations #pragma warning disable 612, 618 modelBuilder .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) - .HasAnnotation("ProductVersion", "10.0.0-rc.2.25502.107") + .HasAnnotation("ProductVersion", "10.0.0") .HasAnnotation("Relational:MaxIdentifierLength", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); @@ -818,6 +818,9 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Migrations b.Property("LastPasswordChangeTime") .HasColumnType("datetimeoffset"); + b.Property("LastSignInTime") + .HasColumnType("datetimeoffset"); + b.Property("LockoutEnabled") .ValueGeneratedOnAdd() .HasColumnType("bit") @@ -1014,6 +1017,26 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Migrations b.ToTable("AbpUserOrganizationUnits", (string)null); }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserPasskey", b => + { + b.Property("CredentialId") + .HasMaxLength(1024) + .HasColumnType("varbinary(1024)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("CredentialId"); + + b.HasIndex("UserId"); + + b.ToTable("AbpUserPasskeys", (string)null); + }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserPasswordHistory", b => { b.Property("UserId") @@ -1503,13 +1526,16 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Migrations .HasColumnName("ExtraProperties"); b.Property("GroupName") - .IsRequired() .HasMaxLength(128) .HasColumnType("nvarchar(128)"); b.Property("IsEnabled") .HasColumnType("bit"); + b.Property("ManagementPermissionName") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + b.Property("MultiTenancySide") .HasColumnType("tinyint"); @@ -1526,6 +1552,10 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Migrations .HasMaxLength(128) .HasColumnType("nvarchar(128)"); + b.Property("ResourceName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + b.Property("StateCheckers") .HasMaxLength(256) .HasColumnType("nvarchar(256)"); @@ -1534,8 +1564,9 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Migrations b.HasIndex("GroupName"); - b.HasIndex("Name") - .IsUnique(); + b.HasIndex("ResourceName", "Name") + .IsUnique() + .HasFilter("[ResourceName] IS NOT NULL"); b.ToTable("AbpPermissions", (string)null); }); @@ -1602,6 +1633,50 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Migrations b.ToTable("AbpPermissionGroups", (string)null); }); + modelBuilder.Entity("Volo.Abp.PermissionManagement.ResourcePermissionGrant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ProviderKey") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ProviderName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ResourceKey") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ResourceName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "Name", "ResourceName", "ResourceKey", "ProviderName", "ProviderKey") + .IsUnique() + .HasFilter("[TenantId] IS NOT NULL"); + + b.ToTable("AbpResourcePermissionGrants", (string)null); + }); + modelBuilder.Entity("Volo.Abp.SettingManagement.Setting", b => { b.Property("Id") @@ -1842,6 +1917,51 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Migrations .IsRequired(); }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserPasskey", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Passkeys") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("Volo.Abp.Identity.IdentityPasskeyData", "Data", b1 => + { + b1.Property("IdentityUserPasskeyCredentialId"); + + b1.Property("AttestationObject"); + + b1.Property("ClientDataJson"); + + b1.Property("CreatedAt"); + + b1.Property("IsBackedUp"); + + b1.Property("IsBackupEligible"); + + b1.Property("IsUserVerified"); + + b1.Property("Name"); + + b1.Property("PublicKey"); + + b1.Property("SignCount"); + + b1.PrimitiveCollection("Transports"); + + b1.HasKey("IdentityUserPasskeyCredentialId"); + + b1.ToTable("AbpUserPasskeys"); + + b1.ToJson("Data"); + + b1.WithOwner() + .HasForeignKey("IdentityUserPasskeyCredentialId"); + }); + + b.Navigation("Data"); + }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserPasswordHistory", b => { b.HasOne("Volo.Abp.Identity.IdentityUser", null) @@ -1949,6 +2069,8 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Migrations b.Navigation("OrganizationUnits"); + b.Navigation("Passkeys"); + b.Navigation("PasswordHistories"); b.Navigation("Roles"); diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/MyCompanyName.MyProjectName.Blazor.Server.csproj b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/MyCompanyName.MyProjectName.Blazor.Server.csproj index 43b1ebfc17..bf85ca3338 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/MyCompanyName.MyProjectName.Blazor.Server.csproj +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/MyCompanyName.MyProjectName.Blazor.Server.csproj @@ -8,8 +8,8 @@ - - + + diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/MyCompanyName.MyProjectName.Blazor.WebAssembly.Client.csproj b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/MyCompanyName.MyProjectName.Blazor.WebAssembly.Client.csproj index c6d1800bc8..dd3805cffa 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/MyCompanyName.MyProjectName.Blazor.WebAssembly.Client.csproj +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/MyCompanyName.MyProjectName.Blazor.WebAssembly.Client.csproj @@ -9,8 +9,8 @@ - - + + diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/Migrations/20251020063514_Initial.Designer.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server/Migrations/20251218020330_Initial.Designer.cs similarity index 94% rename from templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/Migrations/20251020063514_Initial.Designer.cs rename to templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server/Migrations/20251218020330_Initial.Designer.cs index 873b170642..90e5f9a3cd 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/Migrations/20251020063514_Initial.Designer.cs +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server/Migrations/20251218020330_Initial.Designer.cs @@ -5,7 +5,7 @@ using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using MyCompanyName.MyProjectName.EntityFrameworkCore; +using MyCompanyName.MyProjectName.Data; using Volo.Abp.EntityFrameworkCore; #nullable disable @@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore; namespace MyCompanyName.MyProjectName.Migrations { [DbContext(typeof(MyProjectNameDbContext))] - [Migration("20251020063514_Initial")] + [Migration("20251218020330_Initial")] partial class Initial { /// @@ -22,7 +22,7 @@ namespace MyCompanyName.MyProjectName.Migrations #pragma warning disable 612, 618 modelBuilder .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) - .HasAnnotation("ProductVersion", "10.0.0-rc.2.25502.107") + .HasAnnotation("ProductVersion", "10.0.0") .HasAnnotation("Relational:MaxIdentifierLength", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); @@ -318,70 +318,6 @@ namespace MyCompanyName.MyProjectName.Migrations b.ToTable("AbpEntityPropertyChanges", (string)null); }); - modelBuilder.Entity("Volo.Abp.BackgroundJobs.BackgroundJobRecord", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("ApplicationName") - .HasMaxLength(96) - .HasColumnType("nvarchar(96)"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .IsRequired() - .HasMaxLength(40) - .HasColumnType("nvarchar(40)") - .HasColumnName("ConcurrencyStamp"); - - b.Property("CreationTime") - .HasColumnType("datetime2") - .HasColumnName("CreationTime"); - - b.Property("ExtraProperties") - .IsRequired() - .HasColumnType("nvarchar(max)") - .HasColumnName("ExtraProperties"); - - b.Property("IsAbandoned") - .ValueGeneratedOnAdd() - .HasColumnType("bit") - .HasDefaultValue(false); - - b.Property("JobArgs") - .IsRequired() - .HasMaxLength(1048576) - .HasColumnType("nvarchar(max)"); - - b.Property("JobName") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("nvarchar(128)"); - - b.Property("LastTryTime") - .HasColumnType("datetime2"); - - b.Property("NextTryTime") - .HasColumnType("datetime2"); - - b.Property("Priority") - .ValueGeneratedOnAdd() - .HasColumnType("tinyint") - .HasDefaultValue((byte)15); - - b.Property("TryCount") - .ValueGeneratedOnAdd() - .HasColumnType("smallint") - .HasDefaultValue((short)0); - - b.HasKey("Id"); - - b.HasIndex("IsAbandoned", "NextTryTime"); - - b.ToTable("AbpBackgroundJobs", (string)null); - }); - modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureDefinitionRecord", b => { b.Property("Id") @@ -507,6 +443,7 @@ namespace MyCompanyName.MyProjectName.Migrations modelBuilder.Entity("Volo.Abp.Identity.IdentityClaimType", b => { b.Property("Id") + .ValueGeneratedOnAdd() .HasColumnType("uniqueidentifier"); b.Property("ConcurrencyStamp") @@ -559,6 +496,7 @@ namespace MyCompanyName.MyProjectName.Migrations modelBuilder.Entity("Volo.Abp.Identity.IdentityLinkUser", b => { b.Property("Id") + .ValueGeneratedOnAdd() .HasColumnType("uniqueidentifier"); b.Property("SourceTenantId") @@ -585,6 +523,7 @@ namespace MyCompanyName.MyProjectName.Migrations modelBuilder.Entity("Volo.Abp.Identity.IdentityRole", b => { b.Property("Id") + .ValueGeneratedOnAdd() .HasColumnType("uniqueidentifier"); b.Property("ConcurrencyStamp") @@ -670,6 +609,7 @@ namespace MyCompanyName.MyProjectName.Migrations modelBuilder.Entity("Volo.Abp.Identity.IdentitySecurityLog", b => { b.Property("Id") + .ValueGeneratedOnAdd() .HasColumnType("uniqueidentifier"); b.Property("Action") @@ -746,6 +686,7 @@ namespace MyCompanyName.MyProjectName.Migrations modelBuilder.Entity("Volo.Abp.Identity.IdentitySession", b => { b.Property("Id") + .ValueGeneratedOnAdd() .HasColumnType("uniqueidentifier"); b.Property("ClientId") @@ -801,6 +742,7 @@ namespace MyCompanyName.MyProjectName.Migrations modelBuilder.Entity("Volo.Abp.Identity.IdentityUser", b => { b.Property("Id") + .ValueGeneratedOnAdd() .HasColumnType("uniqueidentifier"); b.Property("AccessFailedCount") @@ -879,6 +821,9 @@ namespace MyCompanyName.MyProjectName.Migrations b.Property("LastPasswordChangeTime") .HasColumnType("datetimeoffset"); + b.Property("LastSignInTime") + .HasColumnType("datetimeoffset"); + b.Property("LockoutEnabled") .ValueGeneratedOnAdd() .HasColumnType("bit") @@ -995,6 +940,7 @@ namespace MyCompanyName.MyProjectName.Migrations modelBuilder.Entity("Volo.Abp.Identity.IdentityUserDelegation", b => { b.Property("Id") + .ValueGeneratedOnAdd() .HasColumnType("uniqueidentifier"); b.Property("EndTime") @@ -1074,6 +1020,26 @@ namespace MyCompanyName.MyProjectName.Migrations b.ToTable("AbpUserOrganizationUnits", (string)null); }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserPasskey", b => + { + b.Property("CredentialId") + .HasMaxLength(1024) + .HasColumnType("varbinary(1024)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("CredentialId"); + + b.HasIndex("UserId"); + + b.ToTable("AbpUserPasskeys", (string)null); + }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserPasswordHistory", b => { b.Property("UserId") @@ -1142,6 +1108,7 @@ namespace MyCompanyName.MyProjectName.Migrations modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnit", b => { b.Property("Id") + .ValueGeneratedOnAdd() .HasColumnType("uniqueidentifier"); b.Property("Code") @@ -1562,13 +1529,16 @@ namespace MyCompanyName.MyProjectName.Migrations .HasColumnName("ExtraProperties"); b.Property("GroupName") - .IsRequired() .HasMaxLength(128) .HasColumnType("nvarchar(128)"); b.Property("IsEnabled") .HasColumnType("bit"); + b.Property("ManagementPermissionName") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + b.Property("MultiTenancySide") .HasColumnType("tinyint"); @@ -1585,6 +1555,10 @@ namespace MyCompanyName.MyProjectName.Migrations .HasMaxLength(128) .HasColumnType("nvarchar(128)"); + b.Property("ResourceName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + b.Property("StateCheckers") .HasMaxLength(256) .HasColumnType("nvarchar(256)"); @@ -1593,8 +1567,9 @@ namespace MyCompanyName.MyProjectName.Migrations b.HasIndex("GroupName"); - b.HasIndex("Name") - .IsUnique(); + b.HasIndex("ResourceName", "Name") + .IsUnique() + .HasFilter("[ResourceName] IS NOT NULL"); b.ToTable("AbpPermissions", (string)null); }); @@ -1661,6 +1636,50 @@ namespace MyCompanyName.MyProjectName.Migrations b.ToTable("AbpPermissionGroups", (string)null); }); + modelBuilder.Entity("Volo.Abp.PermissionManagement.ResourcePermissionGrant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ProviderKey") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ProviderName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ResourceKey") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ResourceName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "Name", "ResourceName", "ResourceKey", "ProviderName", "ProviderKey") + .IsUnique() + .HasFilter("[TenantId] IS NOT NULL"); + + b.ToTable("AbpResourcePermissionGrants", (string)null); + }); + modelBuilder.Entity("Volo.Abp.SettingManagement.Setting", b => { b.Property("Id") @@ -1746,6 +1765,7 @@ namespace MyCompanyName.MyProjectName.Migrations modelBuilder.Entity("Volo.Abp.TenantManagement.Tenant", b => { b.Property("Id") + .ValueGeneratedOnAdd() .HasColumnType("uniqueidentifier"); b.Property("ConcurrencyStamp") @@ -1900,6 +1920,51 @@ namespace MyCompanyName.MyProjectName.Migrations .IsRequired(); }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserPasskey", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Passkeys") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("Volo.Abp.Identity.IdentityPasskeyData", "Data", b1 => + { + b1.Property("IdentityUserPasskeyCredentialId"); + + b1.Property("AttestationObject"); + + b1.Property("ClientDataJson"); + + b1.Property("CreatedAt"); + + b1.Property("IsBackedUp"); + + b1.Property("IsBackupEligible"); + + b1.Property("IsUserVerified"); + + b1.Property("Name"); + + b1.Property("PublicKey"); + + b1.Property("SignCount"); + + b1.PrimitiveCollection("Transports"); + + b1.HasKey("IdentityUserPasskeyCredentialId"); + + b1.ToTable("AbpUserPasskeys"); + + b1.ToJson("Data"); + + b1.WithOwner() + .HasForeignKey("IdentityUserPasskeyCredentialId"); + }); + + b.Navigation("Data"); + }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserPasswordHistory", b => { b.HasOne("Volo.Abp.Identity.IdentityUser", null) @@ -2007,6 +2072,8 @@ namespace MyCompanyName.MyProjectName.Migrations b.Navigation("OrganizationUnits"); + b.Navigation("Passkeys"); + b.Navigation("PasswordHistories"); b.Navigation("Roles"); diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/Migrations/20251020063514_Initial.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server/Migrations/20251218020330_Initial.cs similarity index 95% rename from templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/Migrations/20251020063514_Initial.cs rename to templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server/Migrations/20251218020330_Initial.cs index c1814d92ad..3ba2c09d6f 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/Migrations/20251020063514_Initial.cs +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server/Migrations/20251218020330_Initial.cs @@ -60,28 +60,6 @@ namespace MyCompanyName.MyProjectName.Migrations table.PrimaryKey("PK_AbpAuditLogs", x => x.Id); }); - migrationBuilder.CreateTable( - name: "AbpBackgroundJobs", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - ApplicationName = table.Column(type: "nvarchar(96)", maxLength: 96, nullable: true), - JobName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), - JobArgs = table.Column(type: "nvarchar(max)", maxLength: 1048576, nullable: false), - TryCount = table.Column(type: "smallint", nullable: false, defaultValue: (short)0), - CreationTime = table.Column(type: "datetime2", nullable: false), - NextTryTime = table.Column(type: "datetime2", nullable: false), - LastTryTime = table.Column(type: "datetime2", nullable: true), - IsAbandoned = table.Column(type: "bit", nullable: false, defaultValue: false), - Priority = table.Column(type: "tinyint", nullable: false, defaultValue: (byte)15), - ExtraProperties = table.Column(type: "nvarchar(max)", nullable: false), - ConcurrencyStamp = table.Column(type: "nvarchar(40)", maxLength: 40, nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_AbpBackgroundJobs", x => x.Id); - }); - migrationBuilder.CreateTable( name: "AbpClaimTypes", columns: table => new @@ -233,8 +211,10 @@ namespace MyCompanyName.MyProjectName.Migrations columns: table => new { Id = table.Column(type: "uniqueidentifier", nullable: false), - GroupName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + GroupName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: true), Name = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + ResourceName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + ManagementPermissionName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: true), ParentName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: true), DisplayName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), IsEnabled = table.Column(type: "bit", nullable: false), @@ -248,6 +228,23 @@ namespace MyCompanyName.MyProjectName.Migrations table.PrimaryKey("PK_AbpPermissions", x => x.Id); }); + migrationBuilder.CreateTable( + name: "AbpResourcePermissionGrants", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + TenantId = table.Column(type: "uniqueidentifier", nullable: true), + Name = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + ProviderName = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false), + ProviderKey = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false), + ResourceName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), + ResourceKey = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpResourcePermissionGrants", x => x.Id); + }); + migrationBuilder.CreateTable( name: "AbpRoles", columns: table => new @@ -415,6 +412,7 @@ namespace MyCompanyName.MyProjectName.Migrations ShouldChangePasswordOnNextLogin = table.Column(type: "bit", nullable: false), EntityVersion = table.Column(type: "int", nullable: false), LastPasswordChangeTime = table.Column(type: "datetimeoffset", nullable: true), + LastSignInTime = table.Column(type: "datetimeoffset", nullable: true), ExtraProperties = table.Column(type: "nvarchar(max)", nullable: false), ConcurrencyStamp = table.Column(type: "nvarchar(40)", maxLength: 40, nullable: false), CreationTime = table.Column(type: "datetime2", nullable: false), @@ -680,6 +678,26 @@ namespace MyCompanyName.MyProjectName.Migrations onDelete: ReferentialAction.Cascade); }); + migrationBuilder.CreateTable( + name: "AbpUserPasskeys", + columns: table => new + { + CredentialId = table.Column(type: "varbinary(1024)", maxLength: 1024, nullable: false), + TenantId = table.Column(type: "uniqueidentifier", nullable: true), + UserId = table.Column(type: "uniqueidentifier", nullable: false), + Data = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpUserPasskeys", x => x.CredentialId); + table.ForeignKey( + name: "FK_AbpUserPasskeys_AbpUsers_UserId", + column: x => x.UserId, + principalTable: "AbpUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + migrationBuilder.CreateTable( name: "AbpUserPasswordHistories", columns: table => new @@ -848,11 +866,6 @@ namespace MyCompanyName.MyProjectName.Migrations table: "AbpAuditLogs", columns: new[] { "TenantId", "UserId", "ExecutionTime" }); - migrationBuilder.CreateIndex( - name: "IX_AbpBackgroundJobs_IsAbandoned_NextTryTime", - table: "AbpBackgroundJobs", - columns: new[] { "IsAbandoned", "NextTryTime" }); - migrationBuilder.CreateIndex( name: "IX_AbpEntityChanges_AuditLogId", table: "AbpEntityChanges", @@ -933,10 +946,18 @@ namespace MyCompanyName.MyProjectName.Migrations column: "GroupName"); migrationBuilder.CreateIndex( - name: "IX_AbpPermissions_Name", + name: "IX_AbpPermissions_ResourceName_Name", table: "AbpPermissions", - column: "Name", - unique: true); + columns: new[] { "ResourceName", "Name" }, + unique: true, + filter: "[ResourceName] IS NOT NULL"); + + migrationBuilder.CreateIndex( + name: "IX_AbpResourcePermissionGrants_TenantId_Name_ResourceName_ResourceKey_ProviderName_ProviderKey", + table: "AbpResourcePermissionGrants", + columns: new[] { "TenantId", "Name", "ResourceName", "ResourceKey", "ProviderName", "ProviderKey" }, + unique: true, + filter: "[TenantId] IS NOT NULL"); migrationBuilder.CreateIndex( name: "IX_AbpRoleClaims_RoleId", @@ -1021,6 +1042,11 @@ namespace MyCompanyName.MyProjectName.Migrations table: "AbpUserOrganizationUnits", columns: new[] { "UserId", "OrganizationUnitId" }); + migrationBuilder.CreateIndex( + name: "IX_AbpUserPasskeys_UserId", + table: "AbpUserPasskeys", + column: "UserId"); + migrationBuilder.CreateIndex( name: "IX_AbpUserRoles_RoleId_UserId", table: "AbpUserRoles", @@ -1086,9 +1112,6 @@ namespace MyCompanyName.MyProjectName.Migrations migrationBuilder.DropTable( name: "AbpAuditLogExcelFiles"); - migrationBuilder.DropTable( - name: "AbpBackgroundJobs"); - migrationBuilder.DropTable( name: "AbpClaimTypes"); @@ -1119,6 +1142,9 @@ namespace MyCompanyName.MyProjectName.Migrations migrationBuilder.DropTable( name: "AbpPermissions"); + migrationBuilder.DropTable( + name: "AbpResourcePermissionGrants"); + migrationBuilder.DropTable( name: "AbpRoleClaims"); @@ -1149,6 +1175,9 @@ namespace MyCompanyName.MyProjectName.Migrations migrationBuilder.DropTable( name: "AbpUserOrganizationUnits"); + migrationBuilder.DropTable( + name: "AbpUserPasskeys"); + migrationBuilder.DropTable( name: "AbpUserPasswordHistories"); diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server/Migrations/MyProjectNameDbContextModelSnapshot.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server/Migrations/MyProjectNameDbContextModelSnapshot.cs index 18f4b1628f..71ca921e11 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server/Migrations/MyProjectNameDbContextModelSnapshot.cs +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server/Migrations/MyProjectNameDbContextModelSnapshot.cs @@ -19,7 +19,7 @@ namespace MyCompanyName.MyProjectName.Migrations #pragma warning disable 612, 618 modelBuilder .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) - .HasAnnotation("ProductVersion", "10.0.0-rc.2.25502.107") + .HasAnnotation("ProductVersion", "10.0.0") .HasAnnotation("Relational:MaxIdentifierLength", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); @@ -818,6 +818,9 @@ namespace MyCompanyName.MyProjectName.Migrations b.Property("LastPasswordChangeTime") .HasColumnType("datetimeoffset"); + b.Property("LastSignInTime") + .HasColumnType("datetimeoffset"); + b.Property("LockoutEnabled") .ValueGeneratedOnAdd() .HasColumnType("bit") @@ -1014,6 +1017,26 @@ namespace MyCompanyName.MyProjectName.Migrations b.ToTable("AbpUserOrganizationUnits", (string)null); }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserPasskey", b => + { + b.Property("CredentialId") + .HasMaxLength(1024) + .HasColumnType("varbinary(1024)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("CredentialId"); + + b.HasIndex("UserId"); + + b.ToTable("AbpUserPasskeys", (string)null); + }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserPasswordHistory", b => { b.Property("UserId") @@ -1503,13 +1526,16 @@ namespace MyCompanyName.MyProjectName.Migrations .HasColumnName("ExtraProperties"); b.Property("GroupName") - .IsRequired() .HasMaxLength(128) .HasColumnType("nvarchar(128)"); b.Property("IsEnabled") .HasColumnType("bit"); + b.Property("ManagementPermissionName") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + b.Property("MultiTenancySide") .HasColumnType("tinyint"); @@ -1526,6 +1552,10 @@ namespace MyCompanyName.MyProjectName.Migrations .HasMaxLength(128) .HasColumnType("nvarchar(128)"); + b.Property("ResourceName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + b.Property("StateCheckers") .HasMaxLength(256) .HasColumnType("nvarchar(256)"); @@ -1534,8 +1564,9 @@ namespace MyCompanyName.MyProjectName.Migrations b.HasIndex("GroupName"); - b.HasIndex("Name") - .IsUnique(); + b.HasIndex("ResourceName", "Name") + .IsUnique() + .HasFilter("[ResourceName] IS NOT NULL"); b.ToTable("AbpPermissions", (string)null); }); @@ -1602,6 +1633,50 @@ namespace MyCompanyName.MyProjectName.Migrations b.ToTable("AbpPermissionGroups", (string)null); }); + modelBuilder.Entity("Volo.Abp.PermissionManagement.ResourcePermissionGrant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ProviderKey") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ProviderName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ResourceKey") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ResourceName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "Name", "ResourceName", "ResourceKey", "ProviderName", "ProviderKey") + .IsUnique() + .HasFilter("[TenantId] IS NOT NULL"); + + b.ToTable("AbpResourcePermissionGrants", (string)null); + }); + modelBuilder.Entity("Volo.Abp.SettingManagement.Setting", b => { b.Property("Id") @@ -1842,6 +1917,51 @@ namespace MyCompanyName.MyProjectName.Migrations .IsRequired(); }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserPasskey", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Passkeys") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("Volo.Abp.Identity.IdentityPasskeyData", "Data", b1 => + { + b1.Property("IdentityUserPasskeyCredentialId"); + + b1.Property("AttestationObject"); + + b1.Property("ClientDataJson"); + + b1.Property("CreatedAt"); + + b1.Property("IsBackedUp"); + + b1.Property("IsBackupEligible"); + + b1.Property("IsUserVerified"); + + b1.Property("Name"); + + b1.Property("PublicKey"); + + b1.Property("SignCount"); + + b1.PrimitiveCollection("Transports"); + + b1.HasKey("IdentityUserPasskeyCredentialId"); + + b1.ToTable("AbpUserPasskeys"); + + b1.ToJson("Data"); + + b1.WithOwner() + .HasForeignKey("IdentityUserPasskeyCredentialId"); + }); + + b.Navigation("Data"); + }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserPasswordHistory", b => { b.HasOne("Volo.Abp.Identity.IdentityUser", null) @@ -1949,6 +2069,8 @@ namespace MyCompanyName.MyProjectName.Migrations b.Navigation("OrganizationUnits"); + b.Navigation("Passkeys"); + b.Navigation("PasswordHistories"); b.Navigation("Roles"); diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Migrations/20251020063409_Initial.Designer.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Migrations/20251218020004_Initial.Designer.cs similarity index 94% rename from templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Migrations/20251020063409_Initial.Designer.cs rename to templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Migrations/20251218020004_Initial.Designer.cs index 878de122df..80872149d8 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Migrations/20251020063409_Initial.Designer.cs +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Migrations/20251218020004_Initial.Designer.cs @@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore; namespace MyCompanyName.MyProjectName.Host.Migrations { [DbContext(typeof(MyProjectNameDbContext))] - [Migration("20251020063409_Initial")] + [Migration("20251218020004_Initial")] partial class Initial { /// @@ -22,7 +22,7 @@ namespace MyCompanyName.MyProjectName.Host.Migrations #pragma warning disable 612, 618 modelBuilder .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) - .HasAnnotation("ProductVersion", "10.0.0-rc.2.25502.107") + .HasAnnotation("ProductVersion", "10.0.0") .HasAnnotation("Relational:MaxIdentifierLength", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); @@ -821,6 +821,9 @@ namespace MyCompanyName.MyProjectName.Host.Migrations b.Property("LastPasswordChangeTime") .HasColumnType("datetimeoffset"); + b.Property("LastSignInTime") + .HasColumnType("datetimeoffset"); + b.Property("LockoutEnabled") .ValueGeneratedOnAdd() .HasColumnType("bit") @@ -1017,6 +1020,26 @@ namespace MyCompanyName.MyProjectName.Host.Migrations b.ToTable("AbpUserOrganizationUnits", (string)null); }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserPasskey", b => + { + b.Property("CredentialId") + .HasMaxLength(1024) + .HasColumnType("varbinary(1024)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("CredentialId"); + + b.HasIndex("UserId"); + + b.ToTable("AbpUserPasskeys", (string)null); + }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserPasswordHistory", b => { b.Property("UserId") @@ -1506,13 +1529,16 @@ namespace MyCompanyName.MyProjectName.Host.Migrations .HasColumnName("ExtraProperties"); b.Property("GroupName") - .IsRequired() .HasMaxLength(128) .HasColumnType("nvarchar(128)"); b.Property("IsEnabled") .HasColumnType("bit"); + b.Property("ManagementPermissionName") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + b.Property("MultiTenancySide") .HasColumnType("tinyint"); @@ -1529,6 +1555,10 @@ namespace MyCompanyName.MyProjectName.Host.Migrations .HasMaxLength(128) .HasColumnType("nvarchar(128)"); + b.Property("ResourceName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + b.Property("StateCheckers") .HasMaxLength(256) .HasColumnType("nvarchar(256)"); @@ -1537,8 +1567,9 @@ namespace MyCompanyName.MyProjectName.Host.Migrations b.HasIndex("GroupName"); - b.HasIndex("Name") - .IsUnique(); + b.HasIndex("ResourceName", "Name") + .IsUnique() + .HasFilter("[ResourceName] IS NOT NULL"); b.ToTable("AbpPermissions", (string)null); }); @@ -1605,6 +1636,50 @@ namespace MyCompanyName.MyProjectName.Host.Migrations b.ToTable("AbpPermissionGroups", (string)null); }); + modelBuilder.Entity("Volo.Abp.PermissionManagement.ResourcePermissionGrant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ProviderKey") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ProviderName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ResourceKey") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ResourceName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "Name", "ResourceName", "ResourceKey", "ProviderName", "ProviderKey") + .IsUnique() + .HasFilter("[TenantId] IS NOT NULL"); + + b.ToTable("AbpResourcePermissionGrants", (string)null); + }); + modelBuilder.Entity("Volo.Abp.SettingManagement.Setting", b => { b.Property("Id") @@ -1845,6 +1920,51 @@ namespace MyCompanyName.MyProjectName.Host.Migrations .IsRequired(); }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserPasskey", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Passkeys") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("Volo.Abp.Identity.IdentityPasskeyData", "Data", b1 => + { + b1.Property("IdentityUserPasskeyCredentialId"); + + b1.Property("AttestationObject"); + + b1.Property("ClientDataJson"); + + b1.Property("CreatedAt"); + + b1.Property("IsBackedUp"); + + b1.Property("IsBackupEligible"); + + b1.Property("IsUserVerified"); + + b1.Property("Name"); + + b1.Property("PublicKey"); + + b1.Property("SignCount"); + + b1.PrimitiveCollection("Transports"); + + b1.HasKey("IdentityUserPasskeyCredentialId"); + + b1.ToTable("AbpUserPasskeys"); + + b1.ToJson("Data"); + + b1.WithOwner() + .HasForeignKey("IdentityUserPasskeyCredentialId"); + }); + + b.Navigation("Data"); + }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserPasswordHistory", b => { b.HasOne("Volo.Abp.Identity.IdentityUser", null) @@ -1952,6 +2072,8 @@ namespace MyCompanyName.MyProjectName.Host.Migrations b.Navigation("OrganizationUnits"); + b.Navigation("Passkeys"); + b.Navigation("PasswordHistories"); b.Navigation("Roles"); diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Migrations/20251020063409_Initial.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Migrations/20251218020004_Initial.cs similarity index 94% rename from templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Migrations/20251020063409_Initial.cs rename to templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Migrations/20251218020004_Initial.cs index 7f20544311..251be66c16 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Migrations/20251020063409_Initial.cs +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Migrations/20251218020004_Initial.cs @@ -211,8 +211,10 @@ namespace MyCompanyName.MyProjectName.Host.Migrations columns: table => new { Id = table.Column(type: "uniqueidentifier", nullable: false), - GroupName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + GroupName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: true), Name = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + ResourceName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + ManagementPermissionName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: true), ParentName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: true), DisplayName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), IsEnabled = table.Column(type: "bit", nullable: false), @@ -226,6 +228,23 @@ namespace MyCompanyName.MyProjectName.Host.Migrations table.PrimaryKey("PK_AbpPermissions", x => x.Id); }); + migrationBuilder.CreateTable( + name: "AbpResourcePermissionGrants", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + TenantId = table.Column(type: "uniqueidentifier", nullable: true), + Name = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + ProviderName = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false), + ProviderKey = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false), + ResourceName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), + ResourceKey = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpResourcePermissionGrants", x => x.Id); + }); + migrationBuilder.CreateTable( name: "AbpRoles", columns: table => new @@ -393,6 +412,7 @@ namespace MyCompanyName.MyProjectName.Host.Migrations ShouldChangePasswordOnNextLogin = table.Column(type: "bit", nullable: false), EntityVersion = table.Column(type: "int", nullable: false), LastPasswordChangeTime = table.Column(type: "datetimeoffset", nullable: true), + LastSignInTime = table.Column(type: "datetimeoffset", nullable: true), ExtraProperties = table.Column(type: "nvarchar(max)", nullable: false), ConcurrencyStamp = table.Column(type: "nvarchar(40)", maxLength: 40, nullable: false), CreationTime = table.Column(type: "datetime2", nullable: false), @@ -658,6 +678,26 @@ namespace MyCompanyName.MyProjectName.Host.Migrations onDelete: ReferentialAction.Cascade); }); + migrationBuilder.CreateTable( + name: "AbpUserPasskeys", + columns: table => new + { + CredentialId = table.Column(type: "varbinary(1024)", maxLength: 1024, nullable: false), + TenantId = table.Column(type: "uniqueidentifier", nullable: true), + UserId = table.Column(type: "uniqueidentifier", nullable: false), + Data = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpUserPasskeys", x => x.CredentialId); + table.ForeignKey( + name: "FK_AbpUserPasskeys_AbpUsers_UserId", + column: x => x.UserId, + principalTable: "AbpUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + migrationBuilder.CreateTable( name: "AbpUserPasswordHistories", columns: table => new @@ -906,10 +946,18 @@ namespace MyCompanyName.MyProjectName.Host.Migrations column: "GroupName"); migrationBuilder.CreateIndex( - name: "IX_AbpPermissions_Name", + name: "IX_AbpPermissions_ResourceName_Name", table: "AbpPermissions", - column: "Name", - unique: true); + columns: new[] { "ResourceName", "Name" }, + unique: true, + filter: "[ResourceName] IS NOT NULL"); + + migrationBuilder.CreateIndex( + name: "IX_AbpResourcePermissionGrants_TenantId_Name_ResourceName_ResourceKey_ProviderName_ProviderKey", + table: "AbpResourcePermissionGrants", + columns: new[] { "TenantId", "Name", "ResourceName", "ResourceKey", "ProviderName", "ProviderKey" }, + unique: true, + filter: "[TenantId] IS NOT NULL"); migrationBuilder.CreateIndex( name: "IX_AbpRoleClaims_RoleId", @@ -994,6 +1042,11 @@ namespace MyCompanyName.MyProjectName.Host.Migrations table: "AbpUserOrganizationUnits", columns: new[] { "UserId", "OrganizationUnitId" }); + migrationBuilder.CreateIndex( + name: "IX_AbpUserPasskeys_UserId", + table: "AbpUserPasskeys", + column: "UserId"); + migrationBuilder.CreateIndex( name: "IX_AbpUserRoles_RoleId_UserId", table: "AbpUserRoles", @@ -1089,6 +1142,9 @@ namespace MyCompanyName.MyProjectName.Host.Migrations migrationBuilder.DropTable( name: "AbpPermissions"); + migrationBuilder.DropTable( + name: "AbpResourcePermissionGrants"); + migrationBuilder.DropTable( name: "AbpRoleClaims"); @@ -1119,6 +1175,9 @@ namespace MyCompanyName.MyProjectName.Host.Migrations migrationBuilder.DropTable( name: "AbpUserOrganizationUnits"); + migrationBuilder.DropTable( + name: "AbpUserPasskeys"); + migrationBuilder.DropTable( name: "AbpUserPasswordHistories"); diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Migrations/MyProjectNameDbContextModelSnapshot.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Migrations/MyProjectNameDbContextModelSnapshot.cs index ba0c89a002..a5ee954dd9 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Migrations/MyProjectNameDbContextModelSnapshot.cs +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Migrations/MyProjectNameDbContextModelSnapshot.cs @@ -19,7 +19,7 @@ namespace MyCompanyName.MyProjectName.Host.Migrations #pragma warning disable 612, 618 modelBuilder .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) - .HasAnnotation("ProductVersion", "10.0.0-rc.2.25502.107") + .HasAnnotation("ProductVersion", "10.0.0") .HasAnnotation("Relational:MaxIdentifierLength", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); @@ -818,6 +818,9 @@ namespace MyCompanyName.MyProjectName.Host.Migrations b.Property("LastPasswordChangeTime") .HasColumnType("datetimeoffset"); + b.Property("LastSignInTime") + .HasColumnType("datetimeoffset"); + b.Property("LockoutEnabled") .ValueGeneratedOnAdd() .HasColumnType("bit") @@ -1014,6 +1017,26 @@ namespace MyCompanyName.MyProjectName.Host.Migrations b.ToTable("AbpUserOrganizationUnits", (string)null); }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserPasskey", b => + { + b.Property("CredentialId") + .HasMaxLength(1024) + .HasColumnType("varbinary(1024)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("CredentialId"); + + b.HasIndex("UserId"); + + b.ToTable("AbpUserPasskeys", (string)null); + }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserPasswordHistory", b => { b.Property("UserId") @@ -1503,13 +1526,16 @@ namespace MyCompanyName.MyProjectName.Host.Migrations .HasColumnName("ExtraProperties"); b.Property("GroupName") - .IsRequired() .HasMaxLength(128) .HasColumnType("nvarchar(128)"); b.Property("IsEnabled") .HasColumnType("bit"); + b.Property("ManagementPermissionName") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + b.Property("MultiTenancySide") .HasColumnType("tinyint"); @@ -1526,6 +1552,10 @@ namespace MyCompanyName.MyProjectName.Host.Migrations .HasMaxLength(128) .HasColumnType("nvarchar(128)"); + b.Property("ResourceName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + b.Property("StateCheckers") .HasMaxLength(256) .HasColumnType("nvarchar(256)"); @@ -1534,8 +1564,9 @@ namespace MyCompanyName.MyProjectName.Host.Migrations b.HasIndex("GroupName"); - b.HasIndex("Name") - .IsUnique(); + b.HasIndex("ResourceName", "Name") + .IsUnique() + .HasFilter("[ResourceName] IS NOT NULL"); b.ToTable("AbpPermissions", (string)null); }); @@ -1602,6 +1633,50 @@ namespace MyCompanyName.MyProjectName.Host.Migrations b.ToTable("AbpPermissionGroups", (string)null); }); + modelBuilder.Entity("Volo.Abp.PermissionManagement.ResourcePermissionGrant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ProviderKey") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ProviderName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ResourceKey") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ResourceName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "Name", "ResourceName", "ResourceKey", "ProviderName", "ProviderKey") + .IsUnique() + .HasFilter("[TenantId] IS NOT NULL"); + + b.ToTable("AbpResourcePermissionGrants", (string)null); + }); + modelBuilder.Entity("Volo.Abp.SettingManagement.Setting", b => { b.Property("Id") @@ -1842,6 +1917,51 @@ namespace MyCompanyName.MyProjectName.Host.Migrations .IsRequired(); }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserPasskey", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Passkeys") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("Volo.Abp.Identity.IdentityPasskeyData", "Data", b1 => + { + b1.Property("IdentityUserPasskeyCredentialId"); + + b1.Property("AttestationObject"); + + b1.Property("ClientDataJson"); + + b1.Property("CreatedAt"); + + b1.Property("IsBackedUp"); + + b1.Property("IsBackupEligible"); + + b1.Property("IsUserVerified"); + + b1.Property("Name"); + + b1.Property("PublicKey"); + + b1.Property("SignCount"); + + b1.PrimitiveCollection("Transports"); + + b1.HasKey("IdentityUserPasskeyCredentialId"); + + b1.ToTable("AbpUserPasskeys"); + + b1.ToJson("Data"); + + b1.WithOwner() + .HasForeignKey("IdentityUserPasskeyCredentialId"); + }); + + b.Navigation("Data"); + }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserPasswordHistory", b => { b.HasOne("Volo.Abp.Identity.IdentityUser", null) @@ -1949,6 +2069,8 @@ namespace MyCompanyName.MyProjectName.Host.Migrations b.Navigation("OrganizationUnits"); + b.Navigation("Passkeys"); + b.Navigation("PasswordHistories"); b.Navigation("Roles"); diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Migrations/20251020063438_Initial.Designer.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Migrations/20251218020034_Initial.Designer.cs similarity index 94% rename from templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Migrations/20251020063438_Initial.Designer.cs rename to templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Migrations/20251218020034_Initial.Designer.cs index c6d52b6b7b..347bf5c1cc 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Migrations/20251020063438_Initial.Designer.cs +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Migrations/20251218020034_Initial.Designer.cs @@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore; namespace MyCompanyName.MyProjectName.Mvc.Migrations { [DbContext(typeof(MyProjectNameDbContext))] - [Migration("20251020063438_Initial")] + [Migration("20251218020034_Initial")] partial class Initial { /// @@ -22,7 +22,7 @@ namespace MyCompanyName.MyProjectName.Mvc.Migrations #pragma warning disable 612, 618 modelBuilder .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) - .HasAnnotation("ProductVersion", "10.0.0-rc.2.25502.107") + .HasAnnotation("ProductVersion", "10.0.0") .HasAnnotation("Relational:MaxIdentifierLength", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); @@ -821,6 +821,9 @@ namespace MyCompanyName.MyProjectName.Mvc.Migrations b.Property("LastPasswordChangeTime") .HasColumnType("datetimeoffset"); + b.Property("LastSignInTime") + .HasColumnType("datetimeoffset"); + b.Property("LockoutEnabled") .ValueGeneratedOnAdd() .HasColumnType("bit") @@ -1017,6 +1020,26 @@ namespace MyCompanyName.MyProjectName.Mvc.Migrations b.ToTable("AbpUserOrganizationUnits", (string)null); }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserPasskey", b => + { + b.Property("CredentialId") + .HasMaxLength(1024) + .HasColumnType("varbinary(1024)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("CredentialId"); + + b.HasIndex("UserId"); + + b.ToTable("AbpUserPasskeys", (string)null); + }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserPasswordHistory", b => { b.Property("UserId") @@ -1506,13 +1529,16 @@ namespace MyCompanyName.MyProjectName.Mvc.Migrations .HasColumnName("ExtraProperties"); b.Property("GroupName") - .IsRequired() .HasMaxLength(128) .HasColumnType("nvarchar(128)"); b.Property("IsEnabled") .HasColumnType("bit"); + b.Property("ManagementPermissionName") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + b.Property("MultiTenancySide") .HasColumnType("tinyint"); @@ -1529,6 +1555,10 @@ namespace MyCompanyName.MyProjectName.Mvc.Migrations .HasMaxLength(128) .HasColumnType("nvarchar(128)"); + b.Property("ResourceName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + b.Property("StateCheckers") .HasMaxLength(256) .HasColumnType("nvarchar(256)"); @@ -1537,8 +1567,9 @@ namespace MyCompanyName.MyProjectName.Mvc.Migrations b.HasIndex("GroupName"); - b.HasIndex("Name") - .IsUnique(); + b.HasIndex("ResourceName", "Name") + .IsUnique() + .HasFilter("[ResourceName] IS NOT NULL"); b.ToTable("AbpPermissions", (string)null); }); @@ -1605,6 +1636,50 @@ namespace MyCompanyName.MyProjectName.Mvc.Migrations b.ToTable("AbpPermissionGroups", (string)null); }); + modelBuilder.Entity("Volo.Abp.PermissionManagement.ResourcePermissionGrant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ProviderKey") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ProviderName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ResourceKey") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ResourceName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "Name", "ResourceName", "ResourceKey", "ProviderName", "ProviderKey") + .IsUnique() + .HasFilter("[TenantId] IS NOT NULL"); + + b.ToTable("AbpResourcePermissionGrants", (string)null); + }); + modelBuilder.Entity("Volo.Abp.SettingManagement.Setting", b => { b.Property("Id") @@ -1845,6 +1920,51 @@ namespace MyCompanyName.MyProjectName.Mvc.Migrations .IsRequired(); }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserPasskey", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Passkeys") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("Volo.Abp.Identity.IdentityPasskeyData", "Data", b1 => + { + b1.Property("IdentityUserPasskeyCredentialId"); + + b1.Property("AttestationObject"); + + b1.Property("ClientDataJson"); + + b1.Property("CreatedAt"); + + b1.Property("IsBackedUp"); + + b1.Property("IsBackupEligible"); + + b1.Property("IsUserVerified"); + + b1.Property("Name"); + + b1.Property("PublicKey"); + + b1.Property("SignCount"); + + b1.PrimitiveCollection("Transports"); + + b1.HasKey("IdentityUserPasskeyCredentialId"); + + b1.ToTable("AbpUserPasskeys"); + + b1.ToJson("Data"); + + b1.WithOwner() + .HasForeignKey("IdentityUserPasskeyCredentialId"); + }); + + b.Navigation("Data"); + }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserPasswordHistory", b => { b.HasOne("Volo.Abp.Identity.IdentityUser", null) @@ -1952,6 +2072,8 @@ namespace MyCompanyName.MyProjectName.Mvc.Migrations b.Navigation("OrganizationUnits"); + b.Navigation("Passkeys"); + b.Navigation("PasswordHistories"); b.Navigation("Roles"); diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Migrations/20251020063438_Initial.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Migrations/20251218020034_Initial.cs similarity index 94% rename from templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Migrations/20251020063438_Initial.cs rename to templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Migrations/20251218020034_Initial.cs index d8732c6e47..0c0880730b 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Migrations/20251020063438_Initial.cs +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Migrations/20251218020034_Initial.cs @@ -211,8 +211,10 @@ namespace MyCompanyName.MyProjectName.Mvc.Migrations columns: table => new { Id = table.Column(type: "uniqueidentifier", nullable: false), - GroupName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + GroupName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: true), Name = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + ResourceName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + ManagementPermissionName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: true), ParentName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: true), DisplayName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), IsEnabled = table.Column(type: "bit", nullable: false), @@ -226,6 +228,23 @@ namespace MyCompanyName.MyProjectName.Mvc.Migrations table.PrimaryKey("PK_AbpPermissions", x => x.Id); }); + migrationBuilder.CreateTable( + name: "AbpResourcePermissionGrants", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + TenantId = table.Column(type: "uniqueidentifier", nullable: true), + Name = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + ProviderName = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false), + ProviderKey = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false), + ResourceName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), + ResourceKey = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpResourcePermissionGrants", x => x.Id); + }); + migrationBuilder.CreateTable( name: "AbpRoles", columns: table => new @@ -393,6 +412,7 @@ namespace MyCompanyName.MyProjectName.Mvc.Migrations ShouldChangePasswordOnNextLogin = table.Column(type: "bit", nullable: false), EntityVersion = table.Column(type: "int", nullable: false), LastPasswordChangeTime = table.Column(type: "datetimeoffset", nullable: true), + LastSignInTime = table.Column(type: "datetimeoffset", nullable: true), ExtraProperties = table.Column(type: "nvarchar(max)", nullable: false), ConcurrencyStamp = table.Column(type: "nvarchar(40)", maxLength: 40, nullable: false), CreationTime = table.Column(type: "datetime2", nullable: false), @@ -658,6 +678,26 @@ namespace MyCompanyName.MyProjectName.Mvc.Migrations onDelete: ReferentialAction.Cascade); }); + migrationBuilder.CreateTable( + name: "AbpUserPasskeys", + columns: table => new + { + CredentialId = table.Column(type: "varbinary(1024)", maxLength: 1024, nullable: false), + TenantId = table.Column(type: "uniqueidentifier", nullable: true), + UserId = table.Column(type: "uniqueidentifier", nullable: false), + Data = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpUserPasskeys", x => x.CredentialId); + table.ForeignKey( + name: "FK_AbpUserPasskeys_AbpUsers_UserId", + column: x => x.UserId, + principalTable: "AbpUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + migrationBuilder.CreateTable( name: "AbpUserPasswordHistories", columns: table => new @@ -906,10 +946,18 @@ namespace MyCompanyName.MyProjectName.Mvc.Migrations column: "GroupName"); migrationBuilder.CreateIndex( - name: "IX_AbpPermissions_Name", + name: "IX_AbpPermissions_ResourceName_Name", table: "AbpPermissions", - column: "Name", - unique: true); + columns: new[] { "ResourceName", "Name" }, + unique: true, + filter: "[ResourceName] IS NOT NULL"); + + migrationBuilder.CreateIndex( + name: "IX_AbpResourcePermissionGrants_TenantId_Name_ResourceName_ResourceKey_ProviderName_ProviderKey", + table: "AbpResourcePermissionGrants", + columns: new[] { "TenantId", "Name", "ResourceName", "ResourceKey", "ProviderName", "ProviderKey" }, + unique: true, + filter: "[TenantId] IS NOT NULL"); migrationBuilder.CreateIndex( name: "IX_AbpRoleClaims_RoleId", @@ -994,6 +1042,11 @@ namespace MyCompanyName.MyProjectName.Mvc.Migrations table: "AbpUserOrganizationUnits", columns: new[] { "UserId", "OrganizationUnitId" }); + migrationBuilder.CreateIndex( + name: "IX_AbpUserPasskeys_UserId", + table: "AbpUserPasskeys", + column: "UserId"); + migrationBuilder.CreateIndex( name: "IX_AbpUserRoles_RoleId_UserId", table: "AbpUserRoles", @@ -1089,6 +1142,9 @@ namespace MyCompanyName.MyProjectName.Mvc.Migrations migrationBuilder.DropTable( name: "AbpPermissions"); + migrationBuilder.DropTable( + name: "AbpResourcePermissionGrants"); + migrationBuilder.DropTable( name: "AbpRoleClaims"); @@ -1119,6 +1175,9 @@ namespace MyCompanyName.MyProjectName.Mvc.Migrations migrationBuilder.DropTable( name: "AbpUserOrganizationUnits"); + migrationBuilder.DropTable( + name: "AbpUserPasskeys"); + migrationBuilder.DropTable( name: "AbpUserPasswordHistories"); diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Migrations/MyProjectNameDbContextModelSnapshot.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Migrations/MyProjectNameDbContextModelSnapshot.cs index 707e04eb82..72bea3e6f5 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Migrations/MyProjectNameDbContextModelSnapshot.cs +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Migrations/MyProjectNameDbContextModelSnapshot.cs @@ -19,7 +19,7 @@ namespace MyCompanyName.MyProjectName.Mvc.Migrations #pragma warning disable 612, 618 modelBuilder .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) - .HasAnnotation("ProductVersion", "10.0.0-rc.2.25502.107") + .HasAnnotation("ProductVersion", "10.0.0") .HasAnnotation("Relational:MaxIdentifierLength", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); @@ -818,6 +818,9 @@ namespace MyCompanyName.MyProjectName.Mvc.Migrations b.Property("LastPasswordChangeTime") .HasColumnType("datetimeoffset"); + b.Property("LastSignInTime") + .HasColumnType("datetimeoffset"); + b.Property("LockoutEnabled") .ValueGeneratedOnAdd() .HasColumnType("bit") @@ -1014,6 +1017,26 @@ namespace MyCompanyName.MyProjectName.Mvc.Migrations b.ToTable("AbpUserOrganizationUnits", (string)null); }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserPasskey", b => + { + b.Property("CredentialId") + .HasMaxLength(1024) + .HasColumnType("varbinary(1024)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("CredentialId"); + + b.HasIndex("UserId"); + + b.ToTable("AbpUserPasskeys", (string)null); + }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserPasswordHistory", b => { b.Property("UserId") @@ -1503,13 +1526,16 @@ namespace MyCompanyName.MyProjectName.Mvc.Migrations .HasColumnName("ExtraProperties"); b.Property("GroupName") - .IsRequired() .HasMaxLength(128) .HasColumnType("nvarchar(128)"); b.Property("IsEnabled") .HasColumnType("bit"); + b.Property("ManagementPermissionName") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + b.Property("MultiTenancySide") .HasColumnType("tinyint"); @@ -1526,6 +1552,10 @@ namespace MyCompanyName.MyProjectName.Mvc.Migrations .HasMaxLength(128) .HasColumnType("nvarchar(128)"); + b.Property("ResourceName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + b.Property("StateCheckers") .HasMaxLength(256) .HasColumnType("nvarchar(256)"); @@ -1534,8 +1564,9 @@ namespace MyCompanyName.MyProjectName.Mvc.Migrations b.HasIndex("GroupName"); - b.HasIndex("Name") - .IsUnique(); + b.HasIndex("ResourceName", "Name") + .IsUnique() + .HasFilter("[ResourceName] IS NOT NULL"); b.ToTable("AbpPermissions", (string)null); }); @@ -1602,6 +1633,50 @@ namespace MyCompanyName.MyProjectName.Mvc.Migrations b.ToTable("AbpPermissionGroups", (string)null); }); + modelBuilder.Entity("Volo.Abp.PermissionManagement.ResourcePermissionGrant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ProviderKey") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ProviderName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ResourceKey") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ResourceName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "Name", "ResourceName", "ResourceKey", "ProviderName", "ProviderKey") + .IsUnique() + .HasFilter("[TenantId] IS NOT NULL"); + + b.ToTable("AbpResourcePermissionGrants", (string)null); + }); + modelBuilder.Entity("Volo.Abp.SettingManagement.Setting", b => { b.Property("Id") @@ -1842,6 +1917,51 @@ namespace MyCompanyName.MyProjectName.Mvc.Migrations .IsRequired(); }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserPasskey", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Passkeys") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("Volo.Abp.Identity.IdentityPasskeyData", "Data", b1 => + { + b1.Property("IdentityUserPasskeyCredentialId"); + + b1.Property("AttestationObject"); + + b1.Property("ClientDataJson"); + + b1.Property("CreatedAt"); + + b1.Property("IsBackedUp"); + + b1.Property("IsBackupEligible"); + + b1.Property("IsUserVerified"); + + b1.Property("Name"); + + b1.Property("PublicKey"); + + b1.Property("SignCount"); + + b1.PrimitiveCollection("Transports"); + + b1.HasKey("IdentityUserPasskeyCredentialId"); + + b1.ToTable("AbpUserPasskeys"); + + b1.ToJson("Data"); + + b1.WithOwner() + .HasForeignKey("IdentityUserPasskeyCredentialId"); + }); + + b.Navigation("Data"); + }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserPasswordHistory", b => { b.HasOne("Volo.Abp.Identity.IdentityUser", null) @@ -1949,6 +2069,8 @@ namespace MyCompanyName.MyProjectName.Mvc.Migrations b.Navigation("OrganizationUnits"); + b.Navigation("Passkeys"); + b.Navigation("PasswordHistories"); b.Navigation("Roles"); diff --git a/templates/app/angular/package.json b/templates/app/angular/package.json index ac7e253962..57e5e6d457 100644 --- a/templates/app/angular/package.json +++ b/templates/app/angular/package.json @@ -21,15 +21,15 @@ "@abp/ng.tenant-management": "~10.0.1", "@abp/ng.theme.lepton-x": "~5.0.1", "@abp/ng.theme.shared": "~10.0.1", - "@angular/animations": "~20.0.0", - "@angular/common": "~20.0.0", - "@angular/compiler": "~20.0.0", - "@angular/core": "~20.0.0", - "@angular/forms": "~20.0.0", - "@angular/localize": "~20.0.0", - "@angular/platform-browser": "~20.0.0", - "@angular/platform-browser-dynamic": "~20.0.0", - "@angular/router": "~20.0.0", + "@angular/animations": "~21.0.0", + "@angular/common": "~21.0.0", + "@angular/compiler": "~21.0.0", + "@angular/core": "~21.0.0", + "@angular/forms": "~21.0.0", + "@angular/localize": "~21.0.0", + "@angular/platform-browser": "~21.0.0", + "@angular/platform-browser-dynamic": "~21.0.0", + "@angular/router": "~21.0.0", "bootstrap-icons": "~1.8.0", "rxjs": "~7.8.0", "tslib": "^2.0.0", @@ -37,27 +37,26 @@ }, "devDependencies": { "@abp/ng.schematics": "~10.0.1", - "@angular-devkit/build-angular": "~20.0.0", - "@angular-eslint/builder": "~20.0.0", - "@angular-eslint/eslint-plugin": "~20.0.0", - "@angular-eslint/eslint-plugin-template": "~20.0.0", - "@angular-eslint/schematics": "~20.0.0", - "@angular-eslint/template-parser": "~20.0.0", - "@angular/cli": "~20.0.0", - "@angular/compiler-cli": "~20.0.0", - "@angular/language-service": "~20.0.0", - "@angular/build": "~20.0.0", + "@angular-eslint/builder": "~21.0.0", + "@angular-eslint/eslint-plugin": "~21.0.0", + "@angular-eslint/eslint-plugin-template": "~21.0.0", + "@angular-eslint/schematics": "~21.0.0", + "@angular-eslint/template-parser": "~21.0.0", + "@angular/build": "~21.0.0", + "@angular/cli": "~21.0.0", + "@angular/compiler-cli": "~21.0.0", + "@angular/language-service": "~21.0.0", "@types/jasmine": "~3.6.0", "@types/node": "~20.11.0", "@typescript-eslint/eslint-plugin": "7.16.0", "@typescript-eslint/parser": "7.16.0", "eslint": "^8.0.0", "jasmine-core": "~4.0.0", - "karma": "~6.3.0", + "karma": "~6.4.4", "karma-chrome-launcher": "~3.1.0", "karma-coverage": "~2.1.0", "karma-jasmine": "~4.0.0", "karma-jasmine-html-reporter": "^1.7.0", - "typescript": "~5.8.0" + "typescript": "~5.9.3" } } diff --git a/templates/app/angular/src/app/app.config.ts b/templates/app/angular/src/app/app.config.ts index 0afccfde31..1baf8e50ae 100644 --- a/templates/app/angular/src/app/app.config.ts +++ b/templates/app/angular/src/app/app.config.ts @@ -1,3 +1,9 @@ +import { + withValidationBluePrint, + provideAbpThemeShared, + provideLogo, + withEnvironmentOptions, +} from '@abp/ng.theme.shared'; import { ApplicationConfig } from '@angular/core'; import { provideRouter } from '@angular/router'; import { provideAnimations } from '@angular/platform-browser/animations'; @@ -6,9 +12,8 @@ import { appRoutes } from './app.routes'; import { APP_ROUTE_PROVIDER } from './route.provider'; import { provideAbpCore, withOptions } from '@abp/ng.core'; import { environment } from '../environments/environment'; -import { registerLocale } from '@abp/ng.core/locale'; +import { registerLocaleForEsBuild } from '@abp/ng.core/locale'; import { provideAbpOAuth } from '@abp/ng.oauth'; -import { provideAbpThemeShared, provideLogo, withEnvironmentOptions} from '@abp/ng.theme.shared'; import { provideSettingManagementConfig } from '@abp/ng.setting-management/config'; import { provideAccountConfig } from '@abp/ng.account/config'; import { provideIdentityConfig } from '@abp/ng.identity/config'; @@ -24,13 +29,12 @@ export const appConfig: ApplicationConfig = { provideAbpCore( withOptions({ environment, - registerLocaleFn: registerLocale(), + registerLocaleFn: registerLocaleForEsBuild(), }) ), provideThemeLeptonX(), provideSideMenuLayout(), provideAbpOAuth(), - provideAbpThemeShared(), provideSettingManagementConfig(), provideAccountConfig(), provideIdentityConfig(), @@ -38,5 +42,10 @@ export const appConfig: ApplicationConfig = { provideFeatureManagementConfig(), provideAnimations(), provideLogo(withEnvironmentOptions(environment)), + provideAbpThemeShared( + withValidationBluePrint({ + wrongPassword: 'Please choose 1q2w3E*', + }) + ), ], }; diff --git a/templates/app/angular/src/main.ts b/templates/app/angular/src/main.ts index 7180ec1a32..b7ef9f0bc3 100644 --- a/templates/app/angular/src/main.ts +++ b/templates/app/angular/src/main.ts @@ -1,5 +1,6 @@ +import { provideZoneChangeDetection } from "@angular/core"; import { bootstrapApplication } from '@angular/platform-browser'; import { appConfig } from './app/app.config'; import { AppComponent } from './app/app.component'; -bootstrapApplication(AppComponent, appConfig).catch(err => console.error(err)); +bootstrapApplication(AppComponent, {...appConfig, providers: [provideZoneChangeDetection(), ...appConfig.providers]}).catch(err => console.error(err)); diff --git a/templates/app/angular/tsconfig.json b/templates/app/angular/tsconfig.json index 0322d97e4d..9a18adaf46 100644 --- a/templates/app/angular/tsconfig.json +++ b/templates/app/angular/tsconfig.json @@ -10,13 +10,9 @@ "moduleResolution": "bundler", "importHelpers": true, "target": "ES2022", - "module": "esnext", + "module": "ES2022", "skipLibCheck": true, "esModuleInterop": true, - "lib": [ - "es2020", - "dom" - ], "paths": { "@proxy": [ "src/app/proxy/index.ts" diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Client/MyCompanyName.MyProjectName.Blazor.Client.csproj b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Client/MyCompanyName.MyProjectName.Blazor.Client.csproj index a05c7e6476..f098f3f8bb 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Client/MyCompanyName.MyProjectName.Blazor.Client.csproj +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Client/MyCompanyName.MyProjectName.Blazor.Client.csproj @@ -12,8 +12,8 @@ - - + + diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server.Tiered/MyCompanyName.MyProjectName.Blazor.Server.Tiered.csproj b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server.Tiered/MyCompanyName.MyProjectName.Blazor.Server.Tiered.csproj index 03043afdd3..d6ecb3f8a5 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server.Tiered/MyCompanyName.MyProjectName.Blazor.Server.Tiered.csproj +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server.Tiered/MyCompanyName.MyProjectName.Blazor.Server.Tiered.csproj @@ -14,8 +14,8 @@ - - + + diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server/MyCompanyName.MyProjectName.Blazor.Server.csproj b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server/MyCompanyName.MyProjectName.Blazor.Server.csproj index 9c0f3985f8..447561b8d7 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server/MyCompanyName.MyProjectName.Blazor.Server.csproj +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server/MyCompanyName.MyProjectName.Blazor.Server.csproj @@ -15,8 +15,8 @@ - - + + diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp.Client/MyCompanyName.MyProjectName.Blazor.WebApp.Client.csproj b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp.Client/MyCompanyName.MyProjectName.Blazor.WebApp.Client.csproj index aa6591bcab..4cf0f1600a 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp.Client/MyCompanyName.MyProjectName.Blazor.WebApp.Client.csproj +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp.Client/MyCompanyName.MyProjectName.Blazor.WebApp.Client.csproj @@ -13,8 +13,8 @@ - - + + diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp.Tiered.Client/MyCompanyName.MyProjectName.Blazor.WebApp.Tiered.Client.csproj b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp.Tiered.Client/MyCompanyName.MyProjectName.Blazor.WebApp.Tiered.Client.csproj index f8a19a1921..84b1706103 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp.Tiered.Client/MyCompanyName.MyProjectName.Blazor.WebApp.Tiered.Client.csproj +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp.Tiered.Client/MyCompanyName.MyProjectName.Blazor.WebApp.Tiered.Client.csproj @@ -13,8 +13,8 @@ - - + + diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp.Tiered/MyCompanyName.MyProjectName.Blazor.WebApp.Tiered.csproj b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp.Tiered/MyCompanyName.MyProjectName.Blazor.WebApp.Tiered.csproj index 55159c4683..27146946a7 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp.Tiered/MyCompanyName.MyProjectName.Blazor.WebApp.Tiered.csproj +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp.Tiered/MyCompanyName.MyProjectName.Blazor.WebApp.Tiered.csproj @@ -16,8 +16,8 @@ - - + + diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp/MyCompanyName.MyProjectName.Blazor.WebApp.csproj b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp/MyCompanyName.MyProjectName.Blazor.WebApp.csproj index 1aec506594..3347686ce2 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp/MyCompanyName.MyProjectName.Blazor.WebApp.csproj +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp/MyCompanyName.MyProjectName.Blazor.WebApp.csproj @@ -16,8 +16,8 @@ - - + + diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server/Migrations/20251020063648_Initial.Designer.cs b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/Migrations/20251218020130_Initial.Designer.cs similarity index 91% rename from templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server/Migrations/20251020063648_Initial.Designer.cs rename to templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/Migrations/20251218020130_Initial.Designer.cs index 209982f8ed..00548f561d 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server/Migrations/20251020063648_Initial.Designer.cs +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/Migrations/20251218020130_Initial.Designer.cs @@ -5,7 +5,7 @@ using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using MyCompanyName.MyProjectName.Data; +using MyCompanyName.MyProjectName.EntityFrameworkCore; using Volo.Abp.EntityFrameworkCore; #nullable disable @@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore; namespace MyCompanyName.MyProjectName.Migrations { [DbContext(typeof(MyProjectNameDbContext))] - [Migration("20251020063648_Initial")] + [Migration("20251218020130_Initial")] partial class Initial { /// @@ -22,7 +22,7 @@ namespace MyCompanyName.MyProjectName.Migrations #pragma warning disable 612, 618 modelBuilder .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) - .HasAnnotation("ProductVersion", "10.0.0-rc.2.25502.107") + .HasAnnotation("ProductVersion", "10.0.0") .HasAnnotation("Relational:MaxIdentifierLength", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); @@ -318,6 +318,70 @@ namespace MyCompanyName.MyProjectName.Migrations b.ToTable("AbpEntityPropertyChanges", (string)null); }); + modelBuilder.Entity("Volo.Abp.BackgroundJobs.BackgroundJobRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ApplicationName") + .HasMaxLength(96) + .HasColumnType("nvarchar(96)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsAbandoned") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false); + + b.Property("JobArgs") + .IsRequired() + .HasMaxLength(1048576) + .HasColumnType("nvarchar(max)"); + + b.Property("JobName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("LastTryTime") + .HasColumnType("datetime2"); + + b.Property("NextTryTime") + .HasColumnType("datetime2"); + + b.Property("Priority") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint") + .HasDefaultValue((byte)15); + + b.Property("TryCount") + .ValueGeneratedOnAdd() + .HasColumnType("smallint") + .HasDefaultValue((short)0); + + b.HasKey("Id"); + + b.HasIndex("IsAbandoned", "NextTryTime"); + + b.ToTable("AbpBackgroundJobs", (string)null); + }); + modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureDefinitionRecord", b => { b.Property("Id") @@ -443,7 +507,6 @@ namespace MyCompanyName.MyProjectName.Migrations modelBuilder.Entity("Volo.Abp.Identity.IdentityClaimType", b => { b.Property("Id") - .ValueGeneratedOnAdd() .HasColumnType("uniqueidentifier"); b.Property("ConcurrencyStamp") @@ -496,7 +559,6 @@ namespace MyCompanyName.MyProjectName.Migrations modelBuilder.Entity("Volo.Abp.Identity.IdentityLinkUser", b => { b.Property("Id") - .ValueGeneratedOnAdd() .HasColumnType("uniqueidentifier"); b.Property("SourceTenantId") @@ -523,7 +585,6 @@ namespace MyCompanyName.MyProjectName.Migrations modelBuilder.Entity("Volo.Abp.Identity.IdentityRole", b => { b.Property("Id") - .ValueGeneratedOnAdd() .HasColumnType("uniqueidentifier"); b.Property("ConcurrencyStamp") @@ -609,7 +670,6 @@ namespace MyCompanyName.MyProjectName.Migrations modelBuilder.Entity("Volo.Abp.Identity.IdentitySecurityLog", b => { b.Property("Id") - .ValueGeneratedOnAdd() .HasColumnType("uniqueidentifier"); b.Property("Action") @@ -686,7 +746,6 @@ namespace MyCompanyName.MyProjectName.Migrations modelBuilder.Entity("Volo.Abp.Identity.IdentitySession", b => { b.Property("Id") - .ValueGeneratedOnAdd() .HasColumnType("uniqueidentifier"); b.Property("ClientId") @@ -742,7 +801,6 @@ namespace MyCompanyName.MyProjectName.Migrations modelBuilder.Entity("Volo.Abp.Identity.IdentityUser", b => { b.Property("Id") - .ValueGeneratedOnAdd() .HasColumnType("uniqueidentifier"); b.Property("AccessFailedCount") @@ -821,6 +879,9 @@ namespace MyCompanyName.MyProjectName.Migrations b.Property("LastPasswordChangeTime") .HasColumnType("datetimeoffset"); + b.Property("LastSignInTime") + .HasColumnType("datetimeoffset"); + b.Property("LockoutEnabled") .ValueGeneratedOnAdd() .HasColumnType("bit") @@ -937,7 +998,6 @@ namespace MyCompanyName.MyProjectName.Migrations modelBuilder.Entity("Volo.Abp.Identity.IdentityUserDelegation", b => { b.Property("Id") - .ValueGeneratedOnAdd() .HasColumnType("uniqueidentifier"); b.Property("EndTime") @@ -1017,6 +1077,26 @@ namespace MyCompanyName.MyProjectName.Migrations b.ToTable("AbpUserOrganizationUnits", (string)null); }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserPasskey", b => + { + b.Property("CredentialId") + .HasMaxLength(1024) + .HasColumnType("varbinary(1024)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("CredentialId"); + + b.HasIndex("UserId"); + + b.ToTable("AbpUserPasskeys", (string)null); + }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserPasswordHistory", b => { b.Property("UserId") @@ -1085,7 +1165,6 @@ namespace MyCompanyName.MyProjectName.Migrations modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnit", b => { b.Property("Id") - .ValueGeneratedOnAdd() .HasColumnType("uniqueidentifier"); b.Property("Code") @@ -1506,13 +1585,16 @@ namespace MyCompanyName.MyProjectName.Migrations .HasColumnName("ExtraProperties"); b.Property("GroupName") - .IsRequired() .HasMaxLength(128) .HasColumnType("nvarchar(128)"); b.Property("IsEnabled") .HasColumnType("bit"); + b.Property("ManagementPermissionName") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + b.Property("MultiTenancySide") .HasColumnType("tinyint"); @@ -1529,6 +1611,10 @@ namespace MyCompanyName.MyProjectName.Migrations .HasMaxLength(128) .HasColumnType("nvarchar(128)"); + b.Property("ResourceName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + b.Property("StateCheckers") .HasMaxLength(256) .HasColumnType("nvarchar(256)"); @@ -1537,8 +1623,9 @@ namespace MyCompanyName.MyProjectName.Migrations b.HasIndex("GroupName"); - b.HasIndex("Name") - .IsUnique(); + b.HasIndex("ResourceName", "Name") + .IsUnique() + .HasFilter("[ResourceName] IS NOT NULL"); b.ToTable("AbpPermissions", (string)null); }); @@ -1605,6 +1692,50 @@ namespace MyCompanyName.MyProjectName.Migrations b.ToTable("AbpPermissionGroups", (string)null); }); + modelBuilder.Entity("Volo.Abp.PermissionManagement.ResourcePermissionGrant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ProviderKey") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ProviderName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ResourceKey") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ResourceName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "Name", "ResourceName", "ResourceKey", "ProviderName", "ProviderKey") + .IsUnique() + .HasFilter("[TenantId] IS NOT NULL"); + + b.ToTable("AbpResourcePermissionGrants", (string)null); + }); + modelBuilder.Entity("Volo.Abp.SettingManagement.Setting", b => { b.Property("Id") @@ -1690,7 +1821,6 @@ namespace MyCompanyName.MyProjectName.Migrations modelBuilder.Entity("Volo.Abp.TenantManagement.Tenant", b => { b.Property("Id") - .ValueGeneratedOnAdd() .HasColumnType("uniqueidentifier"); b.Property("ConcurrencyStamp") @@ -1845,6 +1975,51 @@ namespace MyCompanyName.MyProjectName.Migrations .IsRequired(); }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserPasskey", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Passkeys") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("Volo.Abp.Identity.IdentityPasskeyData", "Data", b1 => + { + b1.Property("IdentityUserPasskeyCredentialId"); + + b1.Property("AttestationObject"); + + b1.Property("ClientDataJson"); + + b1.Property("CreatedAt"); + + b1.Property("IsBackedUp"); + + b1.Property("IsBackupEligible"); + + b1.Property("IsUserVerified"); + + b1.Property("Name"); + + b1.Property("PublicKey"); + + b1.Property("SignCount"); + + b1.PrimitiveCollection("Transports"); + + b1.HasKey("IdentityUserPasskeyCredentialId"); + + b1.ToTable("AbpUserPasskeys"); + + b1.ToJson("Data"); + + b1.WithOwner() + .HasForeignKey("IdentityUserPasskeyCredentialId"); + }); + + b.Navigation("Data"); + }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserPasswordHistory", b => { b.HasOne("Volo.Abp.Identity.IdentityUser", null) @@ -1952,6 +2127,8 @@ namespace MyCompanyName.MyProjectName.Migrations b.Navigation("OrganizationUnits"); + b.Navigation("Passkeys"); + b.Navigation("PasswordHistories"); b.Navigation("Roles"); diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server/Migrations/20251020063648_Initial.cs b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/Migrations/20251218020130_Initial.cs similarity index 92% rename from templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server/Migrations/20251020063648_Initial.cs rename to templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/Migrations/20251218020130_Initial.cs index 151f09e4d4..2bbe785a0d 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server/Migrations/20251020063648_Initial.cs +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/Migrations/20251218020130_Initial.cs @@ -60,6 +60,28 @@ namespace MyCompanyName.MyProjectName.Migrations table.PrimaryKey("PK_AbpAuditLogs", x => x.Id); }); + migrationBuilder.CreateTable( + name: "AbpBackgroundJobs", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + ApplicationName = table.Column(type: "nvarchar(96)", maxLength: 96, nullable: true), + JobName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + JobArgs = table.Column(type: "nvarchar(max)", maxLength: 1048576, nullable: false), + TryCount = table.Column(type: "smallint", nullable: false, defaultValue: (short)0), + CreationTime = table.Column(type: "datetime2", nullable: false), + NextTryTime = table.Column(type: "datetime2", nullable: false), + LastTryTime = table.Column(type: "datetime2", nullable: true), + IsAbandoned = table.Column(type: "bit", nullable: false, defaultValue: false), + Priority = table.Column(type: "tinyint", nullable: false, defaultValue: (byte)15), + ExtraProperties = table.Column(type: "nvarchar(max)", nullable: false), + ConcurrencyStamp = table.Column(type: "nvarchar(40)", maxLength: 40, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpBackgroundJobs", x => x.Id); + }); + migrationBuilder.CreateTable( name: "AbpClaimTypes", columns: table => new @@ -211,8 +233,10 @@ namespace MyCompanyName.MyProjectName.Migrations columns: table => new { Id = table.Column(type: "uniqueidentifier", nullable: false), - GroupName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + GroupName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: true), Name = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + ResourceName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + ManagementPermissionName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: true), ParentName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: true), DisplayName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), IsEnabled = table.Column(type: "bit", nullable: false), @@ -226,6 +250,23 @@ namespace MyCompanyName.MyProjectName.Migrations table.PrimaryKey("PK_AbpPermissions", x => x.Id); }); + migrationBuilder.CreateTable( + name: "AbpResourcePermissionGrants", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + TenantId = table.Column(type: "uniqueidentifier", nullable: true), + Name = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + ProviderName = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false), + ProviderKey = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false), + ResourceName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), + ResourceKey = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpResourcePermissionGrants", x => x.Id); + }); + migrationBuilder.CreateTable( name: "AbpRoles", columns: table => new @@ -393,6 +434,7 @@ namespace MyCompanyName.MyProjectName.Migrations ShouldChangePasswordOnNextLogin = table.Column(type: "bit", nullable: false), EntityVersion = table.Column(type: "int", nullable: false), LastPasswordChangeTime = table.Column(type: "datetimeoffset", nullable: true), + LastSignInTime = table.Column(type: "datetimeoffset", nullable: true), ExtraProperties = table.Column(type: "nvarchar(max)", nullable: false), ConcurrencyStamp = table.Column(type: "nvarchar(40)", maxLength: 40, nullable: false), CreationTime = table.Column(type: "datetime2", nullable: false), @@ -658,6 +700,26 @@ namespace MyCompanyName.MyProjectName.Migrations onDelete: ReferentialAction.Cascade); }); + migrationBuilder.CreateTable( + name: "AbpUserPasskeys", + columns: table => new + { + CredentialId = table.Column(type: "varbinary(1024)", maxLength: 1024, nullable: false), + TenantId = table.Column(type: "uniqueidentifier", nullable: true), + UserId = table.Column(type: "uniqueidentifier", nullable: false), + Data = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpUserPasskeys", x => x.CredentialId); + table.ForeignKey( + name: "FK_AbpUserPasskeys_AbpUsers_UserId", + column: x => x.UserId, + principalTable: "AbpUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + migrationBuilder.CreateTable( name: "AbpUserPasswordHistories", columns: table => new @@ -826,6 +888,11 @@ namespace MyCompanyName.MyProjectName.Migrations table: "AbpAuditLogs", columns: new[] { "TenantId", "UserId", "ExecutionTime" }); + migrationBuilder.CreateIndex( + name: "IX_AbpBackgroundJobs_IsAbandoned_NextTryTime", + table: "AbpBackgroundJobs", + columns: new[] { "IsAbandoned", "NextTryTime" }); + migrationBuilder.CreateIndex( name: "IX_AbpEntityChanges_AuditLogId", table: "AbpEntityChanges", @@ -906,10 +973,18 @@ namespace MyCompanyName.MyProjectName.Migrations column: "GroupName"); migrationBuilder.CreateIndex( - name: "IX_AbpPermissions_Name", + name: "IX_AbpPermissions_ResourceName_Name", table: "AbpPermissions", - column: "Name", - unique: true); + columns: new[] { "ResourceName", "Name" }, + unique: true, + filter: "[ResourceName] IS NOT NULL"); + + migrationBuilder.CreateIndex( + name: "IX_AbpResourcePermissionGrants_TenantId_Name_ResourceName_ResourceKey_ProviderName_ProviderKey", + table: "AbpResourcePermissionGrants", + columns: new[] { "TenantId", "Name", "ResourceName", "ResourceKey", "ProviderName", "ProviderKey" }, + unique: true, + filter: "[TenantId] IS NOT NULL"); migrationBuilder.CreateIndex( name: "IX_AbpRoleClaims_RoleId", @@ -994,6 +1069,11 @@ namespace MyCompanyName.MyProjectName.Migrations table: "AbpUserOrganizationUnits", columns: new[] { "UserId", "OrganizationUnitId" }); + migrationBuilder.CreateIndex( + name: "IX_AbpUserPasskeys_UserId", + table: "AbpUserPasskeys", + column: "UserId"); + migrationBuilder.CreateIndex( name: "IX_AbpUserRoles_RoleId_UserId", table: "AbpUserRoles", @@ -1059,6 +1139,9 @@ namespace MyCompanyName.MyProjectName.Migrations migrationBuilder.DropTable( name: "AbpAuditLogExcelFiles"); + migrationBuilder.DropTable( + name: "AbpBackgroundJobs"); + migrationBuilder.DropTable( name: "AbpClaimTypes"); @@ -1089,6 +1172,9 @@ namespace MyCompanyName.MyProjectName.Migrations migrationBuilder.DropTable( name: "AbpPermissions"); + migrationBuilder.DropTable( + name: "AbpResourcePermissionGrants"); + migrationBuilder.DropTable( name: "AbpRoleClaims"); @@ -1119,6 +1205,9 @@ namespace MyCompanyName.MyProjectName.Migrations migrationBuilder.DropTable( name: "AbpUserOrganizationUnits"); + migrationBuilder.DropTable( + name: "AbpUserPasskeys"); + migrationBuilder.DropTable( name: "AbpUserPasswordHistories"); diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/Migrations/MyProjectNameDbContextModelSnapshot.cs b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/Migrations/MyProjectNameDbContextModelSnapshot.cs index 5487b9443c..3c2f984477 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/Migrations/MyProjectNameDbContextModelSnapshot.cs +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/Migrations/MyProjectNameDbContextModelSnapshot.cs @@ -19,7 +19,7 @@ namespace MyCompanyName.MyProjectName.Migrations #pragma warning disable 612, 618 modelBuilder .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) - .HasAnnotation("ProductVersion", "10.0.0-rc.2.25502.107") + .HasAnnotation("ProductVersion", "10.0.0") .HasAnnotation("Relational:MaxIdentifierLength", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); @@ -876,6 +876,9 @@ namespace MyCompanyName.MyProjectName.Migrations b.Property("LastPasswordChangeTime") .HasColumnType("datetimeoffset"); + b.Property("LastSignInTime") + .HasColumnType("datetimeoffset"); + b.Property("LockoutEnabled") .ValueGeneratedOnAdd() .HasColumnType("bit") @@ -1071,6 +1074,26 @@ namespace MyCompanyName.MyProjectName.Migrations b.ToTable("AbpUserOrganizationUnits", (string)null); }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserPasskey", b => + { + b.Property("CredentialId") + .HasMaxLength(1024) + .HasColumnType("varbinary(1024)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("CredentialId"); + + b.HasIndex("UserId"); + + b.ToTable("AbpUserPasskeys", (string)null); + }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserPasswordHistory", b => { b.Property("UserId") @@ -1559,13 +1582,16 @@ namespace MyCompanyName.MyProjectName.Migrations .HasColumnName("ExtraProperties"); b.Property("GroupName") - .IsRequired() .HasMaxLength(128) .HasColumnType("nvarchar(128)"); b.Property("IsEnabled") .HasColumnType("bit"); + b.Property("ManagementPermissionName") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + b.Property("MultiTenancySide") .HasColumnType("tinyint"); @@ -1582,6 +1608,10 @@ namespace MyCompanyName.MyProjectName.Migrations .HasMaxLength(128) .HasColumnType("nvarchar(128)"); + b.Property("ResourceName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + b.Property("StateCheckers") .HasMaxLength(256) .HasColumnType("nvarchar(256)"); @@ -1590,8 +1620,9 @@ namespace MyCompanyName.MyProjectName.Migrations b.HasIndex("GroupName"); - b.HasIndex("Name") - .IsUnique(); + b.HasIndex("ResourceName", "Name") + .IsUnique() + .HasFilter("[ResourceName] IS NOT NULL"); b.ToTable("AbpPermissions", (string)null); }); @@ -1658,6 +1689,50 @@ namespace MyCompanyName.MyProjectName.Migrations b.ToTable("AbpPermissionGroups", (string)null); }); + modelBuilder.Entity("Volo.Abp.PermissionManagement.ResourcePermissionGrant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ProviderKey") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ProviderName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ResourceKey") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ResourceName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "Name", "ResourceName", "ResourceKey", "ProviderName", "ProviderKey") + .IsUnique() + .HasFilter("[TenantId] IS NOT NULL"); + + b.ToTable("AbpResourcePermissionGrants", (string)null); + }); + modelBuilder.Entity("Volo.Abp.SettingManagement.Setting", b => { b.Property("Id") @@ -1897,6 +1972,51 @@ namespace MyCompanyName.MyProjectName.Migrations .IsRequired(); }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserPasskey", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Passkeys") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("Volo.Abp.Identity.IdentityPasskeyData", "Data", b1 => + { + b1.Property("IdentityUserPasskeyCredentialId"); + + b1.Property("AttestationObject"); + + b1.Property("ClientDataJson"); + + b1.Property("CreatedAt"); + + b1.Property("IsBackedUp"); + + b1.Property("IsBackupEligible"); + + b1.Property("IsUserVerified"); + + b1.Property("Name"); + + b1.Property("PublicKey"); + + b1.Property("SignCount"); + + b1.PrimitiveCollection("Transports"); + + b1.HasKey("IdentityUserPasskeyCredentialId"); + + b1.ToTable("AbpUserPasskeys"); + + b1.ToJson("Data"); + + b1.WithOwner() + .HasForeignKey("IdentityUserPasskeyCredentialId"); + }); + + b.Navigation("Data"); + }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserPasswordHistory", b => { b.HasOne("Volo.Abp.Identity.IdentityUser", null) @@ -2004,6 +2124,8 @@ namespace MyCompanyName.MyProjectName.Migrations b.Navigation("OrganizationUnits"); + b.Navigation("Passkeys"); + b.Navigation("PasswordHistories"); b.Navigation("Roles"); diff --git a/templates/module/angular/package.json b/templates/module/angular/package.json index 5fc4ebe29e..6e66db9a0d 100644 --- a/templates/module/angular/package.json +++ b/templates/module/angular/package.json @@ -22,44 +22,43 @@ "@abp/ng.tenant-management": "~10.0.1", "@abp/ng.theme.basic": "~10.0.1", "@abp/ng.theme.shared": "~10.0.1", - "@angular/animations": "~20.0.0", - "@angular/common": "~20.0.0", - "@angular/compiler": "~20.0.0", - "@angular/core": "~20.0.0", - "@angular/forms": "~20.0.0", - "@angular/localize": "~20.0.0", - "@angular/platform-browser": "~20.0.0", - "@angular/platform-browser-dynamic": "~20.0.0", - "@angular/router": "~20.0.0", + "@angular/animations": "~21.0.0", + "@angular/common": "~21.0.0", + "@angular/compiler": "~21.0.0", + "@angular/core": "~21.0.0", + "@angular/forms": "~21.0.0", + "@angular/localize": "~21.0.0", + "@angular/platform-browser": "~21.0.0", + "@angular/platform-browser-dynamic": "~21.0.0", + "@angular/router": "~21.0.0", "rxjs": "~7.8.0", "tslib": "^2.0.0", "zone.js": "~0.15.0" }, "devDependencies": { "@abp/ng.schematics": "~10.0.1", - "@angular-devkit/build-angular": "~20.0.0", - "@angular-eslint/builder": "~20.0.0", - "@angular-eslint/eslint-plugin": "~20.0.0", - "@angular-eslint/eslint-plugin-template": "~20.0.0", - "@angular-eslint/schematics": "~20.0.0", - "@angular-eslint/template-parser": "~20.0.0", - "@angular/cli": "~20.0.0", - "@angular/compiler-cli": "~20.0.0", - "@angular/language-service": "~20.0.0", - "@angular/build": "~20.0.0", + "@angular-eslint/builder": "~21.0.0", + "@angular-eslint/eslint-plugin": "~21.0.0", + "@angular-eslint/eslint-plugin-template": "~21.0.0", + "@angular-eslint/schematics": "~21.0.0", + "@angular-eslint/template-parser": "~21.0.0", + "@angular/build": "~21.0.0", + "@angular/cli": "~21.0.0", + "@angular/compiler-cli": "~21.0.0", + "@angular/language-service": "~21.0.0", "@types/jasmine": "~3.6.0", "@types/node": "^12.11.1", "@typescript-eslint/eslint-plugin": "7.16.0", "@typescript-eslint/parser": "7.16.0", "eslint": "^8.0.0", "jasmine-core": "~4.0.0", - "karma": "~6.3.0", + "karma": "~6.4.4", "karma-chrome-launcher": "~3.1.0", "karma-coverage": "~2.1.0", "karma-jasmine": "~4.0.0", "karma-jasmine-html-reporter": "^1.7.0", - "ng-packagr": "~20.0.0", + "ng-packagr": "~21.0.0", "symlink": "^2.0.0", - "typescript": "~5.8.0" + "typescript": "~5.9.0" } } diff --git a/templates/module/angular/projects/dev-app/src/main.ts b/templates/module/angular/projects/dev-app/src/main.ts index 7180ec1a32..b7ef9f0bc3 100644 --- a/templates/module/angular/projects/dev-app/src/main.ts +++ b/templates/module/angular/projects/dev-app/src/main.ts @@ -1,5 +1,6 @@ +import { provideZoneChangeDetection } from "@angular/core"; import { bootstrapApplication } from '@angular/platform-browser'; import { appConfig } from './app/app.config'; import { AppComponent } from './app/app.component'; -bootstrapApplication(AppComponent, appConfig).catch(err => console.error(err)); +bootstrapApplication(AppComponent, {...appConfig, providers: [provideZoneChangeDetection(), ...appConfig.providers]}).catch(err => console.error(err)); diff --git a/templates/module/angular/projects/my-project-name/tsconfig.lib.json b/templates/module/angular/projects/my-project-name/tsconfig.lib.json index 8246b3f0b1..6fea9ad884 100644 --- a/templates/module/angular/projects/my-project-name/tsconfig.lib.json +++ b/templates/module/angular/projects/my-project-name/tsconfig.lib.json @@ -6,11 +6,7 @@ "declaration": true, "declarationMap": true, "inlineSources": true, - "types": [], - "lib": [ - "dom", - "es2018" - ] + "types": [] }, "exclude": [ "src/test.ts", diff --git a/templates/module/angular/tsconfig.json b/templates/module/angular/tsconfig.json index 0cbe9a9de8..2e5e466bf0 100644 --- a/templates/module/angular/tsconfig.json +++ b/templates/module/angular/tsconfig.json @@ -2,6 +2,7 @@ "extends": "./tsconfig.prod.json", "compilerOptions": { "esModuleInterop": true, + "moduleResolution": "bundler", "skipLibCheck": true, "paths": { "@my-company-name/my-project-name": [ diff --git a/templates/module/angular/tsconfig.prod.json b/templates/module/angular/tsconfig.prod.json index 28b0a75f51..2f4afb8ff7 100644 --- a/templates/module/angular/tsconfig.prod.json +++ b/templates/module/angular/tsconfig.prod.json @@ -7,10 +7,10 @@ "declaration": false, "downlevelIteration": true, "experimentalDecorators": true, - "moduleResolution": "node", + "moduleResolution": "bundler", "importHelpers": true, - "target": "es2020", - "module": "esnext", + "target": "es2022", + "module": "ES2022", "esModuleInterop": true, "lib": [ "es2020", diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/Migrations/20251020063552_Initial.Designer.cs b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/Migrations/20251218020219_Initial.Designer.cs similarity index 94% rename from templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/Migrations/20251020063552_Initial.Designer.cs rename to templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/Migrations/20251218020219_Initial.Designer.cs index f63ca1375e..cc4aacd2c8 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/Migrations/20251020063552_Initial.Designer.cs +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/Migrations/20251218020219_Initial.Designer.cs @@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore; namespace MyCompanyName.MyProjectName.Migrations { [DbContext(typeof(AuthServerDbContext))] - [Migration("20251020063552_Initial")] + [Migration("20251218020219_Initial")] partial class Initial { /// @@ -22,7 +22,7 @@ namespace MyCompanyName.MyProjectName.Migrations #pragma warning disable 612, 618 modelBuilder .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) - .HasAnnotation("ProductVersion", "10.0.0-rc.2.25502.107") + .HasAnnotation("ProductVersion", "10.0.0") .HasAnnotation("Relational:MaxIdentifierLength", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); @@ -821,6 +821,9 @@ namespace MyCompanyName.MyProjectName.Migrations b.Property("LastPasswordChangeTime") .HasColumnType("datetimeoffset"); + b.Property("LastSignInTime") + .HasColumnType("datetimeoffset"); + b.Property("LockoutEnabled") .ValueGeneratedOnAdd() .HasColumnType("bit") @@ -1017,6 +1020,26 @@ namespace MyCompanyName.MyProjectName.Migrations b.ToTable("AbpUserOrganizationUnits", (string)null); }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserPasskey", b => + { + b.Property("CredentialId") + .HasMaxLength(1024) + .HasColumnType("varbinary(1024)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("CredentialId"); + + b.HasIndex("UserId"); + + b.ToTable("AbpUserPasskeys", (string)null); + }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserPasswordHistory", b => { b.Property("UserId") @@ -1506,13 +1529,16 @@ namespace MyCompanyName.MyProjectName.Migrations .HasColumnName("ExtraProperties"); b.Property("GroupName") - .IsRequired() .HasMaxLength(128) .HasColumnType("nvarchar(128)"); b.Property("IsEnabled") .HasColumnType("bit"); + b.Property("ManagementPermissionName") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + b.Property("MultiTenancySide") .HasColumnType("tinyint"); @@ -1529,6 +1555,10 @@ namespace MyCompanyName.MyProjectName.Migrations .HasMaxLength(128) .HasColumnType("nvarchar(128)"); + b.Property("ResourceName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + b.Property("StateCheckers") .HasMaxLength(256) .HasColumnType("nvarchar(256)"); @@ -1537,8 +1567,9 @@ namespace MyCompanyName.MyProjectName.Migrations b.HasIndex("GroupName"); - b.HasIndex("Name") - .IsUnique(); + b.HasIndex("ResourceName", "Name") + .IsUnique() + .HasFilter("[ResourceName] IS NOT NULL"); b.ToTable("AbpPermissions", (string)null); }); @@ -1605,6 +1636,50 @@ namespace MyCompanyName.MyProjectName.Migrations b.ToTable("AbpPermissionGroups", (string)null); }); + modelBuilder.Entity("Volo.Abp.PermissionManagement.ResourcePermissionGrant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ProviderKey") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ProviderName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ResourceKey") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ResourceName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "Name", "ResourceName", "ResourceKey", "ProviderName", "ProviderKey") + .IsUnique() + .HasFilter("[TenantId] IS NOT NULL"); + + b.ToTable("AbpResourcePermissionGrants", (string)null); + }); + modelBuilder.Entity("Volo.Abp.SettingManagement.Setting", b => { b.Property("Id") @@ -1845,6 +1920,51 @@ namespace MyCompanyName.MyProjectName.Migrations .IsRequired(); }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserPasskey", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Passkeys") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("Volo.Abp.Identity.IdentityPasskeyData", "Data", b1 => + { + b1.Property("IdentityUserPasskeyCredentialId"); + + b1.Property("AttestationObject"); + + b1.Property("ClientDataJson"); + + b1.Property("CreatedAt"); + + b1.Property("IsBackedUp"); + + b1.Property("IsBackupEligible"); + + b1.Property("IsUserVerified"); + + b1.Property("Name"); + + b1.Property("PublicKey"); + + b1.Property("SignCount"); + + b1.PrimitiveCollection("Transports"); + + b1.HasKey("IdentityUserPasskeyCredentialId"); + + b1.ToTable("AbpUserPasskeys"); + + b1.ToJson("Data"); + + b1.WithOwner() + .HasForeignKey("IdentityUserPasskeyCredentialId"); + }); + + b.Navigation("Data"); + }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserPasswordHistory", b => { b.HasOne("Volo.Abp.Identity.IdentityUser", null) @@ -1952,6 +2072,8 @@ namespace MyCompanyName.MyProjectName.Migrations b.Navigation("OrganizationUnits"); + b.Navigation("Passkeys"); + b.Navigation("PasswordHistories"); b.Navigation("Roles"); diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/Migrations/20251020063552_Initial.cs b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/Migrations/20251218020219_Initial.cs similarity index 94% rename from templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/Migrations/20251020063552_Initial.cs rename to templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/Migrations/20251218020219_Initial.cs index 151f09e4d4..3ba2c09d6f 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/Migrations/20251020063552_Initial.cs +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/Migrations/20251218020219_Initial.cs @@ -211,8 +211,10 @@ namespace MyCompanyName.MyProjectName.Migrations columns: table => new { Id = table.Column(type: "uniqueidentifier", nullable: false), - GroupName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + GroupName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: true), Name = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + ResourceName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + ManagementPermissionName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: true), ParentName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: true), DisplayName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), IsEnabled = table.Column(type: "bit", nullable: false), @@ -226,6 +228,23 @@ namespace MyCompanyName.MyProjectName.Migrations table.PrimaryKey("PK_AbpPermissions", x => x.Id); }); + migrationBuilder.CreateTable( + name: "AbpResourcePermissionGrants", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + TenantId = table.Column(type: "uniqueidentifier", nullable: true), + Name = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + ProviderName = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false), + ProviderKey = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false), + ResourceName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), + ResourceKey = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpResourcePermissionGrants", x => x.Id); + }); + migrationBuilder.CreateTable( name: "AbpRoles", columns: table => new @@ -393,6 +412,7 @@ namespace MyCompanyName.MyProjectName.Migrations ShouldChangePasswordOnNextLogin = table.Column(type: "bit", nullable: false), EntityVersion = table.Column(type: "int", nullable: false), LastPasswordChangeTime = table.Column(type: "datetimeoffset", nullable: true), + LastSignInTime = table.Column(type: "datetimeoffset", nullable: true), ExtraProperties = table.Column(type: "nvarchar(max)", nullable: false), ConcurrencyStamp = table.Column(type: "nvarchar(40)", maxLength: 40, nullable: false), CreationTime = table.Column(type: "datetime2", nullable: false), @@ -658,6 +678,26 @@ namespace MyCompanyName.MyProjectName.Migrations onDelete: ReferentialAction.Cascade); }); + migrationBuilder.CreateTable( + name: "AbpUserPasskeys", + columns: table => new + { + CredentialId = table.Column(type: "varbinary(1024)", maxLength: 1024, nullable: false), + TenantId = table.Column(type: "uniqueidentifier", nullable: true), + UserId = table.Column(type: "uniqueidentifier", nullable: false), + Data = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpUserPasskeys", x => x.CredentialId); + table.ForeignKey( + name: "FK_AbpUserPasskeys_AbpUsers_UserId", + column: x => x.UserId, + principalTable: "AbpUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + migrationBuilder.CreateTable( name: "AbpUserPasswordHistories", columns: table => new @@ -906,10 +946,18 @@ namespace MyCompanyName.MyProjectName.Migrations column: "GroupName"); migrationBuilder.CreateIndex( - name: "IX_AbpPermissions_Name", + name: "IX_AbpPermissions_ResourceName_Name", table: "AbpPermissions", - column: "Name", - unique: true); + columns: new[] { "ResourceName", "Name" }, + unique: true, + filter: "[ResourceName] IS NOT NULL"); + + migrationBuilder.CreateIndex( + name: "IX_AbpResourcePermissionGrants_TenantId_Name_ResourceName_ResourceKey_ProviderName_ProviderKey", + table: "AbpResourcePermissionGrants", + columns: new[] { "TenantId", "Name", "ResourceName", "ResourceKey", "ProviderName", "ProviderKey" }, + unique: true, + filter: "[TenantId] IS NOT NULL"); migrationBuilder.CreateIndex( name: "IX_AbpRoleClaims_RoleId", @@ -994,6 +1042,11 @@ namespace MyCompanyName.MyProjectName.Migrations table: "AbpUserOrganizationUnits", columns: new[] { "UserId", "OrganizationUnitId" }); + migrationBuilder.CreateIndex( + name: "IX_AbpUserPasskeys_UserId", + table: "AbpUserPasskeys", + column: "UserId"); + migrationBuilder.CreateIndex( name: "IX_AbpUserRoles_RoleId_UserId", table: "AbpUserRoles", @@ -1089,6 +1142,9 @@ namespace MyCompanyName.MyProjectName.Migrations migrationBuilder.DropTable( name: "AbpPermissions"); + migrationBuilder.DropTable( + name: "AbpResourcePermissionGrants"); + migrationBuilder.DropTable( name: "AbpRoleClaims"); @@ -1119,6 +1175,9 @@ namespace MyCompanyName.MyProjectName.Migrations migrationBuilder.DropTable( name: "AbpUserOrganizationUnits"); + migrationBuilder.DropTable( + name: "AbpUserPasskeys"); + migrationBuilder.DropTable( name: "AbpUserPasswordHistories"); diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/Migrations/AuthServerDbContextModelSnapshot.cs b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/Migrations/AuthServerDbContextModelSnapshot.cs index b2310baddb..b610d4ea04 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/Migrations/AuthServerDbContextModelSnapshot.cs +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/Migrations/AuthServerDbContextModelSnapshot.cs @@ -19,7 +19,7 @@ namespace MyCompanyName.MyProjectName.Migrations #pragma warning disable 612, 618 modelBuilder .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) - .HasAnnotation("ProductVersion", "10.0.0-rc.2.25502.107") + .HasAnnotation("ProductVersion", "10.0.0") .HasAnnotation("Relational:MaxIdentifierLength", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); @@ -818,6 +818,9 @@ namespace MyCompanyName.MyProjectName.Migrations b.Property("LastPasswordChangeTime") .HasColumnType("datetimeoffset"); + b.Property("LastSignInTime") + .HasColumnType("datetimeoffset"); + b.Property("LockoutEnabled") .ValueGeneratedOnAdd() .HasColumnType("bit") @@ -1014,6 +1017,26 @@ namespace MyCompanyName.MyProjectName.Migrations b.ToTable("AbpUserOrganizationUnits", (string)null); }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserPasskey", b => + { + b.Property("CredentialId") + .HasMaxLength(1024) + .HasColumnType("varbinary(1024)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("CredentialId"); + + b.HasIndex("UserId"); + + b.ToTable("AbpUserPasskeys", (string)null); + }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserPasswordHistory", b => { b.Property("UserId") @@ -1503,13 +1526,16 @@ namespace MyCompanyName.MyProjectName.Migrations .HasColumnName("ExtraProperties"); b.Property("GroupName") - .IsRequired() .HasMaxLength(128) .HasColumnType("nvarchar(128)"); b.Property("IsEnabled") .HasColumnType("bit"); + b.Property("ManagementPermissionName") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + b.Property("MultiTenancySide") .HasColumnType("tinyint"); @@ -1526,6 +1552,10 @@ namespace MyCompanyName.MyProjectName.Migrations .HasMaxLength(128) .HasColumnType("nvarchar(128)"); + b.Property("ResourceName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + b.Property("StateCheckers") .HasMaxLength(256) .HasColumnType("nvarchar(256)"); @@ -1534,8 +1564,9 @@ namespace MyCompanyName.MyProjectName.Migrations b.HasIndex("GroupName"); - b.HasIndex("Name") - .IsUnique(); + b.HasIndex("ResourceName", "Name") + .IsUnique() + .HasFilter("[ResourceName] IS NOT NULL"); b.ToTable("AbpPermissions", (string)null); }); @@ -1602,6 +1633,50 @@ namespace MyCompanyName.MyProjectName.Migrations b.ToTable("AbpPermissionGroups", (string)null); }); + modelBuilder.Entity("Volo.Abp.PermissionManagement.ResourcePermissionGrant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ProviderKey") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ProviderName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ResourceKey") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ResourceName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "Name", "ResourceName", "ResourceKey", "ProviderName", "ProviderKey") + .IsUnique() + .HasFilter("[TenantId] IS NOT NULL"); + + b.ToTable("AbpResourcePermissionGrants", (string)null); + }); + modelBuilder.Entity("Volo.Abp.SettingManagement.Setting", b => { b.Property("Id") @@ -1842,6 +1917,51 @@ namespace MyCompanyName.MyProjectName.Migrations .IsRequired(); }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserPasskey", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Passkeys") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("Volo.Abp.Identity.IdentityPasskeyData", "Data", b1 => + { + b1.Property("IdentityUserPasskeyCredentialId"); + + b1.Property("AttestationObject"); + + b1.Property("ClientDataJson"); + + b1.Property("CreatedAt"); + + b1.Property("IsBackedUp"); + + b1.Property("IsBackupEligible"); + + b1.Property("IsUserVerified"); + + b1.Property("Name"); + + b1.Property("PublicKey"); + + b1.Property("SignCount"); + + b1.PrimitiveCollection("Transports"); + + b1.HasKey("IdentityUserPasskeyCredentialId"); + + b1.ToTable("AbpUserPasskeys"); + + b1.ToJson("Data"); + + b1.WithOwner() + .HasForeignKey("IdentityUserPasskeyCredentialId"); + }); + + b.Navigation("Data"); + }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserPasswordHistory", b => { b.HasOne("Volo.Abp.Identity.IdentityUser", null) @@ -1949,6 +2069,8 @@ namespace MyCompanyName.MyProjectName.Migrations b.Navigation("OrganizationUnits"); + b.Navigation("Passkeys"); + b.Navigation("PasswordHistories"); b.Navigation("Roles"); diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Host.Client/MyCompanyName.MyProjectName.Blazor.Host.Client.csproj b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Host.Client/MyCompanyName.MyProjectName.Blazor.Host.Client.csproj index d44f47d22c..a9f5661242 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Host.Client/MyCompanyName.MyProjectName.Blazor.Host.Client.csproj +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Host.Client/MyCompanyName.MyProjectName.Blazor.Host.Client.csproj @@ -10,8 +10,8 @@ - - + + diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/Migrations/20251020063536_Initial.Designer.cs b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/Migrations/20251218020158_Initial.Designer.cs similarity index 92% rename from templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/Migrations/20251020063536_Initial.Designer.cs rename to templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/Migrations/20251218020158_Initial.Designer.cs index 8b0df2e0e7..5342dd5e01 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/Migrations/20251020063536_Initial.Designer.cs +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/Migrations/20251218020158_Initial.Designer.cs @@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore; namespace MyCompanyName.MyProjectName.Blazor.Server.Host.Migrations { [DbContext(typeof(UnifiedDbContext))] - [Migration("20251020063536_Initial")] + [Migration("20251218020158_Initial")] partial class Initial { /// @@ -22,7 +22,7 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Host.Migrations #pragma warning disable 612, 618 modelBuilder .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) - .HasAnnotation("ProductVersion", "10.0.0-rc.2.25502.107") + .HasAnnotation("ProductVersion", "10.0.0") .HasAnnotation("Relational:MaxIdentifierLength", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); @@ -821,6 +821,9 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Host.Migrations b.Property("LastPasswordChangeTime") .HasColumnType("datetimeoffset"); + b.Property("LastSignInTime") + .HasColumnType("datetimeoffset"); + b.Property("LockoutEnabled") .ValueGeneratedOnAdd() .HasColumnType("bit") @@ -1017,6 +1020,26 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Host.Migrations b.ToTable("AbpUserOrganizationUnits", (string)null); }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserPasskey", b => + { + b.Property("CredentialId") + .HasMaxLength(1024) + .HasColumnType("varbinary(1024)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("CredentialId"); + + b.HasIndex("UserId"); + + b.ToTable("AbpUserPasskeys", (string)null); + }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserPasswordHistory", b => { b.Property("UserId") @@ -1204,13 +1227,16 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Host.Migrations .HasColumnName("ExtraProperties"); b.Property("GroupName") - .IsRequired() .HasMaxLength(128) .HasColumnType("nvarchar(128)"); b.Property("IsEnabled") .HasColumnType("bit"); + b.Property("ManagementPermissionName") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + b.Property("MultiTenancySide") .HasColumnType("tinyint"); @@ -1227,6 +1253,10 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Host.Migrations .HasMaxLength(128) .HasColumnType("nvarchar(128)"); + b.Property("ResourceName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + b.Property("StateCheckers") .HasMaxLength(256) .HasColumnType("nvarchar(256)"); @@ -1235,8 +1265,9 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Host.Migrations b.HasIndex("GroupName"); - b.HasIndex("Name") - .IsUnique(); + b.HasIndex("ResourceName", "Name") + .IsUnique() + .HasFilter("[ResourceName] IS NOT NULL"); b.ToTable("AbpPermissions", (string)null); }); @@ -1303,6 +1334,50 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Host.Migrations b.ToTable("AbpPermissionGroups", (string)null); }); + modelBuilder.Entity("Volo.Abp.PermissionManagement.ResourcePermissionGrant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ProviderKey") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ProviderName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ResourceKey") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ResourceName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "Name", "ResourceName", "ResourceKey", "ProviderName", "ProviderKey") + .IsUnique() + .HasFilter("[TenantId] IS NOT NULL"); + + b.ToTable("AbpResourcePermissionGrants", (string)null); + }); + modelBuilder.Entity("Volo.Abp.SettingManagement.Setting", b => { b.Property("Id") @@ -1543,6 +1618,51 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Host.Migrations .IsRequired(); }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserPasskey", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Passkeys") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("Volo.Abp.Identity.IdentityPasskeyData", "Data", b1 => + { + b1.Property("IdentityUserPasskeyCredentialId"); + + b1.Property("AttestationObject"); + + b1.Property("ClientDataJson"); + + b1.Property("CreatedAt"); + + b1.Property("IsBackedUp"); + + b1.Property("IsBackupEligible"); + + b1.Property("IsUserVerified"); + + b1.Property("Name"); + + b1.Property("PublicKey"); + + b1.Property("SignCount"); + + b1.PrimitiveCollection("Transports"); + + b1.HasKey("IdentityUserPasskeyCredentialId"); + + b1.ToTable("AbpUserPasskeys"); + + b1.ToJson("Data"); + + b1.WithOwner() + .HasForeignKey("IdentityUserPasskeyCredentialId"); + }); + + b.Navigation("Data"); + }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserPasswordHistory", b => { b.HasOne("Volo.Abp.Identity.IdentityUser", null) @@ -1632,6 +1752,8 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Host.Migrations b.Navigation("OrganizationUnits"); + b.Navigation("Passkeys"); + b.Navigation("PasswordHistories"); b.Navigation("Roles"); diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/Migrations/20251020063536_Initial.cs b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/Migrations/20251218020158_Initial.cs similarity index 93% rename from templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/Migrations/20251020063536_Initial.cs rename to templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/Migrations/20251218020158_Initial.cs index 40c04ee9fd..af720e3248 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/Migrations/20251020063536_Initial.cs +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/Migrations/20251218020158_Initial.cs @@ -211,8 +211,10 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Host.Migrations columns: table => new { Id = table.Column(type: "uniqueidentifier", nullable: false), - GroupName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + GroupName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: true), Name = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + ResourceName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + ManagementPermissionName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: true), ParentName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: true), DisplayName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), IsEnabled = table.Column(type: "bit", nullable: false), @@ -226,6 +228,23 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Host.Migrations table.PrimaryKey("PK_AbpPermissions", x => x.Id); }); + migrationBuilder.CreateTable( + name: "AbpResourcePermissionGrants", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + TenantId = table.Column(type: "uniqueidentifier", nullable: true), + Name = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + ProviderName = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false), + ProviderKey = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false), + ResourceName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), + ResourceKey = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpResourcePermissionGrants", x => x.Id); + }); + migrationBuilder.CreateTable( name: "AbpRoles", columns: table => new @@ -393,6 +412,7 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Host.Migrations ShouldChangePasswordOnNextLogin = table.Column(type: "bit", nullable: false), EntityVersion = table.Column(type: "int", nullable: false), LastPasswordChangeTime = table.Column(type: "datetimeoffset", nullable: true), + LastSignInTime = table.Column(type: "datetimeoffset", nullable: true), ExtraProperties = table.Column(type: "nvarchar(max)", nullable: false), ConcurrencyStamp = table.Column(type: "nvarchar(40)", maxLength: 40, nullable: false), CreationTime = table.Column(type: "datetime2", nullable: false), @@ -594,6 +614,26 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Host.Migrations onDelete: ReferentialAction.Cascade); }); + migrationBuilder.CreateTable( + name: "AbpUserPasskeys", + columns: table => new + { + CredentialId = table.Column(type: "varbinary(1024)", maxLength: 1024, nullable: false), + TenantId = table.Column(type: "uniqueidentifier", nullable: true), + UserId = table.Column(type: "uniqueidentifier", nullable: false), + Data = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpUserPasskeys", x => x.CredentialId); + table.ForeignKey( + name: "FK_AbpUserPasskeys_AbpUsers_UserId", + column: x => x.UserId, + principalTable: "AbpUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + migrationBuilder.CreateTable( name: "AbpUserPasswordHistories", columns: table => new @@ -783,10 +823,18 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Host.Migrations column: "GroupName"); migrationBuilder.CreateIndex( - name: "IX_AbpPermissions_Name", + name: "IX_AbpPermissions_ResourceName_Name", table: "AbpPermissions", - column: "Name", - unique: true); + columns: new[] { "ResourceName", "Name" }, + unique: true, + filter: "[ResourceName] IS NOT NULL"); + + migrationBuilder.CreateIndex( + name: "IX_AbpResourcePermissionGrants_TenantId_Name_ResourceName_ResourceKey_ProviderName_ProviderKey", + table: "AbpResourcePermissionGrants", + columns: new[] { "TenantId", "Name", "ResourceName", "ResourceKey", "ProviderName", "ProviderKey" }, + unique: true, + filter: "[TenantId] IS NOT NULL"); migrationBuilder.CreateIndex( name: "IX_AbpRoleClaims_RoleId", @@ -871,6 +919,11 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Host.Migrations table: "AbpUserOrganizationUnits", columns: new[] { "UserId", "OrganizationUnitId" }); + migrationBuilder.CreateIndex( + name: "IX_AbpUserPasskeys_UserId", + table: "AbpUserPasskeys", + column: "UserId"); + migrationBuilder.CreateIndex( name: "IX_AbpUserRoles_RoleId_UserId", table: "AbpUserRoles", @@ -936,6 +989,9 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Host.Migrations migrationBuilder.DropTable( name: "AbpPermissions"); + migrationBuilder.DropTable( + name: "AbpResourcePermissionGrants"); + migrationBuilder.DropTable( name: "AbpRoleClaims"); @@ -966,6 +1022,9 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Host.Migrations migrationBuilder.DropTable( name: "AbpUserOrganizationUnits"); + migrationBuilder.DropTable( + name: "AbpUserPasskeys"); + migrationBuilder.DropTable( name: "AbpUserPasswordHistories"); diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/Migrations/UnifiedDbContextModelSnapshot.cs b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/Migrations/UnifiedDbContextModelSnapshot.cs index 20e84ae02d..5fa6595a41 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/Migrations/UnifiedDbContextModelSnapshot.cs +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/Migrations/UnifiedDbContextModelSnapshot.cs @@ -19,7 +19,7 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Host.Migrations #pragma warning disable 612, 618 modelBuilder .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) - .HasAnnotation("ProductVersion", "10.0.0-rc.2.25502.107") + .HasAnnotation("ProductVersion", "10.0.0") .HasAnnotation("Relational:MaxIdentifierLength", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); @@ -818,6 +818,9 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Host.Migrations b.Property("LastPasswordChangeTime") .HasColumnType("datetimeoffset"); + b.Property("LastSignInTime") + .HasColumnType("datetimeoffset"); + b.Property("LockoutEnabled") .ValueGeneratedOnAdd() .HasColumnType("bit") @@ -1014,6 +1017,26 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Host.Migrations b.ToTable("AbpUserOrganizationUnits", (string)null); }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserPasskey", b => + { + b.Property("CredentialId") + .HasMaxLength(1024) + .HasColumnType("varbinary(1024)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("CredentialId"); + + b.HasIndex("UserId"); + + b.ToTable("AbpUserPasskeys", (string)null); + }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserPasswordHistory", b => { b.Property("UserId") @@ -1201,13 +1224,16 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Host.Migrations .HasColumnName("ExtraProperties"); b.Property("GroupName") - .IsRequired() .HasMaxLength(128) .HasColumnType("nvarchar(128)"); b.Property("IsEnabled") .HasColumnType("bit"); + b.Property("ManagementPermissionName") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + b.Property("MultiTenancySide") .HasColumnType("tinyint"); @@ -1224,6 +1250,10 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Host.Migrations .HasMaxLength(128) .HasColumnType("nvarchar(128)"); + b.Property("ResourceName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + b.Property("StateCheckers") .HasMaxLength(256) .HasColumnType("nvarchar(256)"); @@ -1232,8 +1262,9 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Host.Migrations b.HasIndex("GroupName"); - b.HasIndex("Name") - .IsUnique(); + b.HasIndex("ResourceName", "Name") + .IsUnique() + .HasFilter("[ResourceName] IS NOT NULL"); b.ToTable("AbpPermissions", (string)null); }); @@ -1300,6 +1331,50 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Host.Migrations b.ToTable("AbpPermissionGroups", (string)null); }); + modelBuilder.Entity("Volo.Abp.PermissionManagement.ResourcePermissionGrant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ProviderKey") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ProviderName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ResourceKey") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ResourceName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "Name", "ResourceName", "ResourceKey", "ProviderName", "ProviderKey") + .IsUnique() + .HasFilter("[TenantId] IS NOT NULL"); + + b.ToTable("AbpResourcePermissionGrants", (string)null); + }); + modelBuilder.Entity("Volo.Abp.SettingManagement.Setting", b => { b.Property("Id") @@ -1540,6 +1615,51 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Host.Migrations .IsRequired(); }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserPasskey", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Passkeys") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("Volo.Abp.Identity.IdentityPasskeyData", "Data", b1 => + { + b1.Property("IdentityUserPasskeyCredentialId"); + + b1.Property("AttestationObject"); + + b1.Property("ClientDataJson"); + + b1.Property("CreatedAt"); + + b1.Property("IsBackedUp"); + + b1.Property("IsBackupEligible"); + + b1.Property("IsUserVerified"); + + b1.Property("Name"); + + b1.Property("PublicKey"); + + b1.Property("SignCount"); + + b1.PrimitiveCollection("Transports"); + + b1.HasKey("IdentityUserPasskeyCredentialId"); + + b1.ToTable("AbpUserPasskeys"); + + b1.ToJson("Data"); + + b1.WithOwner() + .HasForeignKey("IdentityUserPasskeyCredentialId"); + }); + + b.Navigation("Data"); + }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserPasswordHistory", b => { b.HasOne("Volo.Abp.Identity.IdentityUser", null) @@ -1629,6 +1749,8 @@ namespace MyCompanyName.MyProjectName.Blazor.Server.Host.Migrations b.Navigation("OrganizationUnits"); + b.Navigation("Passkeys"); + b.Navigation("PasswordHistories"); b.Navigation("Roles"); diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/MyCompanyName.MyProjectName.Blazor.Server.Host.csproj b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/MyCompanyName.MyProjectName.Blazor.Server.Host.csproj index 99bae3ec1f..b541e4584a 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/MyCompanyName.MyProjectName.Blazor.Server.Host.csproj +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/MyCompanyName.MyProjectName.Blazor.Server.Host.csproj @@ -13,8 +13,8 @@ - - + + diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.HttpApi.Host/Migrations/20251020063606_Initial.Designer.cs b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.HttpApi.Host/Migrations/20251218020233_Initial.Designer.cs similarity index 89% rename from templates/module/aspnet-core/host/MyCompanyName.MyProjectName.HttpApi.Host/Migrations/20251020063606_Initial.Designer.cs rename to templates/module/aspnet-core/host/MyCompanyName.MyProjectName.HttpApi.Host/Migrations/20251218020233_Initial.Designer.cs index 785cfc71f1..c3af00901a 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.HttpApi.Host/Migrations/20251020063606_Initial.Designer.cs +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.HttpApi.Host/Migrations/20251218020233_Initial.Designer.cs @@ -12,7 +12,7 @@ using Volo.Abp.EntityFrameworkCore; namespace MyCompanyName.MyProjectName.Migrations { [DbContext(typeof(MyProjectNameHttpApiHostMigrationsDbContext))] - [Migration("20251020063606_Initial")] + [Migration("20251218020233_Initial")] partial class Initial { /// @@ -21,7 +21,7 @@ namespace MyCompanyName.MyProjectName.Migrations #pragma warning disable 612, 618 modelBuilder .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) - .HasAnnotation("ProductVersion", "10.0.0-rc.2.25502.107") + .HasAnnotation("ProductVersion", "10.0.0") .HasAnnotation("Relational:MaxIdentifierLength", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.HttpApi.Host/Migrations/20251020063606_Initial.cs b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.HttpApi.Host/Migrations/20251218020233_Initial.cs similarity index 100% rename from templates/module/aspnet-core/host/MyCompanyName.MyProjectName.HttpApi.Host/Migrations/20251020063606_Initial.cs rename to templates/module/aspnet-core/host/MyCompanyName.MyProjectName.HttpApi.Host/Migrations/20251218020233_Initial.cs diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.HttpApi.Host/Migrations/MyProjectNameHttpApiHostMigrationsDbContextModelSnapshot.cs b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.HttpApi.Host/Migrations/MyProjectNameHttpApiHostMigrationsDbContextModelSnapshot.cs index ca715e35a7..b55edb912d 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.HttpApi.Host/Migrations/MyProjectNameHttpApiHostMigrationsDbContextModelSnapshot.cs +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.HttpApi.Host/Migrations/MyProjectNameHttpApiHostMigrationsDbContextModelSnapshot.cs @@ -18,7 +18,7 @@ namespace MyCompanyName.MyProjectName.Migrations #pragma warning disable 612, 618 modelBuilder .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) - .HasAnnotation("ProductVersion", "10.0.0-rc.2.25502.107") + .HasAnnotation("ProductVersion", "10.0.0") .HasAnnotation("Relational:MaxIdentifierLength", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/Migrations/20251020063621_Initial.Designer.cs b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/Migrations/20251218020255_Initial.Designer.cs similarity index 92% rename from templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/Migrations/20251020063621_Initial.Designer.cs rename to templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/Migrations/20251218020255_Initial.Designer.cs index fff4371d14..33e5dc220e 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/Migrations/20251020063621_Initial.Designer.cs +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/Migrations/20251218020255_Initial.Designer.cs @@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore; namespace MyCompanyName.MyProjectName.Migrations { [DbContext(typeof(UnifiedDbContext))] - [Migration("20251020063621_Initial")] + [Migration("20251218020255_Initial")] partial class Initial { /// @@ -22,7 +22,7 @@ namespace MyCompanyName.MyProjectName.Migrations #pragma warning disable 612, 618 modelBuilder .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) - .HasAnnotation("ProductVersion", "10.0.0-rc.2.25502.107") + .HasAnnotation("ProductVersion", "10.0.0") .HasAnnotation("Relational:MaxIdentifierLength", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); @@ -821,6 +821,9 @@ namespace MyCompanyName.MyProjectName.Migrations b.Property("LastPasswordChangeTime") .HasColumnType("datetimeoffset"); + b.Property("LastSignInTime") + .HasColumnType("datetimeoffset"); + b.Property("LockoutEnabled") .ValueGeneratedOnAdd() .HasColumnType("bit") @@ -1017,6 +1020,26 @@ namespace MyCompanyName.MyProjectName.Migrations b.ToTable("AbpUserOrganizationUnits", (string)null); }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserPasskey", b => + { + b.Property("CredentialId") + .HasMaxLength(1024) + .HasColumnType("varbinary(1024)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("CredentialId"); + + b.HasIndex("UserId"); + + b.ToTable("AbpUserPasskeys", (string)null); + }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserPasswordHistory", b => { b.Property("UserId") @@ -1204,13 +1227,16 @@ namespace MyCompanyName.MyProjectName.Migrations .HasColumnName("ExtraProperties"); b.Property("GroupName") - .IsRequired() .HasMaxLength(128) .HasColumnType("nvarchar(128)"); b.Property("IsEnabled") .HasColumnType("bit"); + b.Property("ManagementPermissionName") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + b.Property("MultiTenancySide") .HasColumnType("tinyint"); @@ -1227,6 +1253,10 @@ namespace MyCompanyName.MyProjectName.Migrations .HasMaxLength(128) .HasColumnType("nvarchar(128)"); + b.Property("ResourceName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + b.Property("StateCheckers") .HasMaxLength(256) .HasColumnType("nvarchar(256)"); @@ -1235,8 +1265,9 @@ namespace MyCompanyName.MyProjectName.Migrations b.HasIndex("GroupName"); - b.HasIndex("Name") - .IsUnique(); + b.HasIndex("ResourceName", "Name") + .IsUnique() + .HasFilter("[ResourceName] IS NOT NULL"); b.ToTable("AbpPermissions", (string)null); }); @@ -1303,6 +1334,50 @@ namespace MyCompanyName.MyProjectName.Migrations b.ToTable("AbpPermissionGroups", (string)null); }); + modelBuilder.Entity("Volo.Abp.PermissionManagement.ResourcePermissionGrant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ProviderKey") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ProviderName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ResourceKey") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ResourceName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "Name", "ResourceName", "ResourceKey", "ProviderName", "ProviderKey") + .IsUnique() + .HasFilter("[TenantId] IS NOT NULL"); + + b.ToTable("AbpResourcePermissionGrants", (string)null); + }); + modelBuilder.Entity("Volo.Abp.SettingManagement.Setting", b => { b.Property("Id") @@ -1543,6 +1618,51 @@ namespace MyCompanyName.MyProjectName.Migrations .IsRequired(); }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserPasskey", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Passkeys") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("Volo.Abp.Identity.IdentityPasskeyData", "Data", b1 => + { + b1.Property("IdentityUserPasskeyCredentialId"); + + b1.Property("AttestationObject"); + + b1.Property("ClientDataJson"); + + b1.Property("CreatedAt"); + + b1.Property("IsBackedUp"); + + b1.Property("IsBackupEligible"); + + b1.Property("IsUserVerified"); + + b1.Property("Name"); + + b1.Property("PublicKey"); + + b1.Property("SignCount"); + + b1.PrimitiveCollection("Transports"); + + b1.HasKey("IdentityUserPasskeyCredentialId"); + + b1.ToTable("AbpUserPasskeys"); + + b1.ToJson("Data"); + + b1.WithOwner() + .HasForeignKey("IdentityUserPasskeyCredentialId"); + }); + + b.Navigation("Data"); + }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserPasswordHistory", b => { b.HasOne("Volo.Abp.Identity.IdentityUser", null) @@ -1632,6 +1752,8 @@ namespace MyCompanyName.MyProjectName.Migrations b.Navigation("OrganizationUnits"); + b.Navigation("Passkeys"); + b.Navigation("PasswordHistories"); b.Navigation("Roles"); diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/Migrations/20251020063621_Initial.cs b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/Migrations/20251218020255_Initial.cs similarity index 93% rename from templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/Migrations/20251020063621_Initial.cs rename to templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/Migrations/20251218020255_Initial.cs index 3cd4785ff9..08fc513bcb 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/Migrations/20251020063621_Initial.cs +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/Migrations/20251218020255_Initial.cs @@ -211,8 +211,10 @@ namespace MyCompanyName.MyProjectName.Migrations columns: table => new { Id = table.Column(type: "uniqueidentifier", nullable: false), - GroupName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + GroupName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: true), Name = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + ResourceName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + ManagementPermissionName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: true), ParentName = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: true), DisplayName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), IsEnabled = table.Column(type: "bit", nullable: false), @@ -226,6 +228,23 @@ namespace MyCompanyName.MyProjectName.Migrations table.PrimaryKey("PK_AbpPermissions", x => x.Id); }); + migrationBuilder.CreateTable( + name: "AbpResourcePermissionGrants", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + TenantId = table.Column(type: "uniqueidentifier", nullable: true), + Name = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + ProviderName = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false), + ProviderKey = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false), + ResourceName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), + ResourceKey = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpResourcePermissionGrants", x => x.Id); + }); + migrationBuilder.CreateTable( name: "AbpRoles", columns: table => new @@ -393,6 +412,7 @@ namespace MyCompanyName.MyProjectName.Migrations ShouldChangePasswordOnNextLogin = table.Column(type: "bit", nullable: false), EntityVersion = table.Column(type: "int", nullable: false), LastPasswordChangeTime = table.Column(type: "datetimeoffset", nullable: true), + LastSignInTime = table.Column(type: "datetimeoffset", nullable: true), ExtraProperties = table.Column(type: "nvarchar(max)", nullable: false), ConcurrencyStamp = table.Column(type: "nvarchar(40)", maxLength: 40, nullable: false), CreationTime = table.Column(type: "datetime2", nullable: false), @@ -594,6 +614,26 @@ namespace MyCompanyName.MyProjectName.Migrations onDelete: ReferentialAction.Cascade); }); + migrationBuilder.CreateTable( + name: "AbpUserPasskeys", + columns: table => new + { + CredentialId = table.Column(type: "varbinary(1024)", maxLength: 1024, nullable: false), + TenantId = table.Column(type: "uniqueidentifier", nullable: true), + UserId = table.Column(type: "uniqueidentifier", nullable: false), + Data = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpUserPasskeys", x => x.CredentialId); + table.ForeignKey( + name: "FK_AbpUserPasskeys_AbpUsers_UserId", + column: x => x.UserId, + principalTable: "AbpUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + migrationBuilder.CreateTable( name: "AbpUserPasswordHistories", columns: table => new @@ -783,10 +823,18 @@ namespace MyCompanyName.MyProjectName.Migrations column: "GroupName"); migrationBuilder.CreateIndex( - name: "IX_AbpPermissions_Name", + name: "IX_AbpPermissions_ResourceName_Name", table: "AbpPermissions", - column: "Name", - unique: true); + columns: new[] { "ResourceName", "Name" }, + unique: true, + filter: "[ResourceName] IS NOT NULL"); + + migrationBuilder.CreateIndex( + name: "IX_AbpResourcePermissionGrants_TenantId_Name_ResourceName_ResourceKey_ProviderName_ProviderKey", + table: "AbpResourcePermissionGrants", + columns: new[] { "TenantId", "Name", "ResourceName", "ResourceKey", "ProviderName", "ProviderKey" }, + unique: true, + filter: "[TenantId] IS NOT NULL"); migrationBuilder.CreateIndex( name: "IX_AbpRoleClaims_RoleId", @@ -871,6 +919,11 @@ namespace MyCompanyName.MyProjectName.Migrations table: "AbpUserOrganizationUnits", columns: new[] { "UserId", "OrganizationUnitId" }); + migrationBuilder.CreateIndex( + name: "IX_AbpUserPasskeys_UserId", + table: "AbpUserPasskeys", + column: "UserId"); + migrationBuilder.CreateIndex( name: "IX_AbpUserRoles_RoleId_UserId", table: "AbpUserRoles", @@ -936,6 +989,9 @@ namespace MyCompanyName.MyProjectName.Migrations migrationBuilder.DropTable( name: "AbpPermissions"); + migrationBuilder.DropTable( + name: "AbpResourcePermissionGrants"); + migrationBuilder.DropTable( name: "AbpRoleClaims"); @@ -966,6 +1022,9 @@ namespace MyCompanyName.MyProjectName.Migrations migrationBuilder.DropTable( name: "AbpUserOrganizationUnits"); + migrationBuilder.DropTable( + name: "AbpUserPasskeys"); + migrationBuilder.DropTable( name: "AbpUserPasswordHistories"); diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/Migrations/UnifiedDbContextModelSnapshot.cs b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/Migrations/UnifiedDbContextModelSnapshot.cs index b865427000..6e6fce7596 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/Migrations/UnifiedDbContextModelSnapshot.cs +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/Migrations/UnifiedDbContextModelSnapshot.cs @@ -19,7 +19,7 @@ namespace MyCompanyName.MyProjectName.Migrations #pragma warning disable 612, 618 modelBuilder .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) - .HasAnnotation("ProductVersion", "10.0.0-rc.2.25502.107") + .HasAnnotation("ProductVersion", "10.0.0") .HasAnnotation("Relational:MaxIdentifierLength", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); @@ -818,6 +818,9 @@ namespace MyCompanyName.MyProjectName.Migrations b.Property("LastPasswordChangeTime") .HasColumnType("datetimeoffset"); + b.Property("LastSignInTime") + .HasColumnType("datetimeoffset"); + b.Property("LockoutEnabled") .ValueGeneratedOnAdd() .HasColumnType("bit") @@ -1014,6 +1017,26 @@ namespace MyCompanyName.MyProjectName.Migrations b.ToTable("AbpUserOrganizationUnits", (string)null); }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserPasskey", b => + { + b.Property("CredentialId") + .HasMaxLength(1024) + .HasColumnType("varbinary(1024)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("CredentialId"); + + b.HasIndex("UserId"); + + b.ToTable("AbpUserPasskeys", (string)null); + }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserPasswordHistory", b => { b.Property("UserId") @@ -1201,13 +1224,16 @@ namespace MyCompanyName.MyProjectName.Migrations .HasColumnName("ExtraProperties"); b.Property("GroupName") - .IsRequired() .HasMaxLength(128) .HasColumnType("nvarchar(128)"); b.Property("IsEnabled") .HasColumnType("bit"); + b.Property("ManagementPermissionName") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + b.Property("MultiTenancySide") .HasColumnType("tinyint"); @@ -1224,6 +1250,10 @@ namespace MyCompanyName.MyProjectName.Migrations .HasMaxLength(128) .HasColumnType("nvarchar(128)"); + b.Property("ResourceName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + b.Property("StateCheckers") .HasMaxLength(256) .HasColumnType("nvarchar(256)"); @@ -1232,8 +1262,9 @@ namespace MyCompanyName.MyProjectName.Migrations b.HasIndex("GroupName"); - b.HasIndex("Name") - .IsUnique(); + b.HasIndex("ResourceName", "Name") + .IsUnique() + .HasFilter("[ResourceName] IS NOT NULL"); b.ToTable("AbpPermissions", (string)null); }); @@ -1300,6 +1331,50 @@ namespace MyCompanyName.MyProjectName.Migrations b.ToTable("AbpPermissionGroups", (string)null); }); + modelBuilder.Entity("Volo.Abp.PermissionManagement.ResourcePermissionGrant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ProviderKey") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ProviderName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ResourceKey") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ResourceName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "Name", "ResourceName", "ResourceKey", "ProviderName", "ProviderKey") + .IsUnique() + .HasFilter("[TenantId] IS NOT NULL"); + + b.ToTable("AbpResourcePermissionGrants", (string)null); + }); + modelBuilder.Entity("Volo.Abp.SettingManagement.Setting", b => { b.Property("Id") @@ -1540,6 +1615,51 @@ namespace MyCompanyName.MyProjectName.Migrations .IsRequired(); }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserPasskey", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Passkeys") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("Volo.Abp.Identity.IdentityPasskeyData", "Data", b1 => + { + b1.Property("IdentityUserPasskeyCredentialId"); + + b1.Property("AttestationObject"); + + b1.Property("ClientDataJson"); + + b1.Property("CreatedAt"); + + b1.Property("IsBackedUp"); + + b1.Property("IsBackupEligible"); + + b1.Property("IsUserVerified"); + + b1.Property("Name"); + + b1.Property("PublicKey"); + + b1.Property("SignCount"); + + b1.PrimitiveCollection("Transports"); + + b1.HasKey("IdentityUserPasskeyCredentialId"); + + b1.ToTable("AbpUserPasskeys"); + + b1.ToJson("Data"); + + b1.WithOwner() + .HasForeignKey("IdentityUserPasskeyCredentialId"); + }); + + b.Navigation("Data"); + }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserPasswordHistory", b => { b.HasOne("Volo.Abp.Identity.IdentityUser", null) @@ -1629,6 +1749,8 @@ namespace MyCompanyName.MyProjectName.Migrations b.Navigation("OrganizationUnits"); + b.Navigation("Passkeys"); + b.Navigation("PasswordHistories"); b.Navigation("Roles");