Browse Source

Merge branch 'dev' of https://github.com/abpframework/abp into feat/volo-2917

pull/4995/head
mehmet-erim 6 years ago
parent
commit
6de93b5c75
  1. 276
      docs/en/Community-Articles/2020-08-07-Passwordless-Authentication/POST.md
  2. 3
      docs/en/Distributed-Event-Bus.md
  3. 8
      docs/en/Entities.md
  4. 2
      docs/en/Local-Event-Bus.md
  5. 10
      docs/en/Tutorials/Part-1.md
  6. 10
      docs/en/Tutorials/Part-2.md
  7. 10
      docs/en/Tutorials/Part-3.md
  8. 10
      docs/en/Tutorials/Part-4.md
  9. 10
      docs/en/Tutorials/Part-5.md
  10. BIN
      docs/en/Tutorials/images/youtube.png
  11. 12
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/AbpExceptionFilter.cs
  12. 8
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/AbpExceptionPageFilter.cs
  13. 20
      framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Builder/VirtualFileSystemApplicationBuilderExtensions.cs
  14. 402
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/VirtualFileSystem/AbpAspNetCoreContentOptions.cs
  15. 39
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/VirtualFileSystem/AbpFileExtensionContentTypeProvider.cs
  16. 2
      framework/src/Volo.Abp.AutoMapper/Volo.Abp.AutoMapper.csproj
  17. 4
      framework/src/Volo.Abp.BlobStoring.Minio/Volo/Abp/BlobStoring/Minio/MinioBlobProvider.cs
  18. 9
      framework/src/Volo.Abp.Core/Microsoft/Extensions/Logging/AbpLoggerExtensions.cs
  19. 76
      framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/AggregateRoot.cs
  20. 95
      framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/BasicAggregateRoot.cs
  21. 8
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs
  22. 7
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/IEfCoreDbContext.cs
  23. 16
      framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/UrlBuilder.cs
  24. 14
      framework/src/Volo.Abp.Ldap/Volo.Abp.Ldap.csproj
  25. 47
      framework/src/Volo.Abp.Ldap/Volo/Abp/Ldap/AbpAbpLdapOptionsFactory.cs
  26. 38
      framework/src/Volo.Abp.Ldap/Volo/Abp/Ldap/AbpLdapModule.cs
  27. 17
      framework/src/Volo.Abp.Ldap/Volo/Abp/Ldap/AbpLdapOptions.cs
  28. 11
      framework/src/Volo.Abp.Ldap/Volo/Abp/Ldap/Exceptions/OrganizationNotExistException.cs
  29. 67
      framework/src/Volo.Abp.Ldap/Volo/Abp/Ldap/ILdapManager.cs
  30. 9
      framework/src/Volo.Abp.Ldap/Volo/Abp/Ldap/LdapCredentials.cs
  31. 34
      framework/src/Volo.Abp.Ldap/Volo/Abp/Ldap/LdapHelps.cs
  32. 328
      framework/src/Volo.Abp.Ldap/Volo/Abp/Ldap/LdapManager.cs
  33. 13
      framework/src/Volo.Abp.Ldap/Volo/Abp/Ldap/LdapSettingNames.cs
  34. 44
      framework/src/Volo.Abp.Ldap/Volo/Abp/Ldap/LdapSettingProvider.cs
  35. 10
      framework/src/Volo.Abp.Ldap/Volo/Abp/Ldap/Localization/LdapResource.cs
  36. 16
      framework/src/Volo.Abp.Ldap/Volo/Abp/Ldap/Localization/en.json
  37. 13
      framework/src/Volo.Abp.Ldap/Volo/Abp/Ldap/Localization/tr.json
  38. 13
      framework/src/Volo.Abp.Ldap/Volo/Abp/Ldap/Localization/zh-Hans.json
  39. 13
      framework/src/Volo.Abp.Ldap/Volo/Abp/Ldap/Localization/zh-Hant.json
  40. 11
      framework/src/Volo.Abp.Ldap/Volo/Abp/Ldap/Modeling/ILdapEntry.cs
  41. 7
      framework/src/Volo.Abp.Ldap/Volo/Abp/Ldap/Modeling/ILdapOrganization.cs
  42. 11
      framework/src/Volo.Abp.Ldap/Volo/Abp/Ldap/Modeling/ILdapUser.cs
  43. 24
      framework/src/Volo.Abp.Ldap/Volo/Abp/Ldap/Modeling/LdapEntryBase.cs
  44. 17
      framework/src/Volo.Abp.Ldap/Volo/Abp/Ldap/Modeling/LdapOrganization.cs
  45. 25
      framework/src/Volo.Abp.Ldap/Volo/Abp/Ldap/Modeling/LdapUser.cs
  46. 171
      framework/src/Volo.Abp.Ldap/readme.md
  47. 14
      framework/src/Volo.Abp.Validation.Abstractions/Volo/Abp/Validation/AbpValidationException.cs
  48. 24
      framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/PersonAppServiceClientProxy_Tests.cs
  49. 4
      framework/test/Volo.Abp.Ldap.Tests/Volo.Abp.Ldap.Tests.csproj
  50. 24
      framework/test/Volo.Abp.Ldap.Tests/Volo/Abp/Ldap/AbpLdapTestModule.cs
  51. 71
      framework/test/Volo.Abp.Ldap.Tests/Volo/Abp/Ldap/Authenticate_Tests.cs
  52. 84
      framework/test/Volo.Abp.Ldap.Tests/Volo/Abp/Ldap/LdapHelps_Tests.cs
  53. 231
      framework/test/Volo.Abp.Ldap.Tests/Volo/Abp/Ldap/LdapManager_Tests.cs
  54. 21
      framework/test/Volo.Abp.Ldap.Tests/Volo/Abp/Ldap/LdapOptions_Tests.cs
  55. 27
      framework/test/Volo.Abp.Ldap.Tests/Volo/Abp/Ldap/LdapTestData.cs
  56. 5
      framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/IPeopleAppService.cs
  57. 11
      framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/PeopleAppService.cs
  58. 32
      modules/account/src/Volo.Abp.Account.Web/Pages/Account/Register.cshtml.cs
  59. 6
      modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp/Migrations/20200810022513_Initial.Designer.cs
  60. 2
      modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp/Migrations/20200810022513_Initial.cs
  61. 2
      modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp/Migrations/DemoAppDbContextModelSnapshot.cs
  62. 4
      modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/Migrations/20200810022447_Initial.Designer.cs
  63. 0
      modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/Migrations/20200810022447_Initial.cs
  64. 2
      modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/Migrations/BlobStoringHostDbContextModelSnapshot.cs
  65. 1178
      modules/blogging/app/Volo.BloggingTestApp.EntityFrameworkCore/Migrations/20200810022522_IdentityUserIsExternal.Designer.cs
  66. 23
      modules/blogging/app/Volo.BloggingTestApp.EntityFrameworkCore/Migrations/20200810022522_IdentityUserIsExternal.cs
  67. 8
      modules/blogging/app/Volo.BloggingTestApp.EntityFrameworkCore/Migrations/BloggingTestAppDbContextModelSnapshot.cs
  68. 6
      modules/cms-kit/host/Volo.CmsKit.IdentityServer/Migrations/20200810022553_Initial.Designer.cs
  69. 0
      modules/cms-kit/host/Volo.CmsKit.IdentityServer/Migrations/20200810022553_Initial.cs
  70. 4
      modules/cms-kit/host/Volo.CmsKit.IdentityServer/Migrations/IdentityServerHostMigrationsDbContextModelSnapshot.cs
  71. 4
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20200810022644_Initial.Designer.cs
  72. 0
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20200810022644_Initial.cs
  73. 2
      modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/UnifiedDbContextModelSnapshot.cs
  74. 10
      modules/docs/app/VoloDocs.EntityFrameworkCore/Migrations/20200810022424_Initial.Designer.cs
  75. 1
      modules/docs/app/VoloDocs.EntityFrameworkCore/Migrations/20200810022424_Initial.cs
  76. 8
      modules/docs/app/VoloDocs.EntityFrameworkCore/Migrations/VoloDocsDbContextModelSnapshot.cs
  77. 5
      modules/docs/src/Volo.Docs.Web/Areas/Documents/TagHelpers/TreeTagHelper.cs
  78. 2
      modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/en.json
  79. 11
      modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Devices/DeviceFlowStore.cs
  80. 50
      modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/Clients/ClientRepository.cs
  81. 4
      templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations/Migrations/20200810022238_Initial.Designer.cs
  82. 0
      templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations/Migrations/20200810022238_Initial.cs
  83. 2
      templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations/Migrations/MyProjectNameMigrationsDbContextModelSnapshot.cs
  84. 9
      templates/console/src/MyCompanyName.MyProjectName/MyCompanyName.MyProjectName.csproj
  85. 41
      templates/console/src/MyCompanyName.MyProjectName/MyProjectNameHostedService.cs
  86. 10
      templates/console/src/MyCompanyName.MyProjectName/MyProjectNameModule.cs
  87. 9
      templates/console/src/MyCompanyName.MyProjectName/Program.cs
  88. 3
      templates/console/src/MyCompanyName.MyProjectName/appsettings.json
  89. 10
      templates/module/aspnet-core/host/MyCompanyName.MyProjectName.IdentityServer/Migrations/20200810022322_Initial.Designer.cs
  90. 1
      templates/module/aspnet-core/host/MyCompanyName.MyProjectName.IdentityServer/Migrations/20200810022322_Initial.cs
  91. 8
      templates/module/aspnet-core/host/MyCompanyName.MyProjectName.IdentityServer/Migrations/IdentityServerHostMigrationsDbContextModelSnapshot.cs
  92. 10
      templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/Migrations/20200810022405_Initial.Designer.cs
  93. 1
      templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/Migrations/20200810022405_Initial.cs
  94. 8
      templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/Migrations/UnifiedDbContextModelSnapshot.cs
  95. 2
      templates/module/aspnet-core/src/MyCompanyName.MyProjectName.MongoDB/MyCompanyName.MyProjectName.MongoDB.csproj

276
docs/en/Community-Articles/2020-08-07-Passwordless-Authentication/POST.md

