From d48e65d23ed912c1e554e4fb49ace48e4ac3e87b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Mon, 1 Feb 2021 20:46:43 +0300 Subject: [PATCH 1/6] Introduce ITenantDatabaseSchemaMigrator. --- .../NullTenantDatabaseSchemaMigrator.cs | 13 +++++++++++++ .../MultiTenancy/TenantDatabaseSchemaMigrator.cs | 13 +++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/NullTenantDatabaseSchemaMigrator.cs create mode 100644 framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/TenantDatabaseSchemaMigrator.cs diff --git a/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/NullTenantDatabaseSchemaMigrator.cs b/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/NullTenantDatabaseSchemaMigrator.cs new file mode 100644 index 0000000000..7d5e76f11b --- /dev/null +++ b/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/NullTenantDatabaseSchemaMigrator.cs @@ -0,0 +1,13 @@ +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.MultiTenancy +{ + public class NullTenantDatabaseSchemaMigrator : ITenantDatabaseSchemaMigrator, ISingletonDependency + { + public Task MigrateAsync() + { + return Task.CompletedTask; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/TenantDatabaseSchemaMigrator.cs b/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/TenantDatabaseSchemaMigrator.cs new file mode 100644 index 0000000000..e6fef5c427 --- /dev/null +++ b/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/TenantDatabaseSchemaMigrator.cs @@ -0,0 +1,13 @@ +using System.Threading.Tasks; + +namespace Volo.Abp.MultiTenancy +{ + public interface ITenantDatabaseSchemaMigrator + { + /// + /// Set Current Tenant before calling this method. + /// + /// + Task MigrateAsync(); + } +} From 8762284482449c85903c5fd40a10b9b82cb0474e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Mon, 1 Feb 2021 20:53:42 +0300 Subject: [PATCH 2/6] Added Volo.Abp.EventBus.Abstractions package. --- framework/Volo.Abp.sln | 7 +++++ .../FodyWeavers.xml | 3 ++ .../FodyWeavers.xsd | 30 +++++++++++++++++++ .../Volo.Abp.EventBus.Abstractions.csproj | 15 ++++++++++ .../AbpEventBusAbstractionsModule.cs | 9 ++++++ .../Volo/Abp/EventBus/EventNameAttribute.cs | 0 .../Abp/EventBus/GenericEventNameAttribute.cs | 0 .../Volo/Abp/EventBus/IEventNameProvider.cs | 0 .../Volo.Abp.EventBus.csproj | 2 +- .../Volo/Abp/EventBus/AbpEventBusModule.cs | 5 +++- nupkg/common.ps1 | 1 + 11 files changed, 70 insertions(+), 2 deletions(-) create mode 100644 framework/src/Volo.Abp.EventBus.Abstractions/FodyWeavers.xml create mode 100644 framework/src/Volo.Abp.EventBus.Abstractions/FodyWeavers.xsd create mode 100644 framework/src/Volo.Abp.EventBus.Abstractions/Volo.Abp.EventBus.Abstractions.csproj create mode 100644 framework/src/Volo.Abp.EventBus.Abstractions/Volo/Abp/EventBus/Abstractions/AbpEventBusAbstractionsModule.cs rename framework/src/{Volo.Abp.EventBus => Volo.Abp.EventBus.Abstractions}/Volo/Abp/EventBus/EventNameAttribute.cs (100%) rename framework/src/{Volo.Abp.EventBus => Volo.Abp.EventBus.Abstractions}/Volo/Abp/EventBus/GenericEventNameAttribute.cs (100%) rename framework/src/{Volo.Abp.EventBus => Volo.Abp.EventBus.Abstractions}/Volo/Abp/EventBus/IEventNameProvider.cs (100%) diff --git a/framework/Volo.Abp.sln b/framework/Volo.Abp.sln index 963472ada8..8c2670fa27 100644 --- a/framework/Volo.Abp.sln +++ b/framework/Volo.Abp.sln @@ -359,6 +359,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Json.Tests", "test EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Http.Tests", "test\Volo.Abp.Http.Tests\Volo.Abp.Http.Tests.csproj", "{A37BFEB5-7C57-4CDC-93B8-B5CE4BB9ACE1}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.EventBus.Abstractions", "src\Volo.Abp.EventBus.Abstractions\Volo.Abp.EventBus.Abstractions.csproj", "{8FDB3BF7-AD89-43F6-8DEB-C3E29B8801FE}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -1069,6 +1071,10 @@ Global {A37BFEB5-7C57-4CDC-93B8-B5CE4BB9ACE1}.Debug|Any CPU.Build.0 = Debug|Any CPU {A37BFEB5-7C57-4CDC-93B8-B5CE4BB9ACE1}.Release|Any CPU.ActiveCfg = Release|Any CPU {A37BFEB5-7C57-4CDC-93B8-B5CE4BB9ACE1}.Release|Any CPU.Build.0 = Release|Any CPU + {8FDB3BF7-AD89-43F6-8DEB-C3E29B8801FE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8FDB3BF7-AD89-43F6-8DEB-C3E29B8801FE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8FDB3BF7-AD89-43F6-8DEB-C3E29B8801FE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8FDB3BF7-AD89-43F6-8DEB-C3E29B8801FE}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1250,6 +1256,7 @@ Global {DD9519E0-5A68-48DC-A051-7BF2AC922F3E} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} {00D07595-993C-40FC-BD90-0DD6331414D3} = {447C8A77-E5F0-4538-8687-7383196D04EA} {A37BFEB5-7C57-4CDC-93B8-B5CE4BB9ACE1} = {447C8A77-E5F0-4538-8687-7383196D04EA} + {8FDB3BF7-AD89-43F6-8DEB-C3E29B8801FE} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5} diff --git a/framework/src/Volo.Abp.EventBus.Abstractions/FodyWeavers.xml b/framework/src/Volo.Abp.EventBus.Abstractions/FodyWeavers.xml new file mode 100644 index 0000000000..bc5a74a236 --- /dev/null +++ b/framework/src/Volo.Abp.EventBus.Abstractions/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + diff --git a/framework/src/Volo.Abp.EventBus.Abstractions/FodyWeavers.xsd b/framework/src/Volo.Abp.EventBus.Abstractions/FodyWeavers.xsd new file mode 100644 index 0000000000..3f3946e282 --- /dev/null +++ b/framework/src/Volo.Abp.EventBus.Abstractions/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/framework/src/Volo.Abp.EventBus.Abstractions/Volo.Abp.EventBus.Abstractions.csproj b/framework/src/Volo.Abp.EventBus.Abstractions/Volo.Abp.EventBus.Abstractions.csproj new file mode 100644 index 0000000000..ae0cef7f59 --- /dev/null +++ b/framework/src/Volo.Abp.EventBus.Abstractions/Volo.Abp.EventBus.Abstractions.csproj @@ -0,0 +1,15 @@ + + + + + + + netstandard2.0 + + + + + + + + diff --git a/framework/src/Volo.Abp.EventBus.Abstractions/Volo/Abp/EventBus/Abstractions/AbpEventBusAbstractionsModule.cs b/framework/src/Volo.Abp.EventBus.Abstractions/Volo/Abp/EventBus/Abstractions/AbpEventBusAbstractionsModule.cs new file mode 100644 index 0000000000..bb9d4585b9 --- /dev/null +++ b/framework/src/Volo.Abp.EventBus.Abstractions/Volo/Abp/EventBus/Abstractions/AbpEventBusAbstractionsModule.cs @@ -0,0 +1,9 @@ +using Volo.Abp.Modularity; + +namespace Volo.Abp.EventBus.Abstractions +{ + public class AbpEventBusAbstractionsModule : AbpModule + { + + } +} diff --git a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventNameAttribute.cs b/framework/src/Volo.Abp.EventBus.Abstractions/Volo/Abp/EventBus/EventNameAttribute.cs similarity index 100% rename from framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventNameAttribute.cs rename to framework/src/Volo.Abp.EventBus.Abstractions/Volo/Abp/EventBus/EventNameAttribute.cs diff --git a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/GenericEventNameAttribute.cs b/framework/src/Volo.Abp.EventBus.Abstractions/Volo/Abp/EventBus/GenericEventNameAttribute.cs similarity index 100% rename from framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/GenericEventNameAttribute.cs rename to framework/src/Volo.Abp.EventBus.Abstractions/Volo/Abp/EventBus/GenericEventNameAttribute.cs diff --git a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/IEventNameProvider.cs b/framework/src/Volo.Abp.EventBus.Abstractions/Volo/Abp/EventBus/IEventNameProvider.cs similarity index 100% rename from framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/IEventNameProvider.cs rename to framework/src/Volo.Abp.EventBus.Abstractions/Volo/Abp/EventBus/IEventNameProvider.cs diff --git a/framework/src/Volo.Abp.EventBus/Volo.Abp.EventBus.csproj b/framework/src/Volo.Abp.EventBus/Volo.Abp.EventBus.csproj index a1799c6673..221454bed3 100644 --- a/framework/src/Volo.Abp.EventBus/Volo.Abp.EventBus.csproj +++ b/framework/src/Volo.Abp.EventBus/Volo.Abp.EventBus.csproj @@ -15,7 +15,7 @@ - + diff --git a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/AbpEventBusModule.cs b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/AbpEventBusModule.cs index 81c3393a50..eb9c17b129 100644 --- a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/AbpEventBusModule.cs +++ b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/AbpEventBusModule.cs @@ -1,6 +1,7 @@ using Microsoft.Extensions.DependencyInjection; using System; using System.Collections.Generic; +using Volo.Abp.EventBus.Abstractions; using Volo.Abp.EventBus.Distributed; using Volo.Abp.EventBus.Local; using Volo.Abp.Modularity; @@ -9,7 +10,9 @@ using Volo.Abp.Reflection; namespace Volo.Abp.EventBus { - [DependsOn(typeof(AbpMultiTenancyModule))] + [DependsOn( + typeof(AbpEventBusAbstractionsModule), + typeof(AbpMultiTenancyModule))] public class AbpEventBusModule : AbpModule { public override void PreConfigureServices(ServiceConfigurationContext context) diff --git a/nupkg/common.ps1 b/nupkg/common.ps1 index 29249ef3f4..16f13513ee 100644 --- a/nupkg/common.ps1 +++ b/nupkg/common.ps1 @@ -91,6 +91,7 @@ $projects = ( "framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql", "framework/src/Volo.Abp.EntityFrameworkCore.Sqlite", "framework/src/Volo.Abp.EntityFrameworkCore.SqlServer", + "framework/src/Volo.Abp.EventBus.Abstractions", "framework/src/Volo.Abp.EventBus", "framework/src/Volo.Abp.EventBus.RabbitMQ", "framework/src/Volo.Abp.EventBus.Kafka", From 6d50fc998d4ed8238fa5495c6f2299a22a579f81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Mon, 1 Feb 2021 20:54:25 +0300 Subject: [PATCH 3/6] Define TenantCreatedEto --- .../Volo.Abp.MultiTenancy.csproj | 1 + .../Volo/Abp/MultiTenancy/AbpMultiTenancyModule.cs | 4 +++- .../Volo/Abp/MultiTenancy/TenantCreatedEto.cs | 13 +++++++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/TenantCreatedEto.cs diff --git a/framework/src/Volo.Abp.MultiTenancy/Volo.Abp.MultiTenancy.csproj b/framework/src/Volo.Abp.MultiTenancy/Volo.Abp.MultiTenancy.csproj index 951712c7f0..344dbfdec0 100644 --- a/framework/src/Volo.Abp.MultiTenancy/Volo.Abp.MultiTenancy.csproj +++ b/framework/src/Volo.Abp.MultiTenancy/Volo.Abp.MultiTenancy.csproj @@ -16,6 +16,7 @@ + diff --git a/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/AbpMultiTenancyModule.cs b/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/AbpMultiTenancyModule.cs index 9dc64ef7f8..c51daf99aa 100644 --- a/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/AbpMultiTenancyModule.cs +++ b/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/AbpMultiTenancyModule.cs @@ -1,5 +1,6 @@ using Microsoft.Extensions.DependencyInjection; using Volo.Abp.Data; +using Volo.Abp.EventBus.Abstractions; using Volo.Abp.Modularity; using Volo.Abp.MultiTenancy.ConfigurationStore; using Volo.Abp.Security; @@ -10,7 +11,8 @@ namespace Volo.Abp.MultiTenancy [DependsOn( typeof(AbpDataModule), - typeof(AbpSecurityModule) + typeof(AbpSecurityModule), + typeof(AbpEventBusAbstractionsModule) )] public class AbpMultiTenancyModule : AbpModule { diff --git a/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/TenantCreatedEto.cs b/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/TenantCreatedEto.cs new file mode 100644 index 0000000000..6cd1544d8c --- /dev/null +++ b/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/TenantCreatedEto.cs @@ -0,0 +1,13 @@ +using System; +using Volo.Abp.EventBus; + +namespace Volo.Abp.MultiTenancy +{ + [EventName("abp.multi_tenancy.tenant.created")] + public class TenantCreatedEto + { + public Guid Id { get; set; } + + public string Name { get; set; } + } +} From a8b4e0dfa9bea041f674d2a92aadd3498913cc42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Tue, 2 Feb 2021 11:31:21 +0300 Subject: [PATCH 4/6] Seed identity & permission data for a tenant via event handlers. --- .../Entities/Events/Distributed/EtoBase.cs | 0 .../Volo/Abp/MultiTenancy/TenantCreatedEto.cs | 4 +- .../Abp/Identity/AbpIdentityDomainModule.cs | 18 ++++- .../Volo/Abp/Identity/AbpIdentityOptions.cs | 5 ++ .../Volo/Abp/Identity/IdentityDataSeeder.cs | 79 ++++++++++--------- .../Identity/IdentityTenantCreatedHandler.cs | 32 ++++++++ .../Abp/Identity/PasswordOptionsExtensions.cs | 35 ++++++++ .../AbpPermissionManagementDomainModule.cs | 23 +++++- .../PermissionDataSeedContributor.cs | 1 - .../PermissionManagementOptions.cs | 6 +- .../PermissionTenantCreatedHandler.cs | 38 +++++++++ 11 files changed, 197 insertions(+), 44 deletions(-) rename framework/src/{Volo.Abp.Ddd.Domain => Volo.Abp.EventBus.Abstractions}/Volo/Abp/Domain/Entities/Events/Distributed/EtoBase.cs (100%) create mode 100644 modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityTenantCreatedHandler.cs create mode 100644 modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/PasswordOptionsExtensions.cs create mode 100644 modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionTenantCreatedHandler.cs diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/EtoBase.cs b/framework/src/Volo.Abp.EventBus.Abstractions/Volo/Abp/Domain/Entities/Events/Distributed/EtoBase.cs similarity index 100% rename from framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/EtoBase.cs rename to framework/src/Volo.Abp.EventBus.Abstractions/Volo/Abp/Domain/Entities/Events/Distributed/EtoBase.cs diff --git a/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/TenantCreatedEto.cs b/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/TenantCreatedEto.cs index 6cd1544d8c..317995c971 100644 --- a/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/TenantCreatedEto.cs +++ b/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/TenantCreatedEto.cs @@ -1,10 +1,12 @@ using System; +using Volo.Abp.Domain.Entities.Events.Distributed; using Volo.Abp.EventBus; namespace Volo.Abp.MultiTenancy { + [Serializable] [EventName("abp.multi_tenancy.tenant.created")] - public class TenantCreatedEto + public class TenantCreatedEto : EtoBase { public Guid Id { get; set; } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentityDomainModule.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentityDomainModule.cs index 4269ae2e01..6c2197b38d 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentityDomainModule.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentityDomainModule.cs @@ -1,10 +1,12 @@ -using Microsoft.AspNetCore.Identity; +using System.Collections.Generic; +using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; using Volo.Abp.AutoMapper; using Volo.Abp.Domain; using Volo.Abp.Domain.Entities.Events.Distributed; +using Volo.Abp.EventBus.Distributed; using Volo.Abp.Modularity; using Volo.Abp.ObjectExtending; using Volo.Abp.ObjectExtending.Modularity; @@ -57,6 +59,20 @@ namespace Volo.Abp.Identity }); context.Services.AddAbpDynamicOptions(); + + Configure(options => + { + var serviceProvider = context.Services.GetServiceProviderOrNull(); + if (serviceProvider != null) + { + var abpIdentityOptions = serviceProvider.GetRequiredService>().Value; + if (!abpIdentityOptions.IsDistributedEventHandlingEnabled) + { + var identityDomainAssembly = typeof(AbpIdentityDomainModule).Assembly; + options.Handlers.RemoveAll(x => x.Assembly == identityDomainAssembly); + } + } + }); } public override void PostConfigureServices(ServiceConfigurationContext context) diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentityOptions.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentityOptions.cs index 6cfaf75c0c..36b7ba587a 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentityOptions.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentityOptions.cs @@ -4,6 +4,11 @@ { public ExternalLoginProviderDictionary ExternalLoginProviders { get; } + /// + /// Default: true. + /// + public bool IsDistributedEventHandlingEnabled { get; } = true; + public AbpIdentityOptions() { ExternalLoginProviders = new ExternalLoginProviderDictionary(); diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityDataSeeder.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityDataSeeder.cs index f6bed1b914..9051dc7d66 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityDataSeeder.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityDataSeeder.cs @@ -49,58 +49,61 @@ namespace Volo.Abp.Identity Check.NotNullOrWhiteSpace(adminEmail, nameof(adminEmail)); Check.NotNullOrWhiteSpace(adminPassword, nameof(adminPassword)); - await IdentityOptions.SetAsync(); - - var result = new IdentityDataSeedResult(); - using (CurrentTenant.Change(tenantId)) { - //"admin" user - const string adminUserName = "admin"; - var adminUser = await UserRepository.FindByNormalizedUserNameAsync( - LookupNormalizer.NormalizeName(adminUserName) - ); + await IdentityOptions.SetAsync(); - if (adminUser != null) + using (IdentityOptions.Value.Password.ClearRequirements()) { - return result; - } + var result = new IdentityDataSeedResult(); + //"admin" user + const string adminUserName = "admin"; + var adminUser = await UserRepository.FindByNormalizedUserNameAsync( + LookupNormalizer.NormalizeName(adminUserName) + ); - adminUser = new IdentityUser( - GuidGenerator.Create(), - adminUserName, - adminEmail, - tenantId - ) - { - Name = adminUserName - }; - - (await UserManager.CreateAsync(adminUser, adminPassword)).CheckErrors(); - result.CreatedAdminUser = true; + if (adminUser != null) + { + return result; + } - //"admin" role - const string adminRoleName = "admin"; - var adminRole = await RoleRepository.FindByNormalizedNameAsync(LookupNormalizer.NormalizeName(adminRoleName)); - if (adminRole == null) - { - adminRole = new IdentityRole( + adminUser = new IdentityUser( GuidGenerator.Create(), - adminRoleName, + adminUserName, + adminEmail, tenantId ) { - IsStatic = true, - IsPublic = true + Name = adminUserName }; - (await RoleManager.CreateAsync(adminRole)).CheckErrors(); - result.CreatedAdminRole = true; - } + (await UserManager.CreateAsync(adminUser, adminPassword)).CheckErrors(); + result.CreatedAdminUser = true; + + //"admin" role + const string adminRoleName = "admin"; + var adminRole = + await RoleRepository.FindByNormalizedNameAsync(LookupNormalizer.NormalizeName(adminRoleName)); + if (adminRole == null) + { + adminRole = new IdentityRole( + GuidGenerator.Create(), + adminRoleName, + tenantId + ) + { + IsStatic = true, + IsPublic = true + }; - (await UserManager.AddToRoleAsync(adminUser, adminRoleName)).CheckErrors(); + (await RoleManager.CreateAsync(adminRole)).CheckErrors(); + result.CreatedAdminRole = true; + } - return result; + (await UserManager.AddToRoleAsync(adminUser, adminRoleName)).CheckErrors(); + + return result; + } } } } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityTenantCreatedHandler.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityTenantCreatedHandler.cs new file mode 100644 index 0000000000..92a0be629c --- /dev/null +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityTenantCreatedHandler.cs @@ -0,0 +1,32 @@ +using System.Threading.Tasks; +using AutoMapper.Internal; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Volo.Abp.DependencyInjection; +using Volo.Abp.EventBus.Distributed; +using Volo.Abp.MultiTenancy; + +namespace Volo.Abp.Identity +{ + public class IdentityTenantCreatedHandler : IDistributedEventHandler, ITransientDependency + { + public ILogger Logger { get; set; } + + protected IIdentityDataSeeder IdentityDataSeeder { get; } + + public IdentityTenantCreatedHandler(IIdentityDataSeeder identityDataSeeder) + { + IdentityDataSeeder = identityDataSeeder; + Logger = NullLogger.Instance; + } + + public async Task HandleEventAsync(TenantCreatedEto eventData) + { + await IdentityDataSeeder.SeedAsync( + eventData.Properties.GetOrDefault("AdminEmail") as string ?? "admin@abp.io", + eventData.Properties.GetOrDefault("AdminPassword") as string ?? "1q2w3E*", + eventData.Id + ); + } + } +} diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/PasswordOptionsExtensions.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/PasswordOptionsExtensions.cs new file mode 100644 index 0000000000..793cc6349e --- /dev/null +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/PasswordOptionsExtensions.cs @@ -0,0 +1,35 @@ +using System; +using Microsoft.AspNetCore.Identity; + +namespace Volo.Abp.Identity +{ + public static class PasswordOptionsExtensions + { + public static IDisposable ClearRequirements(this PasswordOptions options) + { + var oldRequireDigit = options.RequireDigit; + var oldRequiredLength = options.RequiredLength; + var oldRequireLowercase = options.RequireLowercase; + var oldRequireUppercase = options.RequireUppercase; + var oldRequiredUniqueChars = options.RequiredUniqueChars; + var oldRequireNonAlphanumeric = options.RequireNonAlphanumeric; + + options.RequireDigit = false; + options.RequiredLength = 1; + options.RequireLowercase = false; + options.RequireUppercase = false; + options.RequiredUniqueChars = 1; + options.RequireNonAlphanumeric = false; + + return new DisposeAction(() => + { + options.RequireDigit = oldRequireDigit; + options.RequiredLength = oldRequiredLength; + options.RequireLowercase = oldRequireLowercase; + options.RequireUppercase = oldRequireUppercase; + options.RequiredUniqueChars = oldRequiredUniqueChars; + options.RequireNonAlphanumeric = oldRequireNonAlphanumeric; + }); + } + } +} diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/AbpPermissionManagementDomainModule.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/AbpPermissionManagementDomainModule.cs index c199a2130b..5b3eb9f40f 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/AbpPermissionManagementDomainModule.cs +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/AbpPermissionManagementDomainModule.cs @@ -1,6 +1,10 @@ -using Volo.Abp.Authorization; +using System.Collections.Generic; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using Volo.Abp.Authorization; using Volo.Abp.Caching; using Volo.Abp.Domain; +using Volo.Abp.EventBus.Distributed; using Volo.Abp.Json; using Volo.Abp.Modularity; @@ -13,6 +17,21 @@ namespace Volo.Abp.PermissionManagement [DependsOn(typeof(AbpJsonModule))] public class AbpPermissionManagementDomainModule : AbpModule { - + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + var serviceProvider = context.Services.GetServiceProviderOrNull(); + if (serviceProvider != null) + { + var abpIdentityOptions = serviceProvider.GetRequiredService>().Value; + if (!abpIdentityOptions.IsDistributedEventHandlingEnabled) + { + var identityDomainAssembly = typeof(AbpPermissionManagementDomainModule).Assembly; + options.Handlers.RemoveAll(x => x.Assembly == identityDomainAssembly); + } + } + }); + } } } diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionDataSeedContributor.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionDataSeedContributor.cs index ac8274d445..8260df895f 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionDataSeedContributor.cs +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionDataSeedContributor.cs @@ -10,7 +10,6 @@ namespace Volo.Abp.PermissionManagement public class PermissionDataSeedContributor : IDataSeedContributor, ITransientDependency { protected ICurrentTenant CurrentTenant { get; } - protected IPermissionDefinitionManager PermissionDefinitionManager { get; } protected IPermissionDataSeeder PermissionDataSeeder { get; } diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionManagementOptions.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionManagementOptions.cs index 674f5daa88..c36cde49ee 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionManagementOptions.cs +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionManagementOptions.cs @@ -5,11 +5,15 @@ namespace Volo.Abp.PermissionManagement { public class PermissionManagementOptions { - //TODO: rename to Providers public ITypeList ManagementProviders { get; } public Dictionary ProviderPolicies { get; } + /// + /// Default: true. + /// + public bool IsDistributedEventHandlingEnabled { get; } = true; + public PermissionManagementOptions() { ManagementProviders = new TypeList(); diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionTenantCreatedHandler.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionTenantCreatedHandler.cs new file mode 100644 index 0000000000..8962574434 --- /dev/null +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionTenantCreatedHandler.cs @@ -0,0 +1,38 @@ +using System.Linq; +using System.Threading.Tasks; +using Volo.Abp.Authorization.Permissions; +using Volo.Abp.DependencyInjection; +using Volo.Abp.EventBus.Distributed; +using Volo.Abp.MultiTenancy; + +namespace Volo.Abp.PermissionManagement +{ + public class PermissionTenantCreatedHandler : IDistributedEventHandler, ITransientDependency + { + protected IPermissionDefinitionManager PermissionDefinitionManager { get; } + protected IPermissionDataSeeder PermissionDataSeeder { get; } + + public PermissionTenantCreatedHandler(IPermissionDefinitionManager permissionDefinitionManager, IPermissionDataSeeder permissionDataSeeder) + { + PermissionDefinitionManager = permissionDefinitionManager; + PermissionDataSeeder = permissionDataSeeder; + } + + public async Task HandleEventAsync(TenantCreatedEto eventData) + { + var permissionNames = PermissionDefinitionManager + .GetPermissions() + .Where(p => p.MultiTenancySide.HasFlag(MultiTenancySides.Tenant)) + .Where(p => !p.Providers.Any() || p.Providers.Contains(RolePermissionValueProvider.ProviderName)) + .Select(p => p.Name) + .ToArray(); + + await PermissionDataSeeder.SeedAsync( + RolePermissionValueProvider.ProviderName, + "admin", + permissionNames, + eventData.Id + ); + } + } +} From 6413691c0e9fda04119c54f6efdf7424f2cd9d74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Tue, 2 Feb 2021 15:13:25 +0300 Subject: [PATCH 5/6] Cancel event handlers in identity and permission modules. --- .../NullTenantDatabaseSchemaMigrator.cs | 13 ------- .../TenantDatabaseSchemaMigrator.cs | 13 ------- .../Abp/Identity/AbpIdentityDomainModule.cs | 15 -------- .../Volo/Abp/Identity/AbpIdentityOptions.cs | 5 --- .../Identity/IdentityTenantCreatedHandler.cs | 32 ---------------- .../AbpPermissionManagementDomainModule.cs | 18 +-------- .../PermissionManagementOptions.cs | 5 --- .../PermissionTenantCreatedHandler.cs | 38 ------------------- 8 files changed, 1 insertion(+), 138 deletions(-) delete mode 100644 framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/NullTenantDatabaseSchemaMigrator.cs delete mode 100644 framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/TenantDatabaseSchemaMigrator.cs delete mode 100644 modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityTenantCreatedHandler.cs delete mode 100644 modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionTenantCreatedHandler.cs diff --git a/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/NullTenantDatabaseSchemaMigrator.cs b/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/NullTenantDatabaseSchemaMigrator.cs deleted file mode 100644 index 7d5e76f11b..0000000000 --- a/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/NullTenantDatabaseSchemaMigrator.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Threading.Tasks; -using Volo.Abp.DependencyInjection; - -namespace Volo.Abp.MultiTenancy -{ - public class NullTenantDatabaseSchemaMigrator : ITenantDatabaseSchemaMigrator, ISingletonDependency - { - public Task MigrateAsync() - { - return Task.CompletedTask; - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/TenantDatabaseSchemaMigrator.cs b/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/TenantDatabaseSchemaMigrator.cs deleted file mode 100644 index e6fef5c427..0000000000 --- a/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/TenantDatabaseSchemaMigrator.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Threading.Tasks; - -namespace Volo.Abp.MultiTenancy -{ - public interface ITenantDatabaseSchemaMigrator - { - /// - /// Set Current Tenant before calling this method. - /// - /// - Task MigrateAsync(); - } -} diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentityDomainModule.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentityDomainModule.cs index 6c2197b38d..4326d1aeb8 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentityDomainModule.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentityDomainModule.cs @@ -6,7 +6,6 @@ using Microsoft.Extensions.Options; using Volo.Abp.AutoMapper; using Volo.Abp.Domain; using Volo.Abp.Domain.Entities.Events.Distributed; -using Volo.Abp.EventBus.Distributed; using Volo.Abp.Modularity; using Volo.Abp.ObjectExtending; using Volo.Abp.ObjectExtending.Modularity; @@ -59,20 +58,6 @@ namespace Volo.Abp.Identity }); context.Services.AddAbpDynamicOptions(); - - Configure(options => - { - var serviceProvider = context.Services.GetServiceProviderOrNull(); - if (serviceProvider != null) - { - var abpIdentityOptions = serviceProvider.GetRequiredService>().Value; - if (!abpIdentityOptions.IsDistributedEventHandlingEnabled) - { - var identityDomainAssembly = typeof(AbpIdentityDomainModule).Assembly; - options.Handlers.RemoveAll(x => x.Assembly == identityDomainAssembly); - } - } - }); } public override void PostConfigureServices(ServiceConfigurationContext context) diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentityOptions.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentityOptions.cs index 36b7ba587a..6cfaf75c0c 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentityOptions.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentityOptions.cs @@ -4,11 +4,6 @@ { public ExternalLoginProviderDictionary ExternalLoginProviders { get; } - /// - /// Default: true. - /// - public bool IsDistributedEventHandlingEnabled { get; } = true; - public AbpIdentityOptions() { ExternalLoginProviders = new ExternalLoginProviderDictionary(); diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityTenantCreatedHandler.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityTenantCreatedHandler.cs deleted file mode 100644 index 92a0be629c..0000000000 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityTenantCreatedHandler.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System.Threading.Tasks; -using AutoMapper.Internal; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; -using Volo.Abp.DependencyInjection; -using Volo.Abp.EventBus.Distributed; -using Volo.Abp.MultiTenancy; - -namespace Volo.Abp.Identity -{ - public class IdentityTenantCreatedHandler : IDistributedEventHandler, ITransientDependency - { - public ILogger Logger { get; set; } - - protected IIdentityDataSeeder IdentityDataSeeder { get; } - - public IdentityTenantCreatedHandler(IIdentityDataSeeder identityDataSeeder) - { - IdentityDataSeeder = identityDataSeeder; - Logger = NullLogger.Instance; - } - - public async Task HandleEventAsync(TenantCreatedEto eventData) - { - await IdentityDataSeeder.SeedAsync( - eventData.Properties.GetOrDefault("AdminEmail") as string ?? "admin@abp.io", - eventData.Properties.GetOrDefault("AdminPassword") as string ?? "1q2w3E*", - eventData.Id - ); - } - } -} diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/AbpPermissionManagementDomainModule.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/AbpPermissionManagementDomainModule.cs index 5b3eb9f40f..f437802c6b 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/AbpPermissionManagementDomainModule.cs +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/AbpPermissionManagementDomainModule.cs @@ -4,7 +4,6 @@ using Microsoft.Extensions.Options; using Volo.Abp.Authorization; using Volo.Abp.Caching; using Volo.Abp.Domain; -using Volo.Abp.EventBus.Distributed; using Volo.Abp.Json; using Volo.Abp.Modularity; @@ -17,21 +16,6 @@ namespace Volo.Abp.PermissionManagement [DependsOn(typeof(AbpJsonModule))] public class AbpPermissionManagementDomainModule : AbpModule { - public override void ConfigureServices(ServiceConfigurationContext context) - { - Configure(options => - { - var serviceProvider = context.Services.GetServiceProviderOrNull(); - if (serviceProvider != null) - { - var abpIdentityOptions = serviceProvider.GetRequiredService>().Value; - if (!abpIdentityOptions.IsDistributedEventHandlingEnabled) - { - var identityDomainAssembly = typeof(AbpPermissionManagementDomainModule).Assembly; - options.Handlers.RemoveAll(x => x.Assembly == identityDomainAssembly); - } - } - }); - } + } } diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionManagementOptions.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionManagementOptions.cs index c36cde49ee..2e29ab9c29 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionManagementOptions.cs +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionManagementOptions.cs @@ -9,11 +9,6 @@ namespace Volo.Abp.PermissionManagement public Dictionary ProviderPolicies { get; } - /// - /// Default: true. - /// - public bool IsDistributedEventHandlingEnabled { get; } = true; - public PermissionManagementOptions() { ManagementProviders = new TypeList(); diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionTenantCreatedHandler.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionTenantCreatedHandler.cs deleted file mode 100644 index 8962574434..0000000000 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionTenantCreatedHandler.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System.Linq; -using System.Threading.Tasks; -using Volo.Abp.Authorization.Permissions; -using Volo.Abp.DependencyInjection; -using Volo.Abp.EventBus.Distributed; -using Volo.Abp.MultiTenancy; - -namespace Volo.Abp.PermissionManagement -{ - public class PermissionTenantCreatedHandler : IDistributedEventHandler, ITransientDependency - { - protected IPermissionDefinitionManager PermissionDefinitionManager { get; } - protected IPermissionDataSeeder PermissionDataSeeder { get; } - - public PermissionTenantCreatedHandler(IPermissionDefinitionManager permissionDefinitionManager, IPermissionDataSeeder permissionDataSeeder) - { - PermissionDefinitionManager = permissionDefinitionManager; - PermissionDataSeeder = permissionDataSeeder; - } - - public async Task HandleEventAsync(TenantCreatedEto eventData) - { - var permissionNames = PermissionDefinitionManager - .GetPermissions() - .Where(p => p.MultiTenancySide.HasFlag(MultiTenancySides.Tenant)) - .Where(p => !p.Providers.Any() || p.Providers.Contains(RolePermissionValueProvider.ProviderName)) - .Select(p => p.Name) - .ToArray(); - - await PermissionDataSeeder.SeedAsync( - RolePermissionValueProvider.ProviderName, - "admin", - permissionNames, - eventData.Id - ); - } - } -} From cea97c23191abd4e40a3ed526b6fe073e1850ee8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Tue, 2 Feb 2021 16:00:04 +0300 Subject: [PATCH 6/6] Create TenantConnectionStringUpdatedEto.cs --- .../TenantConnectionStringUpdatedEto.cs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/TenantConnectionStringUpdatedEto.cs diff --git a/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/TenantConnectionStringUpdatedEto.cs b/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/TenantConnectionStringUpdatedEto.cs new file mode 100644 index 0000000000..022343821b --- /dev/null +++ b/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/TenantConnectionStringUpdatedEto.cs @@ -0,0 +1,21 @@ +using System; +using Volo.Abp.Domain.Entities.Events.Distributed; +using Volo.Abp.EventBus; + +namespace Volo.Abp.MultiTenancy +{ + [Serializable] + [EventName("abp.multi_tenancy.tenant.connection_string.updated")] + public class TenantConnectionStringUpdatedEto : EtoBase + { + public Guid Id { get; set; } + + public string Name { get; set; } + + public string ConnectionStringName { get; set; } + + public string OldValue { get; set; } + + public string NewValue { get; set; } + } +}