Browse Source

Merge branch 'dev' into issue/4928

pull/4975/head
Yunus Emre Kalkan 6 years ago
parent
commit
d737b93721
  1. 25
      docs/en/Blob-Storing-Aliyun.md
  2. 32
      docs/en/Blob-Storing-Aws.md
  3. 11
      docs/en/Blob-Storing-Azure.md
  4. 15
      docs/en/Blob-Storing-Minio.md
  5. 2
      docs/en/Community-Articles/2020-04-19-Customize-the-SignIn-Manager/POST.md
  6. 25
      docs/zh-Hans/Blob-Storing-Aliyun.md
  7. 31
      docs/zh-Hans/Blob-Storing-Aws.md
  8. 11
      docs/zh-Hans/Blob-Storing-Azure.md
  9. 15
      docs/zh-Hans/Blob-Storing-Minio.md
  10. 99
      modules/account/src/Volo.Abp.Account.Web/Pages/Account/Register.cshtml.cs
  11. 9
      modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Comments/CommentPublicAppService.cs
  12. 9
      modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/FullSearch/Elastic/ElasticDocumentFullSearch.cs
  13. 7
      modules/identity/Volo.Abp.Identity.sln
  14. 5
      modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/AbpIdentityAspNetCoreModule.cs
  15. 9
      modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/AbpIdentityAspNetCoreOptions.cs
  16. 66
      modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/AbpSignInManager.cs
  17. 156
      modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/ExternalLoginProviderBase.cs
  18. 18
      modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/ExternalLoginProviderDictionary.cs
  19. 25
      modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/ExternalLoginProviderInfo.cs
  20. 36
      modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/ExternalLoginUserInfo.cs
  21. 32
      modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/IExternalLoginProvider.cs
  22. 5
      modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityUserConsts.cs
  23. 42
      modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUser.cs
  24. 22
      modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserStore.cs
  25. 3
      modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/IdentityDbContextModelBuilderExtensions.cs
  26. 25
      modules/identity/test/Volo.Abp.Identity.AspNetCore.Tests/Volo.Abp.Identity.AspNetCore.Tests.csproj
  27. 24
      modules/identity/test/Volo.Abp.Identity.AspNetCore.Tests/Volo/Abp/Identity/AspNetCore/AbpIdentityAspNetCoreTestBase.cs
  28. 46
      modules/identity/test/Volo.Abp.Identity.AspNetCore.Tests/Volo/Abp/Identity/AspNetCore/AbpIdentityAspNetCoreTestModule.cs
  29. 19
      modules/identity/test/Volo.Abp.Identity.AspNetCore.Tests/Volo/Abp/Identity/AspNetCore/AbpIdentityAspNetCoreTestStartup.cs
  30. 39
      modules/identity/test/Volo.Abp.Identity.AspNetCore.Tests/Volo/Abp/Identity/AspNetCore/AbpSignInManager_Tests.cs
  31. 57
      modules/identity/test/Volo.Abp.Identity.AspNetCore.Tests/Volo/Abp/Identity/AspNetCore/ExternalLoginProvider_Tests.cs
  32. 55
      modules/identity/test/Volo.Abp.Identity.AspNetCore.Tests/Volo/Abp/Identity/AspNetCore/FakeExternalLoginProvider.cs
  33. 31
      modules/identity/test/Volo.Abp.Identity.AspNetCore.Tests/Volo/Abp/Identity/AspNetCore/SignInTestController.cs
  34. 15
      modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/AbpIdentityExtendedTestBase.cs
  35. 2
      modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/IdentityClaimTypeManager_Tests.cs
  36. 8
      samples/MicroserviceDemo/applications/AuthServer.Host/package.json
  37. 2629
      samples/MicroserviceDemo/applications/AuthServer.Host/yarn.lock
  38. 8
      samples/MicroserviceDemo/applications/BackendAdminApp.Host/package.json
  39. 2629
      samples/MicroserviceDemo/applications/BackendAdminApp.Host/yarn.lock
  40. 9
      samples/MicroserviceDemo/applications/PublicWebSite.Host/package.json
  41. 2965
      samples/MicroserviceDemo/applications/PublicWebSite.Host/yarn.lock
  42. 8
      templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations/Migrations/20200806191520_Initial.Designer.cs
  43. 1
      templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations/Migrations/20200806191520_Initial.cs
  44. 6
      templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations/Migrations/MyProjectNameMigrationsDbContextModelSnapshot.cs

25
docs/en/Blob-Storing-Aliyun.md