@ -0,0 +1,276 @@
# Implementing Passwordless Authentication in ASP.NET Core Identity
## Introduction
To allow a user login with a magic URL, you need to implement a custom token provider. In this tutorial, we will show you how to add a custom token provider to authenticate a user with a link, instead of entering a password.
### Source Code
The completed sample is available on [the GitHub repository](https://github.com/abpframework/abp-samples/tree/master/PasswordlessAuthentication).
## Creating the Solution
Before starting to the development, create a new solution named `PasswordlessAuthentication` and run it by following the [getting started tutorial](https://docs.abp.io/en/abp/latest/Getting-Started?UI=MVC&DB=EF&Tiered=No).
## Step-1
Create a class named **PasswordlessLoginProvider** in your ***.Web** project:
```csharp
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
namespace PasswordlessAuthentication.Web
{
public class PasswordlessLoginProvider<TUser> : TotpSecurityStampBasedTokenProvider<TUser>
where TUser : class
{
public override Task<bool> CanGenerateTwoFactorTokenAsync(UserManager<TUser> manager, TUser user)
{
return Task.FromResult(false);
}
//We need to override this method as well.
public override async Task<string> GetUserModifierAsync(string purpose, UserManager<TUser> manager, TUser user)
{
var userId = await manager.GetUserIdAsync(user);
return "PasswordlessLogin:" + purpose + ":" + userId;
}
}
}
```
## Step-2
Create **IdentityBuilderExtensions.cs** in your ***.Web** project. We will use this extension method in the `ConfigureServices`.
```csharp
using Microsoft.AspNetCore.Identity;
namespace PasswordlessAuthentication.Web
{
public static class IdentityBuilderExtensions
{
public static IdentityBuilder AddPasswordlessLoginProvider(this IdentityBuilder builder)
{
var userType = builder.UserType;
var totpProvider = typeof(PasswordlessLoginProvider<>).MakeGenericType(userType);
return builder.AddTokenProvider("PasswordlessLoginProvider", totpProvider);
}
}
}
```
## Step-3
Add the token provider to the `Identity` middleware. To do this, find the module class (eg: `PasswordlessAuthenticationWebModule.cs` in here) in your ***.Web** project and add the below into the `ConfigureServices()` method.
```csharp
public override void ConfigureServices(ServiceConfigurationContext context)
{
//...
context.Services
.GetObject<IdentityBuilder>()
.AddDefaultTokenProviders()
.AddPasswordlessLoginProvider();
}
```
## Step-4
We need to create a user interface to be able to generate the magic login link. To do this quickly, open your existing **Index.cshtml.cs** in your ***.Web** project. It's under `Pages` folder. And copy-paste the below content.
**Index.cshtml.cs**
```csharp
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.Identity;
namespace PasswordlessAuthentication.Web.Pages
{
public class IndexModel : PasswordlessAuthenticationPageModel
{
protected IdentityUserManager UserManager { get; }
private readonly IIdentityUserRepository _userRepository;
public string PasswordlessLoginUrl { get; set; }
public string Email { get; set; }
public IndexModel(IdentityUserManager userManager, IIdentityUserRepository userRepository)
{
UserManager = userManager;
_userRepository = userRepository;
}
public ActionResult OnGet()
{
if (!CurrentUser.IsAuthenticated)
{
return Redirect("/Account/Login");
}
return Page();
}
//added for passwordless authentication
public async Task<IActionResult> OnPostGeneratePasswordlessTokenAsync()
{
var adminUser = await _userRepository.FindByNormalizedUserNameAsync("admin");
var token = await UserManager.GenerateUserTokenAsync(adminUser, "PasswordlessLoginProvider",
"passwordless-auth");
PasswordlessLoginUrl = Url.Action("Login", "Passwordless",
new {token = token, userId = adminUser.Id.ToString()}, Request.Scheme);
return Page();
}
}
}
```
We added `OnPostGeneratePasswordlessTokenAsync()` action to generate the link. We will generate a link for the **admin** user. Therefore, we injected `IIdentityUserRepository` to get admin user Id. Using the `UserManager.GenerateUserTokenAsync()` method, we generated a token. After that, we created the URL with the admin user Id and the token. Now we will show the `PasswordlessLoginUrl` on the page.
## Step-5
Create a class named **PasswordlessAuthenticationMenus** under `Menus` folder in your ***.Web** project. And set the content as below.
```csharp
namespace PasswordlessAuthentication.Web.Menus
{
public class PasswordlessAuthenticationMenus
{
public const string GroupName = "PasswordlessAuthentication";
public const string Home = GroupName + ".Home";
}
}
```
## Step-6
Open your **Index.cshtml** and set the content as below. We added a form that posts to `GeneratePasswordlessToken` action in the razor page. And it will set the `PasswordlessLoginUrl` field.
```html
@page
@inject IHtmlLocalizer<PasswordlessAuthenticationResource> L
@using Microsoft.AspNetCore.Mvc.Localization
@using PasswordlessAuthentication.Localization
@using PasswordlessAuthentication.Web.Menus
@using Volo.Abp.AspNetCore.Mvc.UI.Layout
@model PasswordlessAuthentication.Web.Pages.IndexModel
@{
ViewBag.PageTitle = "Home";
}
@inject IPageLayout PageLayout;
@{
PageLayout.Content.Title = L["Home"].Value;
PageLayout.Content.BreadCrumb.Add(L["Menu:Home"].Value);
PageLayout.Content.MenuItemName = PasswordlessAuthenticationMenus.Home;
}
<abp-card>
<abp-card-body>
<form asp-page-handler="GeneratePasswordlessToken" method="post">
<abp-button button-type="Dark" type="submit">Generate passwordless token link</abp-button>
@if (Model.PasswordlessLoginUrl != null)
{
<abp-card class="mt-3 p-3">
[@Model.PasswordlessLoginUrl](/en/commercial/latest/how-to/@Model.PasswordlessLoginUrl)
</abp-card>
}
</form>
</abp-card-body>
</abp-card>
```
## Step-7
We implemented token generation infrastructure, now it's time validate the token and let the user in. To do this create a folder named `Controllers` in your ***.Web** project and create a controller, named **PasswordlessController** inside it:
```csharp
using System;
using System.Collections.Generic;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.Identity;
using Volo.Abp.Security.Claims;
using Volo.Abp.Users;
namespace PasswordlessAuthentication.Web.Controllers
{
public class PasswordlessController : AbpController
{
protected IdentityUserManager UserManager { get; }
public PasswordlessController(IdentityUserManager userManager)
{
UserManager = userManager;
}
public virtual async Task<IActionResult> Login(string token, string userId)
{
var user = await UserManager.FindByIdAsync(userId);
var isValid = await UserManager.VerifyUserTokenAsync(user, "PasswordlessLoginProvider", "passwordless-auth", token);
if (!isValid)
{
throw new UnauthorizedAccessException("The token " + token + " is not valid for the user " + userId);
}
await UserManager.UpdateSecurityStampAsync(user);
var roles = await UserManager.GetRolesAsync(user);
var principal = new ClaimsPrincipal(
new ClaimsIdentity(CreateClaims(user, roles), IdentityConstants.ApplicationScheme)
);
await HttpContext.SignInAsync(IdentityConstants.ApplicationScheme, principal);
return Redirect("/");
}
private static IEnumerable<Claim> CreateClaims(IUser user, IEnumerable<string> roles)
{
var claims = new List<Claim>
{
new Claim("sub", user.Id.ToString()),
new Claim(AbpClaimTypes.UserId, user.Id.ToString()),
new Claim(AbpClaimTypes.Email, user.Email),
new Claim(AbpClaimTypes.UserName, user.UserName),
new Claim(AbpClaimTypes.EmailVerified, user.EmailConfirmed.ToString().ToLower()),
};
if (!string.IsNullOrWhiteSpace(user.PhoneNumber))
{
claims.Add(new Claim(AbpClaimTypes.PhoneNumber, user.PhoneNumber));
}
foreach (var role in roles)
{
claims.Add(new Claim(AbpClaimTypes.Role, role));
}
return claims;
}
}
}
```
We created an endpoint for `/Passwordless/Login` that gets the token and the user Id. In this action, we find the user via repository and validate the token via `UserManager.VerifyUserTokenAsync()` method. If it's valid, we create claims of the user then call `HttpContext.SignInAsync` to be able to create an encrypted cookie and add it to the current response. Finally we redirect the page to the root URL.
That's all! We created a passwordless login with 7 steps.
## Source Code
The completed sample is available on [the GitHub repository](https://github.com/abpframework/abp-samples/tree/master/PasswordlessAuthentication).

3
docs/en/Distributed-Event-Bus.md

@ -178,8 +178,11 @@ That's all.
You can inject any service and perform any required logic here. A single event handler class can **subscribe to multiple events** but implementing the `IDistributedEventHandler<TEvent>` interface for each event type.
If you perform **database operations** and use the [repositories](Repositories.md) inside the event handler, you may need to create a [unit of work](Unit-Of-Work.md), because some repository methods need to work inside an **active unit of work**. Make the handle method `virtual` and add a `[UnitOfWork]` attribute for the method, or manually use the `IUnitOfWorkManager` to create a unit of work scope.
> The handler class must be registered to the dependency injection (DI). The sample above uses the `ITransientDependency` to accomplish it. See the [DI document](Dependency-Injection.md) for more options.
## Pre-Defined Events
ABP Framework **automatically publishes** distributed events for **create, update and delete** operations for an [entity](Entities.md) once you configure it.

8
docs/en/Entities.md

@ -234,6 +234,12 @@ ABP Framework does not force you to apply any DDD rule or patterns. However, it
While it's not common (and not suggested) for aggregate roots, it is in fact possible to define composite keys in the same way as defined for the mentioned entities above. Use non-generic `AggregateRoot` base class in that case.
### BasicAggregateRoot Class
`AggregateRoot` class implements the `IHasExtraProperties` and `IHasConcurrencyStamp` interfaces which brings two properties to the derived class. `IHasExtraProperties` makes the entity extensible (see the *Extra Properties* section below) and `IHasConcurrencyStamp` adds a `ConcurrencyStamp` property that is managed by the ABP Framework to implement the [optimistic concurrency](https://docs.microsoft.com/en-us/ef/core/saving/concurrency). In most cases, these are wanted features for aggregate roots.
However, if you don't need these features, you can inherit from the `BasicAggregateRoot<TKey>` (or `BasicAggregateRoot`) for your aggregate root.
## Base Classes & Interfaces for Audit Properties
There are some properties like `CreationTime`, `CreatorId`, `LastModificationTime`... which are very common in all applications. ABP Framework provides some interfaces and base classes to **standardize** these properties and also **sets their values automatically**.
@ -293,7 +299,7 @@ While you can manually implement any of the interfaces defined above, it is sugg
All these base classes also have non-generic versions to take `AuditedEntity` and `FullAuditedAggregateRoot` to support the composite primary keys.
All these base classes also have `...WithUser` pairs, like `FullAuditedAggregateRootWithUser<TUser>` and`FullAuditedAggregateRootWithUser<TKey, TUser>`. This makes possible to add a navigation property to your user entity. However, it is not a good practice to add navigation properties between aggregate roots, so this usage is not suggested (unless you are using an ORM, like EF Core, that well supports this scenario and you really need it - otherwise remember that this approach doesn't work for NoSQL databases like MongoDB where you must truly implement the aggregate pattern).
All these base classes also have `...WithUser` pairs, like `FullAuditedAggregateRootWithUser<TUser>` and `FullAuditedAggregateRootWithUser<TKey, TUser>`. This makes possible to add a navigation property to your user entity. However, it is not a good practice to add navigation properties between aggregate roots, so this usage is not suggested (unless you are using an ORM, like EF Core, that well supports this scenario and you really need it - otherwise remember that this approach doesn't work for NoSQL databases like MongoDB where you must truly implement the aggregate pattern). Also, if you add navigation properties to the AppUser class that comes with the startup template, consider to handle (ignore/map) it on the migration dbcontext (see [the EF Core migration document](Entity-Framework-Core-Migrations.md)).
## Extra Properties

2
docs/en/Local-Event-Bus.md

@ -154,6 +154,8 @@ That's all. `MyHandler` is **automatically discovered** by the ABP Framework and
* **Zero or more handlers** can subscribe to the same event.
* A single event handler class can **subscribe to multiple events** but implementing the `ILocalEventHandler<TEvent>` interface for each event type.
If you perform **database operations** and use the [repositories](Repositories.md) inside the event handler, you may need to create a [unit of work](Unit-Of-Work.md), because some repository methods need to work inside an **active unit of work**. Make the handle method `virtual` and add a `[UnitOfWork]` attribute for the method, or manually use the `IUnitOfWorkManager` to create a unit of work scope.
> The handler class must be registered to the dependency injection (DI). The sample above uses the `ITransientDependency` to accomplish it. See the [DI document](Dependency-Injection.md) for more options.
## Transaction & Exception Behavior

10
docs/en/Tutorials/Part-1.md

@ -45,11 +45,19 @@ This tutorial is organized as the following parts;
### Download the Source Code
This tutorials has multiple versions based on your **UI** and **Database** preferences. We've prepared two combinations of the source code to be downloaded:
This tutorial has multiple versions based on your **UI** and **Database** preferences. We've prepared two combinations of the source code to be downloaded:
* [MVC (Razor Pages) UI with EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Mvc-EfCore)
* [Angular UI with MongoDB](https://github.com/abpframework/abp-samples/tree/master/BookStore-Angular-MongoDb)
{{if UI == "MVC" && DB == "EF"}}
### Video Tutorial
This part is also recorded as a video tutorial and **<a href="https://www.youtube.com/watch?v=cJzyIFfAlp8&list=PLsNclT2aHJcPNaCf7Io3DbMN6yAk_DgWJ&index=1" target="_blank">published on YouTube</a>**.
{{end}}
## Creating the Solution
Before starting to the development, create a new solution named `Acme.BookStore` and run it by following the [getting started tutorial](../Getting-Started.md).

10
docs/en/Tutorials/Part-2.md

@ -45,11 +45,19 @@ This tutorial is organized as the following parts;
### Download the Source Code
This tutorials has multiple versions based on your **UI** and **Database** preferences. We've prepared two combinations of the source code to be downloaded:
This tutorial has multiple versions based on your **UI** and **Database** preferences. We've prepared two combinations of the source code to be downloaded:
* [MVC (Razor Pages) UI with EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Mvc-EfCore)
* [Angular UI with MongoDB](https://github.com/abpframework/abp-samples/tree/master/BookStore-Angular-MongoDb)
{{if UI == "MVC" && DB == "EF"}}
### Video Tutorial
This part is also recorded as a video tutorial and **<a href="https://www.youtube.com/watch?v=UDNlLiPiBiw&list=PLsNclT2aHJcPNaCf7Io3DbMN6yAk_DgWJ&index=2" target="_blank">published on YouTube</a>**.
{{end}}
{{if UI == "MVC"}}
## Dynamic JavaScript Proxies

10
docs/en/Tutorials/Part-3.md

@ -45,11 +45,19 @@ This tutorial is organized as the following parts;
### Download the Source Code
This tutorials has multiple versions based on your **UI** and **Database** preferences. We've prepared two combinations of the source code to be downloaded:
This tutorial has multiple versions based on your **UI** and **Database** preferences. We've prepared two combinations of the source code to be downloaded:
* [MVC (Razor Pages) UI with EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Mvc-EfCore)
* [Angular UI with MongoDB](https://github.com/abpframework/abp-samples/tree/master/BookStore-Angular-MongoDb)
{{if UI == "MVC" && DB == "EF"}}
### Video Tutorial
This part is also recorded as a video tutorial and **<a href="https://www.youtube.com/watch?v=TLShZO8u2VE&list=PLsNclT2aHJcPNaCf7Io3DbMN6yAk_DgWJ&index=3" target="_blank">published on YouTube</a>**.
{{end}}
{{if UI == "MVC"}}
## Creating a New Book

10
docs/en/Tutorials/Part-4.md

@ -45,11 +45,19 @@ This tutorial is organized as the following parts;
### Download the Source Code
This tutorials has multiple versions based on your **UI** and **Database** preferences. We've prepared two combinations of the source code to be downloaded:
This tutorial has multiple versions based on your **UI** and **Database** preferences. We've prepared two combinations of the source code to be downloaded:
* [MVC (Razor Pages) UI with EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Mvc-EfCore)
* [Angular UI with MongoDB](https://github.com/abpframework/abp-samples/tree/master/BookStore-Angular-MongoDb)
{{if UI == "MVC" && DB == "EF"}}
### Video Tutorial
This part is also recorded as a video tutorial and **<a href="https://www.youtube.com/watch?v=aidRB4YFDLM&list=PLsNclT2aHJcPNaCf7Io3DbMN6yAk_DgWJ&index=4" target="_blank">published on YouTube</a>**.
{{end}}
## Test Projects in the Solution
This part covers the **server side** tests. There are several test projects in the solution:

10
docs/en/Tutorials/Part-5.md

@ -45,11 +45,19 @@ This tutorial is organized as the following parts;
### Download the Source Code
This tutorials has multiple versions based on your **UI** and **Database** preferences. We've prepared two combinations of the source code to be downloaded:
This tutorial has multiple versions based on your **UI** and **Database** preferences. We've prepared two combinations of the source code to be downloaded:
* [MVC (Razor Pages) UI with EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Mvc-EfCore)
* [Angular UI with MongoDB](https://github.com/abpframework/abp-samples/tree/master/BookStore-Angular-MongoDb)
{{if UI == "MVC" && DB == "EF"}}
### Video Tutorial
This part is also recorded as a video tutorial and **<a href="https://www.youtube.com/watch?v=1WsfMITN_Jk&list=PLsNclT2aHJcPNaCf7Io3DbMN6yAk_DgWJ&index=5" target="_blank">published on YouTube</a>**.
{{end}}
## Permissions
ABP Framework provides an [authorization system](../Authorization.md) based on the ASP.NET Core's [authorization infrastructure](https://docs.microsoft.com/en-us/aspnet/core/security/authorization/introduction). One major feature added on top of the standard authorization infrastructure is the **permission system** which allows to define permissions and enable/disable per role, user or client.

BIN
docs/en/Tutorials/images/youtube.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

12
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/AbpExceptionFilter.cs

@ -1,4 +1,5 @@
using System;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
@ -25,7 +26,7 @@ namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling
public AbpExceptionFilter(
IExceptionToErrorInfoConverter errorInfoConverter,
IHttpExceptionStatusCodeFinder statusCodeFinder,
IHttpExceptionStatusCodeFinder statusCodeFinder,
IJsonSerializer jsonSerializer)
{
_errorInfoConverter = errorInfoConverter;
@ -54,7 +55,7 @@ namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling
{
return true;
}
if (context.HttpContext.Request.CanAccept(MimeTypes.Application.Json))
{
return true;
@ -81,8 +82,11 @@ namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling
var logLevel = context.Exception.GetLogLevel();
Logger.LogWithLevel(logLevel, $"---------- {nameof(RemoteServiceErrorInfo)} ----------");
Logger.LogWithLevel(logLevel, _jsonSerializer.Serialize(remoteServiceErrorInfo, indented: true));
var remoteServiceErrorInfoBuilder = new StringBuilder();
remoteServiceErrorInfoBuilder.AppendLine($"---------- {nameof(RemoteServiceErrorInfo)} ----------");
remoteServiceErrorInfoBuilder.AppendLine( _jsonSerializer.Serialize(remoteServiceErrorInfo, indented: true));
Logger.LogWithLevel(logLevel, remoteServiceErrorInfoBuilder.ToString());
Logger.LogException(context.Exception, logLevel);
await context.HttpContext

8
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/AbpExceptionPageFilter.cs

@ -1,4 +1,5 @@
using System;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
@ -93,8 +94,11 @@ namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling
var logLevel = context.Exception.GetLogLevel();
Logger.LogWithLevel(logLevel, $"---------- {nameof(RemoteServiceErrorInfo)} ----------");
Logger.LogWithLevel(logLevel, _jsonSerializer.Serialize(remoteServiceErrorInfo, indented: true));
var remoteServiceErrorInfoBuilder = new StringBuilder();
remoteServiceErrorInfoBuilder.AppendLine($"---------- {nameof(RemoteServiceErrorInfo)} ----------");
remoteServiceErrorInfoBuilder.AppendLine( _jsonSerializer.Serialize(remoteServiceErrorInfo, indented: true));
Logger.LogWithLevel(logLevel, remoteServiceErrorInfoBuilder.ToString());
Logger.LogException(context.Exception, logLevel);
await context.HttpContext

20
framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Builder/VirtualFileSystemApplicationBuilderExtensions.cs

@ -1,18 +1,22 @@
using Microsoft.Extensions.DependencyInjection;
using System;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.AspNetCore.VirtualFileSystem;
namespace Microsoft.AspNetCore.Builder
{
public static class VirtualFileSystemApplicationBuilderExtensions
{
public static IApplicationBuilder UseVirtualFiles(this IApplicationBuilder app)
public static IApplicationBuilder UseVirtualFiles(this IApplicationBuilder app, Action<StaticFileOptions> configure = null)
{
return app.UseStaticFiles(
new StaticFileOptions
{
FileProvider = app.ApplicationServices.GetRequiredService<IWebContentFileProvider>()
}
);
var staticFileOptions = new StaticFileOptions
{
FileProvider = app.ApplicationServices.GetRequiredService<IWebContentFileProvider>(),
ContentTypeProvider = app.ApplicationServices.GetRequiredService<AbpFileExtensionContentTypeProvider>()
};
configure?.Invoke(staticFileOptions);
return app.UseStaticFiles(staticFileOptions);
}
}
}

402
framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/VirtualFileSystem/AbpAspNetCoreContentOptions.cs

@ -1,14 +1,401 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Volo.Abp.AspNetCore.VirtualFileSystem
{
public class AbpAspNetCoreContentOptions
{
public Dictionary<string, string> ContentTypeMaps { get; }
public List<string> AllowedExtraWebContentFolders { get; }
public List<string> AllowedExtraWebContentFileExtensions { get; }
public AbpAspNetCoreContentOptions()
{
ContentTypeMaps = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
{".323", "text/h323"},
{".3g2", "video/3gpp2"},
{".3gp2", "video/3gpp2"},
{".3gp", "video/3gpp"},
{".3gpp", "video/3gpp"},
{".aac", "audio/aac"},
{".aaf", "application/octet-stream"},
{".aca", "application/octet-stream"},
{".accdb", "application/msaccess"},
{".accde", "application/msaccess"},
{".accdt", "application/msaccess"},
{".acx", "application/internet-property-stream"},
{".adt", "audio/vnd.dlna.adts"},
{".adts", "audio/vnd.dlna.adts"},
{".afm", "application/octet-stream"},
{".ai", "application/postscript"},
{".aif", "audio/x-aiff"},
{".aifc", "audio/aiff"},
{".aiff", "audio/aiff"},
{".appcache", "text/cache-manifest"},
{".application", "application/x-ms-application"},
{".art", "image/x-jg"},
{".asd", "application/octet-stream"},
{".asf", "video/x-ms-asf"},
{".asi", "application/octet-stream"},
{".asm", "text/plain"},
{".asr", "video/x-ms-asf"},
{".asx", "video/x-ms-asf"},
{".atom", "application/atom+xml"},
{".au", "audio/basic"},
{".avi", "video/x-msvideo"},
{".axs", "application/olescript"},
{".bas", "text/plain"},
{".bcpio", "application/x-bcpio"},
{".bin", "application/octet-stream"},
{".bmp", "image/bmp"},
{".c", "text/plain"},
{".cab", "application/vnd.ms-cab-compressed"},
{".calx", "application/vnd.ms-office.calx"},
{".cat", "application/vnd.ms-pki.seccat"},
{".cdf", "application/x-cdf"},
{".chm", "application/octet-stream"},
{".class", "application/x-java-applet"},
{".clp", "application/x-msclip"},
{".cmx", "image/x-cmx"},
{".cnf", "text/plain"},
{".cod", "image/cis-cod"},
{".cpio", "application/x-cpio"},
{".cpp", "text/plain"},
{".crd", "application/x-mscardfile"},
{".crl", "application/pkix-crl"},
{".crt", "application/x-x509-ca-cert"},
{".csh", "application/x-csh"},
{".css", "text/css"},
{".csv", "application/octet-stream"},
{".cur", "application/octet-stream"},
{".dcr", "application/x-director"},
{".deploy", "application/octet-stream"},
{".der", "application/x-x509-ca-cert"},
{".dib", "image/bmp"},
{".dir", "application/x-director"},
{".disco", "text/xml"},
{".dlm", "text/dlm"},
{".doc", "application/msword"},
{".docm", "application/vnd.ms-word.document.macroEnabled.12"},
{".docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"},
{".dot", "application/msword"},
{".dotm", "application/vnd.ms-word.template.macroEnabled.12"},
{".dotx", "application/vnd.openxmlformats-officedocument.wordprocessingml.template"},
{".dsp", "application/octet-stream"},
{".dtd", "text/xml"},
{".dvi", "application/x-dvi"},
{".dvr-ms", "video/x-ms-dvr"},
{".dwf", "drawing/x-dwf"},
{".dwp", "application/octet-stream"},
{".dxr", "application/x-director"},
{".eml", "message/rfc822"},
{".emz", "application/octet-stream"},
{".eot", "application/vnd.ms-fontobject"},
{".eps", "application/postscript"},
{".etx", "text/x-setext"},
{".evy", "application/envoy"},
{".fdf", "application/vnd.fdf"},
{".fif", "application/fractals"},
{".fla", "application/octet-stream"},
{".flr", "x-world/x-vrml"},
{".flv", "video/x-flv"},
{".gif", "image/gif"},
{".gtar", "application/x-gtar"},
{".gz", "application/x-gzip"},
{".h", "text/plain"},
{".hdf", "application/x-hdf"},
{".hdml", "text/x-hdml"},
{".hhc", "application/x-oleobject"},
{".hhk", "application/octet-stream"},
{".hhp", "application/octet-stream"},
{".hlp", "application/winhlp"},
{".hqx", "application/mac-binhex40"},
{".hta", "application/hta"},
{".htc", "text/x-component"},
{".htm", "text/html"},
{".html", "text/html"},
{".htt", "text/webviewhtml"},
{".hxt", "text/html"},
{".ical", "text/calendar"},
{".icalendar", "text/calendar"},
{".ico", "image/x-icon"},
{".ics", "text/calendar"},
{".ief", "image/ief"},
{".ifb", "text/calendar"},
{".iii", "application/x-iphone"},
{".inf", "application/octet-stream"},
{".ins", "application/x-internet-signup"},
{".isp", "application/x-internet-signup"},
{".IVF", "video/x-ivf"},
{".jar", "application/java-archive"},
{".java", "application/octet-stream"},
{".jck", "application/liquidmotion"},
{".jcz", "application/liquidmotion"},
{".jfif", "image/pjpeg"},
{".jpb", "application/octet-stream"},
{".jpe", "image/jpeg"},
{".jpeg", "image/jpeg"},
{".jpg", "image/jpeg"},
{".js", "application/javascript"},
{".json", "application/json"},
{".jsx", "text/jscript"},
{".latex", "application/x-latex"},
{".lit", "application/x-ms-reader"},
{".lpk", "application/octet-stream"},
{".lsf", "video/x-la-asf"},
{".lsx", "video/x-la-asf"},
{".lzh", "application/octet-stream"},
{".m13", "application/x-msmediaview"},
{".m14", "application/x-msmediaview"},
{".m1v", "video/mpeg"},
{".m2ts", "video/vnd.dlna.mpeg-tts"},
{".m3u", "audio/x-mpegurl"},
{".m4a", "audio/mp4"},
{".m4v", "video/mp4"},
{".man", "application/x-troff-man"},
{".manifest", "application/x-ms-manifest"},
{".map", "text/plain"},
{".markdown", "text/markdown"},
{".md", "text/markdown"},
{".mdb", "application/x-msaccess"},
{".mdp", "application/octet-stream"},
{".me", "application/x-troff-me"},
{".mht", "message/rfc822"},
{".mhtml", "message/rfc822"},
{".mid", "audio/mid"},
{".midi", "audio/mid"},
{".mix", "application/octet-stream"},
{".mmf", "application/x-smaf"},
{".mno", "text/xml"},
{".mny", "application/x-msmoney"},
{".mov", "video/quicktime"},
{".movie", "video/x-sgi-movie"},
{".mp2", "video/mpeg"},
{".mp3", "audio/mpeg"},
{".mp4", "video/mp4"},
{".mp4v", "video/mp4"},
{".mpa", "video/mpeg"},
{".mpe", "video/mpeg"},
{".mpeg", "video/mpeg"},
{".mpg", "video/mpeg"},
{".mpp", "application/vnd.ms-project"},
{".mpv2", "video/mpeg"},
{".ms", "application/x-troff-ms"},
{".msi", "application/octet-stream"},
{".mso", "application/octet-stream"},
{".mvb", "application/x-msmediaview"},
{".mvc", "application/x-miva-compiled"},
{".nc", "application/x-netcdf"},
{".nsc", "video/x-ms-asf"},
{".nws", "message/rfc822"},
{".ocx", "application/octet-stream"},
{".oda", "application/oda"},
{".odc", "text/x-ms-odc"},
{".ods", "application/oleobject"},
{".oga", "audio/ogg"},
{".ogg", "video/ogg"},
{".ogv", "video/ogg"},
{".ogx", "application/ogg"},
{".one", "application/onenote"},
{".onea", "application/onenote"},
{".onetoc", "application/onenote"},
{".onetoc2", "application/onenote"},
{".onetmp", "application/onenote"},
{".onepkg", "application/onenote"},
{".osdx", "application/opensearchdescription+xml"},
{".otf", "font/otf"},
{".p10", "application/pkcs10"},
{".p12", "application/x-pkcs12"},
{".p7b", "application/x-pkcs7-certificates"},
{".p7c", "application/pkcs7-mime"},
{".p7m", "application/pkcs7-mime"},
{".p7r", "application/x-pkcs7-certreqresp"},
{".p7s", "application/pkcs7-signature"},
{".pbm", "image/x-portable-bitmap"},
{".pcx", "application/octet-stream"},
{".pcz", "application/octet-stream"},
{".pdf", "application/pdf"},
{".pfb", "application/octet-stream"},
{".pfm", "application/octet-stream"},
{".pfx", "application/x-pkcs12"},
{".pgm", "image/x-portable-graymap"},
{".pko", "application/vnd.ms-pki.pko"},
{".pma", "application/x-perfmon"},
{".pmc", "application/x-perfmon"},
{".pml", "application/x-perfmon"},
{".pmr", "application/x-perfmon"},
{".pmw", "application/x-perfmon"},
{".png", "image/png"},
{".pnm", "image/x-portable-anymap"},
{".pnz", "image/png"},
{".pot", "application/vnd.ms-powerpoint"},
{".potm", "application/vnd.ms-powerpoint.template.macroEnabled.12"},
{".potx", "application/vnd.openxmlformats-officedocument.presentationml.template"},
{".ppam", "application/vnd.ms-powerpoint.addin.macroEnabled.12"},
{".ppm", "image/x-portable-pixmap"},
{".pps", "application/vnd.ms-powerpoint"},
{".ppsm", "application/vnd.ms-powerpoint.slideshow.macroEnabled.12"},
{".ppsx", "application/vnd.openxmlformats-officedocument.presentationml.slideshow"},
{".ppt", "application/vnd.ms-powerpoint"},
{".pptm", "application/vnd.ms-powerpoint.presentation.macroEnabled.12"},
{".pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"},
{".prf", "application/pics-rules"},
{".prm", "application/octet-stream"},
{".prx", "application/octet-stream"},
{".ps", "application/postscript"},
{".psd", "application/octet-stream"},
{".psm", "application/octet-stream"},
{".psp", "application/octet-stream"},
{".pub", "application/x-mspublisher"},
{".qt", "video/quicktime"},
{".qtl", "application/x-quicktimeplayer"},
{".qxd", "application/octet-stream"},
{".ra", "audio/x-pn-realaudio"},
{".ram", "audio/x-pn-realaudio"},
{".rar", "application/octet-stream"},
{".ras", "image/x-cmu-raster"},
{".rf", "image/vnd.rn-realflash"},
{".rgb", "image/x-rgb"},
{".rm", "application/vnd.rn-realmedia"},
{".rmi", "audio/mid"},
{".roff", "application/x-troff"},
{".rpm", "audio/x-pn-realaudio-plugin"},
{".rtf", "application/rtf"},
{".rtx", "text/richtext"},
{".scd", "application/x-msschedule"},
{".sct", "text/scriptlet"},
{".sea", "application/octet-stream"},
{".setpay", "application/set-payment-initiation"},
{".setreg", "application/set-registration-initiation"},
{".sgml", "text/sgml"},
{".sh", "application/x-sh"},
{".shar", "application/x-shar"},
{".sit", "application/x-stuffit"},
{".sldm", "application/vnd.ms-powerpoint.slide.macroEnabled.12"},
{".sldx", "application/vnd.openxmlformats-officedocument.presentationml.slide"},
{".smd", "audio/x-smd"},
{".smi", "application/octet-stream"},
{".smx", "audio/x-smd"},
{".smz", "audio/x-smd"},
{".snd", "audio/basic"},
{".snp", "application/octet-stream"},
{".spc", "application/x-pkcs7-certificates"},
{".spl", "application/futuresplash"},
{".spx", "audio/ogg"},
{".src", "application/x-wais-source"},
{".ssm", "application/streamingmedia"},
{".sst", "application/vnd.ms-pki.certstore"},
{".stl", "application/vnd.ms-pki.stl"},
{".sv4cpio", "application/x-sv4cpio"},
{".sv4crc", "application/x-sv4crc"},
{".svg", "image/svg+xml"},
{".svgz", "image/svg+xml"},
{".swf", "application/x-shockwave-flash"},
{".t", "application/x-troff"},
{".tar", "application/x-tar"},
{".tcl", "application/x-tcl"},
{".tex", "application/x-tex"},
{".texi", "application/x-texinfo"},
{".texinfo", "application/x-texinfo"},
{".tgz", "application/x-compressed"},
{".thmx", "application/vnd.ms-officetheme"},
{".thn", "application/octet-stream"},
{".tif", "image/tiff"},
{".tiff", "image/tiff"},
{".toc", "application/octet-stream"},
{".tr", "application/x-troff"},
{".trm", "application/x-msterminal"},
{".ts", "video/vnd.dlna.mpeg-tts"},
{".tsv", "text/tab-separated-values"},
{".ttc", "application/x-font-ttf"},
{".ttf", "application/x-font-ttf"},
{".tts", "video/vnd.dlna.mpeg-tts"},
{".txt", "text/plain"},
{".u32", "application/octet-stream"},
{".uls", "text/iuls"},
{".ustar", "application/x-ustar"},
{".vbs", "text/vbscript"},
{".vcf", "text/x-vcard"},
{".vcs", "text/plain"},
{".vdx", "application/vnd.ms-visio.viewer"},
{".vml", "text/xml"},
{".vsd", "application/vnd.visio"},
{".vss", "application/vnd.visio"},
{".vst", "application/vnd.visio"},
{".vsto", "application/x-ms-vsto"},
{".vsw", "application/vnd.visio"},
{".vsx", "application/vnd.visio"},
{".vtx", "application/vnd.visio"},
{".wasm", "application/wasm"},
{".wav", "audio/wav"},
{".wax", "audio/x-ms-wax"},
{".wbmp", "image/vnd.wap.wbmp"},
{".wcm", "application/vnd.ms-works"},
{".wdb", "application/vnd.ms-works"},
{".webm", "video/webm"},
{".webp", "image/webp"},
{".wks", "application/vnd.ms-works"},
{".wm", "video/x-ms-wm"},
{".wma", "audio/x-ms-wma"},
{".wmd", "application/x-ms-wmd"},
{".wmf", "application/x-msmetafile"},
{".wml", "text/vnd.wap.wml"},
{".wmlc", "application/vnd.wap.wmlc"},
{".wmls", "text/vnd.wap.wmlscript"},
{".wmlsc", "application/vnd.wap.wmlscriptc"},
{".wmp", "video/x-ms-wmp"},
{".wmv", "video/x-ms-wmv"},
{".wmx", "video/x-ms-wmx"},
{".wmz", "application/x-ms-wmz"},
{".woff", "application/font-woff"}, // https://www.w3.org/TR/WOFF/#appendix-b
{".woff2", "font/woff2"}, // https://www.w3.org/TR/WOFF2/#IMT
{".wps", "application/vnd.ms-works"},
{".wri", "application/x-mswrite"},
{".wrl", "x-world/x-vrml"},
{".wrz", "x-world/x-vrml"},
{".wsdl", "text/xml"},
{".wtv", "video/x-ms-wtv"},
{".wvx", "video/x-ms-wvx"},
{".x", "application/directx"},
{".xaf", "x-world/x-vrml"},
{".xaml", "application/xaml+xml"},
{".xap", "application/x-silverlight-app"},
{".xbap", "application/x-ms-xbap"},
{".xbm", "image/x-xbitmap"},
{".xdr", "text/plain"},
{".xht", "application/xhtml+xml"},
{".xhtml", "application/xhtml+xml"},
{".xla", "application/vnd.ms-excel"},
{".xlam", "application/vnd.ms-excel.addin.macroEnabled.12"},
{".xlc", "application/vnd.ms-excel"},
{".xlm", "application/vnd.ms-excel"},
{".xls", "application/vnd.ms-excel"},
{".xlsb", "application/vnd.ms-excel.sheet.binary.macroEnabled.12"},
{".xlsm", "application/vnd.ms-excel.sheet.macroEnabled.12"},
{".xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"},
{".xlt", "application/vnd.ms-excel"},
{".xltm", "application/vnd.ms-excel.template.macroEnabled.12"},
{".xltx", "application/vnd.openxmlformats-officedocument.spreadsheetml.template"},
{".xlw", "application/vnd.ms-excel"},
{".xml", "text/xml"},
{".xof", "x-world/x-vrml"},
{".xpm", "image/x-xpixmap"},
{".xps", "application/vnd.ms-xpsdocument"},
{".xsd", "text/xml"},
{".xsf", "text/xml"},
{".xsl", "text/xml"},
{".xslt", "text/xml"},
{".xsn", "application/octet-stream"},
{".xtp", "application/octet-stream"},
{".xwd", "image/x-xwindowdump"},
{".z", "application/x-compress"},
{".zip", "application/x-zip-compressed"},
};
AllowedExtraWebContentFolders = new List<string>
{
"/Pages",
@ -16,18 +403,7 @@ namespace Volo.Abp.AspNetCore.VirtualFileSystem
"/Themes"
};
AllowedExtraWebContentFileExtensions = new List<string>
{
".js",
".css",
".png",
".jpg",
".jpeg",
".woff",
".woff2",
".tff",
".otf"
};
AllowedExtraWebContentFileExtensions = ContentTypeMaps.Select(x => x.Key).ToList();
}
}
}

39
framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/VirtualFileSystem/AbpFileExtensionContentTypeProvider.cs

@ -0,0 +1,39 @@
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.AspNetCore.VirtualFileSystem
{
public class AbpFileExtensionContentTypeProvider : IContentTypeProvider, ITransientDependency
{
protected AbpAspNetCoreContentOptions Options { get; }
public AbpFileExtensionContentTypeProvider(IOptions<AbpAspNetCoreContentOptions> abpAspNetCoreContentOptions)
{
Options = abpAspNetCoreContentOptions.Value;
}
public bool TryGetContentType(string subpath, out string contentType)
{
var extension = GetExtension(subpath);
if (extension == null)
{
contentType = null;
return false;
}
return Options.ContentTypeMaps.TryGetValue(extension, out contentType);
}
protected virtual string GetExtension(string path)
{
if (string.IsNullOrWhiteSpace(path))
{
return null;
}
var index = path.LastIndexOf('.');
return index < 0 ? null : path.Substring(index);
}
}
}

2
framework/src/Volo.Abp.AutoMapper/Volo.Abp.AutoMapper.csproj

@ -21,7 +21,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="AutoMapper" Version="9.0.0" />
<PackageReference Include="AutoMapper" Version="10.0.0" />
</ItemGroup>
</Project>

4
framework/src/Volo.Abp.BlobStoring.Minio/Volo/Abp/BlobStoring/Minio/MinioBlobProvider.cs

@ -72,11 +72,11 @@ namespace Volo.Abp.BlobStoring.Minio
}
var memoryStream = new MemoryStream();
await client.GetObjectAsync(containerName, blobName, stream =>
await client.GetObjectAsync(containerName, blobName, async stream =>
{
if (stream != null)
{
stream.CopyTo(memoryStream);
await stream.CopyToAsync(memoryStream);
}
else
{

9
framework/src/Volo.Abp.Core/Microsoft/Extensions/Logging/AbpLoggerExtensions.cs

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Text;
using Volo.Abp.ExceptionHandling;
using Volo.Abp.Logging;
@ -87,12 +88,14 @@ namespace Microsoft.Extensions.Logging
return;
}
logger.LogWithLevel(logLevel, "---------- Exception Data ----------");
var exceptionData = new StringBuilder();
exceptionData.AppendLine("---------- Exception Data ----------");
foreach (var key in exception.Data.Keys)
{
logger.LogWithLevel(logLevel, $"{key} = {exception.Data[key]}");
exceptionData.AppendLine($"{key} = {exception.Data[key]}");
}
logger.LogWithLevel(logLevel, exceptionData.ToString());
}
private static void LogSelfLogging(ILogger logger, Exception exception)

76
framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/AggregateRoot.cs

@ -9,9 +9,7 @@ using Volo.Abp.ObjectExtending;
namespace Volo.Abp.Domain.Entities
{
[Serializable]
public abstract class AggregateRoot : Entity,
IAggregateRoot,
IGeneratesDomainEvents,
public abstract class AggregateRoot : BasicAggregateRoot,
IHasExtraProperties,
IHasConcurrencyStamp
{
@ -20,9 +18,6 @@ namespace Volo.Abp.Domain.Entities
[DisableAuditing]
public virtual string ConcurrencyStamp { get; set; }
private readonly ICollection<object> _localEvents = new Collection<object>();
private readonly ICollection<object> _distributedEvents = new Collection<object>();
protected AggregateRoot()
{
ConcurrencyStamp = Guid.NewGuid().ToString("N");
@ -30,36 +25,6 @@ namespace Volo.Abp.Domain.Entities
this.SetDefaultsForExtraProperties();
}
protected virtual void AddLocalEvent(object eventData)
{
_localEvents.Add(eventData);
}
protected virtual void AddDistributedEvent(object eventData)
{
_distributedEvents.Add(eventData);
}
public virtual IEnumerable<object> GetLocalEvents()
{
return _localEvents;
}
public virtual IEnumerable<object> GetDistributedEvents()
{
return _distributedEvents;
}
public virtual void ClearLocalEvents()
{
_localEvents.Clear();
}
public virtual void ClearDistributedEvents()
{
_distributedEvents.Clear();
}
public virtual IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
return ExtensibleObjectValidator.GetValidationErrors(
@ -70,9 +35,7 @@ namespace Volo.Abp.Domain.Entities
}
[Serializable]
public abstract class AggregateRoot<TKey> : Entity<TKey>,
IAggregateRoot<TKey>,
IGeneratesDomainEvents,
public abstract class AggregateRoot<TKey> : BasicAggregateRoot<TKey>,
IHasExtraProperties,
IHasConcurrencyStamp
{
@ -81,9 +44,6 @@ namespace Volo.Abp.Domain.Entities
[DisableAuditing]
public virtual string ConcurrencyStamp { get; set; }
private readonly ICollection<object> _localEvents = new Collection<object>();
private readonly ICollection<object> _distributedEvents = new Collection<object>();
protected AggregateRoot()
{
ConcurrencyStamp = Guid.NewGuid().ToString("N");
@ -99,36 +59,6 @@ namespace Volo.Abp.Domain.Entities
this.SetDefaultsForExtraProperties();
}
protected virtual void AddLocalEvent(object eventData)
{
_localEvents.Add(eventData);
}
protected virtual void AddDistributedEvent(object eventData)
{
_distributedEvents.Add(eventData);
}
public virtual IEnumerable<object> GetLocalEvents()
{
return _localEvents;
}
public virtual IEnumerable<object> GetDistributedEvents()
{
return _distributedEvents;
}
public virtual void ClearLocalEvents()
{
_localEvents.Clear();
}
public virtual void ClearDistributedEvents()
{
_distributedEvents.Clear();
}
public virtual IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
return ExtensibleObjectValidator.GetValidationErrors(
@ -137,4 +67,4 @@ namespace Volo.Abp.Domain.Entities
);
}
}
}
}

95
framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/BasicAggregateRoot.cs

@ -0,0 +1,95 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
namespace Volo.Abp.Domain.Entities
{
[Serializable]
public abstract class BasicAggregateRoot : Entity,
IAggregateRoot,
IGeneratesDomainEvents
{
private readonly ICollection<object> _distributedEvents = new Collection<object>();
private readonly ICollection<object> _localEvents = new Collection<object>();
public virtual IEnumerable<object> GetLocalEvents()
{
return _localEvents;
}
public virtual IEnumerable<object> GetDistributedEvents()
{
return _distributedEvents;
}
public virtual void ClearLocalEvents()
{
_localEvents.Clear();
}
public virtual void ClearDistributedEvents()
{
_distributedEvents.Clear();
}
protected virtual void AddLocalEvent(object eventData)
{
_localEvents.Add(eventData);
}
protected virtual void AddDistributedEvent(object eventData)
{
_distributedEvents.Add(eventData);
}
}
[Serializable]
public abstract class BasicAggregateRoot<TKey> : Entity<TKey>,
IAggregateRoot<TKey>,
IGeneratesDomainEvents
{
private readonly ICollection<object> _distributedEvents = new Collection<object>();
private readonly ICollection<object> _localEvents = new Collection<object>();
protected BasicAggregateRoot()
{
}
protected BasicAggregateRoot(TKey id)
: base(id)
{
}
public virtual IEnumerable<object> GetLocalEvents()
{
return _localEvents;
}
public virtual IEnumerable<object> GetDistributedEvents()
{
return _distributedEvents;
}
public virtual void ClearLocalEvents()
{
_localEvents.Clear();
}
public virtual void ClearDistributedEvents()
{
_distributedEvents.Clear();
}
protected virtual void AddLocalEvent(object eventData)
{
_localEvents.Add(eventData);
}
protected virtual void AddDistributedEvent(object eventData)
{
_distributedEvents.Add(eventData);
}
}
}

8
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs

@ -182,6 +182,14 @@ namespace Volo.Abp.EntityFrameworkCore
}
}
/// <summary>
/// This method will call the DbContext <see cref="SaveChangesAsync(bool, CancellationToken)"/> method directly of EF Core, which doesn't apply concepts of abp.
/// </summary>
public virtual Task<int> SaveChangesOnDbContextAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default)
{
return base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
}
public virtual void Initialize(AbpEfCoreDbContextInitializationContext initializationContext)
{
if (initializationContext.UnitOfWork.Options.Timeout.HasValue &&

7
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/IEfCoreDbContext.cs

@ -24,6 +24,11 @@ namespace Volo.Abp.EntityFrameworkCore
Task<int> SaveChangesAsync(CancellationToken cancellationToken = default);
/// <summary>
/// This method will call the DbContext <see cref="SaveChangesAsync(bool, CancellationToken)"/> method directly of EF Core, which doesn't apply concepts of abp.
/// </summary>
Task<int> SaveChangesOnDbContextAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default);
DbSet<T> Set<T>()
where T: class;
@ -50,7 +55,7 @@ namespace Volo.Abp.EntityFrameworkCore
void AttachRange([NotNull] IEnumerable<object> entities);
void AttachRange([NotNull] params object[] entities);
EntityEntry<TEntity> Entry<TEntity>([NotNull] TEntity entity) where TEntity : class;
EntityEntry Entry([NotNull] object entity);

16
framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/UrlBuilder.cs

@ -1,4 +1,5 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
@ -100,7 +101,20 @@ namespace Volo.Abp.Http.Client.DynamicProxying
{
urlBuilder.Append(isFirstParam ? "?" : "&");
urlBuilder.Append(name + "=" + System.Net.WebUtility.UrlEncode(ConvertValueToString(value)));
if (value.GetType().IsArray || (value.GetType().IsGenericType && value is IEnumerable))
{
var index = 0;
foreach (var item in (IEnumerable) value)
{
urlBuilder.Append(name + $"[{index++}]=" + System.Net.WebUtility.UrlEncode(ConvertValueToString(item)) + "&");
}
//remove & at the end of the urlBuilder.
urlBuilder.Remove(urlBuilder.Length - 1, 1);
}
else
{
urlBuilder.Append(name + "=" + System.Net.WebUtility.UrlEncode(ConvertValueToString(value)));
}
}
private static string ConvertValueToString([NotNull] object value)

14
framework/src/Volo.Abp.Ldap/Volo.Abp.Ldap.csproj

@ -15,11 +15,19 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Novell.Directory.Ldap.NETStandard" Version="2.3.8" />
<PackageReference Include="Novell.Directory.Ldap.NETStandard" Version="3.2.0" />
</ItemGroup>
<ItemGroup>
<Content Remove="Volo\Abp\Ldap\Localization\*.json" />
<EmbeddedResource Include="Volo\Abp\Ldap\Localization\*.json" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Volo.Abp.Autofac\Volo.Abp.Autofac.csproj" />
<ProjectReference Include="..\Volo.Abp.Core\Volo.Abp.Core.csproj" />
<ProjectReference Include="..\Volo.Abp.Localization\Volo.Abp.Localization.csproj" />
<ProjectReference Include="..\Volo.Abp.Settings\Volo.Abp.Settings.csproj" />
<ProjectReference Include="..\Volo.Abp.VirtualFileSystem\Volo.Abp.VirtualFileSystem.csproj" />
</ItemGroup>
</Project>

47
framework/src/Volo.Abp.Ldap/Volo/Abp/Ldap/AbpAbpLdapOptionsFactory.cs

@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using Volo.Abp.Options;
using Volo.Abp.Settings;
using Volo.Abp.Threading;
namespace Volo.Abp.Ldap
{
public class AbpAbpLdapOptionsFactory : AbpOptionsFactory<AbpLdapOptions>
{
protected ISettingProvider SettingProvider { get; }
public AbpAbpLdapOptionsFactory(
IEnumerable<IConfigureOptions<AbpLdapOptions>> setups,
IEnumerable<IPostConfigureOptions<AbpLdapOptions>> postConfigures,
ISettingProvider settingProvider)
: base(setups, postConfigures)
{
SettingProvider = settingProvider;
}
public override AbpLdapOptions Create(string name)
{
var options = base.Create(name);
AsyncHelper.RunSync(() => OverrideOptionsAsync(options));
return options;
}
protected virtual async Task OverrideOptionsAsync(AbpLdapOptions options)
{
options.ServerHost = await GetSettingOrDefaultValue(LdapSettingNames.ServerHost, options.ServerHost);
options.ServerPort = await SettingProvider.GetAsync(LdapSettingNames.ServerPort, options.ServerPort);
options.UserName = await GetSettingOrDefaultValue(LdapSettingNames.UserName, options.UserName);
options.Password = await GetSettingOrDefaultValue(LdapSettingNames.Password, options.Password);
}
protected virtual async Task<string> GetSettingOrDefaultValue(string name, string defaultValue)
{
var value = await SettingProvider.GetOrNullAsync(name);
return value.IsNullOrWhiteSpace() ? defaultValue : value;
}
}
}

38
framework/src/Volo.Abp.Ldap/Volo/Abp/Ldap/AbpLdapModule.cs

@ -1,18 +1,44 @@
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Autofac;
using System;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;
using Volo.Abp.Ldap.Localization;
using Volo.Abp.Localization;
using Volo.Abp.Modularity;
using Volo.Abp.Settings;
using Volo.Abp.VirtualFileSystem;
namespace Volo.Abp.Ldap
{
[DependsOn(
typeof(AbpAutofacModule)
)]
typeof(AbpSettingsModule),
typeof(AbpVirtualFileSystemModule),
typeof(AbpLocalizationModule))]
public class AbpLdapModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.Replace(ServiceDescriptor.Transient<IOptionsFactory<AbpLdapOptions>, AbpAbpLdapOptionsFactory>());
context.Services.Replace(ServiceDescriptor.Scoped<IOptions<AbpLdapOptions>, OptionsManager<AbpLdapOptions>>());
var configuration = context.Services.GetConfiguration();
Configure<AbpLdapOptions>(configuration.GetSection("LDAP"));
var ldapConfiguration = configuration["Ldap"];
if (!ldapConfiguration.IsNullOrEmpty())
{
Configure<AbpLdapOptions>(configuration.GetSection("Ldap"));
}
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<AbpLdapModule>();
});
Configure<AbpLocalizationOptions>(options =>
{
options.Resources
.Add<LdapResource>("en")
.AddVirtualJson("/Volo/Abp/Ldap/Localization");
});
}
}
}
}

17
framework/src/Volo.Abp.Ldap/Volo/Abp/Ldap/AbpLdapOptions.cs

@ -6,19 +6,8 @@
public int ServerPort { get; set; }
public bool UseSsl { get; set; }
public string UserName { get; set; }
public string SearchBase { get; set; }
public string DomainName { get; set; }
public string DomainDistinguishedName { get; set; }
public LdapCredentials Credentials { get; set; }
public AbpLdapOptions()
{
Credentials = new LdapCredentials();
}
public string Password { get; set; }
}
}
}

