Merge remote-tracking branch 'abpframework/master' into Translate

pull/1307/head
liangshiwei 6 years ago
commit 1ab67a255c

2
.gitignore vendored

@ -23,7 +23,7 @@ bld/
[Ll]og/
# Visual Studio 2015 cache/options directory
.vs/
**/.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/

@ -8,36 +8,25 @@ See the official [web site (abp.io)](https://abp.io/) for more information.
### Status
This project is in **very early preview** stage and it's not suggested to use it in a real project.
This project is in **preview** stage and it's not suggested to use it in production yet.
### Documentation
See the <a href="https://abp.io/documents/" target="_blank">documentation</a>.
### How to Build
- Run the `build-all.ps1`. It will build all the solutions in this repository.
### Development
#### Pre Requirements
- Visual Studio 2017 15.9.0+
- Visual Studio 2019 16.1.0+
#### Framework
Framework solution is located under the `framework` folder. It has no external dependency. Just open `Volo.Abp.sln` by Visual Studio and start the development.
Framework solution is located under the `framework` folder. It has no external dependency.
#### Modules/Templates
[Modules](modules/) and [Templates](templates/) have their own solutions and have **local references** to the framework. Unfortunately, Visual Studio has some problems with local references to projects those are out of the solution. As a workaround, you should follow the steps below in order to start developing a module/template:
- Disable "*Automatically check for missing packages during build in Visual Studio*" in the Visual Studio options.
![disable-package-restore-visual-studio](docs/en/images/disable-package-restore-visual-studio.png)
- When you open a solution, first run `dotnet restore` in the root folder of the solution.
- When you change a dependency of a project (or any of the dependencies of your projects change their dependencies), run `dotnet restore` again.
[Modules](modules/) and [Templates](templates/) have their own solutions and have **local references** to the framework and each other.
### Contribution

@ -0,0 +1,30 @@
# ABP dokumentace
ABP je **open source aplikační framework** se zaměřením na vývoj webových aplikací založených na ASP.NET Core, zároveň ho však lze využít i k vývoji jiných typů aplikací.
K procházení dokumentace využijte navigační nabídky vlevo.
## Stav projektu
ABP je **novou generací** open source frameworku [ASP.NET Boilerplate](https://aspnetboilerplate.com/). V současné době je v ranné fázi a není připraven k ostrému nasazení. Na dokumentaci se stále pracuje a ještě zdaleka není dokončena.
Pro aplikace na krátkodobé a ostré nasazení je doporučeno použít [ASP.NET Boilerplate](https://aspnetboilerplate.com/) framework, který je bohatý na funkce, vyspělý, aktivně udržovaný a aktuální.
## Začínáme
Nejsnazší cestou jak začít nový projekt s ABP je užití startovací šablony:
* [ASP.NET Core MVC Šablona](Getting-Started-AspNetCore-MVC-Template.md)
Pokud chcete začít od nuly (s prázdným projektem) tak manuálně nainstalujte ABP Framework s pomocí následujících tutoriálů:
* [Konzolová Aplikace](Getting-Started-Console-Application.md)
* [ASP.NET Core Web Aplikace](Getting-Started-AspNetCore-Application.md)
## Zdrojový kód
ABP je hostovaný na GitHub. Zobrazit [zdrojový kód](https://github.com/abpframework/abp).
## Chcete přispět?
ABP je komunitně řízený open source projekt. Podívejte se na [průvodce pro přispěvatele](Contribution/Index.md) pokud chcete být součástí tohoto projektu.

@ -0,0 +1,292 @@
{
"items": [
{
"text": "Začínáme",
"items": [
{
"text": "Ze startovacích šablon",
"items": [
{
"text": "ASP.NET Core MVC",
"path": "Getting-Started-AspNetCore-MVC-Template.md"
}
]
},
{
"text": "Z prázdných projektů",
"items": [
{
"text": "S ASP.NET Core Web aplikací",
"path": "Getting-Started-AspNetCore-Application.md"
},
{
"text": "S konzolovou aplikací",
"path": "Getting-Started-Console-Application.md"
}
]
}
]
},
{
"text": "Tutoriály",
"items": [
{
"text": "Vývoj aplikace",
"items": [
{
"text": "S ASP.NET Core MVC",
"path": "Tutorials/AspNetCore-Mvc/Part-I.md"
}
]
}
]
},
{
"text": "CLI",
"path": "CLI.md"
},
{
"text": "Základy",
"items": [
{
"text": "Vkládání závislostí",
"path": "Dependency-Injection.md",
"items": [
{
"text": "AutoFac integrace",
"path": "Autofac-Integration.md"
}
]
},
{
"text": "Virtuální systém souborů",
"path": "Virtual-File-System.md"
},
{
"text": "Lokalizace",
"path": "Localization.md"
},
{
"text": "Zpracování výjimek",
"path": "Exception-Handling.md"
},
{
"text": "Validace"
},
{
"text": "Autorizace"
},
{
"text": "Ukládání do mezipaměti"
},
{
"text": "Audit"
},
{
"text": "Správa nastavení"
}
]
},
{
"text": "Události",
"items": [
{
"text": "Event bus (místní)"
},
{
"text": "Distribuovaný event bus",
"items": [
{
"text": "RabbitMQ integrace"
}
]
}
]
},
{
"text": "Služby",
"items": [
{
"text": "Serializace objektu"
},
{
"text": "Serializace JSON"
},
{
"text": "Emailování"
},
{
"text": "GUIDy"
},
{
"text": "Vláknování"
},
{
"text": "Časování"
}
]
},
{
"text": "Multitenance",
"path": "Multi-Tenancy.md"
},
{
"text": "Vývoj modulů",
"items": [
{
"text": "Základy",
"path": "Module-Development-Basics.md"
},
{
"text": "Zásuvné moduly"
},
{
"text": "Nejlepší praktiky",
"path": "Best-Practices/Index.md"
}
]
},
{
"text": "Domain driven design",
"path": "Domain-Driven-Design.md",
"items": [
{
"text": "Doménová vrstva",
"items": [
{
"text": "Entity & agregované kořeny",
"path": "Entities.md"
},
{
"text": "Hodnotové objekty"
},
{
"text": "Repozitáře",
"path": "Repositories.md"
},
{
"text": "Doménové služby"
},
{
"text": "Specifikace"
}
]
},
{
"text": "Aplikační vrstva",
"items": [
{
"text": "Aplikační služby",
"path": "Application-Services.md"
},
{
"text": "Objekty přenosu dat"
},
{
"text": "Jednotka práce"
}
]
}
]
},
{
"text": "ASP.NET Core",
"items": [
{
"text": "API",
"items": [
{
"text": "Automatické API řadiče",
"path": "AspNetCore/Auto-API-Controllers.md"
},
{
"text": "Dynamičtí C# API klienti",
"path": "AspNetCore/Dynamic-CSharp-API-Clients.md"
}
]
},
{
"text": "Uživatelské rozhraní",
"items": [
{
"text": "Správa klientských balíčků",
"path": "AspNetCore/Client-Side-Package-Management.md"
},
{
"text": "Svazování & minifikace",
"path": "AspNetCore/Bundling-Minification.md"
},
{
"text": "Tag pomocníci",
"path": "Tag-Helpers.md"
},
{
"text": "Motivy",
"path": "AspNetCore/Theming.md"
}
]
}
]
},
{
"text": "Přístup k datům",
"items": [
{
"text": "Entity Framework Core integrace",
"path": "Entity-Framework-Core.md"
},
{
"text": "MongoDB integrace",
"path": "MongoDB.md"
}
]
},
{
"text": "Pozadí",
"items": [
{
"text": "Úkony na pozadí",
"path": "Background-Jobs.md",
"items": [
{
"text": "Hangfire integrace",
"path": "Background-Jobs-Hangfire.md"
},
{
"text": "RabbitMQ integrace",
"path": "Background-Jobs-RabbitMq.md"
}
]
}
]
},
{
"text": "Vzorky",
"items": [
{
"text": "Mikroslužby demo",
"path": "Samples/Microservice-Demo.md"
}
]
},
{
"text": "Moduly aplikace",
"path": "Modules/Index.md"
},
{
"text": "Architektura mikroslužby",
"path": "Microservice-Architecture.md"
},
{
"text": "Testování"
},
{
"text": "Noční sestavení",
"path": "Nightly-Builds.md"
},
{
"text": "Průvodce pro přispěvatele",
"path": "Contribution/Index.md"
}
]
}

@ -1,5 +1,10 @@
{
"Languages":[
{
"DisplayName" : "Čeština",
"Code" : "cs",
"IsDefault": false
},
{
"DisplayName" : "English",
"Code" : "en",

@ -2,7 +2,7 @@
Once you create an [application service](../Application-Services.md), you generally want to create an API controller to expose this service as an HTTP (REST) API endpoint. A typical API controller does nothing but redirects method calls to the application service and configures the REST API using attributes like [HttpGet], [HttpPost], [Route]... etc.
ABP can **automagically** configures your application services as MVC API Controllers by convention. Most of time you don't care about its detailed configuration, but it's possible fully customize it.
ABP can **automagically** configure your application services as API Controllers by convention. Most of time you don't care about its detailed configuration, but it's possible to fully customize it.
## Configuration

@ -0,0 +1,3 @@
## Dynamic JavaScript API Clients
TODO

@ -1,6 +1,160 @@
# MVC Module Startup Template
TODO
This template can be used to create a **reusable [application module](../Modules/Index.md)** based on the [module development best practices & conventions](../Best-Practices/Index.md). It is also suitable for creating **microservices** (with or without UI).
## How to Start With?
You can use the [ABP CLI](../CLI.md) to create a new project using this startup template. Alternatively, you can directly create & download from the [Get Started](https://abp.io/get-started) page. CLI approach is used here.
First, install the ABP CLI if you haven't installed before:
```bash
dotnet tool install -g Volo.Abp.Cli
```
Then use the `abp new` command in an empty folder to create a new solution:
```bash
abp new Acme.IssueManagement -t mvc-module
```
- `Acme.IssueManagement` is the solution name, like *YourCompany.YourProduct*. You can use single level, two-levels or three-levels naming.
### Without User Interface
The template comes with a UI by default. You can use `--no-ui` option to not include the UI layer.
````bash
abp new Acme.IssueManagement -t mvc-module --no-ui
````
## Solution Structure
Based on the options you've specified, you will get a slightly different solution structure. If you don't specify any option, you will have a solution like shown below:
![issuemanagement-module-solution](../images/issuemanagement-module-solution.png)
Projects are organized as `src`, `test` and `host` folders:
* `src` folder contains the actual module which is layered based on [DDD](../Domain-Driven-Design.md) principles.
* `test` folder contains unit & integration tests.
* `host` folder contains applications with different configurations to demonstrate how to host the module in an application. These are not a part of the module, but useful on development.
The diagram below shows the layers & project dependencies of the module:
![layered-project-dependencies-module](../images/layered-project-dependencies-module.png)
Each section below will explain the related project & its dependencies.
### .Domain.Shared Project
This project contains constants, enums and other objects these are actually a part of the domain layer, but needed to be used by all layers/projects in the solution.
An `IssueType` enum and an `IssueConts` class (which may have some constant fields for the `Issue` entity, like `MaxTitleLength`) are good candidates for this project.
- This project has no dependency to other projects in the solution. All other projects depend on this directly or indirectly.
### .Domain Project
This is the domain layer of the solution. It mainly contains [entities, aggregate roots](../Entities.md), [domain services](../Domain-Services.md), [value types](../Value-Types.md), [repository interfaces](../Repositories.md) and other domain objects.
An `Issue` entity, an `IssueManager` domain service and an `IIssueRepository` interface are good candidates for this project.
- Depends on the `.Domain.Shared` because it uses constants, enums and other objects defined in that project.
### .Application.Contracts Project
This project mainly contains [application service](../Application-Services.md) **interfaces** and [Data Transfer Objects](../Data-Transfer-Objects.md) (DTO) of the application layer. It does exists to separate interface & implementation of the application layer. In this way, the interface project can be shared to the clients as a contract package.
An `IIssueAppService` interface and an `IssueCreationDto` class are good candidates for this project.
- Depends on the `.Domain.Shared` because it may use constants, enums and other shared objects of this project in the application service interfaces and DTOs.
### .Application Project
This project contains the [application service](../Application-Services.md) **implementations** of the interfaces defined in the `.Application.Contracts` project.
An `IssueAppService` class is a good candidate for this project.
- Depends on the `.Application.Contracts` project to be able to implement the interfaces and use the DTOs.
- Depends on the `.Domain` project to be able to use domain objects (entities, repository interfaces... etc.) to perform the application logic.
### .EntityFrameworkCore Project
This is the integration project for EF Core. It defines the `DbContext` and implements repository interfaces defined in the `.Domain` project.
- Depends on the `.Domain` project to be able to reference to entities and repository interfaces.
> You can delete this project if you don't want to support EF Core for your module.
### .MongoDB Project
This is the integration project for MongoDB.
- Depends on the `.Domain` project to be able to reference to entities and repository interfaces.
> You can delete this project if you don't want to support MongoDB for your module.
### Test Projects
The solution has multiple test projects, one for each layer:
- `.Domain.Tests` is used to test the domain layer.
- `.Application.Tests` is used to test the application layer.
- `.EntityFrameworkCore.Tests` is used to test EF Core configuration and custom repositories.
- `.MongoDB.Tests` is used to test EF Core configuration and custom repositories.
- `.TestBase` is a base (shared) project for all tests.
In addition, `.HttpApi.Client.ConsoleTestApp` is a console application (not an automated test project) which demonstrate the usage of HTTP APIs from a Dotnet application.
Test projects are prepared for integration testing;
- It is fully integrated to ABP framework and all services in your application.
- It uses SQLite in-memory database for EF Core. For MongoDB, it uses the [Mongo2Go](https://github.com/Mongo2Go/Mongo2Go) library.
- Authorization is disabled, so any application service can be easily used in tests.
You can still create unit tests for your classes which will be harder to write (because you will need to prepare mock/fake objects), but faster to run (because it only tests a single class and skips all initialization process).
> Domain & Application tests are using EF Core. If you remove EF Core integration or you want to use MongoDB for testing these layers, you should manually change project references & module dependencies.
### Host Projects
The solution has a few host applications to run your module on development. Host applications are used to run your module in a fully configured application. It is useful on development. Host applications includes some other modules in addition to the module being developed:
*
Host applications support two types of scenarios.
#### Single (Unified) Application Scenario
If your module has a UI, then `.Web.Unified` application is used to host the UI and API on a single point. It has its own `appsettings.json` file (that includes the database connection string) and EF Core database migrations.
For the `.Web.Unified` application, there is a single database, named `YourProjectName_Unified` (like *IssueManagement_Unified* for this sample).
> If you've selected the `--no-ui` option, this project will not be in your solution.
##### How to Run?
Set it as the startup project, run `Update-Database` command for the EF Core from Package Manager Console and run your application. Default username is `admin` and password is `1q2w3E*`.
#### Separated Deployment & Databases Scenario
In this scenario, there are three applications;
* `.IdentityServer` application is an authentication server used by other applications. It has its own `appsettings.json` that contains database connection and other configurations.
* `.HttpApi.Host` hosts the HTTP API of the module. It has its own `appsettings.json` that contains database connections and other configurations.
* `.Web.Host` host the UI of the module. This project contains an `appsettings.json` file, but it does not have a connection string because it never connects to the database. Instead, it mainly contains endpoint of the remote API server and the authentication server.
The diagram below shows the relation of the applications:
![tiered-solution-applications](../images/tiered-solution-applications.png)
`.Web.Host` project uses OpenId Connect Authentication to get identity and access tokens for the current user from the `.IdentityServer`. Then uses the access token to call the `.HttpApi.Host`. HTTP API server uses bearer token authentication to obtain claims from the access token to authorize the current user.
##### How to Run?
You should run the application with the given order:
- First, run the `.IdentityServer` since other applications depends on it.
- Then run the `.HttpApi.Host` since it is used by the `.Web.Host` application.
- Finally, you can run the `.Web.Host` project and login to the application using `admin` as the username and `1q2w3E*` as the password.

@ -61,7 +61,9 @@ If you don't specify any option, you will have a solution like shown below:
![bookstore-visual-studio-solution-v3](../images/bookstore-visual-studio-solution-v3.png)
Projects are organized in `src` and `test` folders. `src` folder contains the actual application which is layered based on [DDD](../Domain-Driven-Design.md) principles as mentioned before. The diagram below shows the layers & project dependencies of the solution:
Projects are organized in `src` and `test` folders. `src` folder contains the actual application which is layered based on [DDD](../Domain-Driven-Design.md) principles as mentioned before.
The diagram below shows the layers & project dependencies of the application:
![layered-project-dependencies](../images/layered-project-dependencies.png)
@ -79,7 +81,7 @@ A `BookType` enum and a `BookConts` class (which may have some constant fields f
This is the domain layer of the solution. It mainly contains [entities, aggregate roots](../Entities.md), [domain services](../Domain-Services.md), [value types](../Value-Types.md), [repository interfaces](../Repositories.md) and other domain objects.
A `Book` entity, a `BookManager` domain service and a `IBookRepository` interface are good candidates for this project.
A `Book` entity, a `BookManager` domain service and an `IBookRepository` interface are good candidates for this project.
* Depends on the `.Domain.Shared` because it uses constants, enums and other objects defined in that project.
@ -87,12 +89,16 @@ A `Book` entity, a `BookManager` domain service and a `IBookRepository` interfac
This project mainly contains [application service](../Application-Services.md) **interfaces** and [Data Transfer Objects](../Data-Transfer-Objects.md) (DTO) of the application layer. It does exists to separate interface & implementation of the application layer. In this way, the interface project can be shared to the clients as a contract package.
An `IBookAppService` interface and a `BookCreationDto` class are good candidates for this project.
* Depends on the `.Domain.Shared` because it may use constants, enums and other shared objects of this project in the application service interfaces and DTOs.
#### .Application Project
This project contains the [application service](../Application-Services.md) **implementations** of the interfaces defined in the `.Application.Contracts` project.
A `BookAppService` class is a good candidate for this project.
* Depends on the `.Application.Contracts` project to be able to implement the interfaces and use the DTOs.
* Depends on the `.Domain` project to be able to use domain objects (entities, repository interfaces... etc.) to perform the application logic.
@ -181,7 +187,7 @@ The solution has multiple test projects, one for each layer:
In addition, `.HttpApi.Client.ConsoleTestApp` is a console application (not an automated test project) which demonstrate the usage of HTTP APIs from a Dotnet application.
Test projects are prepared integration testing;
Test projects are prepared for integration testing;
* It is fully integrated to ABP framework and all services in your application.
* It uses SQLite in-memory database for EF Core. For MongoDB, it uses the [Mongo2Go](https://github.com/Mongo2Go/Mongo2Go) library.
@ -218,9 +224,9 @@ As different from the default structure, two new projects come into play: `.Iden
#### .IdentityServer Project
This project is used as an authentication server for other projects. `.Web` project uses OpenId Connect Authentication to get identity and access tokens for the current user. Then uses the access token to call the HTTP API server. HTTP API server uses bearer token authentication to obtain claims from the access token to authorize the current user.
This project is used as an authentication server for other projects. `.Web` project uses OpenId Connect Authentication to get identity and access tokens for the current user from the IdentityServer. Then uses the access token to call the HTTP API server. HTTP API server uses bearer token authentication to obtain claims from the access token to authorize the current user.
![bookstore-visual-studio-solution-v3](../images/tiered-solution-applications.png)
![tiered-solution-applications](../images/tiered-solution-applications.png)
ABP uses the open source [IdentityServer4](https://identityserver.io/) framework for the authentication between applications. See [IdentityServer4 documentation](http://docs.identityserver.io) for details about the IdentityServer4 and OpenID Connect protocol.
@ -228,9 +234,7 @@ It has its own `appsettings.json` that contains database connection and other co
#### .HttpApi.Host Project
This project is an application that hosts the API of the solution.
It has its own `appsettings.json` that contains database connection and other configurations.
This project is an application that hosts the API of the solution. It has its own `appsettings.json` that contains database connection and other configurations.
#### .Web Project
@ -247,5 +251,10 @@ This project contains an `appsettings.json` file, but this time it does not have
You should run the application with the given order:
* First, run the `.IdentityServer` since other applications depends on it.
* Then run the `.HttpApi.Server` since it is used by the `.Web` application.
* Finally, you can run the `.Web` project and login to the application (using `admin` as the username and `1q2w3E*` as the password).
* Then run the `.HttpApi.Host` since it is used by the `.Web` application.
* Finally, you can run the `.Web` project and login to the application (using `admin` as the username and `1q2w3E*` as the password).
## What's Next?
- See [Getting Started With the ASP.NET Core MVC Template](../Getting-Started-AspNetCore-MVC-Template.md) to create a new solution and run it for this template.
- See the [ASP.NET Core MVC Tutorial](../Tutorials/AspNetCore-Mvc/Part-I.md) to learn how to develop applications using this template.

@ -0,0 +1,3 @@
## Value Objects
TODO

@ -1,3 +0,0 @@
## Value Types
TODO

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

@ -2,7 +2,7 @@
创建[应用程序服务](Application-Services.md)后, 通常需要创建API控制器以将此服务公开为HTTP(REST)API端点. 典型的API控制器除了将方法调用重定向到应用程序服务并使用[HttpGet],[HttpPost],[Route]等属性配置REST API之外什么都不做.
ABP可以按照惯例 **自动** 将你的应用程序服务配置为MVC API控制器. 大多数时候你不关心它的详细配置,但它可以完全被自定义.
ABP可以按照惯例 **自动** 将你的应用程序服务配置为API控制器. 大多数时候你不关心它的详细配置,但它可以完全被自定义.
## 配置

@ -0,0 +1,3 @@
## Dynamic JavaScript API Clients
TODO

@ -0,0 +1,3 @@
## Value Objects
TODO

@ -1,3 +0,0 @@
## Value Types
TODO

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

@ -1,6 +1,6 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.28307.168
# Visual Studio Version 16
VisualStudioVersion = 16.0.28922.388
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}"
EndProject
@ -238,6 +238,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.AspNetCore.Mvc.UI.
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.AspNetCore.Mvc.UI.Dashboards", "src\Volo.Abp.AspNetCore.Mvc.UI.Dashboards\Volo.Abp.AspNetCore.Mvc.UI.Dashboards.csproj", "{054D766D-5992-460E-A4D8-936D80BE2C1A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Ldap", "src\Volo.Abp.Ldap\Volo.Abp.Ldap.csproj", "{4DADBBD2-4C63-4C90-9661-EBF6252A7D6F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Ldap.Tests", "test\Volo.Abp.Ldap.Tests\Volo.Abp.Ldap.Tests.csproj", "{38FB8F75-426E-4265-8D0E-E121837B6FCC}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -708,6 +712,14 @@ Global
{054D766D-5992-460E-A4D8-936D80BE2C1A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{054D766D-5992-460E-A4D8-936D80BE2C1A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{054D766D-5992-460E-A4D8-936D80BE2C1A}.Release|Any CPU.Build.0 = Release|Any CPU
{4DADBBD2-4C63-4C90-9661-EBF6252A7D6F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4DADBBD2-4C63-4C90-9661-EBF6252A7D6F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4DADBBD2-4C63-4C90-9661-EBF6252A7D6F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4DADBBD2-4C63-4C90-9661-EBF6252A7D6F}.Release|Any CPU.Build.0 = Release|Any CPU
{38FB8F75-426E-4265-8D0E-E121837B6FCC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{38FB8F75-426E-4265-8D0E-E121837B6FCC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{38FB8F75-426E-4265-8D0E-E121837B6FCC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{38FB8F75-426E-4265-8D0E-E121837B6FCC}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -829,6 +841,8 @@ Global
{F006B0B4-F25D-4511-9FB3-F17AA44BDCEA} = {447C8A77-E5F0-4538-8687-7383196D04EA}
{EE1AAB08-3FBD-487F-B0B4-BEBA4B69528A} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{054D766D-5992-460E-A4D8-936D80BE2C1A} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{4DADBBD2-4C63-4C90-9661-EBF6252A7D6F} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{38FB8F75-426E-4265-8D0E-E121837B6FCC} = {447C8A77-E5F0-4538-8687-7383196D04EA}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5}

@ -0,0 +1,11 @@
{
"culture": "pl",
"texts": {
"GivenTenantIsNotAvailable": "Podany tenant jest niedostępny: {0}",
"Tenant": "Tenant",
"Switch": "zmień",
"Name": "Nazwa",
"SwitchTenantHint": "Pozostaw pole nazwy puste, aby przełączyć się na stronę hosta.",
"NotSelected": "Nie wybrano"
}
}

@ -0,0 +1,11 @@
{
"culture": "vi",
"texts": {
"GivenTenantIsNotAvailable": "Người thuê không có sẵn: {0}",
"Tenant": "Người thuê",
"Switch": "Chuyển đổi",
"Name": "Tên",
"SwitchTenantHint": "Để trống trường tên để chuyển sang phía máy chủ.",
"NotSelected": "Không được chọn"
}
}

@ -0,0 +1,16 @@
using System.Collections.Generic;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
using Volo.Abp.AspNetCore.Mvc.UI.Packages.Core;
using Volo.Abp.Modularity;
namespace Volo.Abp.AspNetCore.Mvc.UI.Packages.FreezeUi
{
[DependsOn(typeof(CoreScriptContributor))]
public class FreezeUiScriptContributor : BundleContributor
{
public override void ConfigureBundle(BundleConfigurationContext context)
{
context.Files.AddIfNotContains("/libs/freeze-ui/freeze-ui.min.css");
}
}
}

@ -0,0 +1,16 @@
using System.Collections.Generic;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
using Volo.Abp.AspNetCore.Mvc.UI.Packages.Bootstrap;
using Volo.Abp.Modularity;
namespace Volo.Abp.AspNetCore.Mvc.UI.Packages.FreezeUi
{
[DependsOn(typeof(BootstrapStyleContributor))]
public class FreezeUiStyleContributor : BundleContributor
{
public override void ConfigureBundle(BundleConfigurationContext context)
{
context.Files.AddIfNotContains("/libs/freeze-ui/freeze-ui.min.css");
}
}
}

@ -3,6 +3,7 @@ using Volo.Abp.AspNetCore.Mvc.UI.Packages.Anchor;
using Volo.Abp.AspNetCore.Mvc.UI.Packages.Bootstrap;
using Volo.Abp.AspNetCore.Mvc.UI.Packages.Clipboard;
using Volo.Abp.AspNetCore.Mvc.UI.Packages.DatatablesNetBs4;
using Volo.Abp.AspNetCore.Mvc.UI.Packages.FreezeUi;
using Volo.Abp.AspNetCore.Mvc.UI.Packages.JQuery;
using Volo.Abp.AspNetCore.Mvc.UI.Packages.JQueryForm;
using Volo.Abp.AspNetCore.Mvc.UI.Packages.JQueryValidationUnobtrusive;
@ -27,7 +28,8 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Bundling
typeof(DatatablesNetBs4ScriptContributor),
typeof(SweetalertScriptContributor),
typeof(ToastrScriptBundleContributor),
typeof(TimeagoScriptContributor))]
typeof(TimeagoScriptContributor),
typeof(FreezeUiScriptContributor))]
public class SharedThemeGlobalScriptContributor : BundleContributor
{
public override void ConfigureBundle(BundleConfigurationContext context)
@ -40,7 +42,8 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Bundling
"/libs/abp/aspnetcore-mvc-ui-theme-shared/bootstrap/modal-manager.js",
"/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-extensions.js",
"/libs/abp/aspnetcore-mvc-ui-theme-shared/sweetalert/abp-sweetalert.js",
"/libs/abp/aspnetcore-mvc-ui-theme-shared/toastr/abp-toastr.js"
"/libs/abp/aspnetcore-mvc-ui-theme-shared/toastr/abp-toastr.js",
"/libs/abp/aspnetcore-mvc-ui-theme-shared/freeze-ui/abp-freeze-ui.js"
});
}
}

@ -2,6 +2,7 @@
using Volo.Abp.AspNetCore.Mvc.UI.Packages.Bootstrap;
using Volo.Abp.AspNetCore.Mvc.UI.Packages.DatatablesNetBs4;
using Volo.Abp.AspNetCore.Mvc.UI.Packages.FontAwesome;
using Volo.Abp.AspNetCore.Mvc.UI.Packages.FreezeUi;
using Volo.Abp.AspNetCore.Mvc.UI.Packages.Select2;
using Volo.Abp.AspNetCore.Mvc.UI.Packages.Toastr;
using Volo.Abp.Modularity;
@ -13,7 +14,8 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Bundling
typeof(FontAwesomeStyleContributor),
typeof(ToastrStyleBundleContributor),
typeof(Select2StyleContributor),
typeof(DatatablesNetBs4StyleContributor)
typeof(DatatablesNetBs4StyleContributor),
typeof(FreezeUiStyleContributor)
)]
public class SharedThemeGlobalStyleContributor : BundleContributor
{

@ -0,0 +1,46 @@
var abp = abp || {};
(function ($) {
if (!window.FreezeUI || !$) {
return;
}
/* MESSAGE **************************************************/
/*Package from https://alexradulescu.github.io/freeze-ui/*/
abp.ui = abp.ui || {};
/* UI BLOCK */
abp.ui.block = function (elm) {
if (elm) {
window.FreezeUI({ selector: elm });
} else {
window.FreezeUI();
}
};
abp.ui.unblock = function () {
window.UnFreezeUI();
};
/* UI BUSY */
abp.ui.setBusy = function (element, text, freezeDelay) {
var opt = { text : text ? text : ' ' };
if (element) {
opt.element = element;
}
if (freezeDelay) {
opt.freezeDelay = freezeDelay;
}
window.FreezeUI(opt);
};
abp.ui.clearBusy = function () {
window.UnFreezeUI();
};
})(jQuery);

@ -11,7 +11,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="NUglify" Version="1.5.12" />
<PackageReference Include="NUglify" Version="1.5.13" />
</ItemGroup>
<ItemGroup>

@ -18,7 +18,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="AutoMapper" Version="8.1.0" />
<PackageReference Include="AutoMapper" Version="8.1.1" />
</ItemGroup>
</Project>

@ -59,14 +59,14 @@ namespace Volo.Abp.AutoMapper
//We should prevent duplicate mapping in an application, since Mapper is static.
if (!_createdMappingsBefore)
{
_createdMappingsBefore = true;
Mapper.Initialize(mapperConfigurationExpression =>
{
ConfigureAll(new AbpAutoMapperConfigurationContext(mapperConfigurationExpression, scope.ServiceProvider));
});
ValidateAll(Mapper.Configuration);
_createdMappingsBefore = true;
}
scope.ServiceProvider.GetRequiredService<MapperAccessor>().Mapper = Mapper.Instance;

@ -17,7 +17,7 @@
<PackageReference Include="Microsoft.AspNetCore.Hosting.Abstractions" Version="2.2.0" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="4.5.1" />
<PackageReference Include="System.Security.Permissions" Version="4.5.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.0.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.1.0" />
</ItemGroup>
<ItemGroup>

@ -6,10 +6,14 @@ namespace Volo.Abp.Cli.ProjectBuilding.Building
{
public abstract class TemplateInfo
{
[NotNull]
public string Name { get; }
public DatabaseProvider DefaultDatabaseProvider { get; }
[CanBeNull]
public string DocumentUrl { get; set; }
protected TemplateInfo(
[NotNull] string name,
DatabaseProvider defaultDatabaseProvider = DatabaseProvider.NotSpecified)

@ -1,5 +1,7 @@
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Volo.Abp.Cli.ProjectBuilding.Building;
using Volo.Abp.DependencyInjection;
@ -7,6 +9,8 @@ namespace Volo.Abp.Cli.ProjectBuilding
{
public class ProjectBuilder : IProjectBuilder, ITransientDependency
{
public ILogger<ProjectBuilder> Logger { get; set; }
protected ITemplateStore TemplateStore { get; }
protected ITemplateInfoProvider TemplateInfoProvider { get; }
@ -14,6 +18,8 @@ namespace Volo.Abp.Cli.ProjectBuilding
{
TemplateStore = templateStore;
TemplateInfoProvider = templateInfoProvider;
Logger = NullLogger<ProjectBuilder>.Instance;
}
public async Task<ProjectBuildResult> BuildAsync(ProjectBuildArgs args)
@ -44,6 +50,11 @@ namespace Volo.Abp.Cli.ProjectBuilding
ProjectBuildPipelineBuilder.Build(context).Execute(context);
if (!templateInfo.DocumentUrl.IsNullOrEmpty())
{
Logger.LogInformation("Check the documentation of this template: " + templateInfo.DocumentUrl);
}
return new ProjectBuildResult(context.Result.ZipContent, args.SolutionName.ProjectName);
}

@ -14,7 +14,7 @@ namespace Volo.Abp.Cli.ProjectBuilding.Templates.Mvc
public MvcTemplate()
: base(TemplateName, DatabaseProvider.EntityFrameworkCore)
{
DocumentUrl = "https://docs.abp.io/en/abp/latest/Startup-Templates/Mvc";
}
public override IEnumerable<ProjectBuildPipelineStep> GetCustomSteps(ProjectBuildContext context)

@ -14,7 +14,7 @@ namespace Volo.Abp.Cli.ProjectBuilding.Templates.MvcModule
public MvcModuleTemplate()
: base(TemplateName)
{
DocumentUrl = "https://docs.abp.io/en/abp/latest/Startup-Templates/Mvc-Module";
}
public override IEnumerable<ProjectBuildPipelineStep> GetCustomSteps(ProjectBuildContext context)

@ -14,7 +14,7 @@
<PackageReference Include="System.ComponentModel.Annotations" Version="4.5.0" />
<PackageReference Include="System.Collections.Immutable" Version="1.5.0" />
<PackageReference Include="System.Runtime.Loader" Version="4.3.0" />
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.0.12" />
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.0.16" />
<PackageReference Include="System.Linq.Queryable" Version="4.3.0" />
<PackageReference Include="Microsoft.Extensions.Localization" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.2.0" />

@ -45,28 +45,46 @@ namespace Volo.Abp.Application.Services
}
public abstract class AsyncCrudAppService<TEntity, TEntityDto, TKey, TGetListInput, TCreateInput, TUpdateInput>
: CrudAppServiceBase<TEntity, TEntityDto, TKey, TGetListInput, TCreateInput, TUpdateInput>,
IAsyncCrudAppService<TEntityDto, TKey, TGetListInput, TCreateInput, TUpdateInput>
: AsyncCrudAppService<TEntity, TEntityDto, TEntityDto, TKey, TGetListInput, TCreateInput, TUpdateInput>
where TEntity : class, IEntity<TKey>
where TEntityDto : IEntityDto<TKey>
{
protected AsyncCrudAppService(IRepository<TEntity, TKey> repository)
: base(repository)
{
}
protected override TEntityDto MapToGetListOutputDto(TEntity entity)
{
return MapToGetOutputDto(entity);
}
}
public abstract class AsyncCrudAppService<TEntity, TGetOutputDto, TGetListOutputDto, TKey, TGetListInput, TCreateInput, TUpdateInput>
: CrudAppServiceBase<TEntity, TGetOutputDto, TGetListOutputDto, TKey, TGetListInput, TCreateInput, TUpdateInput>,
IAsyncCrudAppService<TGetOutputDto, TGetListOutputDto, TKey, TGetListInput, TCreateInput, TUpdateInput>
where TEntity : class, IEntity<TKey>
where TEntityDto : IEntityDto<TKey>
where TGetOutputDto : IEntityDto<TKey>
where TGetListOutputDto : IEntityDto<TKey>
{
public IAsyncQueryableExecuter AsyncQueryableExecuter { get; set; }
protected AsyncCrudAppService(IRepository<TEntity, TKey> repository)
:base(repository)
: base(repository)
{
AsyncQueryableExecuter = DefaultAsyncQueryableExecuter.Instance;
}
public virtual async Task<TEntityDto> GetAsync(TKey id)
public virtual async Task<TGetOutputDto> GetAsync(TKey id)
{
await CheckGetPolicyAsync();
var entity = await GetEntityByIdAsync(id);
return MapToEntityDto(entity);
return MapToGetOutputDto(entity);
}
public virtual async Task<PagedResultDto<TEntityDto>> GetListAsync(TGetListInput input)
public virtual async Task<PagedResultDto<TGetListOutputDto>> GetListAsync(TGetListInput input)
{
await CheckGetListPolicyAsync();
@ -79,29 +97,29 @@ namespace Volo.Abp.Application.Services
var entities = await AsyncQueryableExecuter.ToListAsync(query);
return new PagedResultDto<TEntityDto>(
return new PagedResultDto<TGetListOutputDto>(
totalCount,
entities.Select(MapToEntityDto).ToList()
entities.Select(MapToGetListOutputDto).ToList()
);
}
public virtual async Task<TEntityDto> CreateAsync(TCreateInput input)
public virtual async Task<TGetOutputDto> CreateAsync(TCreateInput input)
{
await CheckCreatePolicyAsync();
var entity = MapToEntity(input);
if(entity is IMultiTenant && !HasTenantIdProperty(entity))
if (entity is IMultiTenant && !HasTenantIdProperty(entity))
{
TryToSetTenantId(entity);
}
await Repository.InsertAsync(entity, autoSave: true);
return MapToEntityDto(entity);
return MapToGetOutputDto(entity);
}
public virtual async Task<TEntityDto> UpdateAsync(TKey id, TUpdateInput input)
public virtual async Task<TGetOutputDto> UpdateAsync(TKey id, TUpdateInput input)
{
await CheckUpdatePolicyAsync();
@ -110,7 +128,7 @@ namespace Volo.Abp.Application.Services
MapToEntity(input, entity);
await Repository.UpdateAsync(entity, autoSave: true);
return MapToEntityDto(entity);
return MapToGetOutputDto(entity);
}
public virtual async Task DeleteAsync(TKey id)

@ -44,8 +44,7 @@ namespace Volo.Abp.Application.Services
}
public abstract class CrudAppService<TEntity, TEntityDto, TKey, TGetListInput, TCreateInput, TUpdateInput>
: CrudAppServiceBase<TEntity, TEntityDto, TKey, TGetListInput, TCreateInput, TUpdateInput>,
ICrudAppService<TEntityDto, TKey, TGetListInput, TCreateInput, TUpdateInput>
: CrudAppService<TEntity, TEntityDto, TEntityDto, TKey, TGetListInput, TCreateInput, TUpdateInput>
where TEntity : class, IEntity<TKey>
where TEntityDto : IEntityDto<TKey>
{
@ -55,15 +54,34 @@ namespace Volo.Abp.Application.Services
}
public virtual TEntityDto Get(TKey id)
protected override TEntityDto MapToGetListOutputDto(TEntity entity)
{
return MapToGetOutputDto(entity);
}
}
public abstract class CrudAppService<TEntity, TGetOutputDto, TGetListOutputDto, TKey, TGetListInput, TCreateInput, TUpdateInput>
: CrudAppServiceBase<TEntity, TGetOutputDto, TGetListOutputDto, TKey, TGetListInput, TCreateInput, TUpdateInput>,
ICrudAppService<TGetOutputDto, TGetListOutputDto, TKey, TGetListInput, TCreateInput, TUpdateInput>
where TEntity : class, IEntity<TKey>
where TGetOutputDto : IEntityDto<TKey>
where TGetListOutputDto : IEntityDto<TKey>
{
protected CrudAppService(IRepository<TEntity, TKey> repository)
: base(repository)
{
}
public virtual TGetOutputDto Get(TKey id)
{
CheckGetPolicy();
var entity = GetEntityById(id);
return MapToEntityDto(entity);
return MapToGetOutputDto(entity);
}
public virtual PagedResultDto<TEntityDto> GetList(TGetListInput input)
public virtual PagedResultDto<TGetListOutputDto> GetList(TGetListInput input)
{
CheckGetListPolicy();
@ -76,29 +94,29 @@ namespace Volo.Abp.Application.Services
var entities = query.ToList();
return new PagedResultDto<TEntityDto>(
return new PagedResultDto<TGetListOutputDto>(
totalCount,
entities.Select(MapToEntityDto).ToList()
entities.Select(MapToGetListOutputDto).ToList()
);
}
public virtual TEntityDto Create(TCreateInput input)
public virtual TGetOutputDto Create(TCreateInput input)
{
CheckCreatePolicy();
var entity = MapToEntity(input);
if(entity is IMultiTenant && !HasTenantIdProperty(entity))
if (entity is IMultiTenant && !HasTenantIdProperty(entity))
{
TryToSetTenantId(entity);
}
Repository.Insert(entity, autoSave: true);
return MapToEntityDto(entity);
return MapToGetOutputDto(entity);
}
public virtual TEntityDto Update(TKey id, TUpdateInput input)
public virtual TGetOutputDto Update(TKey id, TUpdateInput input)
{
CheckUpdatePolicy();
@ -106,7 +124,7 @@ namespace Volo.Abp.Application.Services
MapToEntity(input, entity);
Repository.Update(entity, autoSave: true);
return MapToEntityDto(entity);
return MapToGetOutputDto(entity);
}
public virtual void Delete(TKey id)

@ -13,10 +13,11 @@ namespace Volo.Abp.Application.Services
/// This is a common base class for CrudAppService and AsyncCrudAppService classes.
/// Inherit either from CrudAppService or AsyncCrudAppService, not from this class.
/// </summary>
public abstract class CrudAppServiceBase<TEntity, TEntityDto, TKey, TGetListInput, TCreateInput, TUpdateInput> :
public abstract class CrudAppServiceBase<TEntity, TGetOutputDto, TGetListOutputDto, TKey, TGetListInput, TCreateInput, TUpdateInput> :
ApplicationService
where TEntity : class, IEntity<TKey>
where TEntityDto : IEntityDto<TKey>
where TGetOutputDto : IEntityDto<TKey>
where TGetListOutputDto : IEntityDto<TKey>
{
protected IRepository<TEntity, TKey> Repository { get; }
@ -100,19 +101,29 @@ namespace Volo.Abp.Application.Services
}
/// <summary>
/// Maps <see cref="TEntity"/> to <see cref="TEntityDto"/>.
/// Maps <see cref="TEntity"/> to <see cref="TGetOutputDto"/>.
/// It uses <see cref="IObjectMapper"/> by default.
/// It can be overrided for custom mapping.
/// It can be overriden for custom mapping.
/// </summary>
protected virtual TEntityDto MapToEntityDto(TEntity entity)
protected virtual TGetOutputDto MapToGetOutputDto(TEntity entity)
{
return ObjectMapper.Map<TEntity, TEntityDto>(entity);
return ObjectMapper.Map<TEntity, TGetOutputDto>(entity);
}
/// <summary>
/// Maps <see cref="TEntityDto"/> to <see cref="TEntity"/> to create a new entity.
/// Maps <see cref="TEntity"/> to <see cref="TGetListOutputDto"/>.
/// It uses <see cref="IObjectMapper"/> by default.
/// It can be overrided for custom mapping.
/// It can be overriden for custom mapping.
/// </summary>
protected virtual TGetListOutputDto MapToGetListOutputDto(TEntity entity)
{
return ObjectMapper.Map<TEntity, TGetListOutputDto>(entity);
}
/// <summary>
/// Maps <see cref="TCreateInput"/> to <see cref="TEntity"/> to create a new entity.
/// It uses <see cref="IObjectMapper"/> by default.
/// It can be overriden for custom mapping.
/// </summary>
protected virtual TEntity MapToEntity(TCreateInput createInput)
{
@ -140,7 +151,7 @@ namespace Volo.Abp.Application.Services
/// <summary>
/// Maps <see cref="TUpdateInput"/> to <see cref="TEntity"/> to update the entity.
/// It uses <see cref="IObjectMapper"/> by default.
/// It can be overrided for custom mapping.
/// It can be overriden for custom mapping.
/// </summary>
protected virtual void MapToEntity(TUpdateInput updateInput, TEntity entity)
{

@ -25,16 +25,24 @@ namespace Volo.Abp.Application.Services
}
public interface IAsyncCrudAppService<TEntityDto, in TKey, in TGetListInput, in TCreateInput, in TUpdateInput>
: IApplicationService
: IAsyncCrudAppService<TEntityDto, TEntityDto, TKey, TGetListInput, TCreateInput, TUpdateInput>
where TEntityDto : IEntityDto<TKey>
{
Task<TEntityDto> GetAsync(TKey id);
Task<PagedResultDto<TEntityDto>> GetListAsync(TGetListInput input);
}
public interface IAsyncCrudAppService<TGetOutputDto, TGetListOutputDto, in TKey, in TGetListInput, in TCreateInput, in TUpdateInput>
: IApplicationService
where TGetOutputDto : IEntityDto<TKey>
where TGetListOutputDto : IEntityDto<TKey>
{
Task<TGetOutputDto> GetAsync(TKey id);
Task<PagedResultDto<TGetListOutputDto>> GetListAsync(TGetListInput input);
Task<TEntityDto> CreateAsync(TCreateInput input);
Task<TGetOutputDto> CreateAsync(TCreateInput input);
Task<TEntityDto> UpdateAsync(TKey id, TUpdateInput input);
Task<TGetOutputDto> UpdateAsync(TKey id, TUpdateInput input);
Task DeleteAsync(TKey id);
}

@ -24,16 +24,24 @@ namespace Volo.Abp.Application.Services
}
public interface ICrudAppService<TEntityDto, in TKey, in TGetListInput, in TCreateInput, in TUpdateInput>
: IApplicationService
: ICrudAppService<TEntityDto, TEntityDto, TKey, TGetListInput, TCreateInput, TCreateInput>
where TEntityDto : IEntityDto<TKey>
{
TEntityDto Get(TKey id);
PagedResultDto<TEntityDto> GetList(TGetListInput input);
}
public interface ICrudAppService<TGetOutputDto, TGetListOutputDto, in TKey, in TGetListInput, in TCreateInput, in TUpdateInput>
: IApplicationService
where TGetOutputDto : IEntityDto<TKey>
where TGetListOutputDto : IEntityDto<TKey>
{
TGetOutputDto Get(TKey id);
PagedResultDto<TGetListOutputDto> GetList(TGetListInput input);
TEntityDto Create(TCreateInput input);
TGetOutputDto Create(TCreateInput input);
TEntityDto Update(TKey id, TUpdateInput input);
TGetOutputDto Update(TKey id, TUpdateInput input);
void Delete(TKey id);
}

@ -18,7 +18,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="2.2.0" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="2.2.4" />
</ItemGroup>
</Project>

@ -14,7 +14,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="FluentValidation" Version="8.3.0" />
<PackageReference Include="FluentValidation" Version="8.4.0" />
</ItemGroup>
<ItemGroup>

@ -14,7 +14,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Hangfire.AspNetCore" Version="1.7.1" />
<PackageReference Include="Hangfire.AspNetCore" Version="1.7.3" />
</ItemGroup>
<ItemGroup>

@ -14,7 +14,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="IdentityModel" Version="3.10.9" />
<PackageReference Include="IdentityModel" Version="3.10.10" />
<ProjectReference Include="..\Volo.Abp.Threading\Volo.Abp.Threading.csproj" />
</ItemGroup>

@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<AssemblyName>Volo.Abp.Ldap</AssemblyName>
<PackageId>Volo.Abp.Ldap</PackageId>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Novell.Directory.Ldap.NETStandard" Version="2.3.8" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Volo.Abp.Autofac\Volo.Abp.Autofac.csproj" />
</ItemGroup>
</Project>

@ -0,0 +1,18 @@
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Autofac;
using Volo.Abp.Modularity;
namespace Volo.Abp.Ldap
{
[DependsOn(
typeof(AbpAutofacModule)
)]
public class AbpLdapModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
var configuration = context.Services.GetConfiguration();
Configure<LdapOptions>(configuration.GetSection("LDAP"));
}
}
}

@ -0,0 +1,11 @@
namespace Volo.Abp.Ldap.Exceptions
{
public class OrganizationNotExistException : BusinessException
{
public OrganizationNotExistException(string distinguishedName)
: base("LDAP:000001", $"the organization distinguished named {distinguishedName} does not exist.")
{
}
}
}

@ -0,0 +1,72 @@
using System.Collections.Generic;
using Volo.Abp.Ldap.Modeling;
namespace Volo.Abp.Ldap
{
public interface ILdapManager
{
/// <summary>
/// query the specified organizations.
///
/// filter: (&(name=xxx)(objectClass=organizationalUnit)) when name is not null
/// filter: (&(name=*)(objectClass=organizationalUnit)) when name is null
///
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
IList<LdapOrganization> GetOrganizations(string name = null);
/// <summary>
/// query the specified organization.
///
/// filter: (&(distinguishedName=xxx)(objectClass=organizationalUnit)) when organizationName is not null
///
/// </summary>
/// <param name="distinguishedName"></param>
/// <returns></returns>
LdapOrganization GetOrganization(string distinguishedName);
void AddSubOrganization(string organizationName, LdapOrganization parentOrganization);
void AddSubOrganization(string organizationName, string parentDistinguishedName);
/// <summary>
/// query the specified users.
///
/// filter: (&(name=xxx)(objectCategory=person)(objectClass=user)) when name is not null
/// filter: (&(name=*)(objectCategory=person)(objectClass=user)) when name is null
///
/// filter: (&(displayName=xxx)(objectCategory=person)(objectClass=user)) when displayName is not null
/// filter: (&(displayName=*)(objectCategory=person)(objectClass=user)) when displayName is null
///
/// filter: (&(cn=xxx)(objectCategory=person)(objectClass=user)) when commonName is not null
/// filter: (&(cn=*)(objectCategory=person)(objectClass=user)) when commonName is null
///
/// </summary>
/// <param name="name"></param>
/// <param name="displayName"></param>
/// <param name="commonName"></param>
/// <returns></returns>
IList<LdapUser> GetUsers(string name = null, string displayName = null, string commonName = null);
/// <summary>
/// query the specified User.
///
/// filter: (&(distinguishedName=xxx)(objectCategory=person)(objectClass=user)) when distinguishedName is not null
///
/// </summary>
/// <param name="distinguishedName"></param>
/// <returns></returns>
LdapUser GetUser(string distinguishedName);
void AddUserToOrganization(string userName, string password, LdapOrganization parentOrganization);
void AddUserToOrganization(string userName, string password, string parentDistinguishedName);
/// <summary>
/// Authenticate
/// </summary>
/// <param name="userDomainName">E.g administrator@yourdomain.com.cn </param>
/// <param name="password"></param>
/// <returns></returns>
bool Authenticate(string userDomainName, string password);
}
}

@ -0,0 +1,9 @@
namespace Volo.Abp.Ldap
{
public class LdapCredentials
{
public string DomainUserName { get; set; }
public string Password { get; set; }
}
}

@ -0,0 +1,34 @@
using System.Collections.Generic;
namespace Volo.Abp.Ldap
{
public static class LdapHelps
{
public static string BuildCondition(string name, string value)
{
return string.IsNullOrWhiteSpace(value) ? "" : $"({name}={value})";
}
public static string BuildFilter(Dictionary<string,string> conditions)
{
if (null == conditions )
{
conditions = new Dictionary<string, string>();
}
if (conditions.Keys.Count == 0)
{
conditions.Add("objectClass", "*"); // add default condition
}
var subFilter = string.Empty;
foreach (var keyValuePair in conditions)
{
subFilter += BuildCondition(keyValuePair.Key, keyValuePair.Value);
}
return $"(&{subFilter})";
}
}
}

@ -0,0 +1,332 @@
using System;
using Microsoft.Extensions.Options;
using Novell.Directory.Ldap;
using System.Collections.Generic;
using System.Text;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Ldap.Exceptions;
using Volo.Abp.Ldap.Modeling;
namespace Volo.Abp.Ldap
{
public class LdapManager : ILdapManager, ITransientDependency
{
private readonly string _searchBase;
private readonly LdapOptions _ldapOptions;
private readonly string[] _attributes =
{
"objectCategory", "objectClass", "cn", "name", "distinguishedName",
"ou",
"sAMAccountName", "userPrincipalName", "telephoneNumber", "mail"
};
public LdapManager(IOptions<LdapOptions> ldapSettingsOptions)
{
_ldapOptions = ldapSettingsOptions.Value;
_searchBase = _ldapOptions.SearchBase;
}
#region Organization
/// <summary>
/// query the specified organizations.
///
/// filter: (&(name=xxx)(objectClass=organizationalUnit)) when name is not null
/// filter: (&(objectClass=organizationalUnit)) when name is null
///
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public IList<LdapOrganization> GetOrganizations(string name = null)
{
var conditions = new Dictionary<string, string>
{
{"name", name},
{"objectClass", "organizationalUnit"},
};
return Query<LdapOrganization>(_searchBase, conditions);
}
/// <summary>
/// query the specified organization.
///
/// filter: (&(distinguishedName=xxx)(objectClass=organizationalUnit)) when organizationName is not null
///
/// </summary>
/// <param name="distinguishedName"></param>
/// <returns></returns>
public LdapOrganization GetOrganization(string distinguishedName)
{
distinguishedName = Check.NotNullOrWhiteSpace(distinguishedName, nameof(distinguishedName));
var conditions = new Dictionary<string, string>
{
{"distinguishedName", distinguishedName},
{"objectClass", "organizationalUnit"},
};
return QueryOne<LdapOrganization>(_searchBase, conditions);
}
public void AddSubOrganization(string organizationName, LdapOrganization parentOrganization)
{
organizationName = Check.NotNullOrWhiteSpace(organizationName, nameof(organizationName));
var dn = $"OU={organizationName},{parentOrganization.DistinguishedName}";
var attributeSet = new LdapAttributeSet
{
new LdapAttribute("objectCategory", $"CN=Organizational-Unit,CN=Schema,CN=Configuration,{_ldapOptions.DomainDistinguishedName}"),
new LdapAttribute("objectClass", new[] {"top", "organizationalUnit"}),
new LdapAttribute("name", organizationName),
};
var newEntry = new LdapEntry(dn, attributeSet);
using (var ldapConnection = GetConnection())
{
ldapConnection.Add(newEntry);
}
}
public void AddSubOrganization(string organizationName, string parentDistinguishedName)
{
organizationName = Check.NotNullOrWhiteSpace(organizationName, nameof(organizationName));
parentDistinguishedName =
Check.NotNullOrWhiteSpace(parentDistinguishedName, nameof(parentDistinguishedName));
var parentOrganization = GetOrganization(parentDistinguishedName);
if (null == parentOrganization)
{
throw new OrganizationNotExistException(parentDistinguishedName);
}
AddSubOrganization(organizationName, parentOrganization);
}
#endregion
#region User
/// <summary>
/// query the specified users.
///
/// filter: (&(name=xxx)(objectCategory=person)(objectClass=user)) when name is not null
/// filter: (&(objectCategory=person)(objectClass=user)) when name is null
///
/// filter: (&(displayName=xxx)(objectCategory=person)(objectClass=user)) when displayName is not null
/// filter: (&(objectCategory=person)(objectClass=user)) when displayName is null
///
/// filter: (&(cn=xxx)(objectCategory=person)(objectClass=user)) when commonName is not null
/// filter: (&(objectCategory=person)(objectClass=user)) when commonName is null
///
/// </summary>
/// <param name="name"></param>
/// <param name="displayName"></param>
/// <param name="commonName"></param>
/// <returns></returns>
public IList<LdapUser> GetUsers(string name = null, string displayName = null, string commonName = null)
{
var conditions = new Dictionary<string, string>
{
{"objectCategory", "person"},
{"objectClass", "user"},
{"name", name},
{"displayName", displayName},
{"cn", commonName},
};
return Query<LdapUser>(_searchBase, conditions);
}
/// <summary>
/// query the specified User.
///
/// filter: (&(distinguishedName=xxx)(objectCategory=person)(objectClass=user)) when distinguishedName is not null
///
/// </summary>
/// <param name="distinguishedName"></param>
/// <returns></returns>
public LdapUser GetUser(string distinguishedName)
{
distinguishedName = Check.NotNullOrWhiteSpace(distinguishedName, nameof(distinguishedName));
var conditions = new Dictionary<string, string>
{
{"objectCategory", "person"},
{"objectClass", "user"},
{"distinguishedName", distinguishedName},
};
return QueryOne<LdapUser>(_searchBase, conditions);
}
public void AddUserToOrganization(string userName, string password, LdapOrganization parentOrganization)
{
var dn = $"CN={userName},{parentOrganization.DistinguishedName}";
var mail = $"{userName}@{_ldapOptions.DomainName}";
sbyte[] encodedBytes = SupportClass.ToSByteArray(Encoding.Unicode.GetBytes($"\"{password}\""));
var attributeSet = new LdapAttributeSet
{
new LdapAttribute("instanceType", "4"),
new LdapAttribute("objectCategory", $"CN=Person,CN=Schema,CN=Configuration,{_ldapOptions.DomainDistinguishedName}"),
new LdapAttribute("objectClass", new[] {"top", "person", "organizationalPerson", "user"}),
new LdapAttribute("name", userName),
new LdapAttribute("cn", userName),
new LdapAttribute("sAMAccountName", userName),
new LdapAttribute("userPrincipalName", userName),
new LdapAttribute("sn", userName),
new LdapAttribute("displayName", userName),
new LdapAttribute("unicodePwd", encodedBytes),
new LdapAttribute("userAccountControl", "512"),
new LdapAttribute("mail", mail),
};
var newEntry = new LdapEntry(dn, attributeSet);
using (var ldapConnection = GetConnection())
{
ldapConnection.Add(newEntry);
}
}
public void AddUserToOrganization(string userName, string password, string parentDistinguishedName)
{
var dn = $"CN={userName},{parentDistinguishedName}";
var mail = $"{userName}@{_ldapOptions.DomainName}";
sbyte[] encodedBytes = SupportClass.ToSByteArray(Encoding.Unicode.GetBytes($"\"{password}\""));
var attributeSet = new LdapAttributeSet
{
new LdapAttribute("instanceType", "4"),
new LdapAttribute("objectCategory", $"CN=Person,CN=Schema,CN=Configuration,{_ldapOptions.DomainDistinguishedName}"),
new LdapAttribute("objectClass", new[] {"top", "person", "organizationalPerson", "user"}),
new LdapAttribute("name", userName),
new LdapAttribute("cn", userName),
new LdapAttribute("sAMAccountName", userName),
new LdapAttribute("userPrincipalName", userName),
new LdapAttribute("sn", userName),
new LdapAttribute("displayName", userName),
new LdapAttribute("unicodePwd", encodedBytes),
new LdapAttribute("userAccountControl", "512"),
new LdapAttribute("mail", mail),
};
var newEntry = new LdapEntry(dn, attributeSet);
using (var ldapConnection = GetConnection())
{
ldapConnection.Add(newEntry);
}
}
#endregion
#region Authenticate
/// <summary>
/// Authenticate
/// </summary>
/// <param name="userDomainName">E.g administrator@yourdomain.com.cn </param>
/// <param name="password"></param>
/// <returns></returns>
public bool Authenticate(string userDomainName, string password)
{
try
{
using (GetConnection(userDomainName, password))
{
return true;
}
}
catch (Exception )
{
return false;
}
}
#endregion
private ILdapConnection GetConnection(string bindUserName = null, string bindUserPassword = null)
{
// bindUserName/bindUserPassword only be used when authenticate
bindUserName = bindUserName ?? _ldapOptions.Credentials.DomainUserName;
bindUserPassword = bindUserPassword ?? _ldapOptions.Credentials.Password;
var ldapConnection = new LdapConnection() { SecureSocketLayer = _ldapOptions.UseSsl };
if (_ldapOptions.UseSsl)
{
ldapConnection.UserDefinedServerCertValidationDelegate += (sender, certificate, chain, sslPolicyErrors) => true;
}
ldapConnection.Connect(_ldapOptions.ServerHost, _ldapOptions.ServerPort);
if (_ldapOptions.UseSsl)
{
ldapConnection.Bind(LdapConnection.Ldap_V3, bindUserName, bindUserPassword);
}
else
{
ldapConnection.Bind(bindUserName, bindUserPassword);
}
return ldapConnection;
}
private IList<T> Query<T>(string searchBase, Dictionary<string, string> conditions) where T : class, ILdapEntry
{
var filter = LdapHelps.BuildFilter(conditions);
var result = new List<T>();
using (var ldapConnection = GetConnection())
{
var search = ldapConnection.Search(searchBase, LdapConnection.SCOPE_SUB, filter,
_attributes, false, null, null);
LdapMessage message;
while ((message = search.getResponse()) != null)
{
if (!(message is LdapSearchResult searchResultMessage))
{
continue;
}
var entry = searchResultMessage.Entry;
if (typeof(T) == typeof(LdapOrganization))
{
result.Add(new LdapOrganization(entry.getAttributeSet()) as T);
}
if (typeof(T) == typeof(LdapUser))
{
result.Add(new LdapUser(entry.getAttributeSet()) as T);
}
}
}
return result;
}
private T QueryOne<T>(string searchBase, Dictionary<string, string> conditions) where T : class, ILdapEntry
{
var filter = LdapHelps.BuildFilter(conditions);
using (var ldapConnection = GetConnection())
{
var search = ldapConnection.Search(searchBase, LdapConnection.SCOPE_SUB, filter,
_attributes, false, null, null);
LdapMessage message;
while ((message = search.getResponse()) != null)
{
if (!(message is LdapSearchResult searchResultMessage))
{
continue;
}
var entry = searchResultMessage.Entry;
if (typeof(T) == typeof(LdapOrganization))
{
return new LdapOrganization(entry.getAttributeSet()) as T;
}
if (typeof(T) == typeof(LdapUser))
{
return new LdapUser(entry.getAttributeSet()) as T;
}
return null;
}
}
return null;
}
}
}

@ -0,0 +1,24 @@
namespace Volo.Abp.Ldap
{
public class LdapOptions
{
public string ServerHost { get; set; }
public int ServerPort { get; set; }
public bool UseSsl { get; set; }
public string SearchBase { get; set; }
public string DomainName { get; set; }
public string DomainDistinguishedName { get; set; }
public LdapCredentials Credentials { get; set; }
public LdapOptions()
{
Credentials = new LdapCredentials();
}
}
}

@ -0,0 +1,11 @@
namespace Volo.Abp.Ldap.Modeling
{
public interface ILdapEntry
{
string ObjectCategory { get; set; }
string[] ObjectClass { get; set; }
string Name { get; set; }
string DistinguishedName { get; set; }
string CommonName { get; set; }
}
}

@ -0,0 +1,7 @@
namespace Volo.Abp.Ldap.Modeling
{
public interface ILdapOrganization : ILdapEntry
{
string OrganizationUnit { get; set; }
}
}

@ -0,0 +1,11 @@
namespace Volo.Abp.Ldap.Modeling
{
public interface ILdapUser : ILdapEntry
{
string SamAccountName { get; set; }
string UserPrincipalName { get; set; }
string DisplayName { get; set; }
string Email { get; set; }
string Phone { get; set; }
}
}

@ -0,0 +1,24 @@
using Novell.Directory.Ldap;
namespace Volo.Abp.Ldap.Modeling
{
public abstract class LdapEntryBase : ILdapEntry
{
public string ObjectCategory { get; set; }
public string[] ObjectClass { get; set; }
public string Name { get; set; }
public string CommonName { get; set; }
public string DistinguishedName { get; set; }
protected LdapEntryBase() { }
protected LdapEntryBase(LdapAttributeSet attributeSet)
{
ObjectCategory = attributeSet.getAttribute("objectCategory")?.StringValue;
ObjectClass = attributeSet.getAttribute("objectClass")?.StringValueArray;
Name = attributeSet.getAttribute("name")?.StringValue;
CommonName = attributeSet.getAttribute("cn")?.StringValue;
DistinguishedName = attributeSet.getAttribute("distinguishedName")?.StringValue;
}
}
}

@ -0,0 +1,17 @@
using Novell.Directory.Ldap;
namespace Volo.Abp.Ldap.Modeling
{
public class LdapOrganization : LdapEntryBase, ILdapOrganization
{
public string OrganizationUnit { get; set; }
public LdapOrganization() { }
public LdapOrganization(LdapAttributeSet attributeSet)
: base(attributeSet)
{
OrganizationUnit = attributeSet.getAttribute("ou")?.StringValue;
}
}
}

@ -0,0 +1,25 @@
using Novell.Directory.Ldap;
namespace Volo.Abp.Ldap.Modeling
{
public class LdapUser : LdapEntryBase, ILdapUser
{
public string SamAccountName { get; set; }
public string UserPrincipalName { get; set; }
public string DisplayName { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
public LdapUser() { }
public LdapUser( LdapAttributeSet attributeSet)
: base(attributeSet)
{
SamAccountName = attributeSet.getAttribute("sAMAccountName")?.StringValue;
UserPrincipalName = attributeSet.getAttribute("userPrincipalName")?.StringValue;
DisplayName = attributeSet.getAttribute("displayName")?.StringValue;
Email = attributeSet.getAttribute("mail")?.StringValue;
Phone = attributeSet.getAttribute("telephoneNumber")?.StringValue;
}
}
}

@ -0,0 +1,171 @@
# Volo.Abp.Ldap
# Only Authenticate(not read/write AD)
## Configure
add section in `appsettings.json`
### use SSL
```json
"LDAP": {
"ServerHost": "192.168.101.54",
"ServerPort": 636,
"UseSsl": true
}
```
### not use SSL
```json
"LDAP": {
"ServerHost": "192.168.101.54",
"ServerPort": 389,
"UseSsl": false
}
```
## Authenticate
Injecting `ILdapManager` into a class. For example:
```csharp
public class TaxAppService : ApplicationService
{
private readonly ILdapManager _ldapManager;
public TaxAppService(ILdapManager ldapManager)
{
_ldapManager = ldapManager;
}
public void Authenticate(string userName, string password)
{
var result = _ldapManager.Authenticate(userName, password);
}
}
```
- `userName` must be full domain name. E.g abc@abc.com
# Read/Write AD
## Configure
### use SSL
```json
"LDAP": {
"ServerHost": "192.168.101.54",
"ServerPort": 636,
"UseSsl": true,
"Credentials": {
"DomainUserName": "administrator@yourdomain.com.cn",
"Password": "yH.20190528"
},
"SearchBase": "DC=yourdomain,DC=com,DC=cn",
"DomainName": "yourdomain.com.cn",
"DomainDistinguishedName": "DC=yourdomain,DC=com,DC=cn"
}
```
### not use SSL
```json
"LDAP": {
"ServerHost": "192.168.101.54",
"ServerPort": 389,
"UseSsl": false,
"Credentials": {
"DomainUserName": "administrator@yourdomain.com.cn",
"Password": "yH.20190528"
},
"SearchBase": "DC=yourdomain,DC=com,DC=cn",
"DomainName": "yourdomain.com.cn",
"DomainDistinguishedName": "DC=yourdomain,DC=com,DC=cn"
}
```
- `Credentials:DomainUserName` a administrator of AD.
- `Credentials:Password` the password for the administrator.
- `SearchBase`: where search from AD.
- `DomainName`: name of you domain. no need `www`.
- `DomainDistinguishedName`: distinguished name of root domain.
## Query Organizations
```cs
// query all organizations
// filter: (&(objectClass=organizationalUnit))
_ldapManager.GetOrganizations();
// query organizations by name
// filter: (&(name=abc)(objectClass=organizationalUnit))
_ldapManager.GetOrganizations("abc");
```
## Query Organization
```csharp
// query organization by distinguished name
// filter: (&(distinguishedName=abc)(objectClass=organizationalUnit))
_ldapManager.GetOrganization("abc");
```
## Add Organization
```csharp
// use LdapOrganization
_ldapManager.AddSubOrganization("nameA", parentOrganization);
// or use OrganizationDistinguishedName
_ldapManager.AddSubOrganization("nameA", "OU=Domain Controllers,DC=yourdomain,DC=com,DC=cn");
```
## Query Users
```cs
// query all users
// filter: (&(objectCategory=person)(objectClass=user))
_ldapManager.GetUsers();
// query organizations by name
// filter: (&(name=abc)(objectCategory=person)(objectClass=user))
_ldapManager.GetUsers(name : "abc");
// query organizations by displayName
// filter: (&(displayName=abc)(objectCategory=person)(objectClass=user))
_ldapManager.GetUsers(displayName : "abc");
// query organization by commonName
// filter: (&(cn=abc)(objectCategory=person)(objectClass=user))
_ldapManager.GetUsers(commonName : "abc");
```
## Query User
```csharp
// query a user by distinguished name
// filter: (&(distinguishedName=abc)(objectCategory=person)(objectClass=user))
_ldapManager.GetUser("abc");
```
## Add User
```csharp
// use LdapOrganization
_ldapManager.AddUserToOrganization("nameA", "passwordA", parentOrganization);
// or use OrganizationDistinguishedName
_ldapManager.AddUserToOrganization("nameA", "passwordA", "OU=Domain Controllers,DC=yourdomain,DC=com,DC=cn");
```
# More
See [unit test](../../test/Volo.Abp.Ldap.Tests)

@ -0,0 +1,20 @@
{
"culture": "pl",
"texts": {
"'{0}' and '{1}' do not match.": "'{0}' i '{1}' nie są takie same.",
"The {0} field is not a valid credit card number.": "Pole {0} nie jest poprawnym numerem karty kredytowej.",
"{0} is not valid.": "{0} jest niepoprawne.",
"The {0} field is not a valid e-mail address.": "Pole {0} nie jest poprawnym adresem e-mail.",
"The {0} field only accepts files with the following extensions: {1}": "Polr {0} przyjmuje pliki tylko z następującymi rozszerzeniami: {1}",
"The field {0} must be a string or array type with a maximum length of '{1}'.": "Pole {0} musi być łańcuchem znaków lub talicą o maksymalnej długości '{1}'.",
"The field {0} must be a string or array type with a minimum length of '{1}'.": "Pole {0} musi być łańcuchem znaków lub talicą o minimalnej długości '{1}'.",
"The {0} field is not a valid phone number.": "Pole {0} nie jest poprawnym numerem telefonu.",
"The field {0} must be between {1} and {2}.": "Pole {0} musi być pomiędzy {1} i {2}.",
"The field {0} must match the regular expression '{1}'.": "Pole {0} musi pasować do wyrażenia regularnego '{1}'.",
"The {0} field is required.": "Pole {0} jest wymagane.",
"The field {0} must be a string with a maximum length of {1}.": "Pole {0} musi być łańcuchem znaków o maksymalnej długości {1}.",
"The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "Pole {0} musi być łańcuchem znaków o minimalnej długości {2} i maksymalnej długości {1}.",
"The {0} field is not a valid fully-qualified http, https, or ftp URL.": "Pole {0} nie jest prawidłowym, w pełni kwalifikowanym adresem URL http, https lub ftp.",
"The field {0} is invalid.": "Pole {0} jest niepoprawne."
}
}

@ -0,0 +1,19 @@
{
"culture": "vi",
"texts": {
"'{0}' and '{1}' do not match.": "'{0}' và '{1}' không khớp.",
"The {0} field is not a valid credit card number.": "Trường {0} không phải là một số thẻ tín dụng hợp lệ.",
"{0} is not valid.": "{0} không hợp lệ.",
"The {0} field is not a valid e-mail address.": "Trường {0} không phải là một địa chỉ email hợp lệ.",
"The {0} field only accepts files with the following extensions: {1}": "Trường {0} chỉ chấp nhận các tập tin có phần mở rộng như sau: {1}",
"The field {0} must be a string or array type with a maximum length of '{1}'.": "Trường {0} phải là một chuỗi hoặc một mảng với độ dài tối đa là '{1}'.",
"The {0} field is not a valid phone number.": "Trường {0} không phải là một số điện thoại hợp lệ",
"The field {0} must be between {1} and {2}.": "Trường {0} phải ở giữa {1} and {2}.",
"The field {0} must match the regular expression '{1}'.": "Trường {0} phải khớp với biểu thức chính quy '{1}'.",
"The {0} field is required.": "Trường {0} là bắt buộc.",
"The field {0} must be a string with a maximum length of {1}.": "Trường {0} phải là một chuỗi với độ dài tối đa là {1}.",
"The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "Trường {0} phải là một chuỗi với độ dài tối thiểu {2} và tối đa là {1}.",
"The {0} field is not a valid fully-qualified http, https, or ftp URL.": "Trường {0} không phải là một http, https, hoặc ftp URL đủ điều kiện hợp lệ.",
"The field {0} is invalid.": "Trường {0} không có hiệu lực"
}
}

@ -14,7 +14,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MongoDB.Driver" Version="2.8.0" />
<PackageReference Include="MongoDB.Driver" Version="2.8.1" />
</ItemGroup>
<ItemGroup>

@ -0,0 +1,6 @@
{
"culture": "pl",
"texts": {
"Menu:Administration": "Administracja"
}
}

@ -0,0 +1,6 @@
{
"culture": "vi",
"texts": {
"Menu:Administration": "Quản trị"
}
}

@ -0,0 +1,51 @@
{
"culture": "pl",
"texts": {
"InternalServerErrorMessage": "Błąd wewnętrzny serwera podczas przetwarzania żądania!",
"ValidationErrorMessage": "Twoje żądanie jest niepoprawnie!",
"ValidationNarrativeErrorMessageTitle": "Wykryto następujące błędy podczas walidacji.",
"DefaultErrorMessage": "Wystąpił błąd!",
"DefaultErrorMessageDetail": "Treść błędu nie została wysłana z serwera.",
"DefaultErrorMessage401": "Nie jesteś zalogowany!",
"DefaultErrorMessage401Detail": "Musisz się zalogować, aby wykonać tą operację.",
"DefaultErrorMessage403": "Nie jesteś zautentykowany!",
"DefaultErrorMessage403Detail": "Nie masz uprawnień do wykonania tej operacji!",
"DefaultErrorMessage404": "Nie znaleziono zasobu!",
"DefaultErrorMessage404Detail": "Nie znaleziono zasobu z żądania na serwerze!",
"EntityNotFoundErrorMessage": "Nie istnieje encja {0} z id = {1}!",
"Error": "Błąd",
"AreYouSure": "Czy jesteś pewien?",
"Cancel": "Anuluj",
"Yes": "Tak",
"No": "Nie",
"Close": "Zamknij",
"Save": "Zapisz",
"SavingWithThreeDot": "Zapisywanie...",
"Actions": "Akcje",
"Delete": "Usuń",
"Edit": "Edytuj",
"Refresh": "Odśwież",
"ProcessingWithThreeDot": "Procesowanie...",
"LoadingWithThreeDot": "Ładowanie...",
"Welcome": "Witaj",
"Login": "Zaloguj",
"Register": "Zarejestruj",
"Logout": "Wyloguj",
"Submit": "Wyślij",
"Back": "Cofnij",
"PagerSearch": "Szukaj",
"PagerNext": "Następna",
"PagerPrevious": "Poprzednia",
"PagerFirst": "Pierwsza",
"PagerLast": "Ostatnia",
"PagerInfo": "Pokaż od _START_ do _END_ z _TOTAL_ rekordów",
"PagerInfoEmpty": "Pokaż od 0 do 0 z 0 rekordów",
"PagerInfoFiltered": "(filtruj od _MAX_ wszystkich rekordów)",
"NoDataAvailableInDatatable": "Nie znaleziono danych w tabeli",
"PagerShowMenuEntries": "Pokaż _MENU_ rekordów",
"DatatableActionDropdownDefaultText": "Akcje",
"ChangePassword": "Zmień hasło",
"PersonalInfo": "Mój profil",
"AreYouSureYouWantToCancelEditingWarningMessage": "Masz niezapisane zmiany."
}
}

@ -0,0 +1,51 @@
{
"culture": "vi",
"texts": {
"InternalServerErrorMessage": "Có một lỗi nội bộ xảy ra trong quá trình thực hiện yêu cầu của bạn!",
"ValidationErrorMessage": "Yêu cầu của bạn không hợp lệ!",
"ValidationNarrativeErrorMessageTitle": "Các lỗi sau đây đã được phát hiện trong quá trình xác nhận",
"DefaultErrorMessage": "Một lỗi đã xảy ra",
"DefaultErrorMessageDetail": "Chi tiết lỗi không được gửi bởi máy chủ",
"DefaultErrorMessage401": "Bạn chưa được xác thực",
"DefaultErrorMessage401Detail": "Bạn cần đăng nhập để thực hiện thao tác này.",
"DefaultErrorMessage403": "Bạn không được phép!",
"DefaultErrorMessage403Detail": "Bạn không được phép thực hiện thao tác này!",
"DefaultErrorMessage404": "Tài nguyên không tìm thấy!",
"DefaultErrorMessage404Detail": "Tài nguyên được yêu cầu không được tìm thấy trên máy chủ!",
"EntityNotFoundErrorMessage": "Không có thực thể nào {0} với id = {1}!",
"Error": "Lỗi",
"AreYouSure": "Bạn có chắc không ?",
"Cancel": "Hủy bỏ",
"Yes": "Đồng ý",
"No": "Không",
"Close": "Đóng",
"Save": "Lưu",
"SavingWithThreeDot": "Đang lưu...",
"Actions": "Hành động",
"Delete": "Xóa",
"Edit": "Sửa",
"Refresh": "Làm mới",
"ProcessingWithThreeDot": "Đang xử lý...",
"LoadingWithThreeDot": "Đang tải...",
"Welcome": "Chào mừng bạn",
"Login": "Đăng nhập",
"Register": "Đăng ký",
"Logout": "Đăng xuất",
"Submit": "Gửi",
"Back": "Quay lại",
"PagerSearch": "Tìm kiếm",
"PagerNext": "Trang kế",
"PagerPrevious": "Trang trước",
"PagerFirst": "Trang đầu",
"PagerLast": "Trang cuối",
"PagerInfo": "Hiển thị từ _START_ đến _END_ trong _TOTAL_ mục",
"PagerInfoEmpty": "Hiển thị từ 0 đến 0 trong 0 mục",
"PagerInfoFiltered": "(được lọc từ tổng số _MAX_ mục)",
"NoDataAvailableInDatatable": "Không có dữ liệu trong bảng",
"PagerShowMenuEntries": "Hiển thị _MENU_ mục",
"DatatableActionDropdownDefaultText": "Hành động",
"ChangePassword": "Đổi mật khẩu",
"PersonalInfo": "Hồ sơ của tôi",
"AreYouSureYouWantToCancelEditingWarningMessage": "Bạn có những thay đổi chưa được lưu"
}
}

@ -15,8 +15,8 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" />
<PackageReference Include="NSubstitute" Version="4.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.1.1" />
<PackageReference Include="NSubstitute" Version="4.2.0" />
<PackageReference Include="Shouldly" Version="3.0.2" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.extensibility.execution" Version="2.4.1" />

@ -15,7 +15,7 @@
<ItemGroup>
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" />
<ProjectReference Include="..\..\src\Volo.Abp.AspNetCore.Authentication.OAuth\Volo.Abp.AspNetCore.Authentication.OAuth.csproj" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.1.1" />
</ItemGroup>
</Project>

@ -14,7 +14,7 @@
<ItemGroup>
<ProjectReference Include="..\..\src\Volo.Abp.AspNetCore.MultiTenancy\Volo.Abp.AspNetCore.MultiTenancy.csproj" />
<ProjectReference Include="..\Volo.Abp.AspNetCore.Tests\Volo.Abp.AspNetCore.Tests.csproj" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.1.1" />
</ItemGroup>
</Project>

@ -21,7 +21,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.2.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.1.1" />
</ItemGroup>
<ItemGroup>

@ -0,0 +1,7 @@
{
"culture": "pl",
"texts": {
"BirthDate": "Data urodzenia",
"Value1": "Wartość jeden"
}
}

@ -0,0 +1,7 @@
{
"culture": "vi",
"texts": {
"BirthDate": "Ngày sinh",
"Value1": "Giá trị thứ nhất"
}
}

@ -20,7 +20,7 @@
<ProjectReference Include="..\..\src\Volo.Abp.Autofac\Volo.Abp.Autofac.csproj" />
<ProjectReference Include="..\Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo\Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.csproj" />
<ProjectReference Include="..\Volo.Abp.AspNetCore.Tests\Volo.Abp.AspNetCore.Tests.csproj" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.1.1" />
</ItemGroup>
<!-- Below ItemGroup and Target tags are added according to https://github.com/aspnet/Hosting/issues/959#issuecomment-286351703 -->

@ -17,7 +17,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.2.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.1.1" />
</ItemGroup>
<ItemGroup>

@ -17,7 +17,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.2.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.1.1" />
</ItemGroup>
<ItemGroup>

@ -14,7 +14,7 @@
<ItemGroup>
<ProjectReference Include="..\..\src\Volo.Abp.AspNetCore.TestBase\Volo.Abp.AspNetCore.TestBase.csproj" />
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.1.1" />
</ItemGroup>
</Project>

@ -16,7 +16,7 @@
<ProjectReference Include="..\..\src\Volo.Abp.Auditing\Volo.Abp.Auditing.csproj" />
<ProjectReference Include="..\..\src\Volo.Abp.Autofac\Volo.Abp.Autofac.csproj" />
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.1.1" />
</ItemGroup>
</Project>

@ -16,7 +16,7 @@
<ProjectReference Include="..\..\src\Volo.Abp.Autofac\Volo.Abp.Autofac.csproj" />
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" />
<ProjectReference Include="..\..\src\Volo.Abp.Authorization\Volo.Abp.Authorization.csproj" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.1.1" />
</ItemGroup>
</Project>

@ -14,7 +14,7 @@
<ItemGroup>
<ProjectReference Include="..\..\src\Volo.Abp.AutoMapper\Volo.Abp.AutoMapper.csproj" />
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.1.1" />
</ItemGroup>
</Project>

@ -15,7 +15,7 @@
<ProjectReference Include="..\..\src\Volo.Abp.Autofac\Volo.Abp.Autofac.csproj" />
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" />
<ProjectReference Include="..\Volo.Abp.Core.Tests\Volo.Abp.Core.Tests.csproj" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.1.1" />
</ItemGroup>
</Project>

@ -16,7 +16,7 @@
<ProjectReference Include="..\..\src\Volo.Abp.BackgroundJobs\Volo.Abp.BackgroundJobs.csproj" />
<ProjectReference Include="..\..\src\Volo.Abp.Autofac\Volo.Abp.Autofac.csproj" />
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.1.1" />
</ItemGroup>
</Project>

@ -15,7 +15,7 @@
<ItemGroup>
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" />
<ProjectReference Include="..\..\src\Volo.Abp.Caching\Volo.Abp.Caching.csproj" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.1.1" />
</ItemGroup>
</Project>

@ -14,7 +14,7 @@
<ItemGroup>
<ProjectReference Include="..\..\src\Volo.Abp.Castle.Core\Volo.Abp.Castle.Core.csproj" />
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.1.1" />
</ItemGroup>
</Project>

@ -9,7 +9,7 @@
<ItemGroup>
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" />
<ProjectReference Include="..\..\src\Volo.Abp.Cli.Core\Volo.Abp.Cli.Core.csproj" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.1.1" />
</ItemGroup>
</Project>

@ -14,7 +14,7 @@
<ItemGroup>
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" />
<ProjectReference Include="..\..\src\Volo.Abp.Core\Volo.Abp.Core.csproj" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.1.1" />
</ItemGroup>
</Project>

@ -14,7 +14,7 @@
<ItemGroup>
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" />
<ProjectReference Include="..\..\src\Volo.Abp.Data\Volo.Abp.Data.csproj" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.1.1" />
</ItemGroup>
</Project>

@ -15,7 +15,7 @@
<ItemGroup>
<ProjectReference Include="..\..\src\Volo.Abp.Ddd.Domain\Volo.Abp.Ddd.Domain.csproj" />
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.1.1" />
</ItemGroup>
</Project>

@ -21,7 +21,7 @@
<ProjectReference Include="..\..\src\Volo.Abp.Emailing\Volo.Abp.Emailing.csproj" />
<ProjectReference Include="..\..\src\Volo.Abp.Autofac\Volo.Abp.Autofac.csproj" />
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.1.1" />
</ItemGroup>
</Project>

@ -0,0 +1,6 @@
{
"culture": "pl",
"texts": {
"hello": "witaj"
}
}

@ -0,0 +1,6 @@
{
"culture": "vi",
"texts": {
"hello": "xin chào"
}
}

@ -21,7 +21,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.4" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.1.1" />
</ItemGroup>
</Project>

@ -17,7 +17,7 @@
<ProjectReference Include="..\..\src\Volo.Abp.Ddd.Domain\Volo.Abp.Ddd.Domain.csproj" />
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" />
<ProjectReference Include="..\..\src\Volo.Abp.EventBus\Volo.Abp.EventBus.csproj" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.1.1" />
</ItemGroup>
</Project>

@ -16,7 +16,7 @@
<ProjectReference Include="..\..\src\Volo.Abp.Autofac\Volo.Abp.Autofac.csproj" />
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" />
<ProjectReference Include="..\..\src\Volo.Abp.Features\Volo.Abp.Features.csproj" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.1.1" />
</ItemGroup>
</Project>

@ -15,7 +15,7 @@
<ProjectReference Include="..\..\src\Volo.Abp.Autofac\Volo.Abp.Autofac.csproj" />
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" />
<ProjectReference Include="..\..\src\Volo.Abp.FluentValidation\Volo.Abp.FluentValidation.csproj" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.1.1" />
</ItemGroup>
</Project>

@ -15,7 +15,7 @@
<ProjectReference Include="..\..\src\Volo.Abp.Http.Client\Volo.Abp.Http.Client.csproj" />
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" />
<ProjectReference Include="..\Volo.Abp.AspNetCore.Mvc.Tests\Volo.Abp.AspNetCore.Mvc.Tests.csproj" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.1.1" />
</ItemGroup>
</Project>

@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
<LangVersion>latest</LangVersion>
<IsPackable>false</IsPackable>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.1.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Volo.Abp.Ldap\Volo.Abp.Ldap.csproj" />
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" />
</ItemGroup>
</Project>

@ -0,0 +1,70 @@
using System;
using Shouldly;
using Volo.Abp.Modularity;
using Xunit;
namespace Volo.Abp.Ldap
{
public class Authenticate_Tests : AbpIntegratedTest<Authenticate_Tests.TestModule>
{
protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options)
{
options.UseAutofac();
}
private readonly ILdapManager _ldapManager;
private readonly LdapTestData _testData;
public Authenticate_Tests()
{
// ReSharper disable once VirtualMemberCallInConstructor
_testData = GetRequiredService<LdapTestData>();
_ldapManager = GetRequiredService<ILdapManager>();
}
[Fact(Skip = "need environment AD ")]
public void Authenticate()
{
var result = _ldapManager.Authenticate(_testData.AdministratorDomainName, _testData.AdministratorPassword);
result.ShouldBeTrue();
}
[Fact(Skip = "need environment AD ")]
public void Authenticate_With_Wrong_Password()
{
var result = _ldapManager.Authenticate("NonExistentNameA", "PasswordA");
result.ShouldBeFalse();
}
[DependsOn(typeof(AbpLdapModule))]
public class TestModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
// not use ssl
// "LDAP": {
// "ServerHost": "192.168.101.54",
// "ServerPort": 389,
// "UseSSL": false
// }
// use ssl
// "LDAP": {
// "ServerHost": "192.168.101.54",
// "ServerPort": 636,
// "UseSSL": true
// }
Configure<LdapOptions>(settings =>
{
settings.ServerHost = "192.168.101.54";
settings.ServerPort = 636;
settings.UseSsl = true;
});
}
}
}
}

@ -0,0 +1,84 @@
using System.Collections.Generic;
using Shouldly;
using Xunit;
namespace Volo.Abp.Ldap
{
public class LdapHelps_Tests
{
[Fact]
public void BuildCondition_With_Value()
{
// act
var res = LdapHelps.BuildCondition("objectClass", "testNameA");
// assert
res.ShouldBe("(objectClass=testNameA)");
}
[Fact]
public void BuildCondition_With_Null_Value()
{
// act
var res = LdapHelps.BuildCondition("objectClass", null);
// assert
res.ShouldBeEmpty();
}
[Fact]
public void BuildCondition_With_Empty_Value()
{
// act
var res = LdapHelps.BuildCondition("objectClass", "");
// assert
res.ShouldBeEmpty();
}
[Fact]
public void BuildCondition_With_WhiteSpace_Value()
{
// act
var res = LdapHelps.BuildCondition("objectClass", " ");
// assert
res.ShouldBeEmpty();
}
[Fact]
public void BuildFilter_With_Null_Condition()
{
// act
var res = LdapHelps.BuildFilter(null);
// assert
res.ShouldBe("(&(objectClass=*))");
}
[Fact]
public void BuildFilter_With_Empty_Condition()
{
// act
var res = LdapHelps.BuildFilter(new Dictionary<string, string>());
// assert
res.ShouldBe("(&(objectClass=*))");
}
[Fact]
public void BuildFilter_With_Condition()
{
// act
var conditions = new Dictionary<string, string>
{
{"objectClass", "testClassA"}, {"objectCategory", "testCategoryA"}, {"name", null}
};
var res = LdapHelps.BuildFilter(conditions);
// assert
res.ShouldBe("(&(objectClass=testClassA)(objectCategory=testCategoryA))");
}
}
}

@ -0,0 +1,245 @@
using System;
using Shouldly;
using Volo.Abp.Modularity;
using Xunit;
namespace Volo.Abp.Ldap
{
public class LdapManager_Tests : AbpIntegratedTest<LdapManager_Tests.TestModule>
{
protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options)
{
options.UseAutofac();
}
private readonly ILdapManager _ldapManager;
private readonly LdapTestData _testData;
public LdapManager_Tests()
{
// ReSharper disable once VirtualMemberCallInConstructor
_testData = GetRequiredService<LdapTestData>();
_ldapManager = GetRequiredService<ILdapManager>();
}
[Fact(Skip = "need environment AD ")]
public void GetOrganizations_With_Empty_Condition()
{
var result = _ldapManager.GetOrganizations();
result.ShouldNotBeNull();
result.ShouldContain(e => e.Name == _testData.DomainControllersName);
result.ShouldContain(e => e.DistinguishedName == _testData.DomainControllersDistinguishedName);
}
[Fact(Skip = "need environment AD ")]
public void GetOrganizations_With_Name()
{
var result = _ldapManager.GetOrganizations(_testData.DomainControllersName);
result.ShouldNotBeNull();
result.ShouldHaveSingleItem();
result.ShouldContain(e => e.Name == _testData.DomainControllersName);
result.ShouldContain(e => e.DistinguishedName == _testData.DomainControllersDistinguishedName);
}
[Fact(Skip = "need environment AD ")]
public void GetOrganizations_With_Non_Existent_Name()
{
var result = _ldapManager.GetOrganizations("NonExistentNameA");
result.ShouldNotBeNull();
result.ShouldBeEmpty();
}
[Fact(Skip = "need environment AD ")]
public void GetOrganization()
{
var result = _ldapManager.GetOrganization(_testData.DomainControllersDistinguishedName);
result.ShouldNotBeNull();
result.Name.ShouldBe(_testData.DomainControllersName);
}
[Fact(Skip = "need environment AD ")]
public void GetOrganization_With_Non_Existent_DistinguishedName()
{
var result = _ldapManager.GetOrganization("NonExistentNameA");
result.ShouldBeNull();
}
[Fact(Skip = "need environment AD ")]
public void GetUsers_With_Empty_Condition()
{
var result = _ldapManager.GetUsers();
result.ShouldNotBeNull();
result.ShouldContain(e => e.Name == _testData.AdministratorName);
}
[Fact(Skip = "need environment AD ")]
public void GetUsers_With_Name()
{
var result = _ldapManager.GetUsers(name: _testData.AdministratorName);
result.ShouldNotBeNull();
result.ShouldContain(e => e.Name == _testData.AdministratorName);
}
[Fact(Skip = "need environment AD ")]
public void GetUsers_With_Non_Existent_Name()
{
var result = _ldapManager.GetUsers(name: "NonExistentNameA");
result.ShouldNotBeNull();
result.ShouldBeEmpty();
}
[Fact(Skip = "need environment AD ")]
public void GetUsers_With_CommonName()
{
var result = _ldapManager.GetUsers(commonName: _testData.AdministratorName);
result.ShouldNotBeNull();
result.ShouldContain(e => e.Name == _testData.AdministratorName);
}
[Fact(Skip = "need environment AD ")]
public void GetUsers_With_Non_Existent_CommonName()
{
var result = _ldapManager.GetUsers(commonName: "NonExistentNameA");
result.ShouldNotBeNull();
result.ShouldBeEmpty();
}
[Fact(Skip = "need environment AD ")]
public void GetUsers_With_DisplayName()
{
var result = _ldapManager.GetUsers(displayName: _testData.AdministratorName);
result.ShouldNotBeNull();
// the administrator in AD. not have display name by default.
result.ShouldBeEmpty();
}
[Fact(Skip = "need environment AD ")]
public void GetUser()
{
var result = _ldapManager.GetUser(_testData.AdministratorDistinguishedName);
result.ShouldNotBeNull();
result.Name.ShouldBe(_testData.AdministratorName);
}
[Fact(Skip = "need environment AD ")]
public void GetUser_With_Non_Existent_DistinguishedName()
{
var result = _ldapManager.GetOrganization("NonExistentNameA");
result.ShouldBeNull();
}
[Fact(Skip = "need environment AD ")]
public void Authenticate()
{
var result = _ldapManager.Authenticate(_testData.AdministratorDomainName, _testData.AdministratorPassword);
result.ShouldBeTrue();
}
[Fact(Skip = "need environment AD ")]
public void Authenticate_With_Wrong_Password()
{
var result = _ldapManager.Authenticate("NonExistentNameA", "PasswordA");
result.ShouldBeFalse();
}
[Fact(Skip = "need environment AD ")]
public void AddSubOrganization()
{
var parentOrganization = _ldapManager.GetOrganization(_testData.DomainControllersDistinguishedName);
var randomName = $"Test_{DateTime.Now.Ticks}";
_ldapManager.AddSubOrganization(randomName, parentOrganization);
var result = _ldapManager.GetOrganizations(randomName);
result.ShouldNotBeNull();
result.ShouldContain(e => e.Name == randomName);
}
[Fact(Skip = "need environment AD ")]
public void AddSubOrganization_With_DistinguishedName()
{
var randomName = $"Test_{DateTime.Now.Ticks}";
_ldapManager.AddSubOrganization(randomName, _testData.DomainControllersDistinguishedName);
var result = _ldapManager.GetOrganizations(randomName);
result.ShouldNotBeNull();
result.ShouldContain(e => e.Name == randomName);
}
[Fact(Skip = "need environment AD ")]
public void AddOrganizationUser()
{
var parentOrganization = _ldapManager.GetOrganization(_testData.DomainControllersDistinguishedName);
var randomName = $"Test_{DateTime.Now:yyMMddHHmmss}";
_ldapManager.AddUserToOrganization(randomName, _testData.AdministratorPassword, parentOrganization);
var result = _ldapManager.GetUsers(randomName);
result.ShouldNotBeNull();
result.ShouldContain(e=>e.Name == randomName);
}
[DependsOn(typeof(AbpLdapModule))]
public class TestModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
// not use ssl
// "LDAP": {
// "ServerHost": "192.168.101.54",
// "ServerPort": 389,
// "UseSSL": false,
// "Credentials": {
// "DomainUserName": "administrator@yourdomain.com.cn",
// "Password": "yH.20190528"
// },
// "SearchBase": "CN=Users,DC=yourdomain,DC=com,DC=cn",
// "DomainName": "yourdomain.com.cn",
// "DomainDistinguishedName": "DC=yourdomain,DC=com,DC=cn"
// }
// use ssl
// "LDAP": {
// "ServerHost": "192.168.101.54",
// "ServerPort": 636,
// "UseSSL": true,
// "Credentials": {
// "DomainUserName": "administrator@yourdomain.com.cn",
// "Password": "yH.20190528"
// },
// "SearchBase": "CN=Users,DC=yourdomain,DC=com,DC=cn",
// "DomainName": "yourdomain.com.cn",
// "DomainDistinguishedName": "DC=yourdomain,DC=com,DC=cn"
// }
Configure<LdapOptions>(settings =>
{
settings.ServerHost = "192.168.101.54";
settings.ServerPort = 636;
settings.UseSsl = true;
settings.Credentials.DomainUserName = "administrator@yourdomain.com.cn";
settings.Credentials.Password = "yH.20190528";
settings.SearchBase = "DC=yourdomain,DC=com,DC=cn";
settings.DomainName = "yourdomain.com.cn";
settings.DomainDistinguishedName = "DC=yourdomain,DC=com,DC=cn";
});
}
}
}
}

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

Loading…
Cancel
Save