diff --git a/Sanhe.Abp.Framework.sln b/Sanhe.Abp.Framework.sln
index 9bc6d96..559081b 100644
--- a/Sanhe.Abp.Framework.sln
+++ b/Sanhe.Abp.Framework.sln
@@ -63,11 +63,27 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sanhe.Abp.PinyinConverter",
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sanhe.Abp.PinyinConverter.ToolGoodWords", "modules\common\Sanhe.Abp.PinyinConverter.ToolGoodWords\Sanhe.Abp.PinyinConverter.ToolGoodWords.csproj", "{EE08FB0C-EDC1-4DEF-B0C8-699F4E7D08AF}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sanhe.Abp.RealTime", "modules\common\Sanhe.Abp.RealTime\Sanhe.Abp.RealTime.csproj", "{C99CF772-210B-4C21-84FE-089B7D038A28}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sanhe.Abp.RealTime", "modules\common\Sanhe.Abp.RealTime\Sanhe.Abp.RealTime.csproj", "{C99CF772-210B-4C21-84FE-089B7D038A28}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sanhe.Abp.IdGenerator", "modules\common\Sanhe.Abp.IdGenerator\Sanhe.Abp.IdGenerator.csproj", "{86CDAB3C-D7EC-4F4B-BCB9-2069DAE1D0FC}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sanhe.Abp.IdGenerator", "modules\common\Sanhe.Abp.IdGenerator\Sanhe.Abp.IdGenerator.csproj", "{86CDAB3C-D7EC-4F4B-BCB9-2069DAE1D0FC}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sanhe.Abp.Notifications", "modules\common\Sanhe.Abp.Notifications\Sanhe.Abp.Notifications.csproj", "{8F9EA0EF-6C02-4017-AEB4-A0B4CB4D5983}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sanhe.Abp.Notifications", "modules\common\Sanhe.Abp.Notifications\Sanhe.Abp.Notifications.csproj", "{8F9EA0EF-6C02-4017-AEB4-A0B4CB4D5983}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "identity", "identity", "{BAA5EE9D-A5C6-45F3-A89B-B3CC30FF3DFE}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sanhe.Abp.Identity.Domain.Shared", "modules\identity\Sanhe.Abp.Identity.Domain.Shared\Sanhe.Abp.Identity.Domain.Shared.csproj", "{86E8E59C-FDB8-45E5-B9DE-BEFE51DF05DC}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sanhe.Abp.Identity.Domain", "modules\identity\Sanhe.Abp.Identity.Domain\Sanhe.Abp.Identity.Domain.csproj", "{6531A9CD-E1B2-4861-8C31-9561220D088B}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sanhe.Abp.Identity.Application.Contracts", "modules\identity\Sanhe.Abp.Identity.Application.Contracts\Sanhe.Abp.Identity.Application.Contracts.csproj", "{CA166F9C-7535-4CB7-B8B5-D01971B8C09F}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sanhe.Abp.Identity.Application", "modules\identity\Sanhe.Abp.Identity.Application\Sanhe.Abp.Identity.Application.csproj", "{39102CCF-4AFB-44F2-9D9D-3D2449D71C88}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sanhe.Abp.Identity.EntityFrameworkCore", "modules\identity\Sanhe.Abp.Identity.EntityFrameworkCore\Sanhe.Abp.Identity.EntityFrameworkCore.csproj", "{9C8F2135-A237-41D9-B054-C6170D2CC6B2}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sanhe.Abp.Identity.HttpApi", "modules\identity\Sanhe.Abp.Identity.HttpApi\Sanhe.Abp.Identity.HttpApi.csproj", "{F53FC671-99FD-4CD8-915E-5B77619A6C6B}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sanhe.Abp.Identity.HttpApi.Client", "modules\identity\Sanhe.Abp.Identity.HttpApi.Client\Sanhe.Abp.Identity.HttpApi.Client.csproj", "{6E5D3C18-45B4-478B-B064-140C5CDDD327}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -163,6 +179,34 @@ Global
{8F9EA0EF-6C02-4017-AEB4-A0B4CB4D5983}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8F9EA0EF-6C02-4017-AEB4-A0B4CB4D5983}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8F9EA0EF-6C02-4017-AEB4-A0B4CB4D5983}.Release|Any CPU.Build.0 = Release|Any CPU
+ {86E8E59C-FDB8-45E5-B9DE-BEFE51DF05DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {86E8E59C-FDB8-45E5-B9DE-BEFE51DF05DC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {86E8E59C-FDB8-45E5-B9DE-BEFE51DF05DC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {86E8E59C-FDB8-45E5-B9DE-BEFE51DF05DC}.Release|Any CPU.Build.0 = Release|Any CPU
+ {6531A9CD-E1B2-4861-8C31-9561220D088B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6531A9CD-E1B2-4861-8C31-9561220D088B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6531A9CD-E1B2-4861-8C31-9561220D088B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6531A9CD-E1B2-4861-8C31-9561220D088B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {CA166F9C-7535-4CB7-B8B5-D01971B8C09F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CA166F9C-7535-4CB7-B8B5-D01971B8C09F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CA166F9C-7535-4CB7-B8B5-D01971B8C09F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CA166F9C-7535-4CB7-B8B5-D01971B8C09F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {39102CCF-4AFB-44F2-9D9D-3D2449D71C88}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {39102CCF-4AFB-44F2-9D9D-3D2449D71C88}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {39102CCF-4AFB-44F2-9D9D-3D2449D71C88}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {39102CCF-4AFB-44F2-9D9D-3D2449D71C88}.Release|Any CPU.Build.0 = Release|Any CPU
+ {9C8F2135-A237-41D9-B054-C6170D2CC6B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {9C8F2135-A237-41D9-B054-C6170D2CC6B2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {9C8F2135-A237-41D9-B054-C6170D2CC6B2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {9C8F2135-A237-41D9-B054-C6170D2CC6B2}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F53FC671-99FD-4CD8-915E-5B77619A6C6B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F53FC671-99FD-4CD8-915E-5B77619A6C6B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F53FC671-99FD-4CD8-915E-5B77619A6C6B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F53FC671-99FD-4CD8-915E-5B77619A6C6B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {6E5D3C18-45B4-478B-B064-140C5CDDD327}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6E5D3C18-45B4-478B-B064-140C5CDDD327}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6E5D3C18-45B4-478B-B064-140C5CDDD327}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6E5D3C18-45B4-478B-B064-140C5CDDD327}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -195,6 +239,14 @@ Global
{C99CF772-210B-4C21-84FE-089B7D038A28} = {2A768109-31B7-4C52-928C-3023DAB9F254}
{86CDAB3C-D7EC-4F4B-BCB9-2069DAE1D0FC} = {2A768109-31B7-4C52-928C-3023DAB9F254}
{8F9EA0EF-6C02-4017-AEB4-A0B4CB4D5983} = {2A768109-31B7-4C52-928C-3023DAB9F254}
+ {BAA5EE9D-A5C6-45F3-A89B-B3CC30FF3DFE} = {F5F5D604-531B-4B57-A88E-C9C5CEEC55D7}
+ {86E8E59C-FDB8-45E5-B9DE-BEFE51DF05DC} = {BAA5EE9D-A5C6-45F3-A89B-B3CC30FF3DFE}
+ {6531A9CD-E1B2-4861-8C31-9561220D088B} = {BAA5EE9D-A5C6-45F3-A89B-B3CC30FF3DFE}
+ {CA166F9C-7535-4CB7-B8B5-D01971B8C09F} = {BAA5EE9D-A5C6-45F3-A89B-B3CC30FF3DFE}
+ {39102CCF-4AFB-44F2-9D9D-3D2449D71C88} = {BAA5EE9D-A5C6-45F3-A89B-B3CC30FF3DFE}
+ {9C8F2135-A237-41D9-B054-C6170D2CC6B2} = {BAA5EE9D-A5C6-45F3-A89B-B3CC30FF3DFE}
+ {F53FC671-99FD-4CD8-915E-5B77619A6C6B} = {BAA5EE9D-A5C6-45F3-A89B-B3CC30FF3DFE}
+ {6E5D3C18-45B4-478B-B064-140C5CDDD327} = {BAA5EE9D-A5C6-45F3-A89B-B3CC30FF3DFE}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {AB69BFDE-9DDB-4D16-8CB8-72472C0319CD}
diff --git a/modules/identity/Sanhe.Abp.Identity.Application.Contracts/FodyWeavers.xml b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/FodyWeavers.xml
new file mode 100644
index 0000000..1715698
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe.Abp.Identity.Application.Contracts.csproj b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe.Abp.Identity.Application.Contracts.csproj
new file mode 100644
index 0000000..efa9105
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe.Abp.Identity.Application.Contracts.csproj
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+ netstandard2.0
+
+
+
+
+
+
+
+
+
+
+
diff --git a/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/AbpIdentityApplicationContractsModule.cs b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/AbpIdentityApplicationContractsModule.cs
new file mode 100644
index 0000000..03b5b49
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/AbpIdentityApplicationContractsModule.cs
@@ -0,0 +1,15 @@
+using Volo.Abp.Authorization;
+using Volo.Abp.Modularity;
+
+namespace Sanhe.Abp.Identity
+{
+ [DependsOn(
+ typeof(Volo.Abp.Identity.AbpIdentityApplicationContractsModule),
+ typeof(AbpIdentityDomainSharedModule),
+ typeof(AbpAuthorizationModule)
+ )]
+ public class AbpIdentityApplicationContractsModule : AbpModule
+ {
+
+ }
+}
diff --git a/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/IdentityClaimDto.cs b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/IdentityClaimDto.cs
new file mode 100644
index 0000000..ea99f3a
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/IdentityClaimDto.cs
@@ -0,0 +1,12 @@
+using System;
+using Volo.Abp.Application.Dtos;
+
+namespace Sanhe.Abp.Identity.Dto
+{
+ public class IdentityClaimDto : EntityDto
+ {
+ public string ClaimType { get; set; }
+
+ public string ClaimValue { get; set; }
+ }
+}
diff --git a/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/IdentityClaimTypeCreateDto.cs b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/IdentityClaimTypeCreateDto.cs
new file mode 100644
index 0000000..07f1af8
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/IdentityClaimTypeCreateDto.cs
@@ -0,0 +1,17 @@
+using System.ComponentModel.DataAnnotations;
+using Volo.Abp.Identity;
+using Volo.Abp.Validation;
+
+namespace Sanhe.Abp.Identity.Dto
+{
+ public class IdentityClaimTypeCreateDto : IdentityClaimTypeCreateOrUpdateBaseDto
+ {
+ [Required]
+ [DynamicStringLength(typeof(IdentityClaimTypeConsts), nameof(IdentityClaimTypeConsts.MaxNameLength))]
+ public string Name { get; set; }
+
+ public bool IsStatic { get; set; }
+
+ public IdentityClaimValueType ValueType { get; set; }
+ }
+}
diff --git a/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/IdentityClaimTypeCreateOrUpdateBaseDto.cs b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/IdentityClaimTypeCreateOrUpdateBaseDto.cs
new file mode 100644
index 0000000..523892d
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/IdentityClaimTypeCreateOrUpdateBaseDto.cs
@@ -0,0 +1,20 @@
+using Volo.Abp.Identity;
+using Volo.Abp.ObjectExtending;
+using Volo.Abp.Validation;
+
+namespace Sanhe.Abp.Identity.Dto
+{
+ public class IdentityClaimTypeCreateOrUpdateBaseDto : ExtensibleObject
+ {
+ public bool Required { get; set; }
+
+ [DynamicStringLength(typeof(IdentityClaimTypeConsts), nameof(IdentityClaimTypeConsts.MaxRegexLength))]
+ public string Regex { get; set; }
+
+ [DynamicStringLength(typeof(IdentityClaimTypeConsts), nameof(IdentityClaimTypeConsts.MaxRegexDescriptionLength))]
+ public string RegexDescription { get; set; }
+
+ [DynamicStringLength(typeof(IdentityClaimTypeConsts), nameof(IdentityClaimTypeConsts.MaxDescriptionLength))]
+ public string Description { get; set; }
+ }
+}
diff --git a/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/IdentityClaimTypeDto.cs b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/IdentityClaimTypeDto.cs
new file mode 100644
index 0000000..5ea72ad
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/IdentityClaimTypeDto.cs
@@ -0,0 +1,23 @@
+using System;
+using Volo.Abp.Application.Dtos;
+using Volo.Abp.Identity;
+
+namespace Sanhe.Abp.Identity.Dto
+{
+ public class IdentityClaimTypeDto : ExtensibleEntityDto
+ {
+ public string Name { get; set; }
+
+ public bool Required { get; set; }
+
+ public bool IsStatic { get; set; }
+
+ public string Regex { get; set; }
+
+ public string RegexDescription { get; set; }
+
+ public string Description { get; set; }
+
+ public IdentityClaimValueType ValueType { get; set; }
+ }
+}
diff --git a/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/IdentityClaimTypeGetByPagedDto.cs b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/IdentityClaimTypeGetByPagedDto.cs
new file mode 100644
index 0000000..905d28e
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/IdentityClaimTypeGetByPagedDto.cs
@@ -0,0 +1,9 @@
+using Volo.Abp.Application.Dtos;
+
+namespace Sanhe.Abp.Identity.Dto
+{
+ public class IdentityClaimTypeGetByPagedDto : PagedAndSortedResultRequestDto
+ {
+ public string Filter { get; set; }
+ }
+}
diff --git a/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/IdentityClaimTypeUpdateDto.cs b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/IdentityClaimTypeUpdateDto.cs
new file mode 100644
index 0000000..a2022a0
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/IdentityClaimTypeUpdateDto.cs
@@ -0,0 +1,7 @@
+namespace Sanhe.Abp.Identity.Dto
+{
+ public class IdentityClaimTypeUpdateDto : IdentityClaimTypeCreateOrUpdateBaseDto
+ {
+
+ }
+}
diff --git a/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/IdentityRoleAddOrRemoveOrganizationUnitDto.cs b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/IdentityRoleAddOrRemoveOrganizationUnitDto.cs
new file mode 100644
index 0000000..678d186
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/IdentityRoleAddOrRemoveOrganizationUnitDto.cs
@@ -0,0 +1,11 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+
+namespace Sanhe.Abp.Identity.Dto
+{
+ public class IdentityRoleAddOrRemoveOrganizationUnitDto
+ {
+ [Required]
+ public Guid[] OrganizationUnitIds { get; set; }
+ }
+}
diff --git a/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/IdentityRoleClaimCreateDto.cs b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/IdentityRoleClaimCreateDto.cs
new file mode 100644
index 0000000..b175f4a
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/IdentityRoleClaimCreateDto.cs
@@ -0,0 +1,17 @@
+using System.ComponentModel.DataAnnotations;
+using Volo.Abp.Identity;
+using Volo.Abp.Validation;
+
+namespace Sanhe.Abp.Identity.Dto
+{
+ public class IdentityRoleClaimCreateDto
+ {
+ [Required]
+ [DynamicMaxLength(typeof(IdentityRoleClaimConsts), nameof(IdentityRoleClaimConsts.MaxClaimTypeLength))]
+ public string ClaimType { get; set; }
+
+ [Required]
+ [DynamicMaxLength(typeof(IdentityRoleClaimConsts), nameof(IdentityRoleClaimConsts.MaxClaimValueLength))]
+ public string ClaimValue { get; set; }
+ }
+}
diff --git a/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/IdentityRoleClaimDeleteDto.cs b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/IdentityRoleClaimDeleteDto.cs
new file mode 100644
index 0000000..ff156ee
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/IdentityRoleClaimDeleteDto.cs
@@ -0,0 +1,6 @@
+namespace Sanhe.Abp.Identity.Dto
+{
+ public class IdentityRoleClaimDeleteDto : IdentityRoleClaimCreateDto
+ {
+ }
+}
diff --git a/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/IdentityRoleClaimUpdateDto.cs b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/IdentityRoleClaimUpdateDto.cs
new file mode 100644
index 0000000..356e023
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/IdentityRoleClaimUpdateDto.cs
@@ -0,0 +1,13 @@
+using System.ComponentModel.DataAnnotations;
+using Volo.Abp.Identity;
+using Volo.Abp.Validation;
+
+namespace Sanhe.Abp.Identity.Dto
+{
+ public class IdentityRoleClaimUpdateDto : IdentityRoleClaimCreateDto
+ {
+ [Required]
+ [DynamicMaxLength(typeof(IdentityRoleClaimConsts), nameof(IdentityRoleClaimConsts.MaxClaimValueLength))]
+ public string NewClaimValue { get; set; }
+ }
+}
diff --git a/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/IdentityUserClaimCreateDto.cs b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/IdentityUserClaimCreateDto.cs
new file mode 100644
index 0000000..270eefc
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/IdentityUserClaimCreateDto.cs
@@ -0,0 +1,6 @@
+namespace Sanhe.Abp.Identity.Dto
+{
+ public class IdentityUserClaimCreateDto : IdentityUserClaimCreateOrUpdateDto
+ {
+ }
+}
diff --git a/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/IdentityUserClaimCreateOrUpdateDto.cs b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/IdentityUserClaimCreateOrUpdateDto.cs
new file mode 100644
index 0000000..f7d9c5c
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/IdentityUserClaimCreateOrUpdateDto.cs
@@ -0,0 +1,16 @@
+using System.ComponentModel.DataAnnotations;
+using Volo.Abp.Identity;
+using Volo.Abp.Validation;
+
+namespace Sanhe.Abp.Identity.Dto
+{
+ public abstract class IdentityUserClaimCreateOrUpdateDto
+ {
+ [Required]
+ [DynamicMaxLength(typeof(IdentityUserClaimConsts), nameof(IdentityUserClaimConsts.MaxClaimTypeLength))]
+ public string ClaimType { get; set; }
+
+ [DynamicMaxLength(typeof(IdentityUserClaimConsts), nameof(IdentityUserClaimConsts.MaxClaimValueLength))]
+ public string ClaimValue { get; set; }
+ }
+}
diff --git a/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/IdentityUserClaimDeleteDto.cs b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/IdentityUserClaimDeleteDto.cs
new file mode 100644
index 0000000..7879c5f
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/IdentityUserClaimDeleteDto.cs
@@ -0,0 +1,7 @@
+namespace Sanhe.Abp.Identity.Dto
+{
+ public class IdentityUserClaimDeleteDto : IdentityUserClaimCreateDto
+ {
+
+ }
+}
diff --git a/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/IdentityUserClaimUpdateDto.cs b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/IdentityUserClaimUpdateDto.cs
new file mode 100644
index 0000000..180ca0f
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/IdentityUserClaimUpdateDto.cs
@@ -0,0 +1,11 @@
+using Volo.Abp.Identity;
+using Volo.Abp.Validation;
+
+namespace Sanhe.Abp.Identity.Dto
+{
+ public class IdentityUserClaimUpdateDto : IdentityUserClaimCreateOrUpdateDto
+ {
+ [DynamicMaxLength(typeof(IdentityUserClaimConsts), nameof(IdentityUserClaimConsts.MaxClaimValueLength))]
+ public string NewClaimValue { get; set; }
+ }
+}
diff --git a/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/IdentityUserOrganizationUnitUpdateDto.cs b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/IdentityUserOrganizationUnitUpdateDto.cs
new file mode 100644
index 0000000..1d53d47
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/IdentityUserOrganizationUnitUpdateDto.cs
@@ -0,0 +1,11 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+
+namespace Sanhe.Abp.Identity.Dto
+{
+ public class IdentityUserOrganizationUnitUpdateDto
+ {
+ [Required]
+ public Guid[] OrganizationUnitIds { get; set; }
+ }
+}
diff --git a/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/OrganizationUnitAddRoleDto.cs b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/OrganizationUnitAddRoleDto.cs
new file mode 100644
index 0000000..5f6057b
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/OrganizationUnitAddRoleDto.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+
+namespace Sanhe.Abp.Identity.Dto
+{
+ public class OrganizationUnitAddRoleDto
+ {
+ [Required]
+ public List RoleIds { get; set; }
+ }
+}
diff --git a/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/OrganizationUnitAddUserDto.cs b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/OrganizationUnitAddUserDto.cs
new file mode 100644
index 0000000..c92f406
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/OrganizationUnitAddUserDto.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+
+namespace Sanhe.Abp.Identity.Dto
+{
+ public class OrganizationUnitAddUserDto
+ {
+ [Required]
+ public List UserIds { get; set; }
+ }
+}
diff --git a/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/OrganizationUnitCreateDto.cs b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/OrganizationUnitCreateDto.cs
new file mode 100644
index 0000000..ed39c11
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/OrganizationUnitCreateDto.cs
@@ -0,0 +1,17 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+using Volo.Abp.Identity;
+using Volo.Abp.ObjectExtending;
+using Volo.Abp.Validation;
+
+namespace Sanhe.Abp.Identity.Dto
+{
+ public class OrganizationUnitCreateDto : ExtensibleObject
+ {
+ [Required]
+ [DynamicStringLength(typeof(OrganizationUnitConsts), nameof(OrganizationUnitConsts.MaxDisplayNameLength))]
+ public string DisplayName { get; set; }
+
+ public Guid? ParentId { get; set; }
+ }
+}
diff --git a/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/OrganizationUnitDto.cs b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/OrganizationUnitDto.cs
new file mode 100644
index 0000000..b1d089c
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/OrganizationUnitDto.cs
@@ -0,0 +1,12 @@
+using System;
+using Volo.Abp.Application.Dtos;
+
+namespace Sanhe.Abp.Identity.Dto
+{
+ public class OrganizationUnitDto : ExtensibleAuditedEntityDto
+ {
+ public Guid? ParentId { get; set; }
+ public string Code { get; set; }
+ public string DisplayName { get; set; }
+ }
+}
diff --git a/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/OrganizationUnitGetByPagedDto.cs b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/OrganizationUnitGetByPagedDto.cs
new file mode 100644
index 0000000..f16773d
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/OrganizationUnitGetByPagedDto.cs
@@ -0,0 +1,9 @@
+using Volo.Abp.Application.Dtos;
+
+namespace Sanhe.Abp.Identity.Dto
+{
+ public class OrganizationUnitGetByPagedDto : PagedAndSortedResultRequestDto
+ {
+ public string Filter { get; set; }
+ }
+}
diff --git a/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/OrganizationUnitGetChildrenDto.cs b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/OrganizationUnitGetChildrenDto.cs
new file mode 100644
index 0000000..0075777
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/OrganizationUnitGetChildrenDto.cs
@@ -0,0 +1,13 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+using Volo.Abp.Application.Dtos;
+
+namespace Sanhe.Abp.Identity.Dto
+{
+ public class OrganizationUnitGetChildrenDto : IEntityDto
+ {
+ [Required]
+ public Guid Id { get; set; }
+ public bool Recursive { get; set; }
+ }
+}
diff --git a/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/OrganizationUnitGetUnaddedRoleByPagedDto.cs b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/OrganizationUnitGetUnaddedRoleByPagedDto.cs
new file mode 100644
index 0000000..1ac76c2
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/OrganizationUnitGetUnaddedRoleByPagedDto.cs
@@ -0,0 +1,10 @@
+using Volo.Abp.Application.Dtos;
+
+namespace Sanhe.Abp.Identity.Dto
+{
+ public class OrganizationUnitGetUnaddedRoleByPagedDto : PagedAndSortedResultRequestDto
+ {
+
+ public string Filter { get; set; }
+ }
+}
diff --git a/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/OrganizationUnitGetUnaddedUserByPagedDto.cs b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/OrganizationUnitGetUnaddedUserByPagedDto.cs
new file mode 100644
index 0000000..99c96de
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/OrganizationUnitGetUnaddedUserByPagedDto.cs
@@ -0,0 +1,9 @@
+using Volo.Abp.Application.Dtos;
+
+namespace Sanhe.Abp.Identity.Dto
+{
+ public class OrganizationUnitGetUnaddedUserByPagedDto : PagedAndSortedResultRequestDto
+ {
+ public string Filter { get; set; }
+ }
+}
diff --git a/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/OrganizationUnitMoveDto.cs b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/OrganizationUnitMoveDto.cs
new file mode 100644
index 0000000..e7c4588
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/OrganizationUnitMoveDto.cs
@@ -0,0 +1,9 @@
+using System;
+
+namespace Sanhe.Abp.Identity.Dto
+{
+ public class OrganizationUnitMoveDto
+ {
+ public Guid? ParentId { get; set; }
+ }
+}
diff --git a/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/OrganizationUnitUpdateDto.cs b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/OrganizationUnitUpdateDto.cs
new file mode 100644
index 0000000..db980bd
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/OrganizationUnitUpdateDto.cs
@@ -0,0 +1,9 @@
+using Volo.Abp.ObjectExtending;
+
+namespace Sanhe.Abp.Identity.Dto
+{
+ public class OrganizationUnitUpdateDto : ExtensibleObject
+ {
+ public string DisplayName { get; set; }
+ }
+}
diff --git a/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/TwoFactorEnabledDto.cs b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/TwoFactorEnabledDto.cs
new file mode 100644
index 0000000..0711d46
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/Dto/TwoFactorEnabledDto.cs
@@ -0,0 +1,7 @@
+namespace Sanhe.Abp.Identity.Dto
+{
+ public class TwoFactorEnabledDto
+ {
+ public bool Enabled { get; set; }
+ }
+}
diff --git a/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/IIdentityClaimTypeAppService.cs b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/IIdentityClaimTypeAppService.cs
new file mode 100644
index 0000000..3ad0da6
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/IIdentityClaimTypeAppService.cs
@@ -0,0 +1,18 @@
+using Sanhe.Abp.Identity.Dto;
+using System;
+using System.Threading.Tasks;
+using Volo.Abp.Application.Dtos;
+using Volo.Abp.Application.Services;
+
+namespace Sanhe.Abp.Identity
+{
+ public interface IIdentityClaimTypeAppService : ICrudAppService<
+ IdentityClaimTypeDto,
+ Guid,
+ IdentityClaimTypeGetByPagedDto,
+ IdentityClaimTypeCreateDto,
+ IdentityClaimTypeUpdateDto>
+ {
+ Task> GetAllListAsync();
+ }
+}
diff --git a/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/IIdentityRoleAppService.cs b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/IIdentityRoleAppService.cs
new file mode 100644
index 0000000..7dc7b01
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/IIdentityRoleAppService.cs
@@ -0,0 +1,33 @@
+using Sanhe.Abp.Identity.Dto;
+using System;
+using System.Threading.Tasks;
+using Volo.Abp.Application.Dtos;
+using Volo.Abp.Application.Services;
+
+namespace Sanhe.Abp.Identity
+{
+ public interface IIdentityRoleAppService : IApplicationService
+ {
+ #region OrganizationUnit
+
+ Task> GetOrganizationUnitsAsync(Guid id);
+
+ Task SetOrganizationUnitsAsync(Guid id, IdentityRoleAddOrRemoveOrganizationUnitDto input);
+
+ Task RemoveOrganizationUnitsAsync(Guid id, Guid ouId);
+
+ #endregion
+
+ #region ClaimType
+
+ Task> GetClaimsAsync(Guid id);
+
+ Task AddClaimAsync(Guid id, IdentityRoleClaimCreateDto input);
+
+ Task UpdateClaimAsync(Guid id, IdentityRoleClaimUpdateDto input);
+
+ Task DeleteClaimAsync(Guid id, IdentityRoleClaimDeleteDto input);
+
+ #endregion
+ }
+}
diff --git a/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/IIdentityUserAppService.cs b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/IIdentityUserAppService.cs
new file mode 100644
index 0000000..69f715c
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/IIdentityUserAppService.cs
@@ -0,0 +1,63 @@
+using Sanhe.Abp.Identity.Dto;
+using System;
+using System.Threading.Tasks;
+using Volo.Abp.Application.Dtos;
+using Volo.Abp.Application.Services;
+
+namespace Sanhe.Abp.Identity
+{
+ public interface IIdentityUserAppService : IApplicationService
+ {
+
+ #region OrganizationUnit
+
+ Task> GetOrganizationUnitsAsync(Guid id);
+
+ Task SetOrganizationUnitsAsync(Guid id, IdentityUserOrganizationUnitUpdateDto input);
+
+ Task RemoveOrganizationUnitsAsync(Guid id, Guid ouId);
+
+ #endregion
+
+ #region ClaimType
+
+ Task> GetClaimsAsync(Guid id);
+
+ Task AddClaimAsync(Guid id, IdentityUserClaimCreateDto input);
+
+ Task UpdateClaimAsync(Guid id, IdentityUserClaimUpdateDto input);
+
+ Task DeleteClaimAsync(Guid id, IdentityUserClaimDeleteDto input);
+
+ #endregion
+
+ ///
+ /// 变更用户双因素验证选项
+ ///
+ ///
+ ///
+ ///
+ Task ChangeTwoFactorEnabledAsync(Guid id, TwoFactorEnabledDto input);
+ ///
+ /// 变更用户密码
+ ///
+ ///
+ ///
+ ///
+ /// TODO: 移除api,改为重置用户密码
+ // Task ChangePasswordAsync(Guid id, ChangePasswordInput input);
+ ///
+ /// 锁定
+ ///
+ ///
+ /// 锁定时长
+ ///
+ Task LockAsync(Guid id, int seconds);
+ ///
+ /// 解除锁定
+ ///
+ ///
+ ///
+ Task UnLockAsync(Guid id);
+ }
+}
diff --git a/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/IOrganizationUnitAppService.cs b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/IOrganizationUnitAppService.cs
new file mode 100644
index 0000000..130a52e
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/IOrganizationUnitAppService.cs
@@ -0,0 +1,41 @@
+using Sanhe.Abp.Identity.Dto;
+using System;
+using System.Threading.Tasks;
+using Volo.Abp.Application.Dtos;
+using Volo.Abp.Application.Services;
+using Volo.Abp.Identity;
+
+namespace Sanhe.Abp.Identity
+{
+ public interface IOrganizationUnitAppService : ICrudAppService<
+ OrganizationUnitDto,
+ Guid,
+ OrganizationUnitGetByPagedDto,
+ OrganizationUnitCreateDto,
+ OrganizationUnitUpdateDto>
+ {
+ Task> GetAllListAsync();
+
+ Task GetLastChildOrNullAsync(Guid? parentId);
+
+ Task MoveAsync(Guid id, OrganizationUnitMoveDto input);
+
+ Task> GetRootAsync();
+
+ Task> FindChildrenAsync(OrganizationUnitGetChildrenDto input);
+
+ Task> GetRoleNamesAsync(Guid id);
+
+ Task> GetUnaddedRolesAsync(Guid id, OrganizationUnitGetUnaddedRoleByPagedDto input);
+
+ Task> GetRolesAsync(Guid id, PagedAndSortedResultRequestDto input);
+
+ Task AddRolesAsync(Guid id, OrganizationUnitAddRoleDto input);
+
+ Task> GetUnaddedUsersAsync(Guid id, OrganizationUnitGetUnaddedUserByPagedDto input);
+
+ Task> GetUsersAsync(Guid id, GetIdentityUsersInput input);
+
+ Task AddUsersAsync(Guid id, OrganizationUnitAddUserDto input);
+ }
+}
diff --git a/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/IdentityPermissionDefinitionProvider.cs b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/IdentityPermissionDefinitionProvider.cs
new file mode 100644
index 0000000..8d5a930
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/IdentityPermissionDefinitionProvider.cs
@@ -0,0 +1,50 @@
+using Volo.Abp.Authorization.Permissions;
+using Volo.Abp.Identity.Localization;
+using Volo.Abp.Localization;
+using Volo.Abp.MultiTenancy;
+
+namespace Sanhe.Abp.Identity
+{
+ public class IdentityPermissionDefinitionProvider : PermissionDefinitionProvider
+ {
+ public override void Define(IPermissionDefinitionContext context)
+ {
+ var identityGroup = context.GetGroupOrNull(Volo.Abp.Identity.IdentityPermissions.GroupName);
+ if (identityGroup != null)
+ {
+ var userPermission = identityGroup.GetPermissionOrNull(Volo.Abp.Identity.IdentityPermissions.Users.Default);
+ if (userPermission != null)
+ {
+ userPermission.AddChild(IdentityPermissions.Users.ManageClaims, L("Permission:ManageClaims"));
+ userPermission.AddChild(IdentityPermissions.Users.ManageOrganizationUnits, L("Permission:ManageOrganizationUnits"));
+ }
+
+ var rolePermission = identityGroup.GetPermissionOrNull(Volo.Abp.Identity.IdentityPermissions.Roles.Default);
+ if (rolePermission != null)
+ {
+ rolePermission.AddChild(IdentityPermissions.Roles.ManageClaims, L("Permission:ManageClaims"));
+ rolePermission.AddChild(IdentityPermissions.Roles.ManageOrganizationUnits, L("Permission:ManageOrganizationUnits"));
+ }
+
+ var organizationUnitPermission = identityGroup.AddPermission(IdentityPermissions.OrganizationUnits.Default, L("Permission:OrganizationUnitManagement"));
+ organizationUnitPermission.AddChild(IdentityPermissions.OrganizationUnits.Create, L("Permission:Create"));
+ organizationUnitPermission.AddChild(IdentityPermissions.OrganizationUnits.Update, L("Permission:Edit"));
+ organizationUnitPermission.AddChild(IdentityPermissions.OrganizationUnits.Delete, L("Permission:Delete"));
+ organizationUnitPermission.AddChild(IdentityPermissions.OrganizationUnits.ManageRoles, L("Permission:ManageRoles"));
+ organizationUnitPermission.AddChild(IdentityPermissions.OrganizationUnits.ManageUsers, L("Permission:ManageUsers"));
+ organizationUnitPermission.AddChild(IdentityPermissions.OrganizationUnits.ManagePermissions, L("Permission:ChangePermissions"));
+
+ // 2020-10-23 修复Bug 租户用户也必须能查询自定义的声明, 管理权限只能为主机
+ var identityClaimType = identityGroup.AddPermission(IdentityPermissions.IdentityClaimType.Default, L("Permission:IdentityClaimTypeManagement"));
+ identityClaimType.AddChild(IdentityPermissions.IdentityClaimType.Create, L("Permission:Create"), MultiTenancySides.Host);
+ identityClaimType.AddChild(IdentityPermissions.IdentityClaimType.Update, L("Permission:Edit"), MultiTenancySides.Host);
+ identityClaimType.AddChild(IdentityPermissions.IdentityClaimType.Delete, L("Permission:Delete"), MultiTenancySides.Host);
+ }
+ }
+
+ private static LocalizableString L(string name)
+ {
+ return LocalizableString.Create(name);
+ }
+ }
+}
diff --git a/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/IdentityPermissions.cs b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/IdentityPermissions.cs
new file mode 100644
index 0000000..e8f99ba
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.Application.Contracts/Sanhe/Abp/Identity/IdentityPermissions.cs
@@ -0,0 +1,43 @@
+using Volo.Abp.Reflection;
+
+namespace Sanhe.Abp.Identity
+{
+ public class IdentityPermissions
+ {
+ public static class Roles
+ {
+ public const string ManageClaims = Volo.Abp.Identity.IdentityPermissions.Roles.Default + ".ManageClaims";
+ public const string ManageOrganizationUnits = Volo.Abp.Identity.IdentityPermissions.Roles.Default + ".ManageOrganizationUnits";
+ }
+
+ public static class Users
+ {
+ public const string ManageClaims = Volo.Abp.Identity.IdentityPermissions.Users.Default + ".ManageClaims";
+ public const string ManageOrganizationUnits = Volo.Abp.Identity.IdentityPermissions.Users.Default + ".ManageOrganizationUnits";
+ }
+
+ public static class OrganizationUnits
+ {
+ public const string Default = Volo.Abp.Identity.IdentityPermissions.GroupName + ".OrganizationUnits";
+ public const string Create = Default + ".Create";
+ public const string Update = Default + ".Update";
+ public const string Delete = Default + ".Delete";
+ public const string ManageUsers = Default + ".ManageUsers";
+ public const string ManageRoles = Default + ".ManageRoles";
+ public const string ManagePermissions = Default + ".ManagePermissions";
+ }
+
+ public static class IdentityClaimType
+ {
+ public const string Default = Volo.Abp.Identity.IdentityPermissions.GroupName + ".IdentityClaimTypes";
+ public const string Create = Default + ".Create";
+ public const string Update = Default + ".Update";
+ public const string Delete = Default + ".Delete";
+ }
+
+ public static string[] GetAll()
+ {
+ return ReflectionHelper.GetPublicConstantsRecursively(typeof(IdentityPermissions));
+ }
+ }
+}
diff --git a/modules/identity/Sanhe.Abp.Identity.Application/FodyWeavers.xml b/modules/identity/Sanhe.Abp.Identity.Application/FodyWeavers.xml
new file mode 100644
index 0000000..1715698
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.Application/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/modules/identity/Sanhe.Abp.Identity.Application/Sanhe.Abp.Identity.Application.csproj b/modules/identity/Sanhe.Abp.Identity.Application/Sanhe.Abp.Identity.Application.csproj
new file mode 100644
index 0000000..f312f0d
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.Application/Sanhe.Abp.Identity.Application.csproj
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+ netstandard2.0
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/modules/identity/Sanhe.Abp.Identity.Application/Sanhe/Abp/Identity/AbpIdentityApplicationModule.cs b/modules/identity/Sanhe.Abp.Identity.Application/Sanhe/Abp/Identity/AbpIdentityApplicationModule.cs
new file mode 100644
index 0000000..e833581
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.Application/Sanhe/Abp/Identity/AbpIdentityApplicationModule.cs
@@ -0,0 +1,42 @@
+using Microsoft.Extensions.DependencyInjection;
+using Volo.Abp.AutoMapper;
+using Volo.Abp.Modularity;
+
+namespace Sanhe.Abp.Identity
+{
+ [DependsOn(
+ typeof(Volo.Abp.Identity.AbpIdentityApplicationModule),
+ typeof(AbpIdentityApplicationContractsModule),
+ typeof(AbpIdentityDomainModule))]
+ public class AbpIdentityApplicationModule : AbpModule
+ {
+ // private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner();
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ context.Services.AddAutoMapperObjectMapper();
+
+ Configure(options =>
+ {
+ options.AddProfile(validate: true);
+ });
+ }
+
+ //public override void PostConfigureServices(ServiceConfigurationContext context)
+ //{
+ // OneTimeRunner.Run(() =>
+ // {
+ // ObjectExtensionManager.Instance
+ // .AddOrUpdateProperty(
+ // new[]
+ // {
+ // typeof(IdentityUserDto),
+ // typeof(IdentityUserCreateDto),
+ // typeof(IdentityUserUpdateDto),
+ // typeof(ProfileDto),
+ // typeof(UpdateProfileDto)
+ // },
+ // ExtensionIdentityUserConsts.AvatarUrlField);
+ // });
+ //}
+ }
+}
diff --git a/modules/identity/Sanhe.Abp.Identity.Application/Sanhe/Abp/Identity/AbpIdentityApplicationModuleAutoMapperProfile.cs b/modules/identity/Sanhe.Abp.Identity.Application/Sanhe/Abp/Identity/AbpIdentityApplicationModuleAutoMapperProfile.cs
new file mode 100644
index 0000000..002093e
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.Application/Sanhe/Abp/Identity/AbpIdentityApplicationModuleAutoMapperProfile.cs
@@ -0,0 +1,26 @@
+using AutoMapper;
+using Sanhe.Abp.Identity.Dto;
+using Volo.Abp.Identity;
+
+namespace Sanhe.Abp.Identity
+{
+ public class AbpIdentityApplicationModuleAutoMapperProfile : Profile
+ {
+ public AbpIdentityApplicationModuleAutoMapperProfile()
+ {
+ CreateMap()
+ .MapExtraProperties();
+ CreateMap();
+ CreateMap();
+
+ CreateMap()
+ .MapExtraProperties();
+
+ CreateMap()
+ .MapExtraProperties();
+
+ CreateMap()
+ .MapExtraProperties();
+ }
+ }
+}
diff --git a/modules/identity/Sanhe.Abp.Identity.Application/Sanhe/Abp/Identity/IdentityClaimTypeAppService.cs b/modules/identity/Sanhe.Abp.Identity.Application/Sanhe/Abp/Identity/IdentityClaimTypeAppService.cs
new file mode 100644
index 0000000..1d2062d
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.Application/Sanhe/Abp/Identity/IdentityClaimTypeAppService.cs
@@ -0,0 +1,130 @@
+using Microsoft.AspNetCore.Authorization;
+using Sanhe.Abp.Identity.Dto;
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Volo.Abp;
+using Volo.Abp.Application.Dtos;
+using Volo.Abp.Identity;
+
+namespace Sanhe.Abp.Identity
+{
+ [Authorize(IdentityPermissions.IdentityClaimType.Default)]
+ public class IdentityClaimTypeAppService : IdentityAppServiceBase, IIdentityClaimTypeAppService
+ {
+ protected IdentityClaimTypeManager IdentityClaimTypeManager { get; }
+
+ protected IIdentityClaimTypeRepository IdentityClaimTypeRepository { get; }
+
+ public IdentityClaimTypeAppService(
+ IdentityClaimTypeManager identityClaimTypeManager,
+ IIdentityClaimTypeRepository identityClaimTypeRepository)
+ {
+ IdentityClaimTypeManager = identityClaimTypeManager;
+ IdentityClaimTypeRepository = identityClaimTypeRepository;
+ }
+
+ [Authorize(IdentityPermissions.IdentityClaimType.Create)]
+ public virtual async Task CreateAsync(IdentityClaimTypeCreateDto input)
+ {
+ if (await IdentityClaimTypeRepository.AnyAsync(input.Name))
+ {
+ throw new UserFriendlyException(L["IdentityClaimTypeAlreadyExists", input.Name]);
+ }
+ var identityClaimType = new IdentityClaimType(
+ GuidGenerator.Create(),
+ input.Name,
+ input.Required,
+ input.IsStatic,
+ input.Regex,
+ input.RegexDescription,
+ input.Description,
+ input.ValueType
+ );
+ identityClaimType = await IdentityClaimTypeManager.CreateAsync(identityClaimType);
+ await CurrentUnitOfWork.SaveChangesAsync();
+
+ return ObjectMapper.Map(identityClaimType);
+ }
+
+ [Authorize(IdentityPermissions.IdentityClaimType.Delete)]
+ public virtual async Task DeleteAsync(Guid id)
+ {
+ var identityClaimType = await IdentityClaimTypeRepository.FindAsync(id);
+ if (identityClaimType == null)
+ {
+ return;
+ }
+ CheckDeletionClaimType(identityClaimType);
+ await IdentityClaimTypeRepository.DeleteAsync(identityClaimType);
+ }
+
+ public virtual async Task GetAsync(Guid id)
+ {
+ var identityClaimType = await IdentityClaimTypeRepository.FindAsync(id);
+
+ return ObjectMapper.Map(identityClaimType);
+ }
+
+ public virtual async Task> GetAllListAsync()
+ {
+ var identityClaimTypes = await IdentityClaimTypeRepository
+ .GetListAsync();
+
+ return new ListResultDto(
+ ObjectMapper.Map, List>(identityClaimTypes));
+ }
+
+ public virtual async Task> GetListAsync(IdentityClaimTypeGetByPagedDto input)
+ {
+ var identityClaimTypeCount = await IdentityClaimTypeRepository.GetCountAsync(input.Filter);
+
+ var identityClaimTypes = await IdentityClaimTypeRepository
+ .GetListAsync(input.Sorting, input.MaxResultCount, input.SkipCount, input.Filter);
+
+ return new PagedResultDto(identityClaimTypeCount,
+ ObjectMapper.Map, List>(identityClaimTypes));
+ }
+
+ [Authorize(IdentityPermissions.IdentityClaimType.Update)]
+ public virtual async Task UpdateAsync(Guid id, IdentityClaimTypeUpdateDto input)
+ {
+ var identityClaimType = await IdentityClaimTypeRepository.GetAsync(id);
+ CheckChangingClaimType(identityClaimType);
+ identityClaimType.Required = input.Required;
+ if (!string.Equals(identityClaimType.Regex, input.Regex, StringComparison.InvariantCultureIgnoreCase))
+ {
+ identityClaimType.Regex = input.Regex;
+ }
+ if (!string.Equals(identityClaimType.RegexDescription, input.RegexDescription, StringComparison.InvariantCultureIgnoreCase))
+ {
+ identityClaimType.RegexDescription = input.RegexDescription;
+ }
+ if (!string.Equals(identityClaimType.Description, input.Description, StringComparison.InvariantCultureIgnoreCase))
+ {
+ identityClaimType.Description = input.Description;
+ }
+
+ identityClaimType = await IdentityClaimTypeManager.UpdateAsync(identityClaimType);
+ await CurrentUnitOfWork.SaveChangesAsync();
+
+ return ObjectMapper.Map(identityClaimType);
+ }
+
+ protected virtual void CheckChangingClaimType(IdentityClaimType claimType)
+ {
+ if (claimType.IsStatic)
+ {
+ throw new BusinessException(IdentityErrorCodes.StaticClaimTypeChange);
+ }
+ }
+
+ protected virtual void CheckDeletionClaimType(IdentityClaimType claimType)
+ {
+ if (claimType.IsStatic)
+ {
+ throw new BusinessException(IdentityErrorCodes.StaticClaimTypeDeletion);
+ }
+ }
+ }
+}
diff --git a/modules/identity/Sanhe.Abp.Identity.Application/Sanhe/Abp/Identity/IdentityRoleAppService.cs b/modules/identity/Sanhe.Abp.Identity.Application/Sanhe/Abp/Identity/IdentityRoleAppService.cs
new file mode 100644
index 0000000..84db852
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.Application/Sanhe/Abp/Identity/IdentityRoleAppService.cs
@@ -0,0 +1,124 @@
+using Microsoft.AspNetCore.Authorization;
+using Sanhe.Abp.Identity.Dto;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Security.Claims;
+using System.Threading.Tasks;
+using Volo.Abp;
+using Volo.Abp.Application.Dtos;
+using Volo.Abp.Identity;
+
+namespace Sanhe.Abp.Identity
+{
+ [Authorize(Volo.Abp.Identity.IdentityPermissions.Roles.Default)]
+ public class IdentityRoleAppService : IdentityAppServiceBase, IIdentityRoleAppService
+ {
+ protected IIdentityRoleRepository IdentityRoleRepository { get; }
+ protected OrganizationUnitManager OrganizationUnitManager { get; }
+ protected IOrganizationUnitRepository OrganizationUnitRepository { get; }
+ public IdentityRoleAppService(
+ IIdentityRoleRepository roleRepository,
+ OrganizationUnitManager organizationUnitManager)
+ {
+ OrganizationUnitManager = organizationUnitManager;
+ IdentityRoleRepository = roleRepository;
+ }
+
+ #region OrganizationUnit
+
+ [Authorize(IdentityPermissions.Roles.ManageOrganizationUnits)]
+ public virtual async Task> GetOrganizationUnitsAsync(Guid id)
+ {
+ var organizationUnits = await IdentityRoleRepository.GetOrganizationUnitsAsync(id);
+
+ return new ListResultDto(
+ ObjectMapper.Map, List>(organizationUnits));
+ }
+
+ [Authorize(IdentityPermissions.Roles.ManageOrganizationUnits)]
+ public virtual async Task SetOrganizationUnitsAsync(Guid id, IdentityRoleAddOrRemoveOrganizationUnitDto input)
+ {
+ var organizationUnits = await IdentityRoleRepository.GetOrganizationUnitsAsync(id, true);
+
+ var notInRoleOuIds = input.OrganizationUnitIds.Where(ouid => !organizationUnits.Any(ou => ou.Id.Equals(ouid)));
+
+ foreach (var ouId in notInRoleOuIds)
+ {
+ await OrganizationUnitManager.AddRoleToOrganizationUnitAsync(id, ouId);
+ }
+
+ var removeRoleOriganzationUnits = organizationUnits.Where(ou => !input.OrganizationUnitIds.Contains(ou.Id));
+ foreach (var origanzationUnit in removeRoleOriganzationUnits)
+ {
+ origanzationUnit.RemoveRole(id);
+ }
+
+ await CurrentUnitOfWork.SaveChangesAsync();
+ }
+
+ [Authorize(IdentityPermissions.Roles.ManageOrganizationUnits)]
+ public virtual async Task RemoveOrganizationUnitsAsync(Guid id, Guid ouId)
+ {
+ await OrganizationUnitManager.RemoveRoleFromOrganizationUnitAsync(id, ouId);
+
+ await CurrentUnitOfWork.SaveChangesAsync();
+ }
+
+ #endregion
+
+ #region ClaimType
+
+ public virtual async Task> GetClaimsAsync(Guid id)
+ {
+ var role = await IdentityRoleRepository.GetAsync(id);
+
+ return new ListResultDto(ObjectMapper.Map, List>(role.Claims));
+ }
+
+ [Authorize(IdentityPermissions.Roles.ManageClaims)]
+ public virtual async Task AddClaimAsync(Guid id, IdentityRoleClaimCreateDto input)
+ {
+ var role = await IdentityRoleRepository.GetAsync(id);
+ var claim = new Claim(input.ClaimType, input.ClaimValue);
+ if (role.FindClaim(claim) != null)
+ {
+ throw new UserFriendlyException(L["RoleClaimAlreadyExists"]);
+ }
+
+ role.AddClaim(GuidGenerator, claim);
+ await IdentityRoleRepository.UpdateAsync(role);
+
+ await CurrentUnitOfWork.SaveChangesAsync();
+ }
+
+ [Authorize(IdentityPermissions.Roles.ManageClaims)]
+ public virtual async Task UpdateClaimAsync(Guid id, IdentityRoleClaimUpdateDto input)
+ {
+ var role = await IdentityRoleRepository.GetAsync(id);
+ var oldClaim = role.FindClaim(new Claim(input.ClaimType, input.ClaimValue));
+ if (oldClaim != null)
+ {
+ role.RemoveClaim(oldClaim.ToClaim());
+ role.AddClaim(GuidGenerator, new Claim(input.ClaimType, input.NewClaimValue));
+
+ await IdentityRoleRepository.UpdateAsync(role);
+
+ await CurrentUnitOfWork.SaveChangesAsync();
+ }
+ }
+
+ [Authorize(IdentityPermissions.Roles.ManageClaims)]
+ public virtual async Task DeleteClaimAsync(Guid id, IdentityRoleClaimDeleteDto input)
+ {
+ var role = await IdentityRoleRepository.GetAsync(id);
+ role.RemoveClaim(new Claim(input.ClaimType, input.ClaimValue));
+
+ await IdentityRoleRepository.UpdateAsync(role);
+
+ await CurrentUnitOfWork.SaveChangesAsync();
+ }
+
+ #endregion
+ }
+}
diff --git a/modules/identity/Sanhe.Abp.Identity.Application/Sanhe/Abp/Identity/IdentityUserAppService.cs b/modules/identity/Sanhe.Abp.Identity.Application/Sanhe/Abp/Identity/IdentityUserAppService.cs
new file mode 100644
index 0000000..e8d2f33
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.Application/Sanhe/Abp/Identity/IdentityUserAppService.cs
@@ -0,0 +1,151 @@
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Identity;
+using Microsoft.Extensions.Options;
+using Sanhe.Abp.Identity.Dto;
+using System;
+using System.Collections.Generic;
+using System.Security.Claims;
+using System.Threading.Tasks;
+using Volo.Abp;
+using Volo.Abp.Application.Dtos;
+using Volo.Abp.Identity;
+
+namespace Sanhe.Abp.Identity
+{
+ [Authorize(Volo.Abp.Identity.IdentityPermissions.Users.Default)]
+ public class IdentityUserAppService : IdentityAppServiceBase, IIdentityUserAppService
+ {
+ protected IdentityUserManager UserManager { get; }
+ protected IOptions IdentityOptions { get; }
+ public IdentityUserAppService(
+ IdentityUserManager userManager,
+ IOptions identityOptions)
+ {
+ UserManager = userManager;
+ IdentityOptions = identityOptions;
+ }
+
+ #region OrganizationUnit
+
+
+ [Authorize(IdentityPermissions.Users.ManageOrganizationUnits)]
+ public virtual async Task> GetOrganizationUnitsAsync(Guid id)
+ {
+ var user = await UserManager.GetByIdAsync(id);
+
+ var organizationUnits = await UserManager.GetOrganizationUnitsAsync(user);
+
+ return new ListResultDto(
+ ObjectMapper.Map, List>(organizationUnits));
+ }
+
+ [Authorize(IdentityPermissions.Users.ManageOrganizationUnits)]
+ public virtual async Task SetOrganizationUnitsAsync(Guid id, IdentityUserOrganizationUnitUpdateDto input)
+ {
+ var user = await UserManager.GetByIdAsync(id);
+
+ await UserManager.SetOrganizationUnitsAsync(user, input.OrganizationUnitIds);
+
+ await CurrentUnitOfWork.SaveChangesAsync();
+ }
+
+ [Authorize(IdentityPermissions.Users.ManageOrganizationUnits)]
+ public virtual async Task RemoveOrganizationUnitsAsync(Guid id, Guid ouId)
+ {
+ await UserManager.RemoveFromOrganizationUnitAsync(id, ouId);
+
+ await CurrentUnitOfWork.SaveChangesAsync();
+ }
+
+ #endregion
+
+ #region Claim
+
+ public virtual async Task> GetClaimsAsync(Guid id)
+ {
+ var user = await UserManager.GetByIdAsync(id);
+
+ return new ListResultDto(ObjectMapper.Map, List>(user.Claims));
+ }
+
+ [Authorize(IdentityPermissions.Users.ManageClaims)]
+ public virtual async Task AddClaimAsync(Guid id, IdentityUserClaimCreateDto input)
+ {
+ var user = await UserManager.GetByIdAsync(id);
+ var claim = new Claim(input.ClaimType, input.ClaimValue);
+ if (user.FindClaim(claim) != null)
+ {
+ throw new UserFriendlyException(L["UserClaimAlreadyExists"]);
+ }
+ user.AddClaim(GuidGenerator, claim);
+ (await UserManager.UpdateAsync(user)).CheckErrors();
+
+ await CurrentUnitOfWork.SaveChangesAsync();
+ }
+
+ [Authorize(IdentityPermissions.Users.ManageClaims)]
+ public virtual async Task UpdateClaimAsync(Guid id, IdentityUserClaimUpdateDto input)
+ {
+ var user = await UserManager.GetByIdAsync(id);
+ var oldClaim = new Claim(input.ClaimType, input.ClaimValue);
+ var newClaim = new Claim(input.ClaimType, input.NewClaimValue);
+ user.ReplaceClaim(oldClaim, newClaim);
+ (await UserManager.UpdateAsync(user)).CheckErrors();
+
+ await CurrentUnitOfWork.SaveChangesAsync();
+ }
+
+ [Authorize(IdentityPermissions.Users.ManageClaims)]
+ public virtual async Task DeleteClaimAsync(Guid id, IdentityUserClaimDeleteDto input)
+ {
+ var user = await UserManager.GetByIdAsync(id);
+ user.RemoveClaim(new Claim(input.ClaimType, input.ClaimValue));
+ (await UserManager.UpdateAsync(user)).CheckErrors();
+
+ await CurrentUnitOfWork.SaveChangesAsync();
+ }
+
+ #endregion
+
+ [Authorize(Volo.Abp.Identity.IdentityPermissions.Users.Update)]
+ public virtual async Task ChangeTwoFactorEnabledAsync(Guid id, TwoFactorEnabledDto input)
+ {
+ var user = await GetUserAsync(id);
+
+ (await UserManager.SetTwoFactorEnabledWithAccountConfirmedAsync(user, input.Enabled)).CheckErrors();
+
+ await CurrentUnitOfWork.SaveChangesAsync();
+ }
+
+ [Authorize(Volo.Abp.Identity.IdentityPermissions.Users.Update)]
+ public virtual async Task LockAsync(Guid id, int seconds)
+ {
+ var user = await GetUserAsync(id);
+ //if (!UserManager.SupportsUserLockout)
+ //{
+ // throw new UserFriendlyException(L["Volo.Abp.Identity:UserLockoutNotEnabled"]);
+ //}
+ var endDate = new DateTimeOffset(Clock.Now).AddSeconds(seconds);
+ (await UserManager.SetLockoutEndDateAsync(user, endDate)).CheckErrors();
+
+ await CurrentUnitOfWork.SaveChangesAsync();
+ }
+
+ [Authorize(Volo.Abp.Identity.IdentityPermissions.Users.Update)]
+ public virtual async Task UnLockAsync(Guid id)
+ {
+ var user = await GetUserAsync(id);
+ (await UserManager.SetLockoutEndDateAsync(user, null)).CheckErrors();
+
+ await CurrentUnitOfWork.SaveChangesAsync();
+ }
+
+ protected virtual async Task GetUserAsync(Guid id)
+ {
+ await IdentityOptions.SetAsync();
+ var user = await UserManager.GetByIdAsync(id);
+
+ return user;
+ }
+ }
+}
diff --git a/modules/identity/Sanhe.Abp.Identity.Application/Sanhe/Abp/Identity/OrganizationUnitAppService.cs b/modules/identity/Sanhe.Abp.Identity.Application/Sanhe/Abp/Identity/OrganizationUnitAppService.cs
new file mode 100644
index 0000000..1f6016f
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.Application/Sanhe/Abp/Identity/OrganizationUnitAppService.cs
@@ -0,0 +1,236 @@
+using Microsoft.AspNetCore.Authorization;
+using Sanhe.Abp.Identity.Dto;
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Volo.Abp.Application.Dtos;
+using Volo.Abp.Identity;
+using Volo.Abp.ObjectExtending;
+
+namespace Sanhe.Abp.Identity
+{
+ [Authorize(IdentityPermissions.OrganizationUnits.Default)]
+ public class OrganizationUnitAppService : IdentityAppServiceBase, IOrganizationUnitAppService
+ {
+ protected OrganizationUnitManager OrganizationUnitManager { get; }
+ protected IOrganizationUnitRepository OrganizationUnitRepository { get; }
+
+ protected IdentityUserManager UserManager { get; }
+ protected IIdentityRoleRepository RoleRepository { get; }
+ protected IIdentityUserRepository UserRepository { get; }
+
+ public OrganizationUnitAppService(
+ IdentityUserManager userManager,
+ IIdentityRoleRepository roleRepository,
+ IIdentityUserRepository userRepository,
+ OrganizationUnitManager organizationUnitManager,
+ IOrganizationUnitRepository organizationUnitRepository)
+ {
+ UserManager = userManager;
+ RoleRepository = roleRepository;
+ UserRepository = userRepository;
+ OrganizationUnitManager = organizationUnitManager;
+ OrganizationUnitRepository = organizationUnitRepository;
+
+ ObjectMapperContext = typeof(AbpIdentityApplicationModule);
+ }
+
+ [Authorize(IdentityPermissions.OrganizationUnits.Create)]
+ public virtual async Task CreateAsync(OrganizationUnitCreateDto input)
+ {
+ var organizationUnit = new OrganizationUnit(
+ GuidGenerator.Create(), input.DisplayName, input.ParentId, CurrentTenant.Id)
+ {
+ CreationTime = Clock.Now
+ };
+ input.MapExtraPropertiesTo(organizationUnit);
+
+ await OrganizationUnitManager.CreateAsync(organizationUnit);
+ await CurrentUnitOfWork.SaveChangesAsync();
+
+ return ObjectMapper.Map(organizationUnit);
+ }
+
+ [Authorize(IdentityPermissions.OrganizationUnits.Delete)]
+ public virtual async Task DeleteAsync(Guid id)
+ {
+ var organizationUnit = await OrganizationUnitRepository.FindAsync(id);
+ if (organizationUnit == null)
+ {
+ return;
+ }
+ await OrganizationUnitManager.DeleteAsync(id);
+ }
+
+ public virtual async Task> GetRootAsync()
+ {
+ var rootorganizationUnits = await OrganizationUnitManager.FindChildrenAsync(null, recursive: false);
+
+ return new ListResultDto(
+ ObjectMapper.Map, List>(rootorganizationUnits));
+ }
+
+ public virtual async Task> FindChildrenAsync(OrganizationUnitGetChildrenDto input)
+ {
+ var organizationUnitChildren = await OrganizationUnitManager.FindChildrenAsync(input.Id, input.Recursive);
+
+ return new ListResultDto(
+ ObjectMapper.Map, List>(organizationUnitChildren));
+ }
+
+ public virtual async Task GetAsync(Guid id)
+ {
+ var organizationUnit = await OrganizationUnitRepository.FindAsync(id);
+
+ return ObjectMapper.Map(organizationUnit);
+ }
+
+ public virtual async Task GetLastChildOrNullAsync(Guid? parentId)
+ {
+ var organizationUnitLastChildren = await OrganizationUnitManager.GetLastChildOrNullAsync(parentId);
+
+ return ObjectMapper.Map(organizationUnitLastChildren);
+ }
+
+ public virtual async Task> GetAllListAsync()
+ {
+ var organizationUnits = await OrganizationUnitRepository.GetListAsync(false);
+
+ return new ListResultDto(
+ ObjectMapper.Map, List>(organizationUnits));
+ }
+
+ public virtual async Task> GetListAsync(OrganizationUnitGetByPagedDto input)
+ {
+ var organizationUnitCount = await OrganizationUnitRepository.GetCountAsync();
+ var organizationUnits = await OrganizationUnitRepository
+ .GetListAsync(input.Sorting, input.MaxResultCount, input.SkipCount, false);
+
+ return new PagedResultDto(organizationUnitCount,
+ ObjectMapper.Map, List>(organizationUnits));
+ }
+
+ [Authorize(IdentityPermissions.OrganizationUnits.ManageRoles)]
+ public virtual async Task> GetRoleNamesAsync(Guid id)
+ {
+ var inOrignizationUnitRoleNames = await UserRepository.GetRoleNamesInOrganizationUnitAsync(id);
+ return new ListResultDto(inOrignizationUnitRoleNames);
+ }
+
+ [Authorize(IdentityPermissions.OrganizationUnits.ManageRoles)]
+ public virtual async Task> GetUnaddedRolesAsync(Guid id, OrganizationUnitGetUnaddedRoleByPagedDto input)
+ {
+ var organizationUnit = await OrganizationUnitRepository.GetAsync(id);
+
+ var organizationUnitRoleCount = await OrganizationUnitRepository
+ .GetUnaddedRolesCountAsync(organizationUnit, input.Filter);
+
+ var organizationUnitRoles = await OrganizationUnitRepository
+ .GetUnaddedRolesAsync(organizationUnit,
+ input.Sorting, input.MaxResultCount,
+ input.SkipCount, input.Filter);
+
+ return new PagedResultDto(organizationUnitRoleCount,
+ ObjectMapper.Map, List>(organizationUnitRoles));
+ }
+
+ [Authorize(IdentityPermissions.OrganizationUnits.ManageRoles)]
+ public virtual async Task> GetRolesAsync(Guid id, PagedAndSortedResultRequestDto input)
+ {
+ var organizationUnit = await OrganizationUnitRepository.GetAsync(id);
+
+ var organizationUnitRoleCount = await OrganizationUnitRepository
+ .GetRolesCountAsync(organizationUnit);
+
+ var organizationUnitRoles = await OrganizationUnitRepository
+ .GetRolesAsync(organizationUnit,
+ input.Sorting, input.MaxResultCount,
+ input.SkipCount);
+
+ return new PagedResultDto(organizationUnitRoleCount,
+ ObjectMapper.Map, List>(organizationUnitRoles));
+ }
+
+
+ [Authorize(IdentityPermissions.OrganizationUnits.ManageUsers)]
+ public virtual async Task> GetUnaddedUsersAsync(Guid id, OrganizationUnitGetUnaddedUserByPagedDto input)
+ {
+ var organizationUnit = await OrganizationUnitRepository.GetAsync(id);
+
+ var organizationUnitUserCount = await OrganizationUnitRepository
+ .GetUnaddedUsersCountAsync(organizationUnit, input.Filter);
+ var organizationUnitUsers = await OrganizationUnitRepository
+ .GetUnaddedUsersAsync(organizationUnit,
+ input.Sorting, input.MaxResultCount,
+ input.SkipCount, input.Filter);
+
+ return new PagedResultDto(organizationUnitUserCount,
+ ObjectMapper.Map, List>(organizationUnitUsers));
+ }
+
+ [Authorize(IdentityPermissions.OrganizationUnits.ManageUsers)]
+ public virtual async Task> GetUsersAsync(Guid id, GetIdentityUsersInput input)
+ {
+ var organizationUnit = await OrganizationUnitRepository.GetAsync(id);
+
+ var organizationUnitUserCount = await OrganizationUnitRepository
+ .GetMembersCountAsync(organizationUnit, input.Filter);
+ var organizationUnitUsers = await OrganizationUnitRepository
+ .GetMembersAsync(organizationUnit,
+ input.Sorting, input.MaxResultCount,
+ input.SkipCount, input.Filter);
+
+ return new PagedResultDto(organizationUnitUserCount,
+ ObjectMapper.Map, List>(organizationUnitUsers));
+ }
+
+ [Authorize(IdentityPermissions.OrganizationUnits.Update)]
+ public virtual async Task MoveAsync(Guid id, OrganizationUnitMoveDto input)
+ {
+ await OrganizationUnitManager.MoveAsync(id, input.ParentId);
+ }
+
+ [Authorize(IdentityPermissions.OrganizationUnits.Update)]
+ public virtual async Task UpdateAsync(Guid id, OrganizationUnitUpdateDto input)
+ {
+ var organizationUnit = await OrganizationUnitRepository.GetAsync(id);
+ organizationUnit.DisplayName = input.DisplayName;
+ input.MapExtraPropertiesTo(organizationUnit);
+
+ await OrganizationUnitManager.UpdateAsync(organizationUnit);
+ await CurrentUnitOfWork.SaveChangesAsync();
+
+ return ObjectMapper.Map(organizationUnit);
+ }
+
+ [Authorize(IdentityPermissions.OrganizationUnits.ManageUsers)]
+ public virtual async Task AddUsersAsync(Guid id, OrganizationUnitAddUserDto input)
+ {
+ var organizationUnit = await OrganizationUnitRepository.GetAsync(id);
+ var users = await UserRepository.GetListByIdListAsync(input.UserIds, includeDetails: true);
+
+ // 调用内部方法设置用户组织机构
+ foreach (var user in users)
+ {
+ await UserManager.AddToOrganizationUnitAsync(user, organizationUnit);
+ }
+
+ await CurrentUnitOfWork.SaveChangesAsync();
+ }
+
+ [Authorize(IdentityPermissions.OrganizationUnits.ManageRoles)]
+ public virtual async Task AddRolesAsync(Guid id, OrganizationUnitAddRoleDto input)
+ {
+ var organizationUnit = await OrganizationUnitRepository.GetAsync(id);
+
+ var roles = await RoleRepository.GetListByIdListAsync(input.RoleIds, includeDetails: true);
+
+ foreach (var role in roles)
+ {
+ await OrganizationUnitManager.AddRoleToOrganizationUnitAsync(role, organizationUnit);
+ }
+
+ await CurrentUnitOfWork.SaveChangesAsync();
+ }
+ }
+}
diff --git a/modules/identity/Sanhe.Abp.Identity.Domain.Shared/FodyWeavers.xml b/modules/identity/Sanhe.Abp.Identity.Domain.Shared/FodyWeavers.xml
new file mode 100644
index 0000000..1715698
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.Domain.Shared/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/modules/identity/Sanhe.Abp.Identity.Domain.Shared/Sanhe.Abp.Identity.Domain.Shared.csproj b/modules/identity/Sanhe.Abp.Identity.Domain.Shared/Sanhe.Abp.Identity.Domain.Shared.csproj
new file mode 100644
index 0000000..ccce677
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.Domain.Shared/Sanhe.Abp.Identity.Domain.Shared.csproj
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+ netstandard2.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/modules/identity/Sanhe.Abp.Identity.Domain.Shared/Sanhe/Abp/Identity/AbpIdentityDomainSharedModule.cs b/modules/identity/Sanhe.Abp.Identity.Domain.Shared/Sanhe/Abp/Identity/AbpIdentityDomainSharedModule.cs
new file mode 100644
index 0000000..23e5796
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.Domain.Shared/Sanhe/Abp/Identity/AbpIdentityDomainSharedModule.cs
@@ -0,0 +1,26 @@
+using Volo.Abp.Identity.Localization;
+using Volo.Abp.Localization;
+using Volo.Abp.Modularity;
+using Volo.Abp.VirtualFileSystem;
+
+namespace Sanhe.Abp.Identity
+{
+ [DependsOn(typeof(Volo.Abp.Identity.AbpIdentityDomainSharedModule))]
+ public class AbpIdentityDomainSharedModule : AbpModule
+ {
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ Configure(options =>
+ {
+ options.FileSets.AddEmbedded();
+ });
+
+ Configure(options =>
+ {
+ options.Resources
+ .Get()
+ .AddVirtualJson("/Sanhe/Abp/Identity/Localization");
+ });
+ }
+ }
+}
diff --git a/modules/identity/Sanhe.Abp.Identity.Domain.Shared/Sanhe/Abp/Identity/IUserSecurityCodeSender.cs b/modules/identity/Sanhe.Abp.Identity.Domain.Shared/Sanhe/Abp/Identity/IUserSecurityCodeSender.cs
new file mode 100644
index 0000000..b287ce5
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.Domain.Shared/Sanhe/Abp/Identity/IUserSecurityCodeSender.cs
@@ -0,0 +1,36 @@
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Sanhe.Abp.Identity
+{
+ public interface IUserSecurityCodeSender
+ {
+ ///
+ /// 发送手机确认码
+ ///
+ /// 手机号
+ /// 令牌
+ /// 模板号
+ ///
+ ///
+ Task SendPhoneConfirmedCodeAsync(
+ string phone,
+ string token,
+ string template, // 传递模板号
+ CancellationToken cancellation = default);
+
+ ///
+ /// 发送邮箱确认码
+ ///
+ /// 用户名
+ /// 邮箱
+ /// 令牌
+ ///
+ ///
+ Task SendEmailConfirmedCodeAsync(
+ string userName,
+ string email,
+ string token,
+ CancellationToken cancellation = default);
+ }
+}
diff --git a/modules/identity/Sanhe.Abp.Identity.Domain.Shared/Sanhe/Abp/Identity/IdentityConsts.cs b/modules/identity/Sanhe.Abp.Identity.Domain.Shared/Sanhe/Abp/Identity/IdentityConsts.cs
new file mode 100644
index 0000000..fe924d5
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.Domain.Shared/Sanhe/Abp/Identity/IdentityConsts.cs
@@ -0,0 +1,15 @@
+namespace Sanhe.Abp.Identity
+{
+ public static class IdentityConsts
+ {
+ public static class ClaimType
+ {
+ public static class Avatar
+ {
+ public static string Name { get; set; } = "avatarUrl";
+ public static string DisplayName { get; set; } = "Your avatar url";
+ public static string Description { get; set; } = "Your avatar url";
+ }
+ }
+ }
+}
diff --git a/modules/identity/Sanhe.Abp.Identity.Domain.Shared/Sanhe/Abp/Identity/IdentityErrorCodes.cs b/modules/identity/Sanhe.Abp.Identity.Domain.Shared/Sanhe/Abp/Identity/IdentityErrorCodes.cs
new file mode 100644
index 0000000..9ab046e
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.Domain.Shared/Sanhe/Abp/Identity/IdentityErrorCodes.cs
@@ -0,0 +1,26 @@
+namespace Sanhe.Abp.Identity
+{
+ public static class IdentityErrorCodes
+ {
+ ///
+ /// 无法变更静态声明类型
+ ///
+ public const string StaticClaimTypeChange = "Volo.Abp.Identity:020005";
+ ///
+ /// 无法删除静态声明类型
+ ///
+ public const string StaticClaimTypeDeletion = "Volo.Abp.Identity:020006";
+ ///
+ /// 手机号码已被使用
+ ///
+ public const string DuplicatePhoneNumber = "Volo.Abp.Identity:020007";
+ ///
+ /// 你不能修改你的手机绑定信息
+ ///
+ public const string UsersCanNotChangePhoneNumber = "Volo.Abp.Identity:020008";
+ ///
+ /// 你不能修改你的邮件绑定信息
+ ///
+ public const string UsersCanNotChangeEmailAddress = "Volo.Abp.Identity:020009";
+ }
+}
diff --git a/modules/identity/Sanhe.Abp.Identity.Domain.Shared/Sanhe/Abp/Identity/IdentityException.cs b/modules/identity/Sanhe.Abp.Identity.Domain.Shared/Sanhe/Abp/Identity/IdentityException.cs
new file mode 100644
index 0000000..91b89c5
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.Domain.Shared/Sanhe/Abp/Identity/IdentityException.cs
@@ -0,0 +1,38 @@
+using Microsoft.Extensions.Logging;
+using System;
+using System.Runtime.Serialization;
+using Volo.Abp;
+using Volo.Abp.Logging;
+
+namespace Sanhe.Abp.Identity
+{
+ public class IdentityException : BusinessException, IExceptionWithSelfLogging
+ {
+ public IdentityException(
+ SerializationInfo serializationInfo,
+ StreamingContext context)
+ : base(serializationInfo, context)
+ {
+ }
+
+ public IdentityException(
+ string code = null,
+ string message = null,
+ string details = null,
+ Exception innerException = null,
+ LogLevel logLevel = LogLevel.Warning)
+ : base(code, message, details, innerException, logLevel)
+ {
+ }
+
+ public virtual void Log(ILogger logger)
+ {
+ logger.Log(
+ LogLevel,
+ "An id error occurred,code: {0}, Message: {1}, Details: {2}",
+ Code,
+ Message,
+ Details);
+ }
+ }
+}
diff --git a/modules/identity/Sanhe.Abp.Identity.Domain.Shared/Sanhe/Abp/Identity/Localization/en.json b/modules/identity/Sanhe.Abp.Identity.Domain.Shared/Sanhe/Abp/Identity/Localization/en.json
new file mode 100644
index 0000000..f451ba5
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.Domain.Shared/Sanhe/Abp/Identity/Localization/en.json
@@ -0,0 +1,64 @@
+{
+ "culture": "en",
+ "texts": {
+ "Permission:OrganizationUnitManagement": "Organization unit management",
+ "Permission:ManageRoles": "Management roles",
+ "Permission:ManageUsers": "Management users",
+ "Permission:ManageClaims": "Management claims",
+ "Permission:ManageOrganizationUnits": "Management organization units",
+ "Permission:IdentityClaimTypeManagement": "Management claim types",
+ "OrganizationUnit:Tree": "Organization tree",
+ "OrganizationUnit:New": "Add New",
+ "OrganizationUnit:Members": "Organization Members",
+ "OrganizationUnit:AddRoot": "Add Root",
+ "OrganizationUnit:AddChildren": "Add Children",
+ "OrganizationUnit:AddMember": "Add Member",
+ "OrganizationUnit:AddRole": "Add Role",
+ "OrganizationUnit:DisplayName": "Display Name",
+ "OrganizationUnit:WillDelete": "Organization: {0} will be deleted",
+ "OrganizationUnit:AreYouSureRemoveUser": "Are you sure you want to delete user {0} from your organization?",
+ "OrganizationUnit:AreYouSureRemoveRole": "Are you sure you want to remove the role {0} from the organization?",
+ "OrganizationUnit:SelectUsers": "Select Users",
+ "OrganizationUnit:SelectRoles": "Select Roles",
+ "IdentityClaimTypeAlreadyExists": "The identity claim type {0} already exists!",
+ "UserClaimAlreadyExists": "User claim with the same parameters have been added!",
+ "RoleClaimAlreadyExists": "Role claim with the same parameters has been added!",
+ "DisplayName:ClaimType": "Type",
+ "DisplayName:ClaimValue": "Value",
+ "ClaimSubject": "Claim - {0}",
+ "RoleSubject": "Role - {0}",
+ "AddClaim": "Add claim",
+ "UpdateClaim": "Update claim",
+ "DeleteClaim": "Delete claim",
+ "WillDeleteClaim": "Pending deletion claim type: {0}",
+ "ManageClaim": "Management claim",
+ "IdentityClaim:New": "New claim",
+ "IdentityClaim:Name": "Name",
+ "IdentityClaim:Required": "Required",
+ "IdentityClaim:IsStatic": "Is static",
+ "IdentityClaim:Regex": "Regex",
+ "IdentityClaim:RegexDescription": "Regex description",
+ "IdentityClaim:Description": "Description",
+ "IdentityClaim:ValueType": "Value type",
+ "Volo.Abp.Identity:020005": "The static claim type cannot be changed!",
+ "Volo.Abp.Identity:020006": "Unable to delete static claim type!",
+ "Volo.Abp.Identity:020007": "The phone number is already tied to another user!",
+ "Volo.Abp.Identity:020008": "You can't modify your phone's binding information!",
+ "Volo.Abp.Identity:020009": "You cannot modify your email binding information!",
+ "Volo.Abp.Identity:DuplicatePhoneNumber": "Phone number '{0}' is already taken.",
+ "DisplayName:Abp.Identity.User.SmsNewUserRegister": "Register sms template",
+ "Description:Abp.Identity.User.SmsNewUserRegister": "When the user registers, he/she should send the template number of the SMS verification code and fill in the template number of the corresponding cloud platform registration",
+ "DisplayName:Abp.Identity.User.SmsUserSignin": "Signin sms template",
+ "Description:Abp.Identity.User.SmsUserSignin": "When the user logs in, he/she should send the template number of the SMS verification code and fill in the template number of the corresponding cloud platform registration",
+ "DisplayName:Abp.Identity.User.SmsResetPassword": "Reset password sms template",
+ "Description:Abp.Identity.User.SmsResetPassword": "When the user resets the password, he/she sends the template number of SMS verification code and fills in the template number registered on the cloud platform",
+ "DisplayName:Abp.Identity.User.SmsPhoneNumberConfirmed": "Phone number confirmation template",
+ "Description:Abp.Identity.User.SmsPhoneNumberConfirmed": "The user confirms the mobile phone verification code template",
+ "DisplayName:Abp.Identity.User.SmsRepeatInterval": "SMS verification code validity(min)",
+ "Description:Abp.Identity.User.SmsRepeatInterval": "The valid time for the user to send SMS verification code, unit m, default 3min",
+ "DisplayName:SmsVerifyCode": "SMS verification code",
+ "DisplayName:EmailVerifyCode": "Mail verification code",
+ "DisplayName:WeChatCode": "Wechat login code",
+ "SendRepeatSmsVerifyCode": "Phone verification code cannot be sent repeatedly within {0} minutes!"
+ }
+}
\ No newline at end of file
diff --git a/modules/identity/Sanhe.Abp.Identity.Domain.Shared/Sanhe/Abp/Identity/Localization/zh-Hans.json b/modules/identity/Sanhe.Abp.Identity.Domain.Shared/Sanhe/Abp/Identity/Localization/zh-Hans.json
new file mode 100644
index 0000000..04ed3d9
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.Domain.Shared/Sanhe/Abp/Identity/Localization/zh-Hans.json
@@ -0,0 +1,64 @@
+{
+ "culture": "zh-Hans",
+ "texts": {
+ "Permission:OrganizationUnitManagement": "组织机构管理",
+ "Permission:ManageRoles": "管理角色",
+ "Permission:ManageUsers": "管理用户",
+ "Permission:ManageClaims": "管理声明",
+ "Permission:ManageOrganizationUnits": "管理组织机构",
+ "Permission:IdentityClaimTypeManagement": "管理声明类型",
+ "OrganizationUnit:Tree": "组织机构树",
+ "OrganizationUnit:New": "新组织结构",
+ "OrganizationUnit:Members": "机构成员",
+ "OrganizationUnit:AddRoot": "添加根机构",
+ "OrganizationUnit:AddChildren": "添加子机构",
+ "OrganizationUnit:AddMember": "添加成员",
+ "OrganizationUnit:AddRole": "添加角色",
+ "OrganizationUnit:DisplayName": "显示名称",
+ "OrganizationUnit:WillDelete": "组织机构: {0} 将被删除",
+ "OrganizationUnit:AreYouSureRemoveUser": "你确定要从组织机构中删除用户 {0} 吗?",
+ "OrganizationUnit:AreYouSureRemoveRole": "你确定要从组织机构中删除角色 {0} 吗?",
+ "OrganizationUnit:SelectUsers": "选择用户",
+ "OrganizationUnit:SelectRoles": "选择角色",
+ "IdentityClaimTypeAlreadyExists": "声明类型 {0} 已经存在!",
+ "UserClaimAlreadyExists": "已经添加相同参数的用户声明!",
+ "RoleClaimAlreadyExists": "已经添加相同参数的角色声明",
+ "DisplayName:ClaimType": "声明类型",
+ "DisplayName:ClaimValue": "声明值",
+ "ClaimSubject": "声明 - {0}",
+ "RoleSubject": "角色 - {0}",
+ "AddClaim": "添加声明",
+ "UpdateClaim": "变更声明",
+ "DeleteClaim": "删除声明",
+ "WillDeleteClaim": "声明类型: {0} 将被删除",
+ "ManageClaim": "管理声明",
+ "IdentityClaim:New": "新声明类型",
+ "IdentityClaim:Name": "名称",
+ "IdentityClaim:Required": "是否必须",
+ "IdentityClaim:IsStatic": "是否静态",
+ "IdentityClaim:Regex": "正则",
+ "IdentityClaim:RegexDescription": "正则描述",
+ "IdentityClaim:Description": "描述",
+ "IdentityClaim:ValueType": "值类型",
+ "Volo.Abp.Identity:020005": "无法变更静态声明类型!",
+ "Volo.Abp.Identity:020006": "无法删除静态声明类型!",
+ "Volo.Abp.Identity:020007": "手机号码已被其他用户绑定!",
+ "Volo.Abp.Identity:020008": "你不能修改你的手机绑定信息!",
+ "Volo.Abp.Identity:020009": "你不能修改你的邮件绑定信息!",
+ "Volo.Abp.Identity:DuplicatePhoneNumber": "手机号 '{0}' 已存在.",
+ "DisplayName:Abp.Identity.User.SmsNewUserRegister": "新用户注册模板",
+ "Description:Abp.Identity.User.SmsNewUserRegister": "新用户通过手机注册账号验证码模板",
+ "DisplayName:Abp.Identity.User.SmsUserSignin": "用户登录模板",
+ "Description:Abp.Identity.User.SmsUserSignin": "用户通过手机登录验证码模板",
+ "DisplayName:Abp.Identity.User.SmsResetPassword": "密码找回模板",
+ "Description:Abp.Identity.User.SmsResetPassword": "用户通过手机找回密码验证码模板",
+ "DisplayName:Abp.Identity.User.SmsPhoneNumberConfirmed": "手机确认模板",
+ "Description:Abp.Identity.User.SmsPhoneNumberConfirmed": "用户确认手机号验证码模板",
+ "DisplayName:Abp.Identity.User.SmsRepeatInterval": "重复发送间隔时间(min)",
+ "Description:Abp.Identity.User.SmsRepeatInterval": "验证码重复发送的最小时间差,单位为分",
+ "DisplayName:SmsVerifyCode": "短信验证码",
+ "DisplayName:EmailVerifyCode": "邮件验证码",
+ "DisplayName:WeChatCode": "微信登录凭证",
+ "SendRepeatSmsVerifyCode": "手机验证码不能在 {0} 分钟内重复发送!"
+ }
+}
\ No newline at end of file
diff --git a/modules/identity/Sanhe.Abp.Identity.Domain.Shared/Sanhe/Abp/Identity/Security/DefaultTotpService.cs b/modules/identity/Sanhe.Abp.Identity.Domain.Shared/Sanhe/Abp/Identity/Security/DefaultTotpService.cs
new file mode 100644
index 0000000..343ef3e
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.Domain.Shared/Sanhe/Abp/Identity/Security/DefaultTotpService.cs
@@ -0,0 +1,121 @@
+using System;
+using System.Diagnostics;
+using System.Net;
+using System.Security.Cryptography;
+using System.Text;
+using Volo.Abp.DependencyInjection;
+
+namespace Sanhe.Abp.Identity.Security
+{
+ ///
+ /// 微软的实现
+ /// See: Microsoft.AspNetCore.Identity.Rfc6238AuthenticationService
+ ///
+ internal class DefaultTotpService : ITotpService, ISingletonDependency
+ {
+ private static readonly TimeSpan _timestep = TimeSpan.FromMinutes(3);
+ private static readonly Encoding _encoding = new UTF8Encoding(false, true);
+#if NETSTANDARD2_0
+ private static readonly DateTime _unixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
+ private static readonly RandomNumberGenerator _rng = RandomNumberGenerator.Create();
+#endif
+
+ // Generates a new 80-bit security token
+ public static byte[] GenerateRandomKey()
+ {
+ var bytes = new byte[20];
+#if NETSTANDARD2_0
+ _rng.GetBytes(bytes);
+#else
+ RandomNumberGenerator.Fill(bytes);
+#endif
+ return bytes;
+ }
+
+ internal static int ComputeTotp(HashAlgorithm hashAlgorithm, ulong timestepNumber, string modifier)
+ {
+ // # of 0's = length of pin
+ const int Mod = 1000000;
+
+ // See https://tools.ietf.org/html/rfc4226
+ // We can add an optional modifier
+ var timestepAsBytes = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((long)timestepNumber));
+ var hash = hashAlgorithm.ComputeHash(ApplyModifier(timestepAsBytes, modifier));
+
+ // Generate DT string
+ var offset = hash[hash.Length - 1] & 0xf;
+ Debug.Assert(offset + 4 < hash.Length);
+ var binaryCode = (hash[offset] & 0x7f) << 24
+ | (hash[offset + 1] & 0xff) << 16
+ | (hash[offset + 2] & 0xff) << 8
+ | hash[offset + 3] & 0xff;
+
+ return binaryCode % Mod;
+ }
+
+ private static byte[] ApplyModifier(byte[] input, string modifier)
+ {
+ if (string.IsNullOrEmpty(modifier))
+ {
+ return input;
+ }
+
+ var modifierBytes = _encoding.GetBytes(modifier);
+ var combined = new byte[checked(input.Length + modifierBytes.Length)];
+ Buffer.BlockCopy(input, 0, combined, 0, input.Length);
+ Buffer.BlockCopy(modifierBytes, 0, combined, input.Length, modifierBytes.Length);
+ return combined;
+ }
+
+ // More info: https://tools.ietf.org/html/rfc6238#section-4
+ private static ulong GetCurrentTimeStepNumber()
+ {
+#if NETSTANDARD2_0
+ var delta = DateTime.UtcNow - _unixEpoch;
+#else
+ var delta = DateTimeOffset.UtcNow - DateTimeOffset.UnixEpoch;
+#endif
+ return (ulong)(delta.Ticks / _timestep.Ticks);
+ }
+
+ public int GenerateCode(byte[] securityToken, string modifier = null)
+ {
+ if (securityToken == null)
+ {
+ throw new ArgumentNullException(nameof(securityToken));
+ }
+
+ // Allow a variance of no greater than 9 minutes in either direction
+ var currentTimeStep = GetCurrentTimeStepNumber();
+ using (var hashAlgorithm = new HMACSHA1(securityToken))
+ {
+ return ComputeTotp(hashAlgorithm, currentTimeStep, modifier);
+ }
+ }
+
+ public bool ValidateCode(byte[] securityToken, int code, string modifier = null)
+ {
+ if (securityToken == null)
+ {
+ throw new ArgumentNullException(nameof(securityToken));
+ }
+
+ // Allow a variance of no greater than 9 minutes in either direction
+ var currentTimeStep = GetCurrentTimeStepNumber();
+ using (var hashAlgorithm = new HMACSHA1(securityToken))
+ {
+ for (var i = -2; i <= 2; i++)
+ {
+ var computedTotp = ComputeTotp(hashAlgorithm, (ulong)((long)currentTimeStep + i), modifier);
+ if (computedTotp == code)
+ {
+ return true;
+ }
+ }
+ }
+
+ // No match
+ return false;
+ }
+ }
+}
diff --git a/modules/identity/Sanhe.Abp.Identity.Domain.Shared/Sanhe/Abp/Identity/Security/ITotpService.cs b/modules/identity/Sanhe.Abp.Identity.Domain.Shared/Sanhe/Abp/Identity/Security/ITotpService.cs
new file mode 100644
index 0000000..f14c8c8
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.Domain.Shared/Sanhe/Abp/Identity/Security/ITotpService.cs
@@ -0,0 +1,25 @@
+namespace Sanhe.Abp.Identity.Security
+{
+ ///
+ /// totp算法服务
+ ///
+ public interface ITotpService
+ {
+ ///
+ /// 生成
+ ///
+ ///
+ ///
+ ///
+ int GenerateCode(byte[] securityToken, string modifier = null);
+
+ ///
+ /// 验证
+ ///
+ ///
+ ///
+ ///
+ ///
+ bool ValidateCode(byte[] securityToken, int code, string modifier = null);
+ }
+}
diff --git a/modules/identity/Sanhe.Abp.Identity.Domain.Shared/Sanhe/Abp/Identity/Settings/IdentitySettingDefinitionProvider.cs b/modules/identity/Sanhe.Abp.Identity.Domain.Shared/Sanhe/Abp/Identity/Settings/IdentitySettingDefinitionProvider.cs
new file mode 100644
index 0000000..7d027e7
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.Domain.Shared/Sanhe/Abp/Identity/Settings/IdentitySettingDefinitionProvider.cs
@@ -0,0 +1,75 @@
+using Volo.Abp.Identity.Localization;
+using Volo.Abp.Localization;
+using Volo.Abp.Settings;
+
+namespace Sanhe.Abp.Identity.Settings
+{
+ public class IdentitySettingDefinitionProvider : SettingDefinitionProvider
+ {
+ public override void Define(ISettingDefinitionContext context)
+ {
+ context.Add(
+ new SettingDefinition(
+ name: IdentitySettingNames.User.SmsNewUserRegister,
+ defaultValue: "",
+ displayName: L("DisplayName:Abp.Identity.User.SmsNewUserRegister"),
+ description: L("Description:Abp.Identity.User.SmsNewUserRegister"),
+ isVisibleToClients: true)
+ .WithProviders(
+ DefaultValueSettingValueProvider.ProviderName,
+ ConfigurationSettingValueProvider.ProviderName,
+ GlobalSettingValueProvider.ProviderName,
+ TenantSettingValueProvider.ProviderName),
+ new SettingDefinition(
+ name: IdentitySettingNames.User.SmsUserSignin,
+ defaultValue: "",
+ displayName: L("DisplayName:Abp.Identity.User.SmsUserSignin"),
+ description: L("Description:Abp.Identity.User.SmsUserSignin"),
+ isVisibleToClients: true)
+ .WithProviders(
+ DefaultValueSettingValueProvider.ProviderName,
+ ConfigurationSettingValueProvider.ProviderName,
+ GlobalSettingValueProvider.ProviderName,
+ TenantSettingValueProvider.ProviderName),
+ new SettingDefinition(
+ name: IdentitySettingNames.User.SmsResetPassword,
+ defaultValue: "",
+ displayName: L("DisplayName:Abp.Identity.User.SmsResetPassword"),
+ description: L("Description:Abp.Identity.User.SmsResetPassword"),
+ isVisibleToClients: true)
+ .WithProviders(
+ DefaultValueSettingValueProvider.ProviderName,
+ ConfigurationSettingValueProvider.ProviderName,
+ GlobalSettingValueProvider.ProviderName,
+ TenantSettingValueProvider.ProviderName),
+ new SettingDefinition(
+ name: IdentitySettingNames.User.SmsPhoneNumberConfirmed,
+ defaultValue: "",
+ displayName: L("DisplayName:Abp.Identity.User.SmsPhoneNumberConfirmed"),
+ description: L("Description:Abp.Identity.User.SmsPhoneNumberConfirmed"),
+ isVisibleToClients: true)
+ .WithProviders(
+ DefaultValueSettingValueProvider.ProviderName,
+ ConfigurationSettingValueProvider.ProviderName,
+ GlobalSettingValueProvider.ProviderName,
+ TenantSettingValueProvider.ProviderName),
+ new SettingDefinition(
+ name: IdentitySettingNames.User.SmsRepeatInterval,
+ defaultValue: "5",
+ displayName: L("DisplayName:Abp.Identity.User.SmsRepeatInterval"),
+ description: L("Description:Abp.Identity.User.SmsRepeatInterval"),
+ isVisibleToClients: true)
+ .WithProviders(
+ DefaultValueSettingValueProvider.ProviderName,
+ ConfigurationSettingValueProvider.ProviderName,
+ GlobalSettingValueProvider.ProviderName,
+ TenantSettingValueProvider.ProviderName)
+ );
+ }
+
+ private static LocalizableString L(string name)
+ {
+ return LocalizableString.Create(name);
+ }
+ }
+}
diff --git a/modules/identity/Sanhe.Abp.Identity.Domain.Shared/Sanhe/Abp/Identity/Settings/IdentitySettingNames.cs b/modules/identity/Sanhe.Abp.Identity.Domain.Shared/Sanhe/Abp/Identity/Settings/IdentitySettingNames.cs
new file mode 100644
index 0000000..f327426
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.Domain.Shared/Sanhe/Abp/Identity/Settings/IdentitySettingNames.cs
@@ -0,0 +1,32 @@
+namespace Sanhe.Abp.Identity.Settings
+{
+ public static class IdentitySettingNames
+ {
+ private const string Prefix = "Abp.Identity";
+
+ public static class User
+ {
+ private const string UserPrefix = Prefix + ".User";
+ ///
+ /// 用户手机验证短信模板
+ ///
+ public const string SmsPhoneNumberConfirmed = UserPrefix + ".SmsPhoneNumberConfirmed";
+ ///
+ /// 用户注册短信验证码模板号
+ ///
+ public const string SmsNewUserRegister = UserPrefix + ".SmsNewUserRegister";
+ ///
+ /// 用户登录短信验证码模板号
+ ///
+ public const string SmsUserSignin = UserPrefix + ".SmsUserSignin";
+ ///
+ /// 用户重置密码短信验证码模板号
+ ///
+ public const string SmsResetPassword = UserPrefix + ".SmsResetPassword";
+ ///
+ /// 验证码重复间隔时间
+ ///
+ public const string SmsRepeatInterval = UserPrefix + ".SmsRepeatInterval";
+ }
+ }
+}
diff --git a/modules/identity/Sanhe.Abp.Identity.Domain/FodyWeavers.xml b/modules/identity/Sanhe.Abp.Identity.Domain/FodyWeavers.xml
new file mode 100644
index 0000000..1715698
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.Domain/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/modules/identity/Sanhe.Abp.Identity.Domain/Microsoft/AspNetCore/Identity/IdentityUserManagerExtensions.cs b/modules/identity/Sanhe.Abp.Identity.Domain/Microsoft/AspNetCore/Identity/IdentityUserManagerExtensions.cs
new file mode 100644
index 0000000..5b817d8
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.Domain/Microsoft/AspNetCore/Identity/IdentityUserManagerExtensions.cs
@@ -0,0 +1,39 @@
+using JetBrains.Annotations;
+using System.Threading.Tasks;
+using Volo.Abp;
+using Volo.Abp.Identity;
+
+namespace Microsoft.AspNetCore.Identity
+{
+ public static class IdentityUserManagerExtensions
+ {
+ public static async Task SetTwoFactorEnabledWithAccountConfirmedAsync(
+ [NotNull] this UserManager userManager,
+ [NotNull] TUser user,
+ bool enabled)
+ where TUser : class
+ {
+ Check.NotNull(userManager, nameof(userManager));
+ Check.NotNull(user, nameof(user));
+
+ if (enabled)
+ {
+ var phoneNumberConfirmed = await userManager.IsPhoneNumberConfirmedAsync(user);
+ var emailAddressConfirmed = await userManager.IsEmailConfirmedAsync(user);
+ // 如果其中一个安全选项未确认,无法启用双因素验证
+ if (!phoneNumberConfirmed && !emailAddressConfirmed)
+ {
+ // TODO: 返回标准的 IdentityResult
+ //var error = new IdentityError();
+ //return IdentityResult.Failed(error);
+
+ throw new Sanhe.Abp.Identity.IdentityException(
+ IdentityErrorCodes.CanNotChangeTwoFactor,
+ details: phoneNumberConfirmed ? "phone number not confirmed" : "email address not confirmed");
+ }
+ }
+
+ return await userManager.SetTwoFactorEnabledAsync(user, enabled);
+ }
+ }
+}
diff --git a/modules/identity/Sanhe.Abp.Identity.Domain/Microsoft/AspNetCore/Identity/PhoneNumberUserValidator.cs b/modules/identity/Sanhe.Abp.Identity.Domain/Microsoft/AspNetCore/Identity/PhoneNumberUserValidator.cs
new file mode 100644
index 0000000..dbb8dab
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.Domain/Microsoft/AspNetCore/Identity/PhoneNumberUserValidator.cs
@@ -0,0 +1,58 @@
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Localization;
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Volo.Abp;
+using Volo.Abp.DependencyInjection;
+using Volo.Abp.Identity;
+using Volo.Abp.Identity.Localization;
+using IIdentityUserRepository = Sanhe.Abp.Identity.IIdentityUserRepository;
+
+namespace Microsoft.AspNetCore.Identity
+{
+ [Dependency(ServiceLifetime.Scoped, ReplaceServices = true)]
+ [ExposeServices(typeof(IUserValidator))]
+ public class PhoneNumberUserValidator : UserValidator
+ {
+ private readonly IStringLocalizer _stringLocalizer;
+ private readonly IIdentityUserRepository _userRepository;
+
+ public PhoneNumberUserValidator(
+ IIdentityUserRepository userRepository,
+ IStringLocalizer stringLocalizer)
+ {
+ _userRepository = userRepository;
+ _stringLocalizer = stringLocalizer;
+ }
+
+ public async override Task ValidateAsync(UserManager manager, IdentityUser user)
+ {
+ var errors = new List();
+ await ValidatePhoneNumberAsync(manager, user, errors);
+
+ return (errors.Count > 0)
+ ? IdentityResult.Failed(errors.ToArray())
+ : await base.ValidateAsync(manager, user);
+ }
+
+ protected async virtual Task ValidatePhoneNumberAsync(UserManager manager, IdentityUser user, ICollection errors)
+ {
+ var phoneNumber = await manager.GetPhoneNumberAsync(user);
+ if (phoneNumber.IsNullOrWhiteSpace())
+ {
+ return;
+ }
+ var findUser = await _userRepository.FindByPhoneNumberAsync(phoneNumber, false);
+ if (findUser != null && !findUser.Id.Equals(user.Id))
+ {
+ //errors.Add(new IdentityError
+ //{
+ // Code = "DuplicatePhoneNumber",
+ // Description = _stringLocalizer["DuplicatePhoneNumber", phoneNumber]
+ //});
+ throw new UserFriendlyException(_stringLocalizer["Volo.Abp.Identity:DuplicatePhoneNumber", phoneNumber]);
+ }
+ }
+ }
+}
diff --git a/modules/identity/Sanhe.Abp.Identity.Domain/Sanhe.Abp.Identity.Domain.csproj b/modules/identity/Sanhe.Abp.Identity.Domain/Sanhe.Abp.Identity.Domain.csproj
new file mode 100644
index 0000000..5b90c19
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.Domain/Sanhe.Abp.Identity.Domain.csproj
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+ netstandard2.0
+
+
+
+
+
+
+
+
+
+
+
diff --git a/modules/identity/Sanhe.Abp.Identity.Domain/Sanhe/Abp/Identity/AbpIdentityDomainModule.cs b/modules/identity/Sanhe.Abp.Identity.Domain/Sanhe/Abp/Identity/AbpIdentityDomainModule.cs
new file mode 100644
index 0000000..2477272
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.Domain/Sanhe/Abp/Identity/AbpIdentityDomainModule.cs
@@ -0,0 +1,11 @@
+using Volo.Abp.Modularity;
+
+namespace Sanhe.Abp.Identity
+{
+ [DependsOn(
+ typeof(AbpIdentityDomainSharedModule),
+ typeof(Volo.Abp.Identity.AbpIdentityDomainModule))]
+ public class AbpIdentityDomainModule : AbpModule
+ {
+ }
+}
diff --git a/modules/identity/Sanhe.Abp.Identity.Domain/Sanhe/Abp/Identity/IIdentityRoleRepository.cs b/modules/identity/Sanhe.Abp.Identity.Domain/Sanhe/Abp/Identity/IIdentityRoleRepository.cs
new file mode 100644
index 0000000..2cdf4c6
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.Domain/Sanhe/Abp/Identity/IIdentityRoleRepository.cs
@@ -0,0 +1,38 @@
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using Volo.Abp.Identity;
+
+namespace Sanhe.Abp.Identity
+{
+ public interface IIdentityRoleRepository : Volo.Abp.Identity.IIdentityRoleRepository
+ {
+ Task> GetListByIdListAsync(
+ List roleIds,
+ bool includeDetails = false,
+ CancellationToken cancellationToken = default);
+
+ Task> GetOrganizationUnitsAsync(
+ Guid id,
+ bool includeDetails = false,
+ CancellationToken cancellationToken = default);
+
+ Task> GetOrganizationUnitsAsync(
+ IEnumerable roleNames,
+ bool includeDetails = false,
+ CancellationToken cancellationToken = default);
+
+ Task> GetRolesInOrganizationUnitAsync(
+ Guid organizationUnitId,
+ CancellationToken cancellationToken = default);
+
+ Task> GetRolesInOrganizationsListAsync(
+ List organizationUnitIds,
+ CancellationToken cancellationToken = default);
+
+ Task> GetRolesInOrganizationUnitWithChildrenAsync(
+ string code,
+ CancellationToken cancellationToken = default);
+ }
+}
diff --git a/modules/identity/Sanhe.Abp.Identity.Domain/Sanhe/Abp/Identity/IIdentityUserRepository.cs b/modules/identity/Sanhe.Abp.Identity.Domain/Sanhe/Abp/Identity/IIdentityUserRepository.cs
new file mode 100644
index 0000000..8c24f9c
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.Domain/Sanhe/Abp/Identity/IIdentityUserRepository.cs
@@ -0,0 +1,128 @@
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using Volo.Abp.Identity;
+
+namespace Sanhe.Abp.Identity
+{
+ public interface IIdentityUserRepository : Volo.Abp.Identity.IIdentityUserRepository
+ {
+ ///
+ /// 手机号是否已被使用
+ ///
+ ///
+ ///
+ ///
+ Task IsPhoneNumberUedAsync(
+ string phoneNumber,
+ CancellationToken cancellationToken = default);
+
+ ///
+ /// 手机号是否已确认(绑定)
+ ///
+ ///
+ ///
+ ///
+ Task IsPhoneNumberConfirmedAsync(
+ string phoneNumber,
+ CancellationToken cancellationToken = default);
+
+ ///
+ /// 邮件地址是否已确认(绑定)
+ ///
+ ///
+ ///
+ ///
+ Task IsNormalizedEmailConfirmedAsync(
+ string normalizedEmail,
+ CancellationToken cancellationToken = default);
+
+ ///
+ /// 通过手机号查询用户
+ ///
+ /// 手机号码
+ /// 是否已确认过
+ ///
+ ///
+ ///
+ Task FindByPhoneNumberAsync(
+ string phoneNumber,
+ bool isConfirmed = true,
+ bool includeDetails = false,
+ CancellationToken cancellationToken = default);
+
+ ///
+ /// 通过用户主键列表获取用户
+ ///
+ ///
+ ///
+ ///
+ ///
+ Task> GetListByIdListAsync(
+ List userIds,
+ bool includeDetails = false,
+ CancellationToken cancellationToken = default);
+
+ ///
+ /// 获取用户所有的组织机构列表
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ Task> GetOrganizationUnitsAsync(
+ Guid userId,
+ string filter = null,
+ bool includeDetails = false,
+ int skipCount = 1,
+ int maxResultCount = 10,
+ CancellationToken cancellationToken = default);
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ Task GetUsersInOrganizationUnitCountAsync(
+ Guid organizationUnitId,
+ string filter = null,
+ CancellationToken cancellationToken = default);
+
+ Task> GetUsersInOrganizationUnitAsync(
+ Guid organizationUnitId,
+ string filter = null,
+ int skipCount = 1,
+ int maxResultCount = 10,
+ CancellationToken cancellationToken = default);
+
+ Task GetUsersInOrganizationsListCountAsync(
+ List organizationUnitIds,
+ string filter = null,
+ CancellationToken cancellationToken = default);
+
+ Task> GetUsersInOrganizationsListAsync(
+ List organizationUnitIds,
+ string filter = null,
+ int skipCount = 1,
+ int maxResultCount = 10,
+ CancellationToken cancellationToken = default);
+
+ Task GetUsersInOrganizationUnitWithChildrenCountAsync(
+ string code,
+ string filter = null,
+ CancellationToken cancellationToken = default);
+
+ Task> GetUsersInOrganizationUnitWithChildrenAsync(
+ string code,
+ string filter = null,
+ int skipCount = 1,
+ int maxResultCount = 10,
+ CancellationToken cancellationToken = default);
+ }
+}
diff --git a/modules/identity/Sanhe.Abp.Identity.Domain/Sanhe/Abp/Identity/SmsSecurityTokenCacheItem.cs b/modules/identity/Sanhe.Abp.Identity.Domain/Sanhe/Abp/Identity/SmsSecurityTokenCacheItem.cs
new file mode 100644
index 0000000..08f85c0
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.Domain/Sanhe/Abp/Identity/SmsSecurityTokenCacheItem.cs
@@ -0,0 +1,39 @@
+namespace Sanhe.Abp.Identity
+{
+ ///
+ /// 短信安全令牌验证缓存
+ ///
+ public class SmsSecurityTokenCacheItem
+ {
+ ///
+ /// 用于验证的Token
+ ///
+ public string Token { get; set; }
+ ///
+ /// 用于验证的安全令牌
+ ///
+ public string SecurityToken { get; set; }
+
+ public SmsSecurityTokenCacheItem()
+ {
+
+ }
+
+ public SmsSecurityTokenCacheItem(string token, string securityToken)
+ {
+ Token = token;
+ SecurityToken = securityToken;
+ }
+
+ ///
+ /// 生成查询Key
+ ///
+ /// 手机号
+ /// 安全令牌用途
+ ///
+ public static string CalculateCacheKey(string phoneNumber, string purpose)
+ {
+ return "Totp:" + purpose + ";p:" + phoneNumber;
+ }
+ }
+}
diff --git a/modules/identity/Sanhe.Abp.Identity.EntityFrameworkCore/FodyWeavers.xml b/modules/identity/Sanhe.Abp.Identity.EntityFrameworkCore/FodyWeavers.xml
new file mode 100644
index 0000000..1715698
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.EntityFrameworkCore/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/modules/identity/Sanhe.Abp.Identity.EntityFrameworkCore/Sanhe.Abp.Identity.EntityFrameworkCore.csproj b/modules/identity/Sanhe.Abp.Identity.EntityFrameworkCore/Sanhe.Abp.Identity.EntityFrameworkCore.csproj
new file mode 100644
index 0000000..c8acdf6
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.EntityFrameworkCore/Sanhe.Abp.Identity.EntityFrameworkCore.csproj
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+ net6.0
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/modules/identity/Sanhe.Abp.Identity.EntityFrameworkCore/Sanhe/Abp/Identity/EntityFrameworkCore/AbpIdentityEntityFrameworkCoreModule.cs b/modules/identity/Sanhe.Abp.Identity.EntityFrameworkCore/Sanhe/Abp/Identity/EntityFrameworkCore/AbpIdentityEntityFrameworkCoreModule.cs
new file mode 100644
index 0000000..99c759f
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.EntityFrameworkCore/Sanhe/Abp/Identity/EntityFrameworkCore/AbpIdentityEntityFrameworkCoreModule.cs
@@ -0,0 +1,37 @@
+using Microsoft.Extensions.DependencyInjection;
+using Volo.Abp.Identity;
+using Volo.Abp.Identity.EntityFrameworkCore;
+using Volo.Abp.Modularity;
+
+namespace Sanhe.Abp.Identity.EntityFrameworkCore
+{
+ [DependsOn(typeof(AbpIdentityDomainModule))]
+ [DependsOn(typeof(Volo.Abp.Identity.EntityFrameworkCore.AbpIdentityEntityFrameworkCoreModule))]
+ public class AbpIdentityEntityFrameworkCoreModule : AbpModule
+ {
+ // private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner();
+
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ context.Services.AddAbpDbContext(options =>
+ {
+ options.AddRepository();
+ options.AddRepository();
+ });
+ }
+
+ //public override void PostConfigureServices(ServiceConfigurationContext context)
+ //{
+ // OneTimeRunner.Run(() =>
+ // {
+ // ObjectExtensionManager.Instance
+ // .MapEfCoreProperty(
+ // ExtensionIdentityUserConsts.AvatarUrlField,
+ // (etb, prop) =>
+ // {
+ // prop.HasMaxLength(ExtensionIdentityUserConsts.MaxAvatarUrlLength);
+ // });
+ // });
+ //}
+ }
+}
diff --git a/modules/identity/Sanhe.Abp.Identity.EntityFrameworkCore/Sanhe/Abp/Identity/EntityFrameworkCore/EfCoreIdentityRoleRepository.cs b/modules/identity/Sanhe.Abp.Identity.EntityFrameworkCore/Sanhe/Abp/Identity/EntityFrameworkCore/EfCoreIdentityRoleRepository.cs
new file mode 100644
index 0000000..b245cf1
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.EntityFrameworkCore/Sanhe/Abp/Identity/EntityFrameworkCore/EfCoreIdentityRoleRepository.cs
@@ -0,0 +1,97 @@
+using Microsoft.EntityFrameworkCore;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Dynamic.Core;
+using System.Threading;
+using System.Threading.Tasks;
+using Volo.Abp.EntityFrameworkCore;
+using Volo.Abp.Identity;
+using Volo.Abp.Identity.EntityFrameworkCore;
+
+namespace Sanhe.Abp.Identity.EntityFrameworkCore
+{
+ public class EfCoreIdentityRoleRepository : Volo.Abp.Identity.EntityFrameworkCore.EfCoreIdentityRoleRepository, IIdentityRoleRepository
+ {
+ public EfCoreIdentityRoleRepository(
+ IDbContextProvider dbContextProvider)
+ : base(dbContextProvider)
+ {
+ }
+
+ public virtual async Task> GetListByIdListAsync(
+ List roleIds,
+ bool includeDetails = false,
+ CancellationToken cancellationToken = default
+ )
+ {
+ return await (await GetDbSetAsync()).IncludeDetails(includeDetails)
+ .Where(role => roleIds.Contains(role.Id))
+ .ToListAsync(GetCancellationToken(cancellationToken));
+ }
+
+ public virtual async Task> GetOrganizationUnitsAsync(
+ Guid id,
+ bool includeDetails = false,
+ CancellationToken cancellationToken = default)
+ {
+ var dbContext = await GetDbContextAsync();
+ var query = from roleOU in dbContext.Set()
+ join ou in dbContext.OrganizationUnits.IncludeDetails(includeDetails) on roleOU.OrganizationUnitId equals ou.Id
+ where roleOU.RoleId == id
+ select ou;
+
+ return await query.ToListAsync(GetCancellationToken(cancellationToken));
+ }
+
+ public virtual async Task> GetOrganizationUnitsAsync(
+ IEnumerable roleNames,
+ bool includeDetails = false,
+ CancellationToken cancellationToken = default)
+ {
+ var dbContext = await GetDbContextAsync();
+ var query = from roleOU in dbContext.Set()
+ join role in dbContext.Roles on roleOU.RoleId equals role.Id
+ join ou in dbContext.OrganizationUnits.IncludeDetails(includeDetails) on roleOU.OrganizationUnitId equals ou.Id
+ where roleNames.Contains(role.Name)
+ select ou;
+
+ return await query.ToListAsync(GetCancellationToken(cancellationToken));
+ }
+
+ public virtual async Task> GetRolesInOrganizationsListAsync(
+ List organizationUnitIds,
+ CancellationToken cancellationToken = default)
+ {
+ var query = from roleOu in (await GetDbContextAsync()).Set()
+ join user in await GetDbSetAsync() on roleOu.RoleId equals user.Id
+ where organizationUnitIds.Contains(roleOu.OrganizationUnitId)
+ select user;
+ return await query.ToListAsync(GetCancellationToken(cancellationToken));
+ }
+
+ public virtual async Task> GetRolesInOrganizationUnitAsync(
+ Guid organizationUnitId,
+ CancellationToken cancellationToken = default)
+ {
+ var query = from roleOu in (await GetDbContextAsync()).Set()
+ join user in await GetDbSetAsync() on roleOu.RoleId equals user.Id
+ where roleOu.OrganizationUnitId == organizationUnitId
+ select user;
+ return await query.ToListAsync(GetCancellationToken(cancellationToken));
+ }
+
+ public virtual async Task> GetRolesInOrganizationUnitWithChildrenAsync(
+ string code,
+ CancellationToken cancellationToken = default)
+ {
+ var dbContext = await GetDbContextAsync();
+ var query = from roleOU in dbContext.Set()
+ join user in await GetDbSetAsync() on roleOU.RoleId equals user.Id
+ join ou in dbContext.Set() on roleOU.OrganizationUnitId equals ou.Id
+ where ou.Code.StartsWith(code)
+ select user;
+ return await query.ToListAsync(GetCancellationToken(cancellationToken));
+ }
+ }
+}
diff --git a/modules/identity/Sanhe.Abp.Identity.EntityFrameworkCore/Sanhe/Abp/Identity/EntityFrameworkCore/EfCoreIdentityUserRepository.cs b/modules/identity/Sanhe.Abp.Identity.EntityFrameworkCore/Sanhe/Abp/Identity/EntityFrameworkCore/EfCoreIdentityUserRepository.cs
new file mode 100644
index 0000000..3a76731
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.EntityFrameworkCore/Sanhe/Abp/Identity/EntityFrameworkCore/EfCoreIdentityUserRepository.cs
@@ -0,0 +1,238 @@
+using Microsoft.EntityFrameworkCore;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Dynamic.Core;
+using System.Threading;
+using System.Threading.Tasks;
+using Volo.Abp.EntityFrameworkCore;
+using Volo.Abp.Identity;
+using Volo.Abp.Identity.EntityFrameworkCore;
+
+namespace Sanhe.Abp.Identity.EntityFrameworkCore
+{
+ public class EfCoreIdentityUserRepository : Volo.Abp.Identity.EntityFrameworkCore.EfCoreIdentityUserRepository, IIdentityUserRepository
+ {
+ public EfCoreIdentityUserRepository(
+ IDbContextProvider dbContextProvider)
+ : base(dbContextProvider)
+ {
+ }
+
+ public virtual async Task IsPhoneNumberUedAsync(
+ string phoneNumber,
+ CancellationToken cancellationToken = default)
+ {
+ return await (await GetDbSetAsync()).IncludeDetails(false)
+ .AnyAsync(user => user.PhoneNumber == phoneNumber,
+ GetCancellationToken(cancellationToken));
+ }
+
+ public virtual async Task IsPhoneNumberConfirmedAsync(
+ string phoneNumber,
+ CancellationToken cancellationToken = default)
+ {
+ return await (await GetDbSetAsync()).IncludeDetails(false)
+ .AnyAsync(user => user.PhoneNumber == phoneNumber && user.PhoneNumberConfirmed,
+ GetCancellationToken(cancellationToken));
+ }
+
+ public virtual async Task IsNormalizedEmailConfirmedAsync(
+ string normalizedEmail,
+ CancellationToken cancellationToken = default)
+ {
+ return await (await GetDbSetAsync()).IncludeDetails(false)
+ .AnyAsync(user => user.NormalizedEmail == normalizedEmail && user.EmailConfirmed,
+ GetCancellationToken(cancellationToken));
+ }
+
+ public virtual async Task FindByPhoneNumberAsync(
+ string phoneNumber,
+ bool isConfirmed = true,
+ bool includeDetails = false,
+ CancellationToken cancellationToken = default)
+ {
+ return await (await GetDbSetAsync()).IncludeDetails(includeDetails)
+ .Where(user => user.PhoneNumber == phoneNumber && user.PhoneNumberConfirmed == isConfirmed)
+ .FirstOrDefaultAsync(GetCancellationToken(cancellationToken));
+ }
+
+ public virtual async Task> GetListByIdListAsync(
+ List userIds,
+ bool includeDetails = false,
+ CancellationToken cancellationToken = default
+ )
+ {
+ return await (await GetDbSetAsync()).IncludeDetails(includeDetails)
+ .Where(user => userIds.Contains(user.Id))
+ .ToListAsync(GetCancellationToken(cancellationToken));
+ }
+
+ public virtual async Task> GetOrganizationUnitsAsync(
+ Guid id,
+ string filter = null,
+ bool includeDetails = false,
+ int skipCount = 1,
+ int maxResultCount = 10,
+ CancellationToken cancellationToken = default
+ )
+ {
+ var dbContext = await GetDbContextAsync();
+ //var userUoDbSet = dbContext.Set();
+ //var roleUoDbSet = dbContext.Set();
+ //var userRoleDbSet = dbContext.Set();
+
+ //var userUo = from usrUo in userUoDbSet
+ // join usr in dbContext.Users on usrUo.UserId equals usr.Id
+ // join ou in dbContext.OrganizationUnits.IncludeDetails(includeDetails)
+ // on usrUo.OrganizationUnitId equals ou.Id
+ // where usr.Id == id
+ // select ou;
+
+ //var roleUo = from urol in userRoleDbSet
+ // join rol in dbContext.Roles on urol.RoleId equals rol.Id
+ // join rolUo in roleUoDbSet on rol.Id equals rolUo.RoleId
+ // join ou in dbContext.OrganizationUnits.IncludeDetails(includeDetails)
+ // on rolUo.OrganizationUnitId equals ou.Id
+ // where urol.UserId == id
+ // select ou;
+
+ var query = from userOU in dbContext.Set()
+ join ro in dbContext.Set() on userOU.UserId equals ro.UserId
+ join ou in dbContext.OrganizationUnits.IncludeDetails(includeDetails)
+ on userOU.OrganizationUnitId equals ou.Id
+ where userOU.UserId == id
+ select ou;
+
+ return await query
+ .WhereIf(!filter.IsNullOrWhiteSpace(), ou => ou.Code.Contains(filter) || ou.DisplayName.Contains(filter))
+ .PageBy(skipCount, maxResultCount)
+ .ToListAsync(GetCancellationToken(cancellationToken));
+ }
+
+ public virtual async Task GetUsersInOrganizationUnitCountAsync(
+ Guid organizationUnitId,
+ string filter = null,
+ CancellationToken cancellationToken = default
+ )
+ {
+ var dbContext = await GetDbContextAsync();
+ var query = from userOu in dbContext.Set()
+ join user in await GetDbSetAsync() on userOu.UserId equals user.Id
+ where userOu.OrganizationUnitId == organizationUnitId
+ select user;
+ return await query
+ .WhereIf(!filter.IsNullOrWhiteSpace(),
+ user => user.Name.Contains(filter) || user.UserName.Contains(filter) ||
+ user.Surname.Contains(filter) || user.Email.Contains(filter) ||
+ user.PhoneNumber.Contains(filter))
+ .LongCountAsync(GetCancellationToken(cancellationToken));
+ }
+
+ public virtual async Task> GetUsersInOrganizationUnitAsync(
+ Guid organizationUnitId,
+ string filter = null,
+ int skipCount = 1,
+ int maxResultCount = 10,
+ CancellationToken cancellationToken = default
+ )
+ {
+ var dbContext = await GetDbContextAsync();
+ var query = from userOu in dbContext.Set()
+ join user in await GetDbSetAsync() on userOu.UserId equals user.Id
+ where userOu.OrganizationUnitId == organizationUnitId
+ select user;
+ return await query
+ .WhereIf(!filter.IsNullOrWhiteSpace(),
+ user => user.Name.Contains(filter) || user.UserName.Contains(filter) ||
+ user.Surname.Contains(filter) || user.Email.Contains(filter) ||
+ user.PhoneNumber.Contains(filter))
+ .PageBy(skipCount, maxResultCount)
+ .ToListAsync(GetCancellationToken(cancellationToken));
+ }
+
+ public virtual async Task GetUsersInOrganizationsListCountAsync(
+ List organizationUnitIds,
+ string filter = null,
+ CancellationToken cancellationToken = default
+ )
+ {
+ var dbContext = await GetDbContextAsync();
+ var query = from userOu in dbContext.Set()
+ join user in await GetDbSetAsync() on userOu.UserId equals user.Id
+ where organizationUnitIds.Contains(userOu.OrganizationUnitId)
+ select user;
+ return await query
+ .WhereIf(!filter.IsNullOrWhiteSpace(),
+ user => user.Name.Contains(filter) || user.UserName.Contains(filter) ||
+ user.Surname.Contains(filter) || user.Email.Contains(filter) ||
+ user.PhoneNumber.Contains(filter))
+ .LongCountAsync(GetCancellationToken(cancellationToken));
+ }
+
+ public virtual async Task> GetUsersInOrganizationsListAsync(
+ List organizationUnitIds,
+ string filter = null,
+ int skipCount = 1,
+ int maxResultCount = 10,
+ CancellationToken cancellationToken = default
+ )
+ {
+ var dbContext = await GetDbContextAsync();
+ var query = from userOu in dbContext.Set()
+ join user in await GetDbSetAsync() on userOu.UserId equals user.Id
+ where organizationUnitIds.Contains(userOu.OrganizationUnitId)
+ select user;
+ return await query
+ .WhereIf(!filter.IsNullOrWhiteSpace(),
+ user => user.Name.Contains(filter) || user.UserName.Contains(filter) ||
+ user.Surname.Contains(filter) || user.Email.Contains(filter) ||
+ user.PhoneNumber.Contains(filter))
+ .PageBy(skipCount, maxResultCount)
+ .ToListAsync(GetCancellationToken(cancellationToken));
+ }
+
+ public virtual async Task GetUsersInOrganizationUnitWithChildrenCountAsync(
+ string code,
+ string filter = null,
+ CancellationToken cancellationToken = default
+ )
+ {
+ var dbContext = await GetDbContextAsync();
+ var query = from userOu in dbContext.Set()
+ join user in await GetDbSetAsync() on userOu.UserId equals user.Id
+ join ou in dbContext.Set() on userOu.OrganizationUnitId equals ou.Id
+ where ou.Code.StartsWith(code)
+ select user;
+ return await query
+ .WhereIf(!filter.IsNullOrWhiteSpace(),
+ user => user.Name.Contains(filter) || user.UserName.Contains(filter) ||
+ user.Surname.Contains(filter) || user.Email.Contains(filter) ||
+ user.PhoneNumber.Contains(filter))
+ .LongCountAsync(GetCancellationToken(cancellationToken));
+ }
+
+ public virtual async Task> GetUsersInOrganizationUnitWithChildrenAsync(
+ string code,
+ string filter = null,
+ int skipCount = 1,
+ int maxResultCount = 10,
+ CancellationToken cancellationToken = default
+ )
+ {
+ var dbContext = await GetDbContextAsync();
+ var query = from userOu in dbContext.Set()
+ join user in await GetDbSetAsync() on userOu.UserId equals user.Id
+ join ou in dbContext.Set() on userOu.OrganizationUnitId equals ou.Id
+ where ou.Code.StartsWith(code)
+ select user;
+ return await query
+ .WhereIf(!filter.IsNullOrWhiteSpace(),
+ user => user.Name.Contains(filter) || user.UserName.Contains(filter) ||
+ user.Surname.Contains(filter) || user.Email.Contains(filter) ||
+ user.PhoneNumber.Contains(filter))
+ .PageBy(skipCount, maxResultCount)
+ .ToListAsync(GetCancellationToken(cancellationToken));
+ }
+ }
+}
diff --git a/modules/identity/Sanhe.Abp.Identity.HttpApi.Client/FodyWeavers.xml b/modules/identity/Sanhe.Abp.Identity.HttpApi.Client/FodyWeavers.xml
new file mode 100644
index 0000000..1715698
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.HttpApi.Client/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/modules/identity/Sanhe.Abp.Identity.HttpApi.Client/Sanhe.Abp.Identity.HttpApi.Client.csproj b/modules/identity/Sanhe.Abp.Identity.HttpApi.Client/Sanhe.Abp.Identity.HttpApi.Client.csproj
new file mode 100644
index 0000000..e71bfce
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.HttpApi.Client/Sanhe.Abp.Identity.HttpApi.Client.csproj
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+ netstandard2.0
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/modules/identity/Sanhe.Abp.Identity.HttpApi.Client/Sanhe/Abp/Identity/AbpIdentityHttpApiClientModule.cs b/modules/identity/Sanhe.Abp.Identity.HttpApi.Client/Sanhe/Abp/Identity/AbpIdentityHttpApiClientModule.cs
new file mode 100644
index 0000000..a7b895d
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.HttpApi.Client/Sanhe/Abp/Identity/AbpIdentityHttpApiClientModule.cs
@@ -0,0 +1,20 @@
+using Microsoft.Extensions.DependencyInjection;
+using Volo.Abp.Identity;
+using Volo.Abp.Modularity;
+
+namespace Sanhe.Abp.Identity
+{
+ [DependsOn(
+ typeof(Volo.Abp.Identity.AbpIdentityHttpApiClientModule),
+ typeof(AbpIdentityApplicationContractsModule))]
+ public class AbpIdentityHttpApiClientModule : AbpModule
+ {
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ context.Services.AddHttpClientProxies(
+ typeof(AbpIdentityApplicationContractsModule).Assembly,
+ IdentityRemoteServiceConsts.RemoteServiceName
+ );
+ }
+ }
+}
diff --git a/modules/identity/Sanhe.Abp.Identity.HttpApi/FodyWeavers.xml b/modules/identity/Sanhe.Abp.Identity.HttpApi/FodyWeavers.xml
new file mode 100644
index 0000000..1715698
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.HttpApi/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/modules/identity/Sanhe.Abp.Identity.HttpApi/Sanhe.Abp.Identity.HttpApi.csproj b/modules/identity/Sanhe.Abp.Identity.HttpApi/Sanhe.Abp.Identity.HttpApi.csproj
new file mode 100644
index 0000000..c305a65
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.HttpApi/Sanhe.Abp.Identity.HttpApi.csproj
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+ net6.0
+
+ True
+
+
+
+
+
+
+
+
+
+
diff --git a/modules/identity/Sanhe.Abp.Identity.HttpApi/Sanhe/Abp/Identity/AbpIdentityHttpApiModule.cs b/modules/identity/Sanhe.Abp.Identity.HttpApi/Sanhe/Abp/Identity/AbpIdentityHttpApiModule.cs
new file mode 100644
index 0000000..fa4fb80
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.HttpApi/Sanhe/Abp/Identity/AbpIdentityHttpApiModule.cs
@@ -0,0 +1,27 @@
+using Microsoft.Extensions.DependencyInjection;
+using Volo.Abp.AspNetCore.Mvc.Localization;
+using Volo.Abp.Identity.Localization;
+using Volo.Abp.Modularity;
+
+namespace Sanhe.Abp.Identity
+{
+ [DependsOn(
+ typeof(Volo.Abp.Identity.AbpIdentityHttpApiModule),
+ typeof(AbpIdentityApplicationContractsModule))]
+ public class AbpIdentityHttpApiModule : AbpModule
+ {
+ public override void PreConfigureServices(ServiceConfigurationContext context)
+ {
+ PreConfigure(options =>
+ {
+ options.AddAssemblyResource(typeof(IdentityResource), typeof(AbpIdentityApplicationContractsModule).Assembly);
+ options.AddAssemblyResource(typeof(IdentityResource), typeof(Volo.Abp.Identity.AbpIdentityApplicationContractsModule).Assembly);
+ });
+
+ PreConfigure(mvcBuilder =>
+ {
+ mvcBuilder.AddApplicationPartIfNotExists(typeof(AbpIdentityHttpApiModule).Assembly);
+ });
+ }
+ }
+}
diff --git a/modules/identity/Sanhe.Abp.Identity.HttpApi/Sanhe/Abp/Identity/IdentityClaimTypeController.cs b/modules/identity/Sanhe.Abp.Identity.HttpApi/Sanhe/Abp/Identity/IdentityClaimTypeController.cs
new file mode 100644
index 0000000..fd86f26
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.HttpApi/Sanhe/Abp/Identity/IdentityClaimTypeController.cs
@@ -0,0 +1,64 @@
+using Microsoft.AspNetCore.Mvc;
+using Sanhe.Abp.Identity.Dto;
+using System;
+using System.Threading.Tasks;
+using Volo.Abp;
+using Volo.Abp.Application.Dtos;
+using Volo.Abp.AspNetCore.Mvc;
+using Volo.Abp.Identity;
+
+namespace Sanhe.Abp.Identity
+{
+ [RemoteService(true, Name = IdentityRemoteServiceConsts.RemoteServiceName)]
+ [Area("identity")]
+ [ControllerName("ClaimType")]
+ [Route("api/identity/claim-types")]
+ public class IdentityClaimTypeController : AbpController, IIdentityClaimTypeAppService
+ {
+ protected IIdentityClaimTypeAppService IdentityClaimTypeAppService { get; }
+ public IdentityClaimTypeController(IIdentityClaimTypeAppService identityClaimTypeAppService)
+ {
+ IdentityClaimTypeAppService = identityClaimTypeAppService;
+ }
+
+ [HttpPost]
+ public virtual async Task CreateAsync(IdentityClaimTypeCreateDto input)
+ {
+ return await IdentityClaimTypeAppService.CreateAsync(input);
+ }
+
+ [HttpDelete]
+ [Route("{id}")]
+ public virtual async Task DeleteAsync(Guid id)
+ {
+ await IdentityClaimTypeAppService.DeleteAsync(id);
+ }
+
+ [HttpGet]
+ [Route("actived-list")]
+ public virtual async Task> GetAllListAsync()
+ {
+ return await IdentityClaimTypeAppService.GetAllListAsync();
+ }
+
+ [HttpGet]
+ [Route("{id}")]
+ public virtual async Task GetAsync(Guid id)
+ {
+ return await IdentityClaimTypeAppService.GetAsync(id);
+ }
+
+ [HttpGet]
+ public virtual async Task> GetListAsync(IdentityClaimTypeGetByPagedDto input)
+ {
+ return await IdentityClaimTypeAppService.GetListAsync(input);
+ }
+
+ [HttpPut]
+ [Route("{id}")]
+ public virtual async Task UpdateAsync(Guid id, IdentityClaimTypeUpdateDto input)
+ {
+ return await IdentityClaimTypeAppService.UpdateAsync(id, input);
+ }
+ }
+}
diff --git a/modules/identity/Sanhe.Abp.Identity.HttpApi/Sanhe/Abp/Identity/IdentityRoleController.cs b/modules/identity/Sanhe.Abp.Identity.HttpApi/Sanhe/Abp/Identity/IdentityRoleController.cs
new file mode 100644
index 0000000..dc03589
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.HttpApi/Sanhe/Abp/Identity/IdentityRoleController.cs
@@ -0,0 +1,82 @@
+using Microsoft.AspNetCore.Mvc;
+using Sanhe.Abp.Identity.Dto;
+using System;
+using System.Threading.Tasks;
+using Volo.Abp;
+using Volo.Abp.Application.Dtos;
+using Volo.Abp.AspNetCore.Mvc;
+using Volo.Abp.Identity;
+
+namespace Sanhe.Abp.Identity
+{
+ [RemoteService(true, Name = IdentityRemoteServiceConsts.RemoteServiceName)]
+ [Area("identity")]
+ [ControllerName("Role")]
+ [Route("api/identity/roles")]
+ public class IdentityRoleController : AbpController, IIdentityRoleAppService
+ {
+ protected IIdentityRoleAppService RoleAppService { get; }
+ public IdentityRoleController(
+ IIdentityRoleAppService roleAppService)
+ {
+ RoleAppService = roleAppService;
+ }
+
+ #region OrganizationUnit
+
+ [HttpGet]
+ [Route("{id}/organization-units")]
+ public virtual async Task> GetOrganizationUnitsAsync(Guid id)
+ {
+ return await RoleAppService.GetOrganizationUnitsAsync(id);
+ }
+
+ [HttpPut]
+ [Route("{id}/organization-units")]
+ public virtual async Task SetOrganizationUnitsAsync(Guid id, IdentityRoleAddOrRemoveOrganizationUnitDto input)
+ {
+ await RoleAppService.SetOrganizationUnitsAsync(id, input);
+ }
+
+ [HttpDelete]
+ [Route("{id}/organization-units/{ouId}")]
+ public virtual async Task RemoveOrganizationUnitsAsync(Guid id, Guid ouId)
+ {
+ await RoleAppService.RemoveOrganizationUnitsAsync(id, ouId);
+ }
+
+ #endregion
+
+ #region Claim
+
+ [HttpGet]
+ [Route("{id}/claims")]
+ public virtual async Task> GetClaimsAsync(Guid id)
+ {
+ return await RoleAppService.GetClaimsAsync(id);
+ }
+
+ [HttpPost]
+ [Route("{id}/claims")]
+ public virtual async Task AddClaimAsync(Guid id, IdentityRoleClaimCreateDto input)
+ {
+ await RoleAppService.AddClaimAsync(id, input);
+ }
+
+ [HttpPut]
+ [Route("{id}/claims")]
+ public virtual async Task UpdateClaimAsync(Guid id, IdentityRoleClaimUpdateDto input)
+ {
+ await RoleAppService.UpdateClaimAsync(id, input);
+ }
+
+ [HttpDelete]
+ [Route("{id}/claims")]
+ public virtual async Task DeleteClaimAsync(Guid id, IdentityRoleClaimDeleteDto input)
+ {
+ await RoleAppService.DeleteClaimAsync(id, input);
+ }
+
+ #endregion
+ }
+}
diff --git a/modules/identity/Sanhe.Abp.Identity.HttpApi/Sanhe/Abp/Identity/IdentityUserController.cs b/modules/identity/Sanhe.Abp.Identity.HttpApi/Sanhe/Abp/Identity/IdentityUserController.cs
new file mode 100644
index 0000000..36d732c
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.HttpApi/Sanhe/Abp/Identity/IdentityUserController.cs
@@ -0,0 +1,104 @@
+using Microsoft.AspNetCore.Mvc;
+using Sanhe.Abp.Identity.Dto;
+using System;
+using System.Threading.Tasks;
+using Volo.Abp;
+using Volo.Abp.Application.Dtos;
+using Volo.Abp.AspNetCore.Mvc;
+using Volo.Abp.Identity;
+
+namespace Sanhe.Abp.Identity
+{
+ [RemoteService(true, Name = IdentityRemoteServiceConsts.RemoteServiceName)]
+ [Area("identity")]
+ [ControllerName("User")]
+ [Route("api/identity/users")]
+ public class IdentityUserController : AbpController, IIdentityUserAppService
+ {
+ protected IIdentityUserAppService UserAppService { get; }
+ public IdentityUserController(
+ IIdentityUserAppService userAppService)
+ {
+ UserAppService = userAppService;
+ }
+
+ #region OrganizationUnit
+
+ [HttpGet]
+ [Route("{id}/organization-units")]
+ public virtual async Task> GetOrganizationUnitsAsync(Guid id)
+ {
+ return await UserAppService.GetOrganizationUnitsAsync(id);
+ }
+
+ [HttpPut]
+ [Route("{id}/organization-units")]
+ public virtual async Task SetOrganizationUnitsAsync(Guid id, IdentityUserOrganizationUnitUpdateDto input)
+ {
+ await UserAppService.SetOrganizationUnitsAsync(id, input);
+ }
+
+ [HttpDelete]
+ [Route("{id}/organization-units/{ouId}")]
+ public virtual async Task RemoveOrganizationUnitsAsync(Guid id, Guid ouId)
+ {
+ await UserAppService.RemoveOrganizationUnitsAsync(id, ouId);
+ }
+
+ #endregion
+
+ #region Claim
+
+ [HttpGet]
+ [Route("{id}/claims")]
+ public virtual async Task> GetClaimsAsync(Guid id)
+ {
+ return await UserAppService.GetClaimsAsync(id);
+ }
+
+ [HttpPost]
+ [Route("{id}/claims")]
+ public virtual async Task AddClaimAsync(Guid id, IdentityUserClaimCreateDto input)
+ {
+ await UserAppService.AddClaimAsync(id, input);
+ }
+
+ [HttpPut]
+ [Route("{id}/claims")]
+ public virtual async Task UpdateClaimAsync(Guid id, IdentityUserClaimUpdateDto input)
+ {
+ await UserAppService.UpdateClaimAsync(id, input);
+ }
+
+ [HttpDelete]
+ [Route("{id}/claims")]
+ public virtual async Task DeleteClaimAsync(Guid id, IdentityUserClaimDeleteDto input)
+ {
+ await UserAppService.DeleteClaimAsync(id, input);
+ }
+
+ #endregion
+
+
+ [HttpPut]
+ [Route("change-two-factor")]
+ public virtual async Task ChangeTwoFactorEnabledAsync(Guid id, TwoFactorEnabledDto input)
+ {
+ await UserAppService.ChangeTwoFactorEnabledAsync(id, input);
+ }
+
+ [HttpPut]
+ [Route("{id}/lock/{seconds}")]
+ public virtual async Task LockAsync(Guid id, int seconds)
+ {
+ await UserAppService.LockAsync(id, seconds);
+ }
+
+ [HttpPut]
+ [Route("{id}/unlock")]
+ public virtual async Task UnLockAsync(Guid id)
+ {
+ await UserAppService.UnLockAsync(id);
+ }
+ }
+}
diff --git a/modules/identity/Sanhe.Abp.Identity.HttpApi/Sanhe/Abp/Identity/OrganizationUnitController.cs b/modules/identity/Sanhe.Abp.Identity.HttpApi/Sanhe/Abp/Identity/OrganizationUnitController.cs
new file mode 100644
index 0000000..8686cfe
--- /dev/null
+++ b/modules/identity/Sanhe.Abp.Identity.HttpApi/Sanhe/Abp/Identity/OrganizationUnitController.cs
@@ -0,0 +1,143 @@
+using Microsoft.AspNetCore.Mvc;
+using Sanhe.Abp.Identity.Dto;
+using System;
+using System.Threading.Tasks;
+using Volo.Abp;
+using Volo.Abp.Application.Dtos;
+using Volo.Abp.AspNetCore.Mvc;
+using Volo.Abp.Identity;
+
+namespace Sanhe.Abp.Identity
+{
+ [RemoteService(Name = IdentityRemoteServiceConsts.RemoteServiceName)]
+ [Area("identity")]
+ [ControllerName("organization-units")]
+ [Route("api/identity/organization-units")]
+ public class OrganizationUnitController : AbpController, IOrganizationUnitAppService
+ {
+ protected IOrganizationUnitAppService OrganizationUnitAppService { get; }
+
+ public OrganizationUnitController(
+ IOrganizationUnitAppService organizationUnitAppService)
+ {
+ OrganizationUnitAppService = organizationUnitAppService;
+ }
+
+ [HttpPost]
+ public virtual async Task CreateAsync(OrganizationUnitCreateDto input)
+ {
+ return await OrganizationUnitAppService.CreateAsync(input);
+ }
+
+ [HttpDelete]
+ [Route("{id}")]
+ public virtual async Task DeleteAsync(Guid id)
+ {
+ await OrganizationUnitAppService.DeleteAsync(id);
+ }
+
+ [HttpGet]
+ [Route("find-children")]
+ public virtual async Task> FindChildrenAsync(OrganizationUnitGetChildrenDto input)
+ {
+ return await OrganizationUnitAppService.FindChildrenAsync(input);
+ }
+
+ [HttpGet]
+ [Route("{id}")]
+ public virtual async Task GetAsync(Guid id)
+ {
+ return await OrganizationUnitAppService.GetAsync(id);
+ }
+
+ [HttpGet]
+ [Route("root-node")]
+ public virtual async Task> GetRootAsync()
+ {
+ return await OrganizationUnitAppService.GetRootAsync();
+ }
+
+ [HttpGet]
+ [Route("last-children")]
+ public virtual async Task GetLastChildOrNullAsync(Guid? parentId)
+ {
+ return await OrganizationUnitAppService.GetLastChildOrNullAsync(parentId);
+ }
+
+ [HttpGet]
+ [Route("all")]
+ public virtual async Task> GetAllListAsync()
+ {
+ return await OrganizationUnitAppService.GetAllListAsync();
+ }
+
+ [HttpGet]
+ public virtual async Task> GetListAsync(OrganizationUnitGetByPagedDto input)
+ {
+ return await OrganizationUnitAppService.GetListAsync(input);
+ }
+
+ [HttpGet]
+ [Route("{id}/role-names")]
+ public virtual async Task> GetRoleNamesAsync(Guid id)
+ {
+ return await OrganizationUnitAppService.GetRoleNamesAsync(id);
+ }
+
+ [HttpGet]
+ [Route("{id}/unadded-roles")]
+ public virtual async Task> GetUnaddedRolesAsync(Guid id, OrganizationUnitGetUnaddedRoleByPagedDto input)
+ {
+ return await OrganizationUnitAppService.GetUnaddedRolesAsync(id, input);
+ }
+
+ [HttpGet]
+ [Route("{id}/roles")]
+ public virtual async Task> GetRolesAsync(Guid id, PagedAndSortedResultRequestDto input)
+ {
+ return await OrganizationUnitAppService.GetRolesAsync(id, input);
+ }
+
+ [HttpGet]
+ [Route("{id}/unadded-users")]
+ public virtual async Task> GetUnaddedUsersAsync(Guid id, OrganizationUnitGetUnaddedUserByPagedDto input)
+ {
+ return await OrganizationUnitAppService.GetUnaddedUsersAsync(id, input);
+ }
+
+ [HttpGet]
+ [Route("{id}/users")]
+ public virtual async Task> GetUsersAsync(Guid id, GetIdentityUsersInput input)
+ {
+ return await OrganizationUnitAppService.GetUsersAsync(id, input);
+ }
+
+ [HttpPost]
+ [Route("{id}/users")]
+ public virtual async Task AddUsersAsync(Guid id, OrganizationUnitAddUserDto input)
+ {
+ await OrganizationUnitAppService.AddUsersAsync(id, input);
+ }
+
+ [HttpPost]
+ [Route("{id}/roles")]
+ public virtual async Task AddRolesAsync(Guid id, OrganizationUnitAddRoleDto input)
+ {
+ await OrganizationUnitAppService.AddRolesAsync(id, input);
+ }
+
+ [HttpPut]
+ [Route("{id}/move")]
+ public virtual async Task MoveAsync(Guid id, OrganizationUnitMoveDto input)
+ {
+ await OrganizationUnitAppService.MoveAsync(id, input);
+ }
+
+ [HttpPut]
+ [Route("{id}")]
+ public virtual async Task UpdateAsync(Guid id, OrganizationUnitUpdateDto input)
+ {
+ return await OrganizationUnitAppService.UpdateAsync(id, input);
+ }
+ }
+}
diff --git a/services/book-store/BookStore.csproj b/services/book-store/BookStore.csproj
index d369894..fc2394d 100644
--- a/services/book-store/BookStore.csproj
+++ b/services/book-store/BookStore.csproj
@@ -28,9 +28,6 @@
-
-
-
@@ -486,6 +483,9 @@
+
+
+
diff --git a/services/book-store/BookStoreModule.cs b/services/book-store/BookStoreModule.cs
index da7a570..6a05fd8 100644
--- a/services/book-store/BookStoreModule.cs
+++ b/services/book-store/BookStoreModule.cs
@@ -44,8 +44,8 @@ using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore.PostgreSql;
using Volo.Abp.FeatureManagement;
using Volo.Abp.FeatureManagement.EntityFrameworkCore;
-using Volo.Abp.Identity;
-using Volo.Abp.Identity.EntityFrameworkCore;
+using Sanhe.Abp.Identity;
+using Sanhe.Abp.Identity.EntityFrameworkCore;
using Volo.Abp.IdentityServer.EntityFrameworkCore;
using Volo.Abp.Localization;
using Volo.Abp.Localization.ExceptionHandling;