11
framework/src/Volo.Abp.Ldap/Volo/Abp/Ldap/Exceptions/OrganizationNotExistException.cs

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

67
framework/src/Volo.Abp.Ldap/Volo/Abp/Ldap/ILdapManager.cs

@ -1,72 +1,17 @@
using System.Collections.Generic;
using Volo.Abp.Ldap.Modeling;
namespace Volo.Abp.Ldap
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
///
/// Authenticate with default username/password
/// </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);
bool Authenticate();
/// <summary>
/// Authenticate
/// Authenticate with specified username/password
/// </summary>
/// <param name="userDomainName">E.g administrator@yourdomain.com.cn </param>
/// <param name="password"></param>
/// <returns></returns>
bool Authenticate(string userDomainName, string password);
bool Authenticate(string username, string password);
}
}
}

9
framework/src/Volo.Abp.Ldap/Volo/Abp/Ldap/LdapCredentials.cs

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

34
framework/src/Volo.Abp.Ldap/Volo/Abp/Ldap/LdapHelps.cs

@ -1,34 +0,0 @@
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})";
}
}
}

328
framework/src/Volo.Abp.Ldap/Volo/Abp/Ldap/LdapManager.cs

@ -1,343 +1,61 @@
using System;
using Microsoft.Extensions.Options;
using Novell.Directory.Ldap;
using System.Collections.Generic;
using System.Text;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Volo.Abp.DependencyInjection;
using Volo.Abp.ExceptionHandling;
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 AbpLdapOptions _ldapOptions;
private readonly IHybridServiceScopeFactory _hybridServiceScopeFactory;
public ILogger<LdapManager> Logger { get; set; }
protected AbpLdapOptions LdapOptions { get; }
private readonly string[] _attributes =
public LdapManager(IOptions<AbpLdapOptions> ldapSettingsOptions)
{
"objectCategory", "objectClass", "cn", "name", "distinguishedName",
"ou",
"sAMAccountName", "userPrincipalName", "telephoneNumber", "mail"
};
LdapOptions = ldapSettingsOptions.Value;
public LdapManager(IOptions<AbpLdapOptions> ldapSettingsOptions, IHybridServiceScopeFactory hybridServiceScopeFactory)
{
_hybridServiceScopeFactory = hybridServiceScopeFactory;
_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);
}
Logger = NullLogger<LdapManager>.Instance;
}
public void AddSubOrganization(string organizationName, string parentDistinguishedName)
public virtual bool Authenticate()
{
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);
return Authenticate(LdapOptions.UserName, LdapOptions.Password);
}
/// <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)
public bool Authenticate(string username, string password)
{
try
{
using (GetConnection(userDomainName, password))
{
return true;
}
var conn = CreateLdapConnection();
AuthenticateLdapConnection(conn, username, password);
return true;
}
catch (Exception ex)
{
using (var scope = _hybridServiceScopeFactory.CreateScope())
{
scope.ServiceProvider
.GetRequiredService<IExceptionNotifier>()
.NotifyAsync(ex);
}
Logger.LogException(ex);
return false;
}
}
#endregion
private ILdapConnection GetConnection(string bindUserName = null, string bindUserPassword = null)
protected virtual ILdapConnection CreateLdapConnection()
{
// 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);
}
var ldapConnection = new LdapConnection();
ConfigureLdapConnection(ldapConnection);
ldapConnection.Connect(LdapOptions.ServerHost, LdapOptions.ServerPort);
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);
}
protected virtual void ConfigureLdapConnection(ILdapConnection connection)
{
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
protected virtual void AuthenticateLdapConnection(ILdapConnection connection, string username, string password)
{
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;
connection.Bind(username, password);
}
}
}
}