@ -23,18 +23,21 @@ Configuration is done in the `ConfigureServices` method of your [module](Module-
````csharp
Configure<AbpBlobStoringOptions>(options =>
{
options.Containerscontainer.UseAliyun(aliyun =>
options.Containers.ConfigureDefault(container =>
{
aliyun.AccessKeyId = "your aliyun access key id";
aliyun.AccessKeySecret = "your aliyun access key secret";
aliyun.Endpoint = "your oss endpoint";
aliyun.RegionId = "your sts region id";
aliyun.RoleArn = "the arn of ram role";
aliyun.RoleSessionName = "the name of the certificate";
aliyun.Policy = "policy";
aliyun.DurationSeconds = "expiration date";
aliyun.ContainerName = "your aliyun container name";
aliyun.CreateContainerIfNotExists = false;
container.UseAliyun(aliyun =>
{
aliyun.AccessKeyId = "your aliyun access key id";
aliyun.AccessKeySecret = "your aliyun access key secret";
aliyun.Endpoint = "your oss endpoint";
aliyun.RegionId = "your sts region id";
aliyun.RoleArn = "the arn of ram role";
aliyun.RoleSessionName = "the name of the certificate";
aliyun.Policy = "policy";
aliyun.DurationSeconds = "expiration date";
aliyun.ContainerName = "your aliyun container name";
aliyun.CreateContainerIfNotExists = false;
});
});
});
````

32
docs/en/Blob-Storing-Aws.md

@ -23,23 +23,27 @@ Configuration is done in the `ConfigureServices` method of your [module](Module-
````csharp
Configure<AbpBlobStoringOptions>(options =>
{
options.Containerscontainer.UseAws(Aws =>
options.Containers.ConfigureDefault(container =>
{
Aws.AccessKeyId = "your Aws access key id";
Aws.SecretAccessKey = "your Aws access key secret";
Aws.UseCredentials = "set true to use credentials";
Aws.UseTemporaryCredentials = "set true to use temporary credentials";
Aws.UseTemporaryFederatedCredentials = "set true to use temporary federated credentials";
Aws.ProfileName = "the name of the profile to get credentials from";
Aws.ProfilesLocation = "the path to the aws credentials file to look at";
Aws.Region = "the system name of the service";
Aws.Name = "the name of the federated user";
Aws.Policy = "policy";
Aws.DurationSeconds = "expiration date";
Aws.ContainerName = "your Aws container name";
Aws.CreateContainerIfNotExists = false;
container.UseAws(Aws =>
{
Aws.AccessKeyId = "your Aws access key id";
Aws.SecretAccessKey = "your Aws access key secret";
Aws.UseCredentials = "set true to use credentials";
Aws.UseTemporaryCredentials = "set true to use temporary credentials";
Aws.UseTemporaryFederatedCredentials = "set true to use temporary federated credentials";
Aws.ProfileName = "the name of the profile to get credentials from";
Aws.ProfilesLocation = "the path to the aws credentials file to look at";
Aws.Region = "the system name of the service";
Aws.Name = "the name of the federated user";
Aws.Policy = "policy";
Aws.DurationSeconds = "expiration date";
Aws.ContainerName = "your Aws container name";
Aws.CreateContainerIfNotExists = false;
});
});
});
````
> See the [BLOB Storing document](Blob-Storing.md) to learn how to configure this provider for a specific container.

11
docs/en/Blob-Storing-Azure.md

@ -23,11 +23,14 @@ Configuration is done in the `ConfigureServices` method of your [module](Module-
````csharp
Configure<AbpBlobStoringOptions>(options =>
{
options.Containerscontainer.UseAzure(azure =>
options.Containers.ConfigureDefault(container =>
{
azure.ConnectionString = "your azure connection string";
azure.ContainerName = "your azure container name";
azure.CreateContainerIfNotExists = false;
container.UseAzure(azure =>
{
azure.ConnectionString = "your azure connection string";
azure.ContainerName = "your azure container name";
azure.CreateContainerIfNotExists = false;
});
});
});
````

15
docs/en/Blob-Storing-Minio.md

@ -23,12 +23,15 @@ Configuration is done in the `ConfigureServices` method of your [module](Module-
````csharp
Configure<AbpBlobStoringOptions>(options =>
{
options.Containerscontainer.UseMinio(minio =>
{
minio.EndPoint = "your minio endPoint";
minio.AccessKey = "your minio accessKey";
minio.SecretKey = "your minio secretKey";
minio.BucketName = "your minio bucketName";
options.Containers.ConfigureDefault(container =>
{
container.UseMinio(minio =>
{
minio.EndPoint = "your minio endPoint";
minio.AccessKey = "your minio accessKey";
minio.SecretKey = "your minio secretKey";
minio.BucketName = "your minio bucketName";
});
});
});
````

2
docs/en/Community-Articles/2020-04-19-Customize-the-SignIn-Manager/POST.md

@ -1,6 +1,6 @@
# How to Customize the SignIn Manager for ABP Applications
After creating a new application using the [application startup template](https://docs.abp.io/en/abp/latest/Startup-Templates/Application), you may want extend or change the default behavior of the SignIn Manager for your authentication and registration flow needs. ABP [Account Module](https://docs.abp.io/en/abp/latest/Modules/Account) uses the [Identity Management Module](https://docs.abp.io/en/abp/latest/Modules/Identity) for SignIn Manager and the [Identity Management Module](../Modules/Identity.md) uses default [Microsoft Identity SignIn Manager](https://github.com/dotnet/aspnetcore/blob/master/src/Identity/Core/src/SignInManager.cs) ([see here](https://github.com/abpframework/abp/blob/be32a55449e270d2d456df3dabdc91f3ffdd4fa9/modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/AbpIdentityAspNetCoreModule.cs#L17)).
After creating a new application using the [application startup template](https://docs.abp.io/en/abp/latest/Startup-Templates/Application), you may want extend or change the default behavior of the SignIn Manager for your authentication and registration flow needs. ABP [Account Module](https://docs.abp.io/en/abp/latest/Modules/Account) uses the [Identity Management Module](https://docs.abp.io/en/abp/latest/Modules/Identity) for SignIn Manager and the [Identity Management Module](https://docs.abp.io/en/abp/latest/Modules/Identity) uses default [Microsoft Identity SignIn Manager](https://github.com/dotnet/aspnetcore/blob/master/src/Identity/Core/src/SignInManager.cs) ([see here](https://github.com/abpframework/abp/blob/be32a55449e270d2d456df3dabdc91f3ffdd4fa9/modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/AbpIdentityAspNetCoreModule.cs#L17)).
To write your Custom SignIn Manager, you need to extend [Microsoft Identity SignIn Manager](https://github.com/dotnet/aspnetcore/blob/master/src/Identity/Core/src/SignInManager.cs) class and register it to the DI container.

25
docs/zh-Hans/Blob-Storing-Aliyun.md

@ -23,18 +23,21 @@ BLOB存储Aliyun提供程序可以将BLOB存储在[Aliyun Blob storage](https://
````csharp
Configure<AbpBlobStoringOptions>(options =>
{
options.Containerscontainer.UseAliyun(aliyun =>
options.Containers.ConfigureDefault(container =>
{
aliyun.AccessKeyId = "your aliyun access key id";
aliyun.AccessKeySecret = "your aliyun access key secret";
aliyun.Endpoint = "your oss endpoint";
aliyun.RegionId = "your sts region id";
aliyun.RoleArn = "the arn of ram role";
aliyun.RoleSessionName = "the name of the certificate";
aliyun.Policy = "policy";
aliyun.DurationSeconds = "expiration date";
aliyun.ContainerName = "your aliyun container name";
aliyun.CreateContainerIfNotExists = false;
container.UseAliyun(aliyun =>
{
aliyun.AccessKeyId = "your aliyun access key id";
aliyun.AccessKeySecret = "your aliyun access key secret";
aliyun.Endpoint = "your oss endpoint";
aliyun.RegionId = "your sts region id";
aliyun.RoleArn = "the arn of ram role";
aliyun.RoleSessionName = "the name of the certificate";
aliyun.Policy = "policy";
aliyun.DurationSeconds = "expiration date";
aliyun.ContainerName = "your aliyun container name";
aliyun.CreateContainerIfNotExists = false;
});
});
});
````

31
docs/zh-Hans/Blob-Storing-Aws.md

@ -23,21 +23,24 @@ BLOB存储Aws提供程序可以将BLOB存储在[Amazon Simple Storage Service](h
````csharp
Configure<AbpBlobStoringOptions>(options =>
{
options.Containerscontainer.UseAws(Aws =>
options.Containers.ConfigureDefault(container =>
{
Aws.AccessKeyId = "your Aws access key id";
Aws.SecretAccessKey = "your Aws access key secret";
Aws.UseCredentials = "set true to use credentials";
Aws.UseTemporaryCredentials = "set true to use temporary credentials";
Aws.UseTemporaryFederatedCredentials = "set true to use temporary federated credentials";
Aws.ProfileName = "the name of the profile to get credentials from";
Aws.ProfilesLocation = "the path to the aws credentials file to look at";
Aws.Region = "the system name of the service";
Aws.Name = "the name of the federated user";
Aws.Policy = "policy";
Aws.DurationSeconds = "expiration date";
Aws.ContainerName = "your Aws container name";
Aws.CreateContainerIfNotExists = false;
container.UseAws(Aws =>
{
Aws.AccessKeyId = "your Aws access key id";
Aws.SecretAccessKey = "your Aws access key secret";
Aws.UseCredentials = "set true to use credentials";
Aws.UseTemporaryCredentials = "set true to use temporary credentials";
Aws.UseTemporaryFederatedCredentials = "set true to use temporary federated credentials";
Aws.ProfileName = "the name of the profile to get credentials from";
Aws.ProfilesLocation = "the path to the aws credentials file to look at";
Aws.Region = "the system name of the service";
Aws.Name = "the name of the federated user";
Aws.Policy = "policy";
Aws.DurationSeconds = "expiration date";
Aws.ContainerName = "your Aws container name";
Aws.CreateContainerIfNotExists = false;
});
});
});
````

11
docs/zh-Hans/Blob-Storing-Azure.md

@ -23,11 +23,14 @@ BLOB存储Azure提供程序可以将BLOB存储在[Azure Blob storage](https://az
````csharp
Configure<AbpBlobStoringOptions>(options =>
{
options.Containerscontainer.UseAzure(azure =>
options.Containers.ConfigureDefault(container =>
{
azure.ConnectionString = "your azure connection string";
azure.ContainerName = "your azure container name";
azure.CreateContainerIfNotExists = false;
container.UseAzure(azure =>
{
azure.ConnectionString = "your azure connection string";
azure.ContainerName = "your azure container name";
azure.CreateContainerIfNotExists = false;
});
});
});
````

15
docs/zh-Hans/Blob-Storing-Minio.md

@ -23,12 +23,15 @@ BLOB Storing Minio提供程序帮助你存储对象到 [MinIO Object storage](ht
````csharp
Configure<AbpBlobStoringOptions>(options =>
{
options.Containerscontainer.UseMinio(minio =>
{
minio.EndPoint = "你的 minio endPoint";
minio.AccessKey = "你的 minio accessKey";
minio.SecretKey = "你的 minio secretKey";
minio.BucketName = "你的 minio bucketName";
options.Containers.ConfigureDefault(container =>
{
container.UseMinio(minio =>
{
minio.EndPoint = "你的 minio endPoint";
minio.AccessKey = "你的 minio accessKey";
minio.SecretKey = "你的 minio secretKey";
minio.BucketName = "你的 minio bucketName";
});
});
});
````

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

@ -78,11 +78,6 @@ namespace Volo.Abp.Account.Web.Pages.Account
{
await CheckSelfRegistrationAsync();
var registerDto = new RegisterDto()
{
AppName = "MVC"
};
if (IsExternalLogin)
{
var externalLoginInfo = await SignInManager.GetExternalLoginInfoAsync();
@ -92,90 +87,56 @@ namespace Volo.Abp.Account.Web.Pages.Account
return RedirectToPage("./Login");
}
registerDto.EmailAddress = Input.EmailAddress;
registerDto.UserName = Input.EmailAddress;
registerDto.Password = GeneratePassword();
await RegisterExternalUserAsync(externalLoginInfo, Input.EmailAddress);
}
else
{
ValidateModel();
registerDto.EmailAddress = Input.EmailAddress;
registerDto.Password = Input.Password;
registerDto.UserName = Input.UserName;
}
var userDto = await AccountAppService.RegisterAsync(registerDto);
var user = await UserManager.GetByIdAsync(userDto.Id);
await SignInManager.SignInAsync(user, isPersistent: false);
if (IsExternalLogin)
{
await AddToUserLogins(user);
await RegisterLocalUserAsync();
}
return Redirect(ReturnUrl ?? "~/"); //TODO: How to ensure safety? IdentityServer requires it however it should be checked somehow!
}
protected virtual async Task AddToUserLogins(IdentityUser user)
protected virtual async Task RegisterLocalUserAsync()
{
var externalLoginInfo = await SignInManager.GetExternalLoginInfoAsync();
ValidateModel();
var userLoginAlreadyExists = user.Logins.Any(x =>
x.TenantId == user.TenantId &&
x.LoginProvider == externalLoginInfo.LoginProvider &&
x.ProviderKey == externalLoginInfo.ProviderKey);
var userDto = await AccountAppService.RegisterAsync(
new RegisterDto
{
AppName = "MVC",
EmailAddress = Input.EmailAddress,
Password = Input.Password,
UserName = Input.UserName
}
);
if (!userLoginAlreadyExists)
{
user.AddLogin(new UserLoginInfo(
externalLoginInfo.LoginProvider,
externalLoginInfo.ProviderKey,
externalLoginInfo.ProviderDisplayName
)
);
}
var user = await UserManager.GetByIdAsync(userDto.Id);
await SignInManager.SignInAsync(user, isPersistent: true);
}
protected virtual string GeneratePassword()
protected virtual async Task RegisterExternalUserAsync(ExternalLoginInfo externalLoginInfo, string emailAddress)
{
var random = new Random();
var options = UserManager.Options.Password;
int length = random.Next(options.RequiredLength, IdentityUserConsts.MaxPasswordLength - 1);
var user = new IdentityUser(GuidGenerator.Create(), emailAddress, emailAddress, CurrentTenant.Id);
bool nonAlphanumeric = options.RequireNonAlphanumeric;
bool digit = options.RequireDigit;
bool lowercase = options.RequireLowercase;
bool uppercase = options.RequireUppercase;
(await UserManager.CreateAsync(user)).CheckErrors();
(await UserManager.AddDefaultRolesAsync(user)).CheckErrors();
StringBuilder password = new StringBuilder();
var userLoginAlreadyExists = user.Logins.Any(x =>
x.TenantId == user.TenantId &&
x.LoginProvider == externalLoginInfo.LoginProvider &&
x.ProviderKey == externalLoginInfo.ProviderKey);
while (password.Length < length)
if (!userLoginAlreadyExists)
{
char c = (char)random.Next(32, 126);
password.Append(c);
if (char.IsDigit(c))
digit = false;
else if (char.IsLower(c))
lowercase = false;
else if (char.IsUpper(c))
uppercase = false;
else if (!char.IsLetterOrDigit(c))
nonAlphanumeric = false;
(await UserManager.AddLoginAsync(user, new UserLoginInfo(
externalLoginInfo.LoginProvider,
externalLoginInfo.ProviderKey,
externalLoginInfo.ProviderDisplayName
))).CheckErrors();
}
if (nonAlphanumeric)
password.Append((char)random.Next(33, 48));
if (digit)
password.Append((char)random.Next(48, 58));
if (lowercase)
password.Append((char)random.Next(97, 123));
if (uppercase)
password.Append((char)random.Next(65, 91));
return password.ToString();
await SignInManager.SignInAsync(user, isPersistent: true);
}
protected virtual async Task CheckSelfRegistrationAsync()

9
modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Comments/CommentPublicAppService.cs

@ -8,6 +8,7 @@ using Volo.Abp;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Abp.Authorization;
using Volo.Abp.Users;
using Volo.CmsKit.Users;
namespace Volo.CmsKit.Comments
@ -42,7 +43,7 @@ namespace Volo.CmsKit.Comments
[Authorize]
public async Task<CommentDto> CreateAsync(CreateCommentInput input)
{
var user = await CmsUserLookupService.FindByIdAsync(CurrentUser.Id.Value);
var user = await CmsUserLookupService.FindByIdAsync(CurrentUser.GetId());
if (user == null)
{
@ -55,7 +56,7 @@ namespace Volo.CmsKit.Comments
input.EntityId,
input.Text,
input.RepliedCommentId,
CurrentUser.Id.Value
user.Id
));
return ObjectMapper.Map<Comment, CommentDto>(comment);
@ -66,7 +67,7 @@ namespace Volo.CmsKit.Comments
{
var comment = await CommentRepository.GetAsync(id);
if (comment.CreatorId != CurrentUser.Id)
if (comment.CreatorId != CurrentUser.GetId())
{
throw new BusinessException();
}
@ -83,7 +84,7 @@ namespace Volo.CmsKit.Comments
{
var comment = await CommentRepository.GetAsync(id);
if (comment.CreatorId != CurrentUser.Id)
if (comment.CreatorId != CurrentUser.GetId())
{
throw new BusinessException();
}

9
modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/FullSearch/Elastic/ElasticDocumentFullSearch.cs

@ -73,7 +73,7 @@ namespace Volo.Docs.Documents.FullSearch.Elastic
FileName = document.FileName,
Content = document.Content,
LanguageCode = ConvertLanguageCode(document.LanguageCode),
Version = document.Version
Version = ConvertVersion(document.Version)
};
if (!existsResponse.Exists)
@ -173,7 +173,7 @@ namespace Volo.Docs.Documents.FullSearch.Elastic
new TermQuery
{
Field = "version",
Value = version
Value = ConvertVersion(version)
},
new TermQuery
{
@ -237,5 +237,10 @@ namespace Volo.Docs.Documents.FullSearch.Elastic
{
return languageCode.Replace("-", "").ToLower();
}
protected string ConvertVersion(string version)
{
return version.Replace("-", "").ToLower();
}
}
}

7
modules/identity/Volo.Abp.Identity.sln

@ -39,6 +39,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.Identity.AspNetCor
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.PermissionManagement.Domain.Identity", "src\Volo.Abp.PermissionManagement.Domain.Identity\Volo.Abp.PermissionManagement.Domain.Identity.csproj", "{736F91E7-8A70-441B-89DE-0E29A348E718}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Identity.AspNetCore.Tests", "test\Volo.Abp.Identity.AspNetCore.Tests\Volo.Abp.Identity.AspNetCore.Tests.csproj", "{89C094EB-D80A-4976-9C10-7CE3EBEEE877}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -109,6 +111,10 @@ Global
{736F91E7-8A70-441B-89DE-0E29A348E718}.Debug|Any CPU.Build.0 = Debug|Any CPU
{736F91E7-8A70-441B-89DE-0E29A348E718}.Release|Any CPU.ActiveCfg = Release|Any CPU
{736F91E7-8A70-441B-89DE-0E29A348E718}.Release|Any CPU.Build.0 = Release|Any CPU
{89C094EB-D80A-4976-9C10-7CE3EBEEE877}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{89C094EB-D80A-4976-9C10-7CE3EBEEE877}.Debug|Any CPU.Build.0 = Debug|Any CPU
{89C094EB-D80A-4976-9C10-7CE3EBEEE877}.Release|Any CPU.ActiveCfg = Release|Any CPU
{89C094EB-D80A-4976-9C10-7CE3EBEEE877}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -130,6 +136,7 @@ Global
{588B6E38-323B-4251-AC21-5F67C815A44E} = {9FACAF96-A681-4B36-A938-A37DCA0B7EC1}
{D5EFC912-75A0-4856-9B8D-DFDD4CD66BAB} = {AADC5A0A-F100-4511-87DE-B74E55F5B69B}
{736F91E7-8A70-441B-89DE-0E29A348E718} = {AADC5A0A-F100-4511-87DE-B74E55F5B69B}
{89C094EB-D80A-4976-9C10-7CE3EBEEE877} = {9FACAF96-A681-4B36-A938-A37DCA0B7EC1}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {05740D37-83CF-4041-9C2A-D89F1B3DB5A4}

5
modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/AbpIdentityAspNetCoreModule.cs

@ -13,8 +13,9 @@ namespace Volo.Abp.Identity.AspNetCore
{
PreConfigure<IdentityBuilder>(builder =>
{
builder.AddDefaultTokenProviders();
builder.AddSignInManager();
builder
.AddDefaultTokenProviders()
.AddSignInManager<AbpSignInManager>();
});
}

9
modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/AbpIdentityAspNetCoreOptions.cs

@ -6,5 +6,12 @@
/// Default: true.
/// </summary>
public bool ConfigureAuthentication { get; set; } = true;
public ExternalLoginProviderDictionary ExternalLoginProviders { get; }
public AbpIdentityAspNetCoreOptions()
{
ExternalLoginProviders = new ExternalLoginProviderDictionary();
}
}
}
}

66
modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/AbpSignInManager.cs

@ -0,0 +1,66 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace Volo.Abp.Identity.AspNetCore
{
public class AbpSignInManager : SignInManager<IdentityUser>
{
protected AbpIdentityAspNetCoreOptions AbpOptions { get; }
public AbpSignInManager(
IdentityUserManager userManager,
IHttpContextAccessor contextAccessor,
IUserClaimsPrincipalFactory<IdentityUser> claimsFactory,
IOptions<IdentityOptions> optionsAccessor,
ILogger<SignInManager<IdentityUser>> logger,
IAuthenticationSchemeProvider schemes,
IUserConfirmation<IdentityUser> confirmation,
IOptions<AbpIdentityAspNetCoreOptions> options
) : base(
userManager,
contextAccessor,
claimsFactory,
optionsAccessor,
logger,
schemes,
confirmation)
{
AbpOptions = options.Value;
}
public override async Task<SignInResult> PasswordSignInAsync(
string userName,
string password,
bool isPersistent,
bool lockoutOnFailure)
{
foreach (var externalLoginProviderInfo in AbpOptions.ExternalLoginProviders.Values)
{
var externalLoginProvider = (IExternalLoginProvider) Context.RequestServices
.GetRequiredService(externalLoginProviderInfo.Type);
if (await externalLoginProvider.TryAuthenticateAsync(userName, password))
{
var user = await UserManager.FindByNameAsync(userName);
if (user == null)
{
user = await externalLoginProvider.CreateUserAsync(userName, externalLoginProviderInfo.Name);
}
else
{
await externalLoginProvider.UpdateUserAsync(user, externalLoginProviderInfo.Name);
}
return await SignInOrTwoFactorAsync(user, isPersistent);
}
}
return await base.PasswordSignInAsync(userName, password, isPersistent, lockoutOnFailure);
}
}
}

156
modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/ExternalLoginProviderBase.cs

@ -0,0 +1,156 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Guids;
using Volo.Abp.MultiTenancy;
namespace Volo.Abp.Identity.AspNetCore
{
public abstract class ExternalLoginProviderBase : IExternalLoginProvider
{
protected IGuidGenerator GuidGenerator { get; }
protected ICurrentTenant CurrentTenant { get; }
protected IdentityUserManager UserManager { get; }
protected IIdentityUserRepository IdentityUserRepository { get; }
protected ExternalLoginProviderBase(
IGuidGenerator guidGenerator,
ICurrentTenant currentTenant,
IdentityUserManager userManager,
IIdentityUserRepository identityUserRepository)
{
GuidGenerator = guidGenerator;
CurrentTenant = currentTenant;
UserManager = userManager;
IdentityUserRepository = identityUserRepository;
}
public abstract Task<bool> TryAuthenticateAsync(string userName, string plainPassword);
public virtual async Task<IdentityUser> CreateUserAsync(string userName, string providerName)
{
var externalUser = await GetUserInfoAsync(userName);
NormalizeExternalLoginUserInfo(externalUser, userName);
var user = new IdentityUser(
GuidGenerator.Create(),
userName,
externalUser.Email,
tenantId: CurrentTenant.Id
);
user.Name = externalUser.Name;
user.Surname = externalUser.Surname;
user.IsExternal = true;
user.SetEmailConfirmed(externalUser.EmailConfirmed ?? false);
user.SetPhoneNumber(externalUser.PhoneNumber, externalUser.PhoneNumberConfirmed ?? false);
(await UserManager.CreateAsync(user)).CheckErrors();
if (externalUser.TwoFactorEnabled != null)
{
(await UserManager.SetTwoFactorEnabledAsync(user, externalUser.TwoFactorEnabled.Value)).CheckErrors();
}
(await UserManager.AddDefaultRolesAsync(user)).CheckErrors();
(await UserManager.AddLoginAsync(
user,
new UserLoginInfo(
providerName,
externalUser.ProviderKey ,
providerName
)
)
).CheckErrors();
return user;
}
public virtual async Task UpdateUserAsync(IdentityUser user, string providerName)
{
var externalUser = await GetUserInfoAsync(user);
NormalizeExternalLoginUserInfo(externalUser, user.UserName);
if (!externalUser.Name.IsNullOrWhiteSpace())
{
user.Name = externalUser.Name;
}
if (!externalUser.Surname.IsNullOrWhiteSpace())
{
user.Surname = externalUser.Surname;
}
if (user.PhoneNumber != externalUser.PhoneNumber)
{
if (!externalUser.PhoneNumber.IsNullOrWhiteSpace())
{
await UserManager.SetPhoneNumberAsync(user, externalUser.PhoneNumber);
user.SetPhoneNumberConfirmed(externalUser.PhoneNumberConfirmed == true);
}
}
else
{
if (!user.PhoneNumber.IsNullOrWhiteSpace() &&
user.PhoneNumberConfirmed == false &&
externalUser.PhoneNumberConfirmed == true)
{
user.SetPhoneNumberConfirmed(true);
}
}
if (!string.Equals(user.Email, externalUser.Email, StringComparison.OrdinalIgnoreCase))
{
(await UserManager.SetEmailAsync(user, externalUser.Email)).CheckErrors();
user.SetEmailConfirmed(externalUser.EmailConfirmed ?? false);
}
if (externalUser.TwoFactorEnabled != null)
{
(await UserManager.SetTwoFactorEnabledAsync(user, externalUser.TwoFactorEnabled.Value)).CheckErrors();
}
await IdentityUserRepository.EnsureCollectionLoadedAsync(user, u => u.Logins);
var userLogin = user.Logins.FirstOrDefault(l => l.LoginProvider == providerName);
if (userLogin != null)
{
if (userLogin.ProviderKey != externalUser.ProviderKey)
{
(await UserManager.RemoveLoginAsync(user, providerName, userLogin.ProviderKey)).CheckErrors();
(await UserManager.AddLoginAsync(user, new UserLoginInfo(providerName, externalUser.ProviderKey, providerName))).CheckErrors();
}
}
else
{
(await UserManager.AddLoginAsync(user, new UserLoginInfo(providerName, externalUser.ProviderKey, providerName))).CheckErrors();
}
user.IsExternal = true;
(await UserManager.UpdateAsync(user)).CheckErrors();
}
protected abstract Task<ExternalLoginUserInfo> GetUserInfoAsync(string userName);
protected virtual Task<ExternalLoginUserInfo> GetUserInfoAsync(IdentityUser user)
{
return GetUserInfoAsync(user.UserName);
}
private static void NormalizeExternalLoginUserInfo(
ExternalLoginUserInfo externalUser,
string userName
)
{
if (externalUser.ProviderKey.IsNullOrWhiteSpace())
{
externalUser.ProviderKey = userName;
}
}
}
}

18
modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/ExternalLoginProviderDictionary.cs

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using JetBrains.Annotations;
namespace Volo.Abp.Identity.AspNetCore
{
public class ExternalLoginProviderDictionary : Dictionary<string, ExternalLoginProviderInfo>
{
/// <summary>
/// Adds or replaces a provider.
/// </summary>
public void Add<TProvider>([NotNull] string name)
where TProvider : IExternalLoginProvider
{
this[name] = new ExternalLoginProviderInfo(name, typeof(TProvider));
}
}
}

25
modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/ExternalLoginProviderInfo.cs

@ -0,0 +1,25 @@
using System;
using JetBrains.Annotations;
namespace Volo.Abp.Identity.AspNetCore
{
public class ExternalLoginProviderInfo
{
public string Name { get; }
public Type Type
{
get => _type;
set => _type = Check.NotNull(value, nameof(value));
}
private Type _type;
public ExternalLoginProviderInfo(
[NotNull] string name,
[NotNull] Type type)
{
Name = Check.NotNullOrWhiteSpace(name, nameof(name));
Type = Check.AssignableTo<IExternalLoginProvider>(type, nameof(type));
}
}
}

36
modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/ExternalLoginUserInfo.cs

@ -0,0 +1,36 @@
using JetBrains.Annotations;
namespace Volo.Abp.Identity.AspNetCore
{
public class ExternalLoginUserInfo
{
[CanBeNull]
public string Name { get; set; }
[CanBeNull]
public string Surname { get; set; }
[CanBeNull]
public string PhoneNumber { get; set; }
[NotNull]
public string Email { get; private set; }
[CanBeNull]
public bool? PhoneNumberConfirmed { get; set; }
[CanBeNull]
public bool? EmailConfirmed { get; set; }
[CanBeNull]
public bool? TwoFactorEnabled { get; set; }
[CanBeNull]
public string ProviderKey { get; set; }
public ExternalLoginUserInfo([System.Diagnostics.CodeAnalysis.NotNull] string email)
{
Email = Check.NotNullOrWhiteSpace(email, nameof(email));
}
}
}

32
modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/IExternalLoginProvider.cs

@ -0,0 +1,32 @@
using System.Threading.Tasks;
namespace Volo.Abp.Identity.AspNetCore
{
public interface IExternalLoginProvider
{
/// <summary>
/// Used to try authenticate a user by this source.
/// </summary>
/// <param name="userName">User name or email address</param>
/// <param name="plainPassword">Plain password of the user</param>
/// <returns>True, indicates that this used has authenticated by this source</returns>
Task<bool> TryAuthenticateAsync(string userName, string plainPassword);
/// <summary>
/// This method is called when a user is authenticated by this source but the user does not exists yet.
/// So, the source should create the user and fill the properties.
/// </summary>
/// <param name="userName">User name</param>
/// <param name="providerName">The name of this provider</param>
/// <returns>Newly created user</returns>
Task<IdentityUser> CreateUserAsync(string userName, string providerName);
/// <summary>
/// This method is called after an existing user is authenticated by this source.
/// It can be used to update some properties of the user by the source.
/// </summary>
/// <param name="providerName">The name of this provider</param>
/// <param name="user">The user that can be updated</param>
Task UpdateUserAsync(IdentityUser user, string providerName);
}
}

5
modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityUserConsts.cs

@ -32,5 +32,10 @@ namespace Volo.Abp.Identity
/// Default value: 256
/// </summary>
public static int MaxSecurityStampLength { get; set; } = 256;
/// <summary>
/// Default value: 16
/// </summary>
public static int MaxLoginProviderLength { get; set; } = 16;
}
}

42
modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUser.cs

@ -30,11 +30,13 @@ namespace Volo.Abp.Identity
/// <summary>
/// Gets or sets the Name for the user.
/// </summary>
[CanBeNull]
public virtual string Name { get; set; }
/// <summary>
/// Gets or sets the Surname for the user.
/// </summary>
[CanBeNull]
public virtual string Surname { get; set; }
/// <summary>
@ -66,9 +68,12 @@ namespace Volo.Abp.Identity
[DisableAuditing]
public virtual string SecurityStamp { get; protected internal set; }
public virtual bool IsExternal { get; set; }
/// <summary>
/// Gets or sets a telephone number for the user.
/// </summary>
[CanBeNull]
public virtual string PhoneNumber { get; protected internal set; }
/// <summary>
@ -133,7 +138,11 @@ namespace Volo.Abp.Identity
{
}
public IdentityUser(Guid id, [NotNull] string userName, [NotNull] string email, Guid? tenantId = null)
public IdentityUser(
Guid id,
[NotNull] string userName,
[NotNull] string email,
Guid? tenantId = null)
{
Check.NotNull(userName, nameof(userName));
Check.NotNull(email, nameof(email));
@ -254,7 +263,8 @@ namespace Volo.Abp.Identity
Check.NotNull(loginProvider, nameof(loginProvider));
Check.NotNull(providerKey, nameof(providerKey));
Logins.RemoveAll(userLogin => userLogin.LoginProvider == loginProvider && userLogin.ProviderKey == providerKey);
Logins.RemoveAll(userLogin =>
userLogin.LoginProvider == loginProvider && userLogin.ProviderKey == providerKey);
}
[CanBeNull]
@ -316,9 +326,37 @@ namespace Volo.Abp.Identity
);
}
/// <summary>
/// Use <see cref="IdentityUserManager.ConfirmEmailAsync"/> for regular email confirmation.
/// Using this skips the confirmation process and directly sets the <see cref="EmailConfirmed"/>.
/// </summary>
public virtual void SetEmailConfirmed(bool confirmed)
{
EmailConfirmed = confirmed;
}
public virtual void SetPhoneNumberConfirmed(bool confirmed)
{
PhoneNumberConfirmed = confirmed;
}
public override string ToString()
{
return $"{base.ToString()}, UserName = {UserName}";
}
/// <summary>
/// Normally use <see cref="IdentityUserManager.ChangePhoneNumberAsync"/> to change the phone number
/// in the application code.
/// This method is to directly set it with a confirmation information.
/// </summary>
/// <param name="phoneNumber"></param>
/// <param name="confirmed"></param>
/// <exception cref="NotImplementedException"></exception>
public void SetPhoneNumber(string phoneNumber, bool confirmed)
{
PhoneNumber = phoneNumber;
PhoneNumberConfirmed = !phoneNumber.IsNullOrWhiteSpace() && confirmed;
}
}
}

22
modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserStore.cs

@ -291,7 +291,7 @@ namespace Volo.Abp.Identity
/// </summary>
/// <param name="user">The user to retrieve the password hash for.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>A <see cref="Task{TResult}"/> containing a flag indicating if the specified user has a password. If the
/// <returns>A <see cref="Task{TResult}"/> containing a flag indicating if the specified user has a password. If the
/// user has a password the returned value with be true, otherwise it will be false.</returns>
public virtual Task<bool> HasPasswordAsync([NotNull] IdentityUser user, CancellationToken cancellationToken = default)
{
@ -326,7 +326,7 @@ namespace Volo.Abp.Identity
{
throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "Role {0} does not exist!", normalizedRoleName));
}
await UserRepository.EnsureCollectionLoadedAsync(user, u => u.Roles, cancellationToken);
user.AddRole(role.Id);
@ -353,7 +353,7 @@ namespace Volo.Abp.Identity
}
await UserRepository.EnsureCollectionLoadedAsync(user, u => u.Roles, cancellationToken);
user.RemoveRole(role.Id);
}
@ -384,15 +384,15 @@ namespace Volo.Abp.Identity
/// <param name="user">The user whose role membership should be checked.</param>
/// <param name="normalizedRoleName">The role to check membership of</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>A <see cref="Task{TResult}"/> containing a flag indicating if the specified user is a member of the given group. If the
/// <returns>A <see cref="Task{TResult}"/> containing a flag indicating if the specified user is a member of the given group. If the
/// user is a member of the group the returned value with be true, otherwise it will be false.</returns>
public virtual async Task<bool> IsInRoleAsync(
[NotNull] IdentityUser user,
[NotNull] IdentityUser user,
[NotNull] string normalizedRoleName,
CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
Check.NotNull(user, nameof(user));
Check.NotNullOrWhiteSpace(normalizedRoleName, nameof(normalizedRoleName));
@ -589,7 +589,7 @@ namespace Volo.Abp.Identity
Check.NotNull(user, nameof(user));
user.EmailConfirmed = confirmed;
user.SetEmailConfirmed(confirmed);
return Task.CompletedTask;
}
@ -864,7 +864,7 @@ namespace Volo.Abp.Identity
Check.NotNull(user, nameof(user));
user.PhoneNumberConfirmed = confirmed;
user.SetPhoneNumberConfirmed(confirmed);
return Task.CompletedTask;
}
@ -928,7 +928,7 @@ namespace Volo.Abp.Identity
/// <param name="user">The user whose two factor authentication enabled status should be set.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>
/// The <see cref="Task"/> that represents the asynchronous operation, containing a flag indicating whether the specified
/// The <see cref="Task"/> that represents the asynchronous operation, containing a flag indicating whether the specified
/// <paramref name="user"/> has two factor authentication enabled or not.
/// </returns>
public virtual Task<bool> GetTwoFactorEnabledAsync([NotNull] IdentityUser user, CancellationToken cancellationToken = default)
@ -946,7 +946,7 @@ namespace Volo.Abp.Identity
/// <param name="claim">The claim whose users should be retrieved.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>
/// The <see cref="Task"/> contains a list of users, if any, that contain the specified claim.
/// The <see cref="Task"/> contains a list of users, if any, that contain the specified claim.
/// </returns>
public virtual async Task<IList<IdentityUser>> GetUsersForClaimAsync([NotNull] Claim claim, CancellationToken cancellationToken = default)
{
@ -963,7 +963,7 @@ namespace Volo.Abp.Identity
/// <param name="normalizedRoleName">The role whose users should be retrieved.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>
/// The <see cref="Task"/> contains a list of users, if any, that are in the specified role.
/// The <see cref="Task"/> contains a list of users, if any, that are in the specified role.
/// </returns>
public virtual async Task<IList<IdentityUser>> GetUsersInRoleAsync([NotNull] string normalizedRoleName, CancellationToken cancellationToken = default)
{

3
modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/IdentityDbContextModelBuilderExtensions.cs

@ -43,6 +43,9 @@ namespace Volo.Abp.Identity.EntityFrameworkCore
b.Property(u => u.LockoutEnabled).HasDefaultValue(false)
.HasColumnName(nameof(IdentityUser.LockoutEnabled));
b.Property(u => u.IsExternal).IsRequired().HasDefaultValue(false)
.HasColumnName(nameof(IdentityUser.IsExternal));
b.Property(u => u.AccessFailedCount)
.If(!builder.IsUsingOracle(), p => p.HasDefaultValue(0))
.HasColumnName(nameof(IdentityUser.AccessFailedCount));

25
modules/identity/test/Volo.Abp.Identity.AspNetCore.Tests/Volo.Abp.Identity.AspNetCore.Tests.csproj

@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<AssemblyName>Volo.Abp.Identity.AspNetCore.Tests</AssemblyName>
<PackageId>Volo.Abp.Identity.AspNetCore.Tests</PackageId>
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.AspNetCore.TestBase\Volo.Abp.AspNetCore.TestBase.csproj" />
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.AspNetCore.Mvc\Volo.Abp.AspNetCore.Mvc.csproj" />
<ProjectReference Include="..\..\src\Volo.Abp.Identity.AspNetCore\Volo.Abp.Identity.AspNetCore.csproj" />
<ProjectReference Include="..\Volo.Abp.Identity.Domain.Tests\Volo.Abp.Identity.Domain.Tests.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
</ItemGroup>
</Project>

24
modules/identity/test/Volo.Abp.Identity.AspNetCore.Tests/Volo/Abp/Identity/AspNetCore/AbpIdentityAspNetCoreTestBase.cs

@ -0,0 +1,24 @@
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Shouldly;
using Volo.Abp.AspNetCore.TestBase;
namespace Volo.Abp.Identity.AspNetCore
{
public abstract class AbpIdentityAspNetCoreTestBase : AbpAspNetCoreIntegratedTestBase<AbpIdentityAspNetCoreTestStartup>
{
protected virtual async Task<string> GetResponseAsStringAsync(string url, HttpStatusCode expectedStatusCode = HttpStatusCode.OK)
{
var response = await GetResponseAsync(url, expectedStatusCode);
return await response.Content.ReadAsStringAsync();
}
protected virtual async Task<HttpResponseMessage> GetResponseAsync(string url, HttpStatusCode expectedStatusCode = HttpStatusCode.OK)
{
var response = await Client.GetAsync(url);
response.StatusCode.ShouldBe(expectedStatusCode);
return response;
}
}
}

46
modules/identity/test/Volo.Abp.Identity.AspNetCore.Tests/Volo/Abp/Identity/AspNetCore/AbpIdentityAspNetCoreTestModule.cs

@ -0,0 +1,46 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Mvc.ApplicationParts;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.TestBase;
using Volo.Abp.Modularity;
namespace Volo.Abp.Identity.AspNetCore
{
[DependsOn(
typeof(AbpIdentityAspNetCoreModule),
typeof(AbpIdentityDomainTestModule),
typeof(AbpAspNetCoreTestBaseModule),
typeof(AbpAspNetCoreMvcModule)
)]
public class AbpIdentityAspNetCoreTestModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
context.Services.PreConfigure<IMvcBuilder>(builder =>
{
builder.PartManager.ApplicationParts.Add(new AssemblyPart(typeof(AbpIdentityAspNetCoreTestModule).Assembly));
});
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpIdentityAspNetCoreOptions>(options =>
{
options.ExternalLoginProviders.Add<FakeExternalLoginProvider>(FakeExternalLoginProvider.Name);
});
}
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
var app = context.GetApplicationBuilder();
var env = context.GetEnvironment();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseConfiguredEndpoints();
}
}
}

19
modules/identity/test/Volo.Abp.Identity.AspNetCore.Tests/Volo/Abp/Identity/AspNetCore/AbpIdentityAspNetCoreTestStartup.cs

@ -0,0 +1,19 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace Volo.Abp.Identity.AspNetCore
{
public class AbpIdentityAspNetCoreTestStartup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddApplication<AbpIdentityAspNetCoreTestModule>();
}
public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
{
app.InitializeApplication();
}
}
}

39
modules/identity/test/Volo.Abp.Identity.AspNetCore.Tests/Volo/Abp/Identity/AspNetCore/AbpSignInManager_Tests.cs

@ -0,0 +1,39 @@
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Shouldly;
using Xunit;
namespace Volo.Abp.Identity.AspNetCore
{
public class AbpSignInManager_Tests : AbpIdentityAspNetCoreTestBase
{
[Fact]
public void Should_Resolve_AbpSignInManager()
{
var signInManager = GetRequiredService<SignInManager<IdentityUser>>();
signInManager.ShouldBeOfType<AbpSignInManager>();
}
[Fact]
public async Task Should_SignIn_With_Correct_Credentials()
{
var result = await GetResponseAsStringAsync(
"api/signin-test/password?userName=admin&password=1q2w3E*"
);
result.ShouldBe("Succeeded");
}
[Fact]
public async Task Should_Not_SignIn_With_Wrong_Credentials()
{
var result = await GetResponseAsStringAsync(
"api/signin-test/password?userName=admin&password=WRONG_PASSWORD"
);
result.ShouldBe("Failed");
}
}
}

57
modules/identity/test/Volo.Abp.Identity.AspNetCore.Tests/Volo/Abp/Identity/AspNetCore/ExternalLoginProvider_Tests.cs

@ -0,0 +1,57 @@
using System.Linq;
using System.Threading.Tasks;
using Shouldly;
using Xunit;
namespace Volo.Abp.Identity.AspNetCore
{
public class ExternalLoginProvider_Tests : AbpIdentityAspNetCoreTestBase
{
[Fact]
public async Task Should_SignIn_With_ExternalLoginProvider()
{
// User does not exists yet
(await GetRequiredService<IdentityUserManager>().FindByNameAsync("ext_user")).ShouldBeNull();
// Try to login
var result = await GetResponseAsStringAsync(
"api/signin-test/password?userName=ext_user&password=abc"
);
result.ShouldBe("Succeeded");
// User should be created now
await CheckUserAsync();
// Re-login
result = await GetResponseAsStringAsync(
"api/signin-test/password?userName=ext_user&password=abc"
);
result.ShouldBe("Succeeded");
await CheckUserAsync();
}
private async Task CheckUserAsync()
{
var userRepository = GetRequiredService<IIdentityUserRepository>();
var user = await userRepository.FindByNormalizedUserNameAsync("EXT_USER");
user.Name.ShouldBe("Test Name");
user.Surname.ShouldBe("Test Surname");
user.EmailConfirmed.ShouldBeTrue();
user.TwoFactorEnabled.ShouldBeFalse();
user.PhoneNumber.ShouldBe("123");
user.PhoneNumberConfirmed.ShouldBeFalse();
user.IsExternal.ShouldBeTrue();
var logins = user.Logins.Where(l => l.LoginProvider == "Fake").ToList();
logins.Count.ShouldBe(1);
logins[0].ProviderKey.ShouldBe("123");
}
}
}

55
modules/identity/test/Volo.Abp.Identity.AspNetCore.Tests/Volo/Abp/Identity/AspNetCore/FakeExternalLoginProvider.cs

@ -0,0 +1,55 @@
using System;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Guids;
using Volo.Abp.MultiTenancy;
namespace Volo.Abp.Identity.AspNetCore
{
public class FakeExternalLoginProvider : ExternalLoginProviderBase, ITransientDependency
{
public const string Name = "Fake";
public FakeExternalLoginProvider(
IGuidGenerator guidGenerator,
ICurrentTenant currentTenant,
IdentityUserManager userManager,
IIdentityUserRepository identityUserRepository)
: base(
guidGenerator,
currentTenant,
userManager,
identityUserRepository)
{
}
public override Task<bool> TryAuthenticateAsync(string userName, string plainPassword)
{
return Task.FromResult(
userName == "ext_user" && plainPassword == "abc"
);
}
protected override Task<ExternalLoginUserInfo> GetUserInfoAsync(string userName)
{
if (userName != "ext_user")
{
throw new ArgumentException();
}
return Task.FromResult(
new ExternalLoginUserInfo("ext_user@test.com")
{
Name = "Test Name", //optional, if the provider knows it
Surname = "Test Surname", //optional, if the provider knows it
EmailConfirmed = true, //optional, if the provider knows it
TwoFactorEnabled = false, //optional, if the provider knows it
PhoneNumber = "123", //optional, if the provider knows it
PhoneNumberConfirmed = false, //optional, if the provider knows it
ProviderKey = "123" //The id of the user on the provider side
}
);
}
}
}

31
modules/identity/test/Volo.Abp.Identity.AspNetCore.Tests/Volo/Abp/Identity/AspNetCore/SignInTestController.cs

@ -0,0 +1,31 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc;
namespace Volo.Abp.Identity.AspNetCore
{
[Route("api/signin-test")]
public class SignInTestController : AbpController
{
private readonly SignInManager<IdentityUser> _signInManager;
public SignInTestController(SignInManager<IdentityUser> signInManager)
{
_signInManager = signInManager;
}
[Route("password")]
public async Task<ActionResult> PasswordLogin(string userName, string password)
{
var result = await _signInManager.PasswordSignInAsync(
userName,
password,
false,
false
);
return Content(result.ToString());
}
}
}

15
modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/AbpIdentityExtendedTestBase.cs

@ -1,12 +1,14 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Identity.EntityFrameworkCore;
using Volo.Abp.Modularity;
using Volo.Abp.Uow;
namespace Volo.Abp.Identity
{
public abstract class AbpIdentityExtendedTestBase<TStartupModule> : AbpIdentityTestBase<TStartupModule>
public abstract class AbpIdentityExtendedTestBase<TStartupModule> : AbpIdentityTestBase<TStartupModule>
where TStartupModule : IAbpModule
{
protected virtual IdentityUser GetUser(string userName)
@ -40,5 +42,14 @@ namespace Volo.Abp.Identity
return action.Invoke(dbContext);
}
}
protected virtual async Task UsingUowAsync(Func<Task> action)
{
using (var uow = GetRequiredService<IUnitOfWorkManager>().Begin())
{
await action();
await uow.CompleteAsync();
}
}
}
}
}

2
modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/IdentityClaimTypeManager_Tests.cs

@ -1,6 +1,4 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Shouldly;
using Xunit;

8
samples/MicroserviceDemo/applications/AuthServer.Host/package.json

@ -1,8 +0,0 @@
{
"version": "0.1.0",
"name": "msdemo-authserver-host",
"private": true,
"dependencies": {
"@abp/aspnetcore.mvc.ui.theme.basic": "^3.0.5"
}
}

2629
samples/MicroserviceDemo/applications/AuthServer.Host/yarn.lock

File diff suppressed because it is too large

8
samples/MicroserviceDemo/applications/BackendAdminApp.Host/package.json

@ -1,8 +0,0 @@
{
"version": "0.1.0",
"name": "msdemo-backend-admin-app-host",
"private": true,
"dependencies": {
"@abp/aspnetcore.mvc.ui.theme.basic": "^3.0.5"
}
}

2629
samples/MicroserviceDemo/applications/BackendAdminApp.Host/yarn.lock

File diff suppressed because it is too large

9
samples/MicroserviceDemo/applications/PublicWebSite.Host/package.json

@ -1,9 +0,0 @@
{
"version": "0.1.0",
"name": "msdemo-backend-admin-app-host",
"private": true,
"dependencies": {
"@abp/aspnetcore.mvc.ui.theme.basic": "^3.0.5",
"@abp/blogging": "^3.0.5"
}
}

2965
samples/MicroserviceDemo/applications/PublicWebSite.Host/yarn.lock

File diff suppressed because it is too large

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

@ -11,7 +11,7 @@ using Volo.Abp.EntityFrameworkCore;
namespace MyCompanyName.MyProjectName.Migrations
{
[DbContext(typeof(MyProjectNameMigrationsDbContext))]
[Migration("20200710064926_Initial")]
[Migration("20200806191520_Initial")]
partial class Initial
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
@ -619,6 +619,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/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations/Migrations/20200710064926_Initial.cs → templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations/Migrations/20200806191520_Initial.cs

@ -244,6 +244,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),

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

@ -617,6 +617,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");

Loading…
Cancel
Save