13
framework/src/Volo.Abp.Ldap/Volo/Abp/Ldap/LdapSettingNames.cs

@ -0,0 +1,13 @@
namespace Volo.Abp.Ldap
{
public static class LdapSettingNames
{
public const string ServerHost = "Abp.Ldap.ServerHost";
public const string ServerPort = "Abp.Ldap.ServerPort";
public const string UserName = "Abp.Ldap.UserName";
public const string Password = "Abp.Ldap.Password";
}
}

44
framework/src/Volo.Abp.Ldap/Volo/Abp/Ldap/LdapSettingProvider.cs

@ -0,0 +1,44 @@
using Volo.Abp.Ldap.Localization;
using Volo.Abp.Localization;
using Volo.Abp.Settings;
namespace Volo.Abp.Ldap
{
public class LdapSettingProvider : SettingDefinitionProvider
{
public override void Define(ISettingDefinitionContext context)
{
context.Add(
new SettingDefinition(
LdapSettingNames.ServerHost,
"",
L("DisplayName:Abp.Ldap.ServerHost"),
L("Description:Abp.Ldap.ServerHost")),
new SettingDefinition(
LdapSettingNames.ServerPort,
"389",
L("DisplayName:Abp.Ldap.ServerPort"),
L("Description:Abp.Ldap.ServerPort")),
new SettingDefinition(
LdapSettingNames.UserName,
"",
L("DisplayName:Abp.Ldap.UserName"),
L("Description:Abp.Ldap.UserName")),
new SettingDefinition(
LdapSettingNames.Password,
"",
L("DisplayName:Abp.Ldap.Password"),
L("Description:Abp.Ldap.Password"),
isEncrypted: true)
);
}
private static LocalizableString L(string name)
{
return LocalizableString.Create<LdapResource>(name);
}
}
}

10
framework/src/Volo.Abp.Ldap/Volo/Abp/Ldap/Localization/LdapResource.cs

@ -0,0 +1,10 @@
using Volo.Abp.Localization;
namespace Volo.Abp.Ldap.Localization
{
[LocalizationResourceName("AbpLdap")]
public class LdapResource
{
}
}

16
framework/src/Volo.Abp.Ldap/Volo/Abp/Ldap/Localization/en.json

@ -0,0 +1,16 @@
{
"culture": "en",
"texts": {
"DisplayName:Abp.Ldap.ServerHost": "Server host",
"Description:Abp.Ldap.ServerHost": "Server host",
"DisplayName:Abp.Ldap.ServerPort": "Server port",
"Description:Abp.Ldap.ServerPort": "Server port",
"DisplayName:Abp.Ldap.UserName": "Username",
"Description:Abp.Ldap.UserName": "Username",
"DisplayName:Abp.Ldap.Password": "Password",
"Description:Abp.Ldap.Password": "Password"
}
}

13
framework/src/Volo.Abp.Ldap/Volo/Abp/Ldap/Localization/tr.json

@ -0,0 +1,13 @@
{
"culture": "tr",
"texts": {
"DisplayName:Abp.Ldap.ServerHost": "Sunucu Ana Bilgisayarı",
"Description:Abp.Ldap.ServerHost": "Sunucu Ana Bilgisayarı",
"DisplayName:Abp.Ldap.ServerPort": "Sunucu portu",
"Description:Abp.Ldap.ServerPort": "Sunucu portu",
"DisplayName:Abp.Ldap.UserName": "Kullanıcı adı",
"Description:Abp.Ldap.UserName": "Kullanıcı adı",
"DisplayName:Abp.Ldap.Password": "parola",
"Description:Abp.Ldap.Password": "parola"
}
}

13
framework/src/Volo.Abp.Ldap/Volo/Abp/Ldap/Localization/zh-Hans.json

@ -0,0 +1,13 @@
{
"culture": "zh-Hans",
"texts": {
"DisplayName:Abp.Ldap.ServerHost": "服务器主机",
"Description:Abp.Ldap.ServerHost": "服务器主机",
"DisplayName:Abp.Ldap.ServerPort": "服务器端口",
"Description:Abp.Ldap.ServerPort": "服务器端口",
"DisplayName:Abp.Ldap.UserName": "用户名",
"Description:Abp.Ldap.UserName": "用户名",
"DisplayName:Abp.Ldap.Password": "密码",
"Description:Abp.Ldap.Password": "密码"
}
}

13
framework/src/Volo.Abp.Ldap/Volo/Abp/Ldap/Localization/zh-Hant.json

@ -0,0 +1,13 @@
{
"culture": "zh-Hant",
"texts": {
"DisplayName:Abp.Ldap.ServerHost": "服務器主機",
"Description:Abp.Ldap.ServerHost": "服務器主機",
"DisplayName:Abp.Ldap.ServerPort": "服務器端口",
"Description:Abp.Ldap.ServerPort": "服務器端口",
"DisplayName:Abp.Ldap.UserName": "用戶名",
"Description:Abp.Ldap.UserName": "用戶名",
"DisplayName:Abp.Ldap.Password": "密碼",
"Description:Abp.Ldap.Password": "密碼"
}
}

11
framework/src/Volo.Abp.Ldap/Volo/Abp/Ldap/Modeling/ILdapEntry.cs

@ -1,11 +0,0 @@
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; }
}
}

7
framework/src/Volo.Abp.Ldap/Volo/Abp/Ldap/Modeling/ILdapOrganization.cs

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

11
framework/src/Volo.Abp.Ldap/Volo/Abp/Ldap/Modeling/ILdapUser.cs

@ -1,11 +0,0 @@
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; }
}
}

24
framework/src/Volo.Abp.Ldap/Volo/Abp/Ldap/Modeling/LdapEntryBase.cs

@ -1,24 +0,0 @@
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;
}
}
}

17
framework/src/Volo.Abp.Ldap/Volo/Abp/Ldap/Modeling/LdapOrganization.cs

@ -1,17 +0,0 @@
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;
}
}
}

25
framework/src/Volo.Abp.Ldap/Volo/Abp/Ldap/Modeling/LdapUser.cs

@ -1,25 +0,0 @@
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;
}
}
}

171
framework/src/Volo.Abp.Ldap/readme.md

@ -1,171 +0,0 @@
# 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)

14
framework/src/Volo.Abp.Validation.Abstractions/Volo/Abp/Validation/AbpValidationException.cs

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using Microsoft.Extensions.Logging;
using Volo.Abp.Logging;
@ -12,9 +13,9 @@ namespace Volo.Abp.Validation
/// This exception type is used to throws validation exceptions.
/// </summary>
[Serializable]
public class AbpValidationException : AbpException,
IHasLogLevel,
IHasValidationErrors,
public class AbpValidationException : AbpException,
IHasLogLevel,
IHasValidationErrors,
IExceptionWithSelfLogging
{
/// <summary>
@ -99,7 +100,8 @@ namespace Volo.Abp.Validation
return;
}
logger.LogWithLevel(LogLevel, "There are " + ValidationErrors.Count + " validation errors:");
var validationErrors = new StringBuilder();
validationErrors.AppendLine("There are " + ValidationErrors.Count + " validation errors:");
foreach (var validationResult in ValidationErrors)
{
var memberNames = "";
@ -108,8 +110,10 @@ namespace Volo.Abp.Validation
memberNames = " (" + string.Join(", ", validationResult.MemberNames) + ")";
}
logger.LogWithLevel(LogLevel, validationResult.ErrorMessage + memberNames);
validationErrors.AppendLine(validationResult.ErrorMessage + memberNames);
}
logger.LogWithLevel(LogLevel, validationErrors.ToString());
}
}
}

24
framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/PersonAppServiceClientProxy_Tests.cs

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
@ -40,12 +41,29 @@ namespace Volo.Abp.Http.DynamicProxying
[Fact]
public async Task GetList()
{
var people = await _peopleAppService.GetListAsync(new PagedAndSortedResultRequestDto())
;
var people = await _peopleAppService.GetListAsync(new PagedAndSortedResultRequestDto());
people.TotalCount.ShouldBeGreaterThan(0);
people.Items.Count.ShouldBe((int) people.TotalCount);
}
[Fact]
public async Task GetParams()
{
var id1 = Guid.NewGuid();
var id2 = Guid.NewGuid();
var @params = await _peopleAppService.GetParams(new List<Guid>
{
id1,
id2
}, new[] {"name1", "name2"});
@params.ShouldContain(id1.ToString("N"));
@params.ShouldContain(id2.ToString("N"));
@params.ShouldContain("name1");
@params.ShouldContain("name2");
}
[Fact]
public async Task Delete()
{
@ -151,4 +169,4 @@ namespace Volo.Abp.Http.DynamicProxying
result.Inner1.Inner2.Value3.ShouldBe("value three");
}
}
}
}

4
framework/test/Volo.Abp.Ldap.Tests/Volo.Abp.Ldap.Tests.csproj

@ -9,10 +9,8 @@
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Volo.Abp.Ldap\Volo.Abp.Ldap.csproj" />
<ProjectReference Include="..\..\src\Volo.Abp.Autofac\Volo.Abp.Autofac.csproj" />
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" />
</ItemGroup>
</Project>

24
framework/test/Volo.Abp.Ldap.Tests/Volo/Abp/Ldap/AbpLdapTestModule.cs

@ -0,0 +1,24 @@
using Volo.Abp.Autofac;
using Volo.Abp.Modularity;
namespace Volo.Abp.Ldap
{
[DependsOn(
typeof(AbpAutofacModule),
typeof(AbpLdapModule),
typeof(AbpTestBaseModule)
)]
public class AbpLdapTestModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpLdapOptions>(options =>
{
options.ServerHost = "192.168.0.3";
options.ServerPort = 389;
options.UserName = "cn=admin,dc=abp,dc=io";
options.Password = "123qwe";
});
}
}
}

71
framework/test/Volo.Abp.Ldap.Tests/Volo/Abp/Ldap/Authenticate_Tests.cs

@ -1,71 +0,0 @@
using System;
using Shouldly;
using Volo.Abp.Modularity;
using Volo.Abp.Testing;
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<AbpLdapOptions>(settings =>
{
settings.ServerHost = "192.168.101.54";
settings.ServerPort = 636;
settings.UseSsl = true;
});
}
}
}
}

84
framework/test/Volo.Abp.Ldap.Tests/Volo/Abp/Ldap/LdapHelps_Tests.cs

@ -1,84 +0,0 @@
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))");
}
}
}

231
framework/test/Volo.Abp.Ldap.Tests/Volo/Abp/Ldap/LdapManager_Tests.cs

@ -1,246 +1,31 @@
using System;
using Shouldly;
using Volo.Abp.Modularity;
using Volo.Abp.Testing;
using Xunit;
namespace Volo.Abp.Ldap
{
public class LdapManager_Tests : AbpIntegratedTest<LdapManager_Tests.TestModule>
public class LdapManager_Tests : AbpIntegratedTest<AbpLdapTestModule>
{
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()
protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options)
{
var result = _ldapManager.GetOrganization("NonExistentNameA");
result.ShouldBeNull();
options.UseAutofac();
}
[Fact(Skip = "need environment AD ")]
[Fact(Skip = "Required Ldap environment")]
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);
_ldapManager.Authenticate().ShouldBe(true);
_ldapManager.Authenticate("cn=abp,dc=abp,dc=io", "123qwe").ShouldBe(true);
_ldapManager.Authenticate("NoExists", "123qwe").ShouldBe(false);
}
[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<AbpLdapOptions>(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";
});
}
}
}
}
}

21
framework/test/Volo.Abp.Ldap.Tests/Volo/Abp/Ldap/LdapOptions_Tests.cs

@ -0,0 +1,21 @@
using Microsoft.Extensions.Options;
using Shouldly;
using Volo.Abp.Testing;
using Xunit;
namespace Volo.Abp.Ldap
{
public class LdapOptions_Tests : AbpIntegratedTest<AbpLdapTestModule>
{
protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options)
{
options.UseAutofac();
}
[Fact]
public void Should_Resolve_AbpAbpLdapOptionsFactory()
{
GetRequiredService<IOptionsFactory<AbpLdapOptions>>().ShouldBeOfType(typeof(AbpAbpLdapOptionsFactory));
}
}
}

27
framework/test/Volo.Abp.Ldap.Tests/Volo/Abp/Ldap/LdapTestData.cs

@ -1,27 +0,0 @@
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Ldap
{
public class LdapTestData : ISingletonDependency
{
public string AdministratorName { get; } = "Administrator";
public string AdministratorPassword { get; } = "yH.20190528";
public string AdministratorDistinguishedName { get; } = "CN=Administrator,CN=Users,DC=yourdomain,DC=com,DC=cn";
public string AdministratorDomainName { get; } = "Administrator@yourdomain.com.cn";
public string DomainControllersName = "Domain Controllers";
public string DomainControllersDistinguishedName = "OU=Domain Controllers,DC=yourdomain,DC=com,DC=cn";
public string RootDistinguishedName { get; } = "DC=yourdomain,DC=com,DC=cn";
public string Organization001Name { get; } = "Test_A";
public string Test001Name { get; } = "test001";
public string Test001Password { get; } = "yH.20190528";
public string Test001Email { get; } = "test001@yourdomain.com.cn";
public string Test002Name { get; } = "test002";
public string Test002Password { get; } = "yH.20190528";
public string Test002WrongPassword { get; } = "yH.20190529";
}
}

5
framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/IPeopleAppService.cs

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
@ -10,6 +11,8 @@ namespace Volo.Abp.TestApp.Application
{
Task<ListResultDto<PhoneDto>> GetPhones(Guid id, GetPersonPhonesFilter filter);
Task<List<string>> GetParams(IEnumerable<Guid> ids, string[] names);
Task<PhoneDto> AddPhone(Guid id, PhoneDto phoneDto);
Task RemovePhone(Guid id, string number);
@ -18,4 +21,4 @@ namespace Volo.Abp.TestApp.Application
Task<GetWithComplexTypeInput> GetWithComplexType(GetWithComplexTypeInput input);
}
}
}

11
framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/PeopleAppService.cs

@ -18,18 +18,25 @@ namespace Volo.Abp.TestApp.Application
{
}
public async Task<ListResultDto<PhoneDto>> GetPhones(Guid id, GetPersonPhonesFilter filter)
{
var phones = (await GetEntityByIdAsync(id)).Phones
.WhereIf(filter.Type.HasValue, p => p.Type == filter.Type)
.ToList();
return new ListResultDto<PhoneDto>(
ObjectMapper.Map<List<Phone>, List<PhoneDto>>(phones)
);
}
public Task<List<string>> GetParams(IEnumerable<Guid> ids, string[] names)
{
var @params = ids.Select(id => id.ToString("N")).ToList();
@params.AddRange(names);
return Task.FromResult(@params.ToList());
}
public async Task<PhoneDto> AddPhone(Guid id, PhoneDto phoneDto)
{
var person = await GetEntityByIdAsync(id);

32
modules/account/src/Volo.Abp.Account.Web/Pages/Account/Register.cshtml.cs

@ -76,25 +76,33 @@ namespace Volo.Abp.Account.Web.Pages.Account
public virtual async Task<IActionResult> OnPostAsync()
{
await CheckSelfRegistrationAsync();
if (IsExternalLogin)
try
{
var externalLoginInfo = await SignInManager.GetExternalLoginInfoAsync();
if (externalLoginInfo == null)
await CheckSelfRegistrationAsync();
if (IsExternalLogin)
{
var externalLoginInfo = await SignInManager.GetExternalLoginInfoAsync();
if (externalLoginInfo == null)
{
Logger.LogWarning("External login info is not available");
return RedirectToPage("./Login");
}
await RegisterExternalUserAsync(externalLoginInfo, Input.EmailAddress);
}
else
{
Logger.LogWarning("External login info is not available");
return RedirectToPage("./Login");
await RegisterLocalUserAsync();
}
await RegisterExternalUserAsync(externalLoginInfo, Input.EmailAddress);
return Redirect(ReturnUrl ?? "~/"); //TODO: How to ensure safety? IdentityServer requires it however it should be checked somehow!
}
else
catch (BusinessException e)
{
await RegisterLocalUserAsync();
Alerts.Danger(e.Message);
return Page();
}
return Redirect(ReturnUrl ?? "~/"); //TODO: How to ensure safety? IdentityServer requires it however it should be checked somehow!
}
protected virtual async Task RegisterLocalUserAsync()

6
modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp/Migrations/20200624023633_BackgroundJobs_Initial.Designer.cs → modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp/Migrations/20200810022513_Initial.Designer.cs

@ -11,15 +11,15 @@ using Volo.Abp.EntityFrameworkCore;
namespace Volo.Abp.BackgroundJobs.DemoApp.Migrations
{
[DbContext(typeof(DemoAppDbContext))]
[Migration("20200624023633_BackgroundJobs_Initial")]
partial class BackgroundJobs_Initial
[Migration("20200810022513_Initial")]
partial class Initial
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer)
.HasAnnotation("ProductVersion", "3.1.5")
.HasAnnotation("ProductVersion", "3.1.6")
.HasAnnotation("Relational:MaxIdentifierLength", 128)
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

2
modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp/Migrations/20200624023633_BackgroundJobs_Initial.cs → modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp/Migrations/20200810022513_Initial.cs

@ -3,7 +3,7 @@ using Microsoft.EntityFrameworkCore.Migrations;
namespace Volo.Abp.BackgroundJobs.DemoApp.Migrations
{
public partial class BackgroundJobs_Initial : Migration
public partial class Initial : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{

2
modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp/Migrations/DemoAppDbContextModelSnapshot.cs

@ -17,7 +17,7 @@ namespace Volo.Abp.BackgroundJobs.DemoApp.Migrations
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer)
.HasAnnotation("ProductVersion", "3.1.5")
.HasAnnotation("ProductVersion", "3.1.6")
.HasAnnotation("Relational:MaxIdentifierLength", 128)
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

4
modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/Migrations/20200624023744_Initial.Designer.cs → modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/Migrations/20200810022447_Initial.Designer.cs

@ -11,7 +11,7 @@ using Volo.Abp.EntityFrameworkCore;
namespace BlobStoring.Database.Host.ConsoleApp.ConsoleApp.Migrations
{
[DbContext(typeof(BlobStoringHostDbContext))]
[Migration("20200624023744_Initial")]
[Migration("20200810022447_Initial")]
partial class Initial
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
@ -19,7 +19,7 @@ namespace BlobStoring.Database.Host.ConsoleApp.ConsoleApp.Migrations
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer)
.HasAnnotation("ProductVersion", "3.1.5")
.HasAnnotation("ProductVersion", "3.1.6")
.HasAnnotation("Relational:MaxIdentifierLength", 128)
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

0
modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/Migrations/20200624023744_Initial.cs → modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/Migrations/20200810022447_Initial.cs

2
modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/Migrations/BlobStoringHostDbContextModelSnapshot.cs

@ -17,7 +17,7 @@ namespace BlobStoring.Database.Host.ConsoleApp.ConsoleApp.Migrations
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer)
.HasAnnotation("ProductVersion", "3.1.5")
.HasAnnotation("ProductVersion", "3.1.6")
.HasAnnotation("Relational:MaxIdentifierLength", 128)
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

1178
modules/blogging/app/Volo.BloggingTestApp.EntityFrameworkCore/Migrations/20200810022522_IdentityUserIsExternal.Designer.cs

File diff suppressed because it is too large

23
modules/blogging/app/Volo.BloggingTestApp.EntityFrameworkCore/Migrations/20200810022522_IdentityUserIsExternal.cs

@ -0,0 +1,23 @@
using Microsoft.EntityFrameworkCore.Migrations;
namespace Volo.BloggingTestApp.EntityFrameworkCore.Migrations
{
public partial class IdentityUserIsExternal : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<bool>(
name: "IsExternal",
table: "AbpUsers",
nullable: false,
defaultValue: false);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "IsExternal",
table: "AbpUsers");
}
}
}

8
modules/blogging/app/Volo.BloggingTestApp.EntityFrameworkCore/Migrations/BloggingTestAppDbContextModelSnapshot.cs

@ -17,7 +17,7 @@ namespace Volo.BloggingTestApp.EntityFrameworkCore.Migrations
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer)
.HasAnnotation("ProductVersion", "3.1.5")
.HasAnnotation("ProductVersion", "3.1.6")
.HasAnnotation("Relational:MaxIdentifierLength", 128)
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
@ -276,6 +276,12 @@ namespace Volo.BloggingTestApp.EntityFrameworkCore.Migrations
.HasColumnType("bit")
.HasDefaultValue(false);
b.Property<bool>("IsExternal")
.ValueGeneratedOnAdd()
.HasColumnName("IsExternal")
.HasColumnType("bit")
.HasDefaultValue(false);
b.Property<DateTime?>("LastModificationTime")
.HasColumnName("LastModificationTime")
.HasColumnType("datetime2");

6
modules/cms-kit/host/Volo.CmsKit.IdentityServer/Migrations/20200624023331_Initial.Designer.cs → modules/cms-kit/host/Volo.CmsKit.IdentityServer/Migrations/20200810022553_Initial.Designer.cs

@ -5,13 +5,13 @@ using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Volo.CmsKit.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;
using Volo.CmsKit.EntityFrameworkCore;
namespace Volo.CmsKit.Migrations
{
[DbContext(typeof(IdentityServerHostMigrationsDbContext))]
[Migration("20200624023331_Initial")]
[Migration("20200810022553_Initial")]
partial class Initial
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
@ -19,7 +19,7 @@ namespace Volo.CmsKit.Migrations
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer)
.HasAnnotation("ProductVersion", "3.1.5")
.HasAnnotation("ProductVersion", "3.1.6")
.HasAnnotation("Relational:MaxIdentifierLength", 128)
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

0
modules/cms-kit/host/Volo.CmsKit.IdentityServer/Migrations/20200624023331_Initial.cs → modules/cms-kit/host/Volo.CmsKit.IdentityServer/Migrations/20200810022553_Initial.cs

4
modules/cms-kit/host/Volo.CmsKit.IdentityServer/Migrations/IdentityServerHostMigrationsDbContextModelSnapshot.cs

@ -4,8 +4,8 @@ using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Volo.CmsKit.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;
using Volo.CmsKit.EntityFrameworkCore;
namespace Volo.CmsKit.Migrations
{
@ -17,7 +17,7 @@ namespace Volo.CmsKit.Migrations
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer)
.HasAnnotation("ProductVersion", "3.1.5")
.HasAnnotation("ProductVersion", "3.1.6")
.HasAnnotation("Relational:MaxIdentifierLength", 128)
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

4
modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20200729125121_Initial.Designer.cs → modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20200810022644_Initial.Designer.cs

@ -11,7 +11,7 @@ using Volo.CmsKit.EntityFrameworkCore;
namespace Volo.CmsKit.Migrations
{
[DbContext(typeof(UnifiedDbContext))]
[Migration("20200729125121_Initial")]
[Migration("20200810022644_Initial")]
partial class Initial
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
@ -19,7 +19,7 @@ namespace Volo.CmsKit.Migrations
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer)
.HasAnnotation("ProductVersion", "3.1.5")
.HasAnnotation("ProductVersion", "3.1.6")
.HasAnnotation("Relational:MaxIdentifierLength", 128)
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

0
modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20200729125121_Initial.cs → modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/20200810022644_Initial.cs

2
modules/cms-kit/host/Volo.CmsKit.Web.Unified/Migrations/UnifiedDbContextModelSnapshot.cs

@ -17,7 +17,7 @@ namespace Volo.CmsKit.Migrations
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer)
.HasAnnotation("ProductVersion", "3.1.5")
.HasAnnotation("ProductVersion", "3.1.6")
.HasAnnotation("Relational:MaxIdentifierLength", 128)
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

10
modules/docs/app/VoloDocs.EntityFrameworkCore/Migrations/20200722033325_Initial.Designer.cs → modules/docs/app/VoloDocs.EntityFrameworkCore/Migrations/20200810022424_Initial.Designer.cs

@ -11,7 +11,7 @@ using VoloDocs.EntityFrameworkCore;
namespace VoloDocs.EntityFrameworkCore.Migrations
{
[DbContext(typeof(VoloDocsDbContext))]
[Migration("20200722033325_Initial")]
[Migration("20200810022424_Initial")]
partial class Initial
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
@ -19,7 +19,7 @@ namespace VoloDocs.EntityFrameworkCore.Migrations
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer)
.HasAnnotation("ProductVersion", "3.1.5")
.HasAnnotation("ProductVersion", "3.1.6")
.HasAnnotation("Relational:MaxIdentifierLength", 128)
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
@ -278,6 +278,12 @@ namespace VoloDocs.EntityFrameworkCore.Migrations
.HasColumnType("bit")
.HasDefaultValue(false);
b.Property<bool>("IsExternal")
.ValueGeneratedOnAdd()
.HasColumnName("IsExternal")
.HasColumnType("bit")
.HasDefaultValue(false);
b.Property<DateTime?>("LastModificationTime")
.HasColumnName("LastModificationTime")
.HasColumnType("datetime2");

1
modules/docs/app/VoloDocs.EntityFrameworkCore/Migrations/20200722033325_Initial.cs → modules/docs/app/VoloDocs.EntityFrameworkCore/Migrations/20200810022424_Initial.cs

@ -155,6 +155,7 @@ namespace VoloDocs.EntityFrameworkCore.Migrations
EmailConfirmed = table.Column<bool>(nullable: false, defaultValue: false),
PasswordHash = table.Column<string>(maxLength: 256, nullable: true),
SecurityStamp = table.Column<string>(maxLength: 256, nullable: false),
IsExternal = table.Column<bool>(nullable: false, defaultValue: false),
PhoneNumber = table.Column<string>(maxLength: 16, nullable: true),
PhoneNumberConfirmed = table.Column<bool>(nullable: false, defaultValue: false),
TwoFactorEnabled = table.Column<bool>(nullable: false, defaultValue: false),

8
modules/docs/app/VoloDocs.EntityFrameworkCore/Migrations/VoloDocsDbContextModelSnapshot.cs

@ -17,7 +17,7 @@ namespace VoloDocs.EntityFrameworkCore.Migrations
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer)
.HasAnnotation("ProductVersion", "3.1.5")
.HasAnnotation("ProductVersion", "3.1.6")
.HasAnnotation("Relational:MaxIdentifierLength", 128)
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
@ -276,6 +276,12 @@ namespace VoloDocs.EntityFrameworkCore.Migrations
.HasColumnType("bit")
.HasDefaultValue(false);
b.Property<bool>("IsExternal")
.ValueGeneratedOnAdd()
.HasColumnName("IsExternal")
.HasColumnType("bit")
.HasDefaultValue(false);
b.Property<DateTime?>("LastModificationTime")
.HasColumnName("LastModificationTime")
.HasColumnType("datetime2");

5
modules/docs/src/Volo.Docs.Web/Areas/Documents/TagHelpers/TreeTagHelper.cs

@ -1,4 +1,4 @@
using System;
using System;
using System.Linq;
using System.Text;
using Microsoft.AspNetCore.Razor.TagHelpers;
@ -132,8 +132,7 @@ namespace Volo.Docs.Areas.Documents.TagHelpers
var newBadge = "<span class='badge badge-primary ml-2' title=\"" + _localizer["NewExplanation"] + "\">" + _localizer["New"] + "</span>";
badge += newBadge;
}
if (node.LastSignificantUpdateTime != null && node.LastSignificantUpdateTime + TimeSpan.FromDays(14) > DateTime.Now)
else if (node.LastSignificantUpdateTime != null && node.LastSignificantUpdateTime + TimeSpan.FromDays(14) > DateTime.Now)
{
var updBadge = "<span class='badge badge-light ml-2' title=\"" + _localizer["UpdatedExplanation"] + "\">" + _localizer["Upd"] + "</span>";
badge += updBadge;

2
modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/en.json

@ -25,7 +25,7 @@
"DisplayName:Email": "Email address",
"DisplayName:PhoneNumber": "Phone number",
"DisplayName:TwoFactorEnabled": "Two factor verification",
"DisplayName:LockoutEnabled": "Locking account after failed login attempts",
"DisplayName:LockoutEnabled": "Lock account after failed login attempts",
"NewRole": "New role",
"RoleName": "Role name",
"CreationTime": "Creation time",

11
modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Devices/DeviceFlowStore.cs

@ -5,20 +5,19 @@ using IdentityServer4.Models;
using IdentityServer4.Stores;
using IdentityServer4.Stores.Serialization;
using JetBrains.Annotations;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Guids;
namespace Volo.Abp.IdentityServer.Devices
{
public class DeviceFlowStore : IDeviceFlowStore, ITransientDependency
public class DeviceFlowStore : IDeviceFlowStore
{
protected IDeviceFlowCodesRepository DeviceFlowCodesRepository { get; }
protected IGuidGenerator GuidGenerator { get; }
protected IPersistentGrantSerializer PersistentGrantSerializer { get; }
public DeviceFlowStore(
IDeviceFlowCodesRepository deviceFlowCodesRepository,
IGuidGenerator guidGenerator,
IDeviceFlowCodesRepository deviceFlowCodesRepository,
IGuidGenerator guidGenerator,
IPersistentGrantSerializer persistentGrantSerializer)
{
DeviceFlowCodesRepository = deviceFlowCodesRepository;
@ -46,7 +45,7 @@ namespace Volo.Abp.IdentityServer.Devices
}
);
}
public virtual async Task<DeviceCode> FindByUserCodeAsync(string userCode)
{
Check.NotNull(userCode, nameof(userCode));
@ -70,7 +69,7 @@ namespace Volo.Abp.IdentityServer.Devices
var deviceCodes = await DeviceFlowCodesRepository
.FindByDeviceCodeAsync(deviceCode)
;
if (deviceCodes == null)
{
return null;

50
modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/Clients/ClientRepository.cs

@ -53,6 +53,56 @@ namespace Volo.Abp.IdentityServer.Clients
return await DbSet.AnyAsync(c => c.Id != expectedId && c.ClientId == clientId, cancellationToken: cancellationToken);
}
public override async Task DeleteAsync(Guid id, bool autoSave = false, CancellationToken cancellationToken = default)
{
foreach (var clientGrantType in DbContext.Set<ClientGrantType>().Where(x => x.ClientId == id))
{
DbContext.Set<ClientGrantType>().Remove(clientGrantType);
}
foreach (var clientRedirectUri in DbContext.Set<ClientRedirectUri>().Where(x => x.ClientId == id))
{
DbContext.Set<ClientRedirectUri>().Remove(clientRedirectUri);
}
foreach (var clientPostLogoutRedirectUri in DbContext.Set<ClientPostLogoutRedirectUri>().Where(x => x.ClientId == id))
{
DbContext.Set<ClientPostLogoutRedirectUri>().Remove(clientPostLogoutRedirectUri);
}
foreach (var clientScope in DbContext.Set<ClientScope>().Where(x => x.ClientId == id))
{
DbContext.Set<ClientScope>().Remove(clientScope);
}
foreach (var clientSecret in DbContext.Set<ClientSecret>().Where(x => x.ClientId == id))
{
DbContext.Set<ClientSecret>().Remove(clientSecret);
}
foreach (var clientClaim in DbContext.Set<ClientClaim>().Where(x => x.ClientId == id))
{
DbContext.Set<ClientClaim>().Remove(clientClaim);
}
foreach (var clientIdPRestriction in DbContext.Set<ClientIdPRestriction>().Where(x => x.ClientId == id))
{
DbContext.Set<ClientIdPRestriction>().Remove(clientIdPRestriction);
}
foreach (var clientCorsOrigin in DbContext.Set<ClientCorsOrigin>().Where(x => x.ClientId == id))
{
DbContext.Set<ClientCorsOrigin>().Remove(clientCorsOrigin);
}
foreach (var clientProperty in DbContext.Set<ClientProperty>().Where(x => x.ClientId == id))
{
DbContext.Set<ClientProperty>().Remove(clientProperty);
}
await base.DeleteAsync(id, autoSave, cancellationToken);
}
public override IQueryable<Client> WithDetails()
{
return GetQueryable().IncludeDetails();

4
templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations/Migrations/20200806191520_Initial.Designer.cs → templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations/Migrations/20200810022238_Initial.Designer.cs

@ -11,7 +11,7 @@ using Volo.Abp.EntityFrameworkCore;
namespace MyCompanyName.MyProjectName.Migrations
{
[DbContext(typeof(MyProjectNameMigrationsDbContext))]
[Migration("20200806191520_Initial")]
[Migration("20200810022238_Initial")]
partial class Initial
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
@ -19,7 +19,7 @@ namespace MyCompanyName.MyProjectName.Migrations
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer)
.HasAnnotation("ProductVersion", "3.1.5")
.HasAnnotation("ProductVersion", "3.1.6")
.HasAnnotation("Relational:MaxIdentifierLength", 128)
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

0
templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations/Migrations/20200806191520_Initial.cs → templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations/Migrations/20200810022238_Initial.cs

2
templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations/Migrations/MyProjectNameMigrationsDbContextModelSnapshot.cs

@ -17,7 +17,7 @@ namespace MyCompanyName.MyProjectName.Migrations
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer)
.HasAnnotation("ProductVersion", "3.1.5")
.HasAnnotation("ProductVersion", "3.1.6")
.HasAnnotation("Relational:MaxIdentifierLength", 128)
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

9
templates/console/src/MyCompanyName.MyProjectName/MyCompanyName.MyProjectName.csproj

@ -13,10 +13,17 @@
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="3.1.6" />
<PackageReference Include="Serilog.Extensions.Logging" Version="3.0.1" />
<PackageReference Include="Serilog.Extensions.Hosting" Version="3.1.0" />
<PackageReference Include="Serilog.Sinks.Async" Version="1.4.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
<PackageReference Include="Serilog.Sinks.File" Version="4.1.0" />
</ItemGroup>
<ItemGroup>
<Content Include="appsettings.json">
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>

41
templates/console/src/MyCompanyName.MyProjectName/MyProjectNameHostedService.cs

@ -1,34 +1,41 @@
using System.Threading;
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Serilog;
using Volo.Abp;
namespace MyCompanyName.MyProjectName
{
public class MyProjectNameHostedService : IHostedService
{
public Task StartAsync(CancellationToken cancellationToken)
private readonly IAbpApplicationWithExternalServiceProvider _application;
private readonly IServiceProvider _serviceProvider;
private readonly HelloWorldService _helloWorldService;
public MyProjectNameHostedService(
IAbpApplicationWithExternalServiceProvider application,
IServiceProvider serviceProvider,
HelloWorldService helloWorldService)
{
using (var application = AbpApplicationFactory.Create<MyProjectNameModule>(options =>
{
options.UseAutofac(); //Autofac integration
options.Services.AddLogging(c => c.AddSerilog());
}))
{
application.Initialize();
_application = application;
_serviceProvider = serviceProvider;
_helloWorldService = helloWorldService;
}
//Resolve a service and use it
var helloWorldService = application.ServiceProvider.GetService<HelloWorldService>();
helloWorldService.SayHello();
public Task StartAsync(CancellationToken cancellationToken)
{
_application.Initialize(_serviceProvider);
application.Shutdown();
}
_helloWorldService.SayHello();
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
public Task StopAsync(CancellationToken cancellationToken)
{
_application.Shutdown();
return Task.CompletedTask;
}
}
}

10
templates/console/src/MyCompanyName.MyProjectName/MyProjectNameModule.cs

@ -1,4 +1,6 @@
using Volo.Abp.Autofac;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Volo.Abp.Autofac;
using Volo.Abp.Modularity;
namespace MyCompanyName.MyProjectName
@ -9,6 +11,12 @@ namespace MyCompanyName.MyProjectName
)]
public class MyProjectNameModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
var configuration = context.Services.GetConfiguration();
var hostEnvironment = context.Services.GetSingletonInstance<IHostEnvironment>();
context.Services.AddHostedService<MyProjectNameHostedService>();
}
}
}

9
templates/console/src/MyCompanyName.MyProjectName/Program.cs

@ -1,5 +1,6 @@
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Serilog;
@ -43,9 +44,15 @@ namespace MyCompanyName.MyProjectName
internal static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseAutofac()
.UseSerilog()
.ConfigureAppConfiguration((context, config) =>
{
//setup your additional configuration sources
})
.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<MyProjectNameHostedService>();
services.AddApplication<MyProjectNameModule>();
});
}
}

3
templates/console/src/MyCompanyName.MyProjectName/appsettings.json

@ -0,0 +1,3 @@
{
}

10
templates/module/aspnet-core/host/MyCompanyName.MyProjectName.IdentityServer/Migrations/20200710065039_Initial.Designer.cs → templates/module/aspnet-core/host/MyCompanyName.MyProjectName.IdentityServer/Migrations/20200810022322_Initial.Designer.cs

@ -11,7 +11,7 @@ using Volo.Abp.EntityFrameworkCore;
namespace MyCompanyName.MyProjectName.Migrations
{
[DbContext(typeof(IdentityServerHostMigrationsDbContext))]
[Migration("20200710065039_Initial")]
[Migration("20200810022322_Initial")]
partial class Initial
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
@ -19,7 +19,7 @@ namespace MyCompanyName.MyProjectName.Migrations
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer)
.HasAnnotation("ProductVersion", "3.1.5")
.HasAnnotation("ProductVersion", "3.1.6")
.HasAnnotation("Relational:MaxIdentifierLength", 128)
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
@ -530,6 +530,12 @@ namespace MyCompanyName.MyProjectName.Migrations
.HasColumnType("bit")
.HasDefaultValue(false);
b.Property<bool>("IsExternal")
.ValueGeneratedOnAdd()
.HasColumnName("IsExternal")
.HasColumnType("bit")
.HasDefaultValue(false);
b.Property<DateTime?>("LastModificationTime")
.HasColumnName("LastModificationTime")
.HasColumnType("datetime2");

1
templates/module/aspnet-core/host/MyCompanyName.MyProjectName.IdentityServer/Migrations/20200710065039_Initial.cs → templates/module/aspnet-core/host/MyCompanyName.MyProjectName.IdentityServer/Migrations/20200810022322_Initial.cs

@ -208,6 +208,7 @@ namespace MyCompanyName.MyProjectName.Migrations
EmailConfirmed = table.Column<bool>(nullable: false, defaultValue: false),
PasswordHash = table.Column<string>(maxLength: 256, nullable: true),
SecurityStamp = table.Column<string>(maxLength: 256, nullable: false),
IsExternal = table.Column<bool>(nullable: false, defaultValue: false),
PhoneNumber = table.Column<string>(maxLength: 16, nullable: true),
PhoneNumberConfirmed = table.Column<bool>(nullable: false, defaultValue: false),
TwoFactorEnabled = table.Column<bool>(nullable: false, defaultValue: false),

8
templates/module/aspnet-core/host/MyCompanyName.MyProjectName.IdentityServer/Migrations/IdentityServerHostMigrationsDbContextModelSnapshot.cs

@ -17,7 +17,7 @@ namespace MyCompanyName.MyProjectName.Migrations
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer)
.HasAnnotation("ProductVersion", "3.1.5")
.HasAnnotation("ProductVersion", "3.1.6")
.HasAnnotation("Relational:MaxIdentifierLength", 128)
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
@ -528,6 +528,12 @@ namespace MyCompanyName.MyProjectName.Migrations
.HasColumnType("bit")
.HasDefaultValue(false);
b.Property<bool>("IsExternal")
.ValueGeneratedOnAdd()
.HasColumnName("IsExternal")
.HasColumnType("bit")
.HasDefaultValue(false);
b.Property<DateTime?>("LastModificationTime")
.HasColumnName("LastModificationTime")
.HasColumnType("datetime2");

10
templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/Migrations/20200710065052_Initial.Designer.cs → templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/Migrations/20200810022405_Initial.Designer.cs

@ -11,7 +11,7 @@ using Volo.Abp.EntityFrameworkCore;
namespace MyCompanyName.MyProjectName.Migrations
{
[DbContext(typeof(UnifiedDbContext))]
[Migration("20200710065052_Initial")]
[Migration("20200810022405_Initial")]
partial class Initial
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
@ -19,7 +19,7 @@ namespace MyCompanyName.MyProjectName.Migrations
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer)
.HasAnnotation("ProductVersion", "3.1.5")
.HasAnnotation("ProductVersion", "3.1.6")
.HasAnnotation("Relational:MaxIdentifierLength", 128)
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
@ -530,6 +530,12 @@ namespace MyCompanyName.MyProjectName.Migrations
.HasColumnType("bit")
.HasDefaultValue(false);
b.Property<bool>("IsExternal")
.ValueGeneratedOnAdd()
.HasColumnName("IsExternal")
.HasColumnType("bit")
.HasDefaultValue(false);
b.Property<DateTime?>("LastModificationTime")
.HasColumnName("LastModificationTime")
.HasColumnType("datetime2");

1
templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/Migrations/20200710065052_Initial.cs → templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/Migrations/20200810022405_Initial.cs

@ -208,6 +208,7 @@ namespace MyCompanyName.MyProjectName.Migrations
EmailConfirmed = table.Column<bool>(nullable: false, defaultValue: false),
PasswordHash = table.Column<string>(maxLength: 256, nullable: true),
SecurityStamp = table.Column<string>(maxLength: 256, nullable: false),
IsExternal = table.Column<bool>(nullable: false, defaultValue: false),
PhoneNumber = table.Column<string>(maxLength: 16, nullable: true),
PhoneNumberConfirmed = table.Column<bool>(nullable: false, defaultValue: false),
TwoFactorEnabled = table.Column<bool>(nullable: false, defaultValue: false),

8
templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/Migrations/UnifiedDbContextModelSnapshot.cs

@ -17,7 +17,7 @@ namespace MyCompanyName.MyProjectName.Migrations
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer)
.HasAnnotation("ProductVersion", "3.1.5")
.HasAnnotation("ProductVersion", "3.1.6")
.HasAnnotation("Relational:MaxIdentifierLength", 128)
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
@ -528,6 +528,12 @@ namespace MyCompanyName.MyProjectName.Migrations
.HasColumnType("bit")
.HasDefaultValue(false);
b.Property<bool>("IsExternal")
.ValueGeneratedOnAdd()
.HasColumnName("IsExternal")
.HasColumnType("bit")
.HasDefaultValue(false);
b.Property<DateTime?>("LastModificationTime")
.HasColumnName("LastModificationTime")
.HasColumnType("datetime2");

2
templates/module/aspnet-core/src/MyCompanyName.MyProjectName.MongoDB/MyCompanyName.MyProjectName.MongoDB.csproj

@ -3,7 +3,7 @@
<Import Project="..\..\common.props" />
<PropertyGroup>
<TargetFramework>netstandart2.0</TargetFramework>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace>MyCompanyName.MyProjectName</RootNamespace>
</PropertyGroup>

Loading…
Cancel
Save