diff --git a/Abp.Zero.sln b/Abp.Zero.sln index 6e6c9dee..1325916f 100644 --- a/Abp.Zero.sln +++ b/Abp.Zero.sln @@ -1,44 +1,71 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.25420.1 +# Visual Studio 15 +VisualStudioVersion = 15.0.26430.6 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{364398C2-9B6A-40B3-9845-23ABFB8D12AB}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{974A8277-6807-4C84-A0EA-B98AEC770EF8}" ProjectSection(SolutionItems) = preProject - global.json = global.json + appveyor.yml = appveyor.yml + common.props = common.props EndProjectSection EndProject -Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Abp.Zero", "src\Abp.Zero\Abp.Zero.xproj", "{E06353A0-8403-44A0-8D57-200D55E2F2C5}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{E1AD01DD-1A0D-40DF-AFBE-F2AA3B4160AB}" EndProject -Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Abp.Zero.Ldap", "src\Abp.Zero.Ldap\Abp.Zero.Ldap.xproj", "{C28F608B-F34E-411C-8D88-C5D566141735}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Abp.Zero", "src\Abp.Zero\Abp.Zero.csproj", "{E06353A0-8403-44A0-8D57-200D55E2F2C5}" EndProject -Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Abp.Zero.EntityFramework", "src\Abp.Zero.EntityFramework\Abp.Zero.EntityFramework.xproj", "{A57787C3-AD9A-40A8-B955-EDA596397429}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Abp.Zero.Ldap", "src\Abp.Zero.Ldap\Abp.Zero.Ldap.csproj", "{C28F608B-F34E-411C-8D88-C5D566141735}" EndProject -Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Abp.Zero.NHibernate", "src\Abp.Zero.NHibernate\Abp.Zero.NHibernate.xproj", "{C64C24CD-74CE-47A2-A03C-06F4F78413F1}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Abp.Zero.EntityFramework", "src\Abp.Zero.EntityFramework\Abp.Zero.EntityFramework.csproj", "{A57787C3-AD9A-40A8-B955-EDA596397429}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{E1AD01DD-1A0D-40DF-AFBE-F2AA3B4160AB}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Abp.Zero.NHibernate", "src\Abp.Zero.NHibernate\Abp.Zero.NHibernate.csproj", "{C64C24CD-74CE-47A2-A03C-06F4F78413F1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Abp.Zero.SampleApp", "test\Abp.Zero.SampleApp\Abp.Zero.SampleApp.csproj", "{667801A0-99A5-414A-8685-A035408BF413}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Abp.Zero.SampleApp.EntityFramework", "test\Abp.Zero.SampleApp.EntityFramework\Abp.Zero.SampleApp.EntityFramework.csproj", "{97670397-EDF9-4968-9A25-0EB71C68D5F8}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Abp.Zero.SampleApp.NHibernateTests", "test\Abp.Zero.SampleApp.NHibernateTests\Abp.Zero.SampleApp.NHibernateTests.csproj", "{FA3E87C5-7273-461C-AE9F-67B416E07949}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Abp.Zero.SampleApp.Tests", "test\Abp.Zero.SampleApp.Tests\Abp.Zero.SampleApp.Tests.csproj", "{41F88D4F-FAB9-4D0A-85E5-C71F8B63B973}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Abp.Zero.EntityFrameworkCore", "src\Abp.Zero.EntityFrameworkCore\Abp.Zero.EntityFrameworkCore.csproj", "{83108E5E-FD31-4762-8854-D795A8FB482F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Abp.Zero.SampleApp.EntityFrameworkCore", "test\Abp.Zero.SampleApp.EntityFrameworkCore\Abp.Zero.SampleApp.EntityFrameworkCore.csproj", "{76E2CCE7-A61F-4700-9054-9923645E41CC}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Abp.Zero.SampleApp.EntityFrameworkCore.Tests", "test\Abp.Zero.SampleApp.EntityFrameworkCore.Tests\Abp.Zero.SampleApp.EntityFrameworkCore.Tests.csproj", "{9223F076-ECE7-4381-9BBF-F3E33073B91A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Abp.Zero.SampleApp.EntityFrameworkCore.ConsoleAppTest", "test\Abp.Zero.SampleApp.EntityFrameworkCore.ConsoleAppTest\Abp.Zero.SampleApp.EntityFrameworkCore.ConsoleAppTest.csproj", "{25F73233-3922-4BC0-AC1C-2042613DEC5E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Abp.Zero.Owin", "src\Abp.Zero.Owin\Abp.Zero.Owin.csproj", "{81D2351F-6CA1-4976-86EB-63946EBD0A36}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Abp.Zero.AspNetCore", "src\Abp.Zero.AspNetCore\Abp.Zero.AspNetCore.csproj", "{8EE2ACFF-CB44-4A7E-8FE7-5709AD245F44}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Abp.ZeroCore", "src\Abp.ZeroCore\Abp.ZeroCore.csproj", "{C5F4218F-2637-4629-B43B-7728D28CB19E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Abp.Zero.Common", "src\Abp.Zero.Common\Abp.Zero.Common.csproj", "{EABE7BBB-A837-4607-9B0D-BE41E4A0F81F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Abp.ZeroCore.EntityFrameworkCore", "src\Abp.ZeroCore.EntityFrameworkCore\Abp.ZeroCore.EntityFrameworkCore.csproj", "{39A3807E-4309-43D7-98C1-246547108006}" EndProject -Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Abp.Zero.SampleApp", "test\Abp.Zero.SampleApp\Abp.Zero.SampleApp.xproj", "{667801A0-99A5-414A-8685-A035408BF413}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ZeroCore", "ZeroCore", "{A6CB86B2-8C58-42D7-917A-F1DEB29C121A}" EndProject -Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Abp.Zero.SampleApp.EntityFramework", "test\Abp.Zero.SampleApp.EntityFramework\Abp.Zero.SampleApp.EntityFramework.xproj", "{97670397-EDF9-4968-9A25-0EB71C68D5F8}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Zero", "Zero", "{22551F30-C7C5-40D5-AE24-3D675ABC9B46}" EndProject -Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Abp.Zero.SampleApp.NHibernateTests", "test\Abp.Zero.SampleApp.NHibernateTests\Abp.Zero.SampleApp.NHibernateTests.xproj", "{FA3E87C5-7273-461C-AE9F-67B416E07949}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Zero", "Zero", "{4A2AFB7B-F4F7-4E47-A8A3-F1F848C9F1B9}" EndProject -Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Abp.Zero.SampleApp.Tests", "test\Abp.Zero.SampleApp.Tests\Abp.Zero.SampleApp.Tests.xproj", "{41F88D4F-FAB9-4D0A-85E5-C71F8B63B973}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{12068527-E860-4AC1-88C3-62B435E4BBE5}" EndProject -Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Abp.Zero.EntityFrameworkCore", "src\Abp.Zero.EntityFrameworkCore\Abp.Zero.EntityFrameworkCore.xproj", "{83108E5E-FD31-4762-8854-D795A8FB482F}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Abp.ZeroCore.IdentityServer4", "src\Abp.ZeroCore.IdentityServer4\Abp.ZeroCore.IdentityServer4.csproj", "{41608BBD-C619-4579-AA32-CA6FC6B7F810}" EndProject -Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Abp.Zero.SampleApp.EntityFrameworkCore", "test\Abp.Zero.SampleApp.EntityFrameworkCore\Abp.Zero.SampleApp.EntityFrameworkCore.xproj", "{76E2CCE7-A61F-4700-9054-9923645E41CC}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ZeroCore", "ZeroCore", "{01532A9D-77F4-477E-A43E-0E7078E4E9DD}" EndProject -Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Abp.Zero.SampleApp.EntityFrameworkCore.Tests", "test\Abp.Zero.SampleApp.EntityFrameworkCore.Tests\Abp.Zero.SampleApp.EntityFrameworkCore.Tests.xproj", "{9223F076-ECE7-4381-9BBF-F3E33073B91A}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Abp.ZeroCore.IdentityServer4.Tests", "test\Abp.ZeroCore.IdentityServer4.Tests\Abp.ZeroCore.IdentityServer4.Tests.csproj", "{4B03D73D-D453-4FCB-95C8-F23D1B300DE7}" EndProject -Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Abp.Zero.SampleApp.EntityFrameworkCore.ConsoleAppTest", "test\Abp.Zero.SampleApp.EntityFrameworkCore.ConsoleAppTest\Abp.Zero.SampleApp.EntityFrameworkCore.ConsoleAppTest.xproj", "{25F73233-3922-4BC0-AC1C-2042613DEC5E}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Abp.ZeroCore.SampleApp", "test\Abp.ZeroCore.SampleApp\Abp.ZeroCore.SampleApp.csproj", "{D1084B66-375B-433A-9742-FD4A703994E1}" EndProject -Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Abp.Zero.Owin", "src\Abp.Zero.Owin\Abp.Zero.Owin.xproj", "{81D2351F-6CA1-4976-86EB-63946EBD0A36}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Abp.ZeroCore.Tests", "test\Abp.ZeroCore.Tests\Abp.ZeroCore.Tests.csproj", "{72427FA6-BC94-42AA-B9AC-3CF2E257E41B}" EndProject -Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Abp.Zero.AspNetCore", "src\Abp.Zero.AspNetCore\Abp.Zero.AspNetCore.xproj", "{8EE2ACFF-CB44-4A7E-8FE7-5709AD245F44}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Abp.ZeroCore.IdentityServer4.EntityFrameworkCore", "src\Abp.ZeroCore.IdentityServer4.EntityFrameworkCore\Abp.ZeroCore.IdentityServer4.EntityFrameworkCore.csproj", "{F4A295A8-3FE6-4397-B3F5-0CFF9EC483E9}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -102,24 +129,69 @@ Global {8EE2ACFF-CB44-4A7E-8FE7-5709AD245F44}.Debug|Any CPU.Build.0 = Debug|Any CPU {8EE2ACFF-CB44-4A7E-8FE7-5709AD245F44}.Release|Any CPU.ActiveCfg = Release|Any CPU {8EE2ACFF-CB44-4A7E-8FE7-5709AD245F44}.Release|Any CPU.Build.0 = Release|Any CPU + {C5F4218F-2637-4629-B43B-7728D28CB19E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C5F4218F-2637-4629-B43B-7728D28CB19E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C5F4218F-2637-4629-B43B-7728D28CB19E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C5F4218F-2637-4629-B43B-7728D28CB19E}.Release|Any CPU.Build.0 = Release|Any CPU + {EABE7BBB-A837-4607-9B0D-BE41E4A0F81F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EABE7BBB-A837-4607-9B0D-BE41E4A0F81F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EABE7BBB-A837-4607-9B0D-BE41E4A0F81F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EABE7BBB-A837-4607-9B0D-BE41E4A0F81F}.Release|Any CPU.Build.0 = Release|Any CPU + {39A3807E-4309-43D7-98C1-246547108006}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {39A3807E-4309-43D7-98C1-246547108006}.Debug|Any CPU.Build.0 = Debug|Any CPU + {39A3807E-4309-43D7-98C1-246547108006}.Release|Any CPU.ActiveCfg = Release|Any CPU + {39A3807E-4309-43D7-98C1-246547108006}.Release|Any CPU.Build.0 = Release|Any CPU + {41608BBD-C619-4579-AA32-CA6FC6B7F810}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {41608BBD-C619-4579-AA32-CA6FC6B7F810}.Debug|Any CPU.Build.0 = Debug|Any CPU + {41608BBD-C619-4579-AA32-CA6FC6B7F810}.Release|Any CPU.ActiveCfg = Release|Any CPU + {41608BBD-C619-4579-AA32-CA6FC6B7F810}.Release|Any CPU.Build.0 = Release|Any CPU + {4B03D73D-D453-4FCB-95C8-F23D1B300DE7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4B03D73D-D453-4FCB-95C8-F23D1B300DE7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4B03D73D-D453-4FCB-95C8-F23D1B300DE7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4B03D73D-D453-4FCB-95C8-F23D1B300DE7}.Release|Any CPU.Build.0 = Release|Any CPU + {D1084B66-375B-433A-9742-FD4A703994E1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D1084B66-375B-433A-9742-FD4A703994E1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D1084B66-375B-433A-9742-FD4A703994E1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D1084B66-375B-433A-9742-FD4A703994E1}.Release|Any CPU.Build.0 = Release|Any CPU + {72427FA6-BC94-42AA-B9AC-3CF2E257E41B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {72427FA6-BC94-42AA-B9AC-3CF2E257E41B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {72427FA6-BC94-42AA-B9AC-3CF2E257E41B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {72427FA6-BC94-42AA-B9AC-3CF2E257E41B}.Release|Any CPU.Build.0 = Release|Any CPU + {F4A295A8-3FE6-4397-B3F5-0CFF9EC483E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F4A295A8-3FE6-4397-B3F5-0CFF9EC483E9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F4A295A8-3FE6-4397-B3F5-0CFF9EC483E9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F4A295A8-3FE6-4397-B3F5-0CFF9EC483E9}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {E06353A0-8403-44A0-8D57-200D55E2F2C5} = {364398C2-9B6A-40B3-9845-23ABFB8D12AB} - {C28F608B-F34E-411C-8D88-C5D566141735} = {364398C2-9B6A-40B3-9845-23ABFB8D12AB} - {A57787C3-AD9A-40A8-B955-EDA596397429} = {364398C2-9B6A-40B3-9845-23ABFB8D12AB} - {C64C24CD-74CE-47A2-A03C-06F4F78413F1} = {364398C2-9B6A-40B3-9845-23ABFB8D12AB} - {667801A0-99A5-414A-8685-A035408BF413} = {E1AD01DD-1A0D-40DF-AFBE-F2AA3B4160AB} - {97670397-EDF9-4968-9A25-0EB71C68D5F8} = {E1AD01DD-1A0D-40DF-AFBE-F2AA3B4160AB} - {FA3E87C5-7273-461C-AE9F-67B416E07949} = {E1AD01DD-1A0D-40DF-AFBE-F2AA3B4160AB} - {41F88D4F-FAB9-4D0A-85E5-C71F8B63B973} = {E1AD01DD-1A0D-40DF-AFBE-F2AA3B4160AB} - {83108E5E-FD31-4762-8854-D795A8FB482F} = {364398C2-9B6A-40B3-9845-23ABFB8D12AB} - {76E2CCE7-A61F-4700-9054-9923645E41CC} = {E1AD01DD-1A0D-40DF-AFBE-F2AA3B4160AB} - {9223F076-ECE7-4381-9BBF-F3E33073B91A} = {E1AD01DD-1A0D-40DF-AFBE-F2AA3B4160AB} - {25F73233-3922-4BC0-AC1C-2042613DEC5E} = {E1AD01DD-1A0D-40DF-AFBE-F2AA3B4160AB} - {81D2351F-6CA1-4976-86EB-63946EBD0A36} = {364398C2-9B6A-40B3-9845-23ABFB8D12AB} - {8EE2ACFF-CB44-4A7E-8FE7-5709AD245F44} = {364398C2-9B6A-40B3-9845-23ABFB8D12AB} + {E06353A0-8403-44A0-8D57-200D55E2F2C5} = {22551F30-C7C5-40D5-AE24-3D675ABC9B46} + {C28F608B-F34E-411C-8D88-C5D566141735} = {12068527-E860-4AC1-88C3-62B435E4BBE5} + {A57787C3-AD9A-40A8-B955-EDA596397429} = {22551F30-C7C5-40D5-AE24-3D675ABC9B46} + {C64C24CD-74CE-47A2-A03C-06F4F78413F1} = {22551F30-C7C5-40D5-AE24-3D675ABC9B46} + {667801A0-99A5-414A-8685-A035408BF413} = {4A2AFB7B-F4F7-4E47-A8A3-F1F848C9F1B9} + {97670397-EDF9-4968-9A25-0EB71C68D5F8} = {4A2AFB7B-F4F7-4E47-A8A3-F1F848C9F1B9} + {FA3E87C5-7273-461C-AE9F-67B416E07949} = {4A2AFB7B-F4F7-4E47-A8A3-F1F848C9F1B9} + {41F88D4F-FAB9-4D0A-85E5-C71F8B63B973} = {4A2AFB7B-F4F7-4E47-A8A3-F1F848C9F1B9} + {83108E5E-FD31-4762-8854-D795A8FB482F} = {22551F30-C7C5-40D5-AE24-3D675ABC9B46} + {76E2CCE7-A61F-4700-9054-9923645E41CC} = {4A2AFB7B-F4F7-4E47-A8A3-F1F848C9F1B9} + {9223F076-ECE7-4381-9BBF-F3E33073B91A} = {4A2AFB7B-F4F7-4E47-A8A3-F1F848C9F1B9} + {25F73233-3922-4BC0-AC1C-2042613DEC5E} = {4A2AFB7B-F4F7-4E47-A8A3-F1F848C9F1B9} + {81D2351F-6CA1-4976-86EB-63946EBD0A36} = {22551F30-C7C5-40D5-AE24-3D675ABC9B46} + {8EE2ACFF-CB44-4A7E-8FE7-5709AD245F44} = {22551F30-C7C5-40D5-AE24-3D675ABC9B46} + {C5F4218F-2637-4629-B43B-7728D28CB19E} = {A6CB86B2-8C58-42D7-917A-F1DEB29C121A} + {EABE7BBB-A837-4607-9B0D-BE41E4A0F81F} = {12068527-E860-4AC1-88C3-62B435E4BBE5} + {39A3807E-4309-43D7-98C1-246547108006} = {A6CB86B2-8C58-42D7-917A-F1DEB29C121A} + {A6CB86B2-8C58-42D7-917A-F1DEB29C121A} = {364398C2-9B6A-40B3-9845-23ABFB8D12AB} + {22551F30-C7C5-40D5-AE24-3D675ABC9B46} = {364398C2-9B6A-40B3-9845-23ABFB8D12AB} + {4A2AFB7B-F4F7-4E47-A8A3-F1F848C9F1B9} = {E1AD01DD-1A0D-40DF-AFBE-F2AA3B4160AB} + {12068527-E860-4AC1-88C3-62B435E4BBE5} = {364398C2-9B6A-40B3-9845-23ABFB8D12AB} + {41608BBD-C619-4579-AA32-CA6FC6B7F810} = {A6CB86B2-8C58-42D7-917A-F1DEB29C121A} + {01532A9D-77F4-477E-A43E-0E7078E4E9DD} = {E1AD01DD-1A0D-40DF-AFBE-F2AA3B4160AB} + {4B03D73D-D453-4FCB-95C8-F23D1B300DE7} = {01532A9D-77F4-477E-A43E-0E7078E4E9DD} + {D1084B66-375B-433A-9742-FD4A703994E1} = {01532A9D-77F4-477E-A43E-0E7078E4E9DD} + {72427FA6-BC94-42AA-B9AC-3CF2E257E41B} = {01532A9D-77F4-477E-A43E-0E7078E4E9DD} + {F4A295A8-3FE6-4397-B3F5-0CFF9EC483E9} = {A6CB86B2-8C58-42D7-917A-F1DEB29C121A} EndGlobalSection EndGlobal diff --git a/README.md b/README.md index 5a9db717..90519d97 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,3 @@ -ASP.NET Boilerplate - Module Zero -=========== +**IMPORTANT NOTICE:** This repository is moved to ASP.NET Boilerplate repository and it is deprecated now. Please refer to ASP.NET Boilerplate repository. -AppVeyor: [![Build status](https://ci.appveyor.com/api/projects/status/56e9cadwavg3lj64?svg=true)](https://ci.appveyor.com/project/hikalkan/module-zero) -What is 'module zero' ----------- - -Module-Zero is the module that implements abstract concepts of ASP.NET Boilerplate framework, also adds some useful stuff for enterprise web applications: - -See http://www.aspnetboilerplate.com/Pages/Documents/Zero/Overall for more information. - -How to start? -------------------- -Follow the documentation to start: http://www.aspnetboilerplate.com/Pages/Documents/Zero/Installation - -Sample Projects -------------------- - -* https://github.com/aspnetboilerplate/eventcloud -* https://github.com/aspnetboilerplate/questions-answers diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 00000000..af2aae6f --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,36 @@ +configuration: Release +version: 2.0.0-beta{build} +image: Visual Studio 2017 + +nuget: + disable_publish_on_pr: true + +before_build: +- cmd: set PATH=C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\MSBuild\15.0\Bin;C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin;C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\15.0\Bin;%PATH% +- cmd: nuget install nuget.commandline -source https://dotnet.myget.org/F/nuget-build -prerelease -excludeversion +- cmd: set PATH=%appveyor_build_folder%\NuGet.CommandLine\tools;%PATH% +- cmd: dotnet.exe restore + +after_build: +- ps: >- + $versionSuffix=(Get-Item Env:APPVEYOR_BUILD_NUMBER).Value + + Set-Location "nupkg" + + .\pack.ps1 -versionSuffix "$versionSuffix" + +build: + verbosity: minimal + +pull_requests: + do_not_increment_build_number: true + +test: + assemblies: + - test\Abp.Zero.SampleApp.Tests\bin\Release\net452\Abp.Zero.SampleApp.Tests.dll + # - test\Abp.Zero.SampleApp.EntityFrameworkCore.Tests\bin\Release\net461\Abp.Zero.SampleApp.EntityFrameworkCore.Tests.dll + # - test\Abp.Zero.SampleApp.NHibernateTests\bin\Release\net452\Abp.Zero.SampleApp.NHibernateTests.dll + +artifacts: +- path: nupkg\*.nupkg + name: nuget-packages \ No newline at end of file diff --git a/common.props b/common.props new file mode 100644 index 00000000..04f695e1 --- /dev/null +++ b/common.props @@ -0,0 +1,19 @@ + + + 2.3.0 + $(NoWarn);CS1591 + http://www.aspnetboilerplate.com/images/abp_nupkg.png + http://www.aspnetboilerplate.com/ + https://github.com/aspnetboilerplate/aspnetboilerplate/blob/master/LICENSE + git + https://github.com/aspnetboilerplate/aspnetboilerplate + + + + NET452 + + + + + + \ No newline at end of file diff --git a/doc/img/module-zero-core-template.png b/doc/img/module-zero-core-template.png new file mode 100644 index 00000000..3cecbf53 Binary files /dev/null and b/doc/img/module-zero-core-template.png differ diff --git a/global.json b/global.json deleted file mode 100644 index 5e6422e6..00000000 --- a/global.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "projects": [ "src", "test" ] -} diff --git a/nupkg/pack.bat b/nupkg/pack.bat deleted file mode 100644 index dbd246c5..00000000 --- a/nupkg/pack.bat +++ /dev/null @@ -1,14 +0,0 @@ -REM "..\tools\gitlink\GitLink.exe" ..\ -u https://github.com/aspnetboilerplate/module-zero -c release - -@ECHO OFF -SET /P VERSION_SUFFIX=Please enter version-suffix (can be left empty): - -dotnet "pack" "..\src\Abp.Zero" -c "Release" -o "." --version-suffix "%VERSION_SUFFIX%" -dotnet "pack" "..\src\Abp.Zero.Owin" -c "Release" -o "." --version-suffix "%VERSION_SUFFIX%" -dotnet "pack" "..\src\Abp.Zero.AspNetCore" -c "Release" -o "." --version-suffix "%VERSION_SUFFIX%" -dotnet "pack" "..\src\Abp.Zero.Ldap" -c "Release" -o "." --version-suffix "%VERSION_SUFFIX%" -dotnet "pack" "..\src\Abp.Zero.EntityFramework" -c "Release" -o "." --version-suffix "%VERSION_SUFFIX%" -dotnet "pack" "..\src\Abp.Zero.EntityFrameworkCore" -c "Release" -o "." --version-suffix "%VERSION_SUFFIX%" -dotnet "pack" "..\src\Abp.Zero.NHibernate" -c "Release" -o "." --version-suffix "%VERSION_SUFFIX%" - -pause diff --git a/nupkg/pack.ps1 b/nupkg/pack.ps1 new file mode 100644 index 00000000..ce3e4452 --- /dev/null +++ b/nupkg/pack.ps1 @@ -0,0 +1,43 @@ +# Paths +$packFolder = (Get-Item -Path "./" -Verbose).FullName +$slnPath = Join-Path $packFolder "../" +$srcPath = Join-Path $slnPath "src" + +# List of projects +$projects = ( + "Abp.Zero.Common", + "Abp.Zero.Ldap", + "Abp.Zero", + "Abp.Zero.Owin", + "Abp.Zero.AspNetCore", + "Abp.Zero.EntityFramework", + "Abp.Zero.EntityFrameworkCore", + "Abp.Zero.NHibernate", + "Abp.ZeroCore", + "Abp.ZeroCore.EntityFrameworkCore", + "Abp.ZeroCore.IdentityServer4", + "Abp.ZeroCore.IdentityServer4.EntityFrameworkCore" +) + +# Rebuild solution +Set-Location $slnPath +& dotnet restore + +# Copy all nuget packages to the pack folder +foreach($project in $projects) { + + $projectFolder = Join-Path $srcPath $project + + # Create nuget pack + Set-Location $projectFolder + Remove-Item -Recurse (Join-Path $projectFolder "bin/Release") + & dotnet msbuild /t:pack /p:Configuration=Release /p:IncludeSymbols=true /p:SourceLinkCreate=true + + # Copy nuget package + $projectPackPath = Join-Path $projectFolder ("/bin/Release/" + $project + ".*.nupkg") + Move-Item $projectPackPath $packFolder + +} + +# Go back to the pack folder +Set-Location $packFolder \ No newline at end of file diff --git a/src/Abp.Zero.AspNetCore/Abp.Zero.AspNetCore.csproj b/src/Abp.Zero.AspNetCore/Abp.Zero.AspNetCore.csproj new file mode 100644 index 00000000..b73e2dba --- /dev/null +++ b/src/Abp.Zero.AspNetCore/Abp.Zero.AspNetCore.csproj @@ -0,0 +1,33 @@ + + + + + + net46 + true + Abp.Zero.AspNetCore + Abp.Zero.AspNetCore + asp.net;asp.net mvc;boilerplate;application framework;web framework;framework;domain driven design;multitenancy;user management;role management;identity;aspnet core + false + false + false + + + + + lib/net46/ + true + + + + + + + + + + + + + + diff --git a/src/Abp.Zero.AspNetCore/Abp.Zero.AspNetCore.xproj b/src/Abp.Zero.AspNetCore/Abp.Zero.AspNetCore.xproj deleted file mode 100644 index 8a819dc2..00000000 --- a/src/Abp.Zero.AspNetCore/Abp.Zero.AspNetCore.xproj +++ /dev/null @@ -1,21 +0,0 @@ - - - - 14.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - - - - - 8ee2acff-cb44-4a7e-8fe7-5709ad245f44 - Abp.Zero.AspNetCore - .\obj - .\bin\ - v4.6.1 - - - - 2.0 - - - diff --git a/src/Abp.Zero.AspNetCore/AbpZeroAspNetCoreModule.cs b/src/Abp.Zero.AspNetCore/AbpZeroAspNetCoreModule.cs index 837c10fd..6f3b3a0c 100644 --- a/src/Abp.Zero.AspNetCore/AbpZeroAspNetCoreModule.cs +++ b/src/Abp.Zero.AspNetCore/AbpZeroAspNetCoreModule.cs @@ -1,4 +1,6 @@ using System.Reflection; +using Abp.Authorization.Users; +using Abp.Configuration.Startup; using Abp.Modules; namespace Abp.Zero.AspNetCore @@ -9,6 +11,7 @@ public class AbpZeroAspNetCoreModule : AbpModule public override void PreInitialize() { IocManager.Register(); + Configuration.ReplaceService(); } public override void Initialize() diff --git a/src/Abp.Zero.AspNetCore/AspNetCoreUserTokenProviderAccessor.cs b/src/Abp.Zero.AspNetCore/AspNetCoreUserTokenProviderAccessor.cs new file mode 100644 index 00000000..53244835 --- /dev/null +++ b/src/Abp.Zero.AspNetCore/AspNetCoreUserTokenProviderAccessor.cs @@ -0,0 +1,22 @@ +using Abp.Authorization.Users; +using Microsoft.AspNet.Identity; +using Microsoft.AspNetCore.DataProtection; + +namespace Abp.Zero.AspNetCore +{ + public class AspNetCoreUserTokenProviderAccessor : IUserTokenProviderAccessor + { + private readonly IDataProtectionProvider _dataProtectionProvider; + + public AspNetCoreUserTokenProviderAccessor(IDataProtectionProvider dataProtectionProvider) + { + _dataProtectionProvider = dataProtectionProvider; + } + + public IUserTokenProvider GetUserTokenProviderOrNull() + where TUser : AbpUser + { + return new DataProtectorUserTokenProvider(_dataProtectionProvider.CreateProtector("ASP.NET Identity")); + } + } +} \ No newline at end of file diff --git a/src/Abp.Zero.AspNetCore/DataProtectorUserTokenProvider.cs b/src/Abp.Zero.AspNetCore/DataProtectorUserTokenProvider.cs new file mode 100644 index 00000000..6645f800 --- /dev/null +++ b/src/Abp.Zero.AspNetCore/DataProtectorUserTokenProvider.cs @@ -0,0 +1,120 @@ +using System; +using System.Globalization; +using System.IO; +using System.Threading.Tasks; +using Abp.Zero.AspNetCore.Internal; +using Microsoft.AspNet.Identity; +using Microsoft.AspNetCore.DataProtection; + +namespace Abp.Zero.AspNetCore +{ + public class DataProtectorUserTokenProvider : IUserTokenProvider + where TUser : class, IUser + { + public IDataProtector Protector { get; } + + public TimeSpan TokenLifespan { get; set; } + + public DataProtectorUserTokenProvider(IDataProtector protector) + { + if (protector == null) + { + throw new ArgumentNullException(nameof(protector)); + } + + Protector = protector; + TokenLifespan = TimeSpan.FromDays(1.0); + } + + public async Task GenerateAsync(string purpose, UserManager manager, TUser user) + { + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + + using (var ms = new MemoryStream()) + { + using (var binaryWriter = ms.CreateWriter()) + { + binaryWriter.Write(DateTimeOffset.UtcNow); + binaryWriter.Write(Convert.ToString((object)user.Id, (IFormatProvider)CultureInfo.InvariantCulture)); + binaryWriter.Write(purpose ?? ""); + + if (manager.SupportsUserSecurityStamp) + { + binaryWriter.Write(await manager.GetSecurityStampAsync(user.Id)); + } + else + { + binaryWriter.Write(""); + } + } + + return Convert.ToBase64String(Protector.Protect(ms.ToArray())); + } + } + + public Task IsValidProviderForUserAsync(UserManager manager, TUser user) + { + return Task.FromResult(true); + } + + public Task NotifyAsync(string token, UserManager manager, TUser user) + { + return Task.FromResult(0); + } + + public async Task ValidateAsync(string purpose, string token, UserManager manager, TUser user) + { + try + { + var unprotectedData = this.Protector.Unprotect(Convert.FromBase64String(token)); + using (var ms = new MemoryStream(unprotectedData)) + { + using (var binaryReader = ms.CreateReader()) + { + var creationTime = binaryReader.ReadDateTimeOffset(); + var expirationTime = creationTime + TokenLifespan; + if (expirationTime < DateTimeOffset.UtcNow) + { + return false; + } + + var userId = binaryReader.ReadString(); + if (!string.Equals(userId, Convert.ToString((object) user.Id, CultureInfo.InvariantCulture))) + { + return false; + } + + var purp = binaryReader.ReadString(); + if (!string.Equals(purp, purpose)) + { + return false; + } + + var stamp = binaryReader.ReadString(); + if (binaryReader.PeekChar() != -1) + { + return false; + } + + if (!manager.SupportsUserSecurityStamp) + { + return stamp == ""; + } + + var expectedStamp = await manager.GetSecurityStampAsync(user.Id); + + return stamp == expectedStamp; + } + } + } + catch + { + } + + return false; + } + } +} diff --git a/src/Abp.Zero.AspNetCore/Internal/StreamExtensions.cs b/src/Abp.Zero.AspNetCore/Internal/StreamExtensions.cs new file mode 100644 index 00000000..af4f5609 --- /dev/null +++ b/src/Abp.Zero.AspNetCore/Internal/StreamExtensions.cs @@ -0,0 +1,31 @@ +using System; +using System.IO; +using System.Text; + +namespace Abp.Zero.AspNetCore.Internal +{ + internal static class StreamExtensions + { + internal static readonly Encoding DefaultEncoding = new UTF8Encoding(false, true); + + public static BinaryReader CreateReader(this Stream stream) + { + return new BinaryReader(stream, DefaultEncoding, true); + } + + public static BinaryWriter CreateWriter(this Stream stream) + { + return new BinaryWriter(stream, DefaultEncoding, true); + } + + public static DateTimeOffset ReadDateTimeOffset(this BinaryReader reader) + { + return new DateTimeOffset(reader.ReadInt64(), TimeSpan.Zero); + } + + public static void Write(this BinaryWriter writer, DateTimeOffset value) + { + writer.Write(value.UtcTicks); + } + } +} \ No newline at end of file diff --git a/src/Abp.Zero.AspNetCore/project.json b/src/Abp.Zero.AspNetCore/project.json deleted file mode 100644 index e7145a45..00000000 --- a/src/Abp.Zero.AspNetCore/project.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "version": "1.0.0.0-*", - - "dependencies": { - "Abp.Zero": "1.0.0.0-*", - "Microsoft.AspNetCore.Mvc": "1.0.0" - }, - - "frameworks": { - "net461": { - } - }, - - "buildOptions": { - "nowarn": [ - "CS1591" - ], - "xmlDoc": true - }, - - "packOptions": { - "summary": "ASP.NET Boilerplate - Module Zero - ASP.NET Core Adapter.", - "tags": [ "asp.net", "asp.net mvc", "boilerplate", "application framework", "web framework", "framework", "domain driven design", "multitenancy", "user management", "role management", "identity", "aspnet core" ], - "owners": [ "Halil İbrahim Kalkan" ], - "iconUrl": "http://www.aspnetboilerplate.com/images/abp_nupkg.png", - "projectUrl": "http://www.aspnetboilerplate.com/", - "licenseUrl": "https://github.com/aspnetboilerplate/module-zero/blob/master/LICENSE", - "requireLicenseAcceptance": false, - "repository": { - "type": "git", - "url": "https://github.com/aspnetboilerplate/module-zero" - }, - "files": { - "mappings": { - "lib/net461/": "bin/Release/net461/Abp.Zero.AspNetCore.pdb" - } - } - } -} diff --git a/src/Abp.Zero.Common/Abp.Zero.Common.csproj b/src/Abp.Zero.Common/Abp.Zero.Common.csproj new file mode 100644 index 00000000..d7b6be64 --- /dev/null +++ b/src/Abp.Zero.Common/Abp.Zero.Common.csproj @@ -0,0 +1,40 @@ + + + + + + net46;netstandard1.6 + true + Abp.Zero.Common + Abp.Zero.Common + asp.net;asp.net mvc;boilerplate;application framework;web framework;framework;domain driven design;multitenancy;user management;role management;identity;aspnet core + false + false + false + Abp + + + + + + + + + + + + + lib/net46/ + true + + + lib/netstandard1.6/ + true + + + + + + + + \ No newline at end of file diff --git a/src/Abp.Zero/Application/Editions/AbpEditionManager.cs b/src/Abp.Zero.Common/Application/Editions/AbpEditionManager.cs similarity index 93% rename from src/Abp.Zero/Application/Editions/AbpEditionManager.cs rename to src/Abp.Zero.Common/Application/Editions/AbpEditionManager.cs index 462dd7b0..8779b85d 100644 --- a/src/Abp.Zero/Application/Editions/AbpEditionManager.cs +++ b/src/Abp.Zero.Common/Application/Editions/AbpEditionManager.cs @@ -1,92 +1,92 @@ -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Abp.Application.Features; -using Abp.Collections.Extensions; -using Abp.Domain.Repositories; -using Abp.Domain.Services; -using Abp.Runtime.Caching; - -namespace Abp.Application.Editions -{ - public abstract class AbpEditionManager : IDomainService - { - private readonly IAbpZeroFeatureValueStore _featureValueStore; - - public IQueryable Editions => EditionRepository.GetAll(); - - public ICacheManager CacheManager { get; set; } - - public IFeatureManager FeatureManager { get; set; } - - protected IRepository EditionRepository { get; set; } - - protected AbpEditionManager( - IRepository editionRepository, - IAbpZeroFeatureValueStore featureValueStore) - { - _featureValueStore = featureValueStore; - EditionRepository = editionRepository; - } - - public virtual Task GetFeatureValueOrNullAsync(int editionId, string featureName) - { - return _featureValueStore.GetEditionValueOrNullAsync(editionId, featureName); - } - - public virtual Task SetFeatureValueAsync(int editionId, string featureName, string value) - { - return _featureValueStore.SetEditionFeatureValueAsync(editionId, featureName, value); - } - - public virtual async Task> GetFeatureValuesAsync(int editionId) - { - var values = new List(); - - foreach (var feature in FeatureManager.GetAll()) - { - values.Add(new NameValue(feature.Name, await GetFeatureValueOrNullAsync(editionId, feature.Name) ?? feature.DefaultValue)); - } - - return values; - } - - public virtual async Task SetFeatureValuesAsync(int editionId, params NameValue[] values) - { - if (values.IsNullOrEmpty()) - { - return; - } - - foreach (var value in values) - { - await SetFeatureValueAsync(editionId, value.Name, value.Value); - } - } - - public virtual Task CreateAsync(Edition edition) - { - return EditionRepository.InsertAsync(edition); - } - - public virtual Task FindByNameAsync(string name) - { - return EditionRepository.FirstOrDefaultAsync(edition => edition.Name == name); - } - - public virtual Task FindByIdAsync(int id) - { - return EditionRepository.FirstOrDefaultAsync(id); - } - - public virtual Task GetByIdAsync(int id) - { - return EditionRepository.GetAsync(id); - } - - public virtual Task DeleteAsync(Edition edition) - { - return EditionRepository.DeleteAsync(edition); - } - } -} +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Abp.Application.Features; +using Abp.Collections.Extensions; +using Abp.Domain.Repositories; +using Abp.Domain.Services; +using Abp.Runtime.Caching; + +namespace Abp.Application.Editions +{ + public class AbpEditionManager : IDomainService + { + private readonly IAbpZeroFeatureValueStore _featureValueStore; + + public IQueryable Editions => EditionRepository.GetAll(); + + public ICacheManager CacheManager { get; set; } + + public IFeatureManager FeatureManager { get; set; } + + protected IRepository EditionRepository { get; set; } + + public AbpEditionManager( + IRepository editionRepository, + IAbpZeroFeatureValueStore featureValueStore) + { + _featureValueStore = featureValueStore; + EditionRepository = editionRepository; + } + + public virtual Task GetFeatureValueOrNullAsync(int editionId, string featureName) + { + return _featureValueStore.GetEditionValueOrNullAsync(editionId, featureName); + } + + public virtual Task SetFeatureValueAsync(int editionId, string featureName, string value) + { + return _featureValueStore.SetEditionFeatureValueAsync(editionId, featureName, value); + } + + public virtual async Task> GetFeatureValuesAsync(int editionId) + { + var values = new List(); + + foreach (var feature in FeatureManager.GetAll()) + { + values.Add(new NameValue(feature.Name, await GetFeatureValueOrNullAsync(editionId, feature.Name) ?? feature.DefaultValue)); + } + + return values; + } + + public virtual async Task SetFeatureValuesAsync(int editionId, params NameValue[] values) + { + if (values.IsNullOrEmpty()) + { + return; + } + + foreach (var value in values) + { + await SetFeatureValueAsync(editionId, value.Name, value.Value); + } + } + + public virtual Task CreateAsync(Edition edition) + { + return EditionRepository.InsertAsync(edition); + } + + public virtual Task FindByNameAsync(string name) + { + return EditionRepository.FirstOrDefaultAsync(edition => edition.Name == name); + } + + public virtual Task FindByIdAsync(int id) + { + return EditionRepository.FirstOrDefaultAsync(id); + } + + public virtual Task GetByIdAsync(int id) + { + return EditionRepository.GetAsync(id); + } + + public virtual Task DeleteAsync(Edition edition) + { + return EditionRepository.DeleteAsync(edition); + } + } +} diff --git a/src/Abp.Zero/Application/Editions/Edition.cs b/src/Abp.Zero.Common/Application/Editions/Edition.cs similarity index 96% rename from src/Abp.Zero/Application/Editions/Edition.cs rename to src/Abp.Zero.Common/Application/Editions/Edition.cs index 9ac9dc43..56cc1f92 100644 --- a/src/Abp.Zero/Application/Editions/Edition.cs +++ b/src/Abp.Zero.Common/Application/Editions/Edition.cs @@ -1,51 +1,51 @@ -using System; -using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; -using Abp.Domain.Entities.Auditing; -using Abp.MultiTenancy; - -namespace Abp.Application.Editions -{ - /// - /// Represents an edition of the application. - /// - [Table("AbpEditions")] - [MultiTenancySide(MultiTenancySides.Host)] - public class Edition : FullAuditedEntity - { - /// - /// Maximum length of the property. - /// - public const int MaxNameLength = 32; - - /// - /// Maximum length of the property. - /// - public const int MaxDisplayNameLength = 64; - - /// - /// Unique name of this edition. - /// - [Required] - [StringLength(MaxNameLength)] - public virtual string Name { get; set; } - - /// - /// Display name of this edition. - /// - [Required] - [StringLength(MaxDisplayNameLength)] - public virtual string DisplayName { get; set; } - - public Edition() - { - Name = Guid.NewGuid().ToString("N"); - } - - public Edition(string displayName) - : this() - { - DisplayName = displayName; - } - } +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using Abp.Domain.Entities.Auditing; +using Abp.MultiTenancy; + +namespace Abp.Application.Editions +{ + /// + /// Represents an edition of the application. + /// + [Table("AbpEditions")] + [MultiTenancySide(MultiTenancySides.Host)] + public class Edition : FullAuditedEntity + { + /// + /// Maximum length of the property. + /// + public const int MaxNameLength = 32; + + /// + /// Maximum length of the property. + /// + public const int MaxDisplayNameLength = 64; + + /// + /// Unique name of this edition. + /// + [Required] + [StringLength(MaxNameLength)] + public virtual string Name { get; set; } + + /// + /// Display name of this edition. + /// + [Required] + [StringLength(MaxDisplayNameLength)] + public virtual string DisplayName { get; set; } + + public Edition() + { + Name = Guid.NewGuid().ToString("N"); + } + + public Edition(string displayName) + : this() + { + DisplayName = displayName; + } + } } \ No newline at end of file diff --git a/src/Abp.Zero/Application/Editions/EditionfeatureCacheItem.cs b/src/Abp.Zero.Common/Application/Editions/EditionfeatureCacheItem.cs similarity index 96% rename from src/Abp.Zero/Application/Editions/EditionfeatureCacheItem.cs rename to src/Abp.Zero.Common/Application/Editions/EditionfeatureCacheItem.cs index ec9bb4f2..395d8dba 100644 --- a/src/Abp.Zero/Application/Editions/EditionfeatureCacheItem.cs +++ b/src/Abp.Zero.Common/Application/Editions/EditionfeatureCacheItem.cs @@ -1,18 +1,18 @@ -using System; -using System.Collections.Generic; - -namespace Abp.Application.Editions -{ - [Serializable] - public class EditionfeatureCacheItem - { - public const string CacheStoreName = "AbpZeroEditionFeatures"; - - public IDictionary FeatureValues { get; set; } - - public EditionfeatureCacheItem() - { - FeatureValues = new Dictionary(); - } - } +using System; +using System.Collections.Generic; + +namespace Abp.Application.Editions +{ + [Serializable] + public class EditionfeatureCacheItem + { + public const string CacheStoreName = "AbpZeroEditionFeatures"; + + public IDictionary FeatureValues { get; set; } + + public EditionfeatureCacheItem() + { + FeatureValues = new Dictionary(); + } + } } \ No newline at end of file diff --git a/src/Abp.Zero/Application/Features/AbpFeatureValueStore.cs b/src/Abp.Zero.Common/Application/Features/AbpFeatureValueStore.cs similarity index 92% rename from src/Abp.Zero/Application/Features/AbpFeatureValueStore.cs rename to src/Abp.Zero.Common/Application/Features/AbpFeatureValueStore.cs index 160bb841..55629c89 100644 --- a/src/Abp.Zero/Application/Features/AbpFeatureValueStore.cs +++ b/src/Abp.Zero.Common/Application/Features/AbpFeatureValueStore.cs @@ -1,190 +1,190 @@ -using System.Threading.Tasks; -using Abp.Application.Editions; -using Abp.Authorization.Users; -using Abp.Collections.Extensions; -using Abp.Dependency; -using Abp.Domain.Repositories; -using Abp.Domain.Uow; -using Abp.Events.Bus.Entities; -using Abp.Events.Bus.Handlers; -using Abp.MultiTenancy; -using Abp.Runtime.Caching; - -namespace Abp.Application.Features -{ - /// - /// Implements . - /// - public abstract class AbpFeatureValueStore : - IAbpZeroFeatureValueStore, - ITransientDependency, - IEventHandler>, - IEventHandler> - - where TTenant : AbpTenant - where TUser : AbpUser - { - private readonly ICacheManager _cacheManager; - private readonly IRepository _tenantFeatureRepository; - private readonly IRepository _tenantRepository; - private readonly IRepository _editionFeatureRepository; - private readonly IFeatureManager _featureManager; - private readonly IUnitOfWorkManager _unitOfWorkManager; - - /// - /// Initializes a new instance of the class. - /// - protected AbpFeatureValueStore( - ICacheManager cacheManager, - IRepository tenantFeatureRepository, - IRepository tenantRepository, - IRepository editionFeatureRepository, - IFeatureManager featureManager, - IUnitOfWorkManager unitOfWorkManager) - { - _cacheManager = cacheManager; - _tenantFeatureRepository = tenantFeatureRepository; - _tenantRepository = tenantRepository; - _editionFeatureRepository = editionFeatureRepository; - _featureManager = featureManager; - _unitOfWorkManager = unitOfWorkManager; - } - - /// - public virtual Task GetValueOrNullAsync(int tenantId, Feature feature) - { - return GetValueOrNullAsync(tenantId, feature.Name); - } - - public virtual async Task GetEditionValueOrNullAsync(int editionId, string featureName) - { - var cacheItem = await GetEditionFeatureCacheItemAsync(editionId); - return cacheItem.FeatureValues.GetOrDefault(featureName); - } - - public async Task GetValueOrNullAsync(int tenantId, string featureName) - { - var cacheItem = await GetTenantFeatureCacheItemAsync(tenantId); - var value = cacheItem.FeatureValues.GetOrDefault(featureName); - if (value != null) - { - return value; - } - - if (cacheItem.EditionId.HasValue) - { - value = await GetEditionValueOrNullAsync(cacheItem.EditionId.Value, featureName); - if (value != null) - { - return value; - } - } - - return null; - } - - [UnitOfWork] - public virtual async Task SetEditionFeatureValueAsync(int editionId, string featureName, string value) - { - if (await GetEditionValueOrNullAsync(editionId, featureName) == value) - { - return; - } - - var currentFeature = await _editionFeatureRepository.FirstOrDefaultAsync(f => f.EditionId == editionId && f.Name == featureName); - - var feature = _featureManager.GetOrNull(featureName); - if (feature == null || feature.DefaultValue == value) - { - if (currentFeature != null) - { - await _editionFeatureRepository.DeleteAsync(currentFeature); - } - - return; - } - - if (currentFeature == null) - { - await _editionFeatureRepository.InsertAsync(new EditionFeatureSetting(editionId, featureName, value)); - } - else - { - currentFeature.Value = value; - } - } - - protected async Task GetTenantFeatureCacheItemAsync(int tenantId) - { - return await _cacheManager.GetTenantFeatureCache().GetAsync(tenantId, async () => - { - TTenant tenant; - using (var uow = _unitOfWorkManager.Begin()) - { - using (_unitOfWorkManager.Current.SetTenantId(null)) - { - tenant = await _tenantRepository.GetAsync(tenantId); - - await uow.CompleteAsync(); - } - } - - var newCacheItem = new TenantFeatureCacheItem { EditionId = tenant.EditionId }; - - using (var uow = _unitOfWorkManager.Begin()) - { - using (_unitOfWorkManager.Current.SetTenantId(tenantId)) - { - var featureSettings = await _tenantFeatureRepository.GetAllListAsync(); - foreach (var featureSetting in featureSettings) - { - newCacheItem.FeatureValues[featureSetting.Name] = featureSetting.Value; - } - - await uow.CompleteAsync(); - } - } - - return newCacheItem; - }); - } - - protected virtual async Task GetEditionFeatureCacheItemAsync(int editionId) - { - return await _cacheManager - .GetEditionFeatureCache() - .GetAsync( - editionId, - async () => await CreateEditionFeatureCacheItem(editionId) - ); - } - - protected virtual async Task CreateEditionFeatureCacheItem(int editionId) - { - var newCacheItem = new EditionfeatureCacheItem(); - - var featureSettings = await _editionFeatureRepository.GetAllListAsync(f => f.EditionId == editionId); - foreach (var featureSetting in featureSettings) - { - newCacheItem.FeatureValues[featureSetting.Name] = featureSetting.Value; - } - - return newCacheItem; - } - - public virtual void HandleEvent(EntityChangedEventData eventData) - { - _cacheManager.GetEditionFeatureCache().Remove(eventData.Entity.EditionId); - } - - public virtual void HandleEvent(EntityChangedEventData eventData) - { - if (eventData.Entity.IsTransient()) - { - return; - } - - _cacheManager.GetEditionFeatureCache().Remove(eventData.Entity.Id); - } - } +using System.Threading.Tasks; +using Abp.Application.Editions; +using Abp.Authorization.Users; +using Abp.Collections.Extensions; +using Abp.Dependency; +using Abp.Domain.Repositories; +using Abp.Domain.Uow; +using Abp.Events.Bus.Entities; +using Abp.Events.Bus.Handlers; +using Abp.MultiTenancy; +using Abp.Runtime.Caching; + +namespace Abp.Application.Features +{ + /// + /// Implements . + /// + public class AbpFeatureValueStore : + IAbpZeroFeatureValueStore, + ITransientDependency, + IEventHandler>, + IEventHandler> + + where TTenant : AbpTenant + where TUser : AbpUserBase + { + private readonly ICacheManager _cacheManager; + private readonly IRepository _tenantFeatureRepository; + private readonly IRepository _tenantRepository; + private readonly IRepository _editionFeatureRepository; + private readonly IFeatureManager _featureManager; + private readonly IUnitOfWorkManager _unitOfWorkManager; + + /// + /// Initializes a new instance of the class. + /// + public AbpFeatureValueStore( + ICacheManager cacheManager, + IRepository tenantFeatureRepository, + IRepository tenantRepository, + IRepository editionFeatureRepository, + IFeatureManager featureManager, + IUnitOfWorkManager unitOfWorkManager) + { + _cacheManager = cacheManager; + _tenantFeatureRepository = tenantFeatureRepository; + _tenantRepository = tenantRepository; + _editionFeatureRepository = editionFeatureRepository; + _featureManager = featureManager; + _unitOfWorkManager = unitOfWorkManager; + } + + /// + public virtual Task GetValueOrNullAsync(int tenantId, Feature feature) + { + return GetValueOrNullAsync(tenantId, feature.Name); + } + + public virtual async Task GetEditionValueOrNullAsync(int editionId, string featureName) + { + var cacheItem = await GetEditionFeatureCacheItemAsync(editionId); + return cacheItem.FeatureValues.GetOrDefault(featureName); + } + + public virtual async Task GetValueOrNullAsync(int tenantId, string featureName) + { + var cacheItem = await GetTenantFeatureCacheItemAsync(tenantId); + var value = cacheItem.FeatureValues.GetOrDefault(featureName); + if (value != null) + { + return value; + } + + if (cacheItem.EditionId.HasValue) + { + value = await GetEditionValueOrNullAsync(cacheItem.EditionId.Value, featureName); + if (value != null) + { + return value; + } + } + + return null; + } + + [UnitOfWork] + public virtual async Task SetEditionFeatureValueAsync(int editionId, string featureName, string value) + { + if (await GetEditionValueOrNullAsync(editionId, featureName) == value) + { + return; + } + + var currentFeature = await _editionFeatureRepository.FirstOrDefaultAsync(f => f.EditionId == editionId && f.Name == featureName); + + var feature = _featureManager.GetOrNull(featureName); + if (feature == null || feature.DefaultValue == value) + { + if (currentFeature != null) + { + await _editionFeatureRepository.DeleteAsync(currentFeature); + } + + return; + } + + if (currentFeature == null) + { + await _editionFeatureRepository.InsertAsync(new EditionFeatureSetting(editionId, featureName, value)); + } + else + { + currentFeature.Value = value; + } + } + + protected virtual async Task GetTenantFeatureCacheItemAsync(int tenantId) + { + return await _cacheManager.GetTenantFeatureCache().GetAsync(tenantId, async () => + { + TTenant tenant; + using (var uow = _unitOfWorkManager.Begin()) + { + using (_unitOfWorkManager.Current.SetTenantId(null)) + { + tenant = await _tenantRepository.GetAsync(tenantId); + + await uow.CompleteAsync(); + } + } + + var newCacheItem = new TenantFeatureCacheItem { EditionId = tenant.EditionId }; + + using (var uow = _unitOfWorkManager.Begin()) + { + using (_unitOfWorkManager.Current.SetTenantId(tenantId)) + { + var featureSettings = await _tenantFeatureRepository.GetAllListAsync(); + foreach (var featureSetting in featureSettings) + { + newCacheItem.FeatureValues[featureSetting.Name] = featureSetting.Value; + } + + await uow.CompleteAsync(); + } + } + + return newCacheItem; + }); + } + + protected virtual async Task GetEditionFeatureCacheItemAsync(int editionId) + { + return await _cacheManager + .GetEditionFeatureCache() + .GetAsync( + editionId, + async () => await CreateEditionFeatureCacheItem(editionId) + ); + } + + protected virtual async Task CreateEditionFeatureCacheItem(int editionId) + { + var newCacheItem = new EditionfeatureCacheItem(); + + var featureSettings = await _editionFeatureRepository.GetAllListAsync(f => f.EditionId == editionId); + foreach (var featureSetting in featureSettings) + { + newCacheItem.FeatureValues[featureSetting.Name] = featureSetting.Value; + } + + return newCacheItem; + } + + public virtual void HandleEvent(EntityChangedEventData eventData) + { + _cacheManager.GetEditionFeatureCache().Remove(eventData.Entity.EditionId); + } + + public virtual void HandleEvent(EntityChangedEventData eventData) + { + if (eventData.Entity.IsTransient()) + { + return; + } + + _cacheManager.GetEditionFeatureCache().Remove(eventData.Entity.Id); + } + } } \ No newline at end of file diff --git a/src/Abp.Zero/Application/Features/EditionFeatureSetting.cs b/src/Abp.Zero.Common/Application/Features/EditionFeatureSetting.cs similarity index 96% rename from src/Abp.Zero/Application/Features/EditionFeatureSetting.cs rename to src/Abp.Zero.Common/Application/Features/EditionFeatureSetting.cs index 7023c844..3d02c894 100644 --- a/src/Abp.Zero/Application/Features/EditionFeatureSetting.cs +++ b/src/Abp.Zero.Common/Application/Features/EditionFeatureSetting.cs @@ -1,48 +1,48 @@ -using System.ComponentModel.DataAnnotations.Schema; -using Abp.Application.Editions; - -namespace Abp.Application.Features -{ - /// - /// Feature setting for an . - /// - public class EditionFeatureSetting : FeatureSetting - { - /// - /// Gets or sets the edition. - /// - /// - /// The edition. - /// - [ForeignKey("EditionId")] - public virtual Edition Edition { get; set; } - - /// - /// Gets or sets the edition Id. - /// - /// - /// The edition Id. - /// - public virtual int EditionId { get; set; } - - /// - /// Initializes a new instance of the class. - /// - public EditionFeatureSetting() - { - - } - - /// - /// Initializes a new instance of the class. - /// - /// The edition Id. - /// Feature name. - /// Feature value. - public EditionFeatureSetting(int editionId, string name, string value) - :base(name, value) - { - EditionId = editionId; - } - } +using System.ComponentModel.DataAnnotations.Schema; +using Abp.Application.Editions; + +namespace Abp.Application.Features +{ + /// + /// Feature setting for an . + /// + public class EditionFeatureSetting : FeatureSetting + { + /// + /// Gets or sets the edition. + /// + /// + /// The edition. + /// + [ForeignKey("EditionId")] + public virtual Edition Edition { get; set; } + + /// + /// Gets or sets the edition Id. + /// + /// + /// The edition Id. + /// + public virtual int EditionId { get; set; } + + /// + /// Initializes a new instance of the class. + /// + public EditionFeatureSetting() + { + + } + + /// + /// Initializes a new instance of the class. + /// + /// The edition Id. + /// Feature name. + /// Feature value. + public EditionFeatureSetting(int editionId, string name, string value) + :base(name, value) + { + EditionId = editionId; + } + } } \ No newline at end of file diff --git a/src/Abp.Zero/Application/Features/FeatureSetting.cs b/src/Abp.Zero.Common/Application/Features/FeatureSetting.cs similarity index 96% rename from src/Abp.Zero/Application/Features/FeatureSetting.cs rename to src/Abp.Zero.Common/Application/Features/FeatureSetting.cs index 19c4c89b..42eeee7b 100644 --- a/src/Abp.Zero/Application/Features/FeatureSetting.cs +++ b/src/Abp.Zero.Common/Application/Features/FeatureSetting.cs @@ -1,58 +1,58 @@ -using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; -using Abp.Domain.Entities.Auditing; -using Abp.MultiTenancy; - -namespace Abp.Application.Features -{ - /// - /// Base class for feature settings - /// - [Table("AbpFeatures")] - [MultiTenancySide(MultiTenancySides.Host)] - public abstract class FeatureSetting : CreationAuditedEntity - { - /// - /// Maximum length of the field. - /// - public const int MaxNameLength = 128; - - /// - /// Maximum length of the property. - /// - public const int MaxValueLength = 2000; - - /// - /// Feature name. - /// - [Required] - [MaxLength(MaxNameLength)] - public virtual string Name { get; set; } - - /// - /// Value. - /// - [Required(AllowEmptyStrings = true)] - [MaxLength(MaxValueLength)] - public virtual string Value { get; set; } - - /// - /// Initializes a new instance of the class. - /// - protected FeatureSetting() - { - - } - - /// - /// Initializes a new instance of the class. - /// - /// Feature name. - /// Feature value. - protected FeatureSetting(string name, string value) - { - Name = name; - Value = value; - } - } +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using Abp.Domain.Entities.Auditing; +using Abp.MultiTenancy; + +namespace Abp.Application.Features +{ + /// + /// Base class for feature settings + /// + [Table("AbpFeatures")] + [MultiTenancySide(MultiTenancySides.Host)] + public abstract class FeatureSetting : CreationAuditedEntity + { + /// + /// Maximum length of the field. + /// + public const int MaxNameLength = 128; + + /// + /// Maximum length of the property. + /// + public const int MaxValueLength = 2000; + + /// + /// Feature name. + /// + [Required] + [MaxLength(MaxNameLength)] + public virtual string Name { get; set; } + + /// + /// Value. + /// + [Required(AllowEmptyStrings = true)] + [MaxLength(MaxValueLength)] + public virtual string Value { get; set; } + + /// + /// Initializes a new instance of the class. + /// + protected FeatureSetting() + { + + } + + /// + /// Initializes a new instance of the class. + /// + /// Feature name. + /// Feature value. + protected FeatureSetting(string name, string value) + { + Name = name; + Value = value; + } + } } \ No newline at end of file diff --git a/src/Abp.Zero/Application/Features/IAbpZeroFeatureValueStore.cs b/src/Abp.Zero.Common/Application/Features/IAbpZeroFeatureValueStore.cs similarity index 100% rename from src/Abp.Zero/Application/Features/IAbpZeroFeatureValueStore.cs rename to src/Abp.Zero.Common/Application/Features/IAbpZeroFeatureValueStore.cs diff --git a/src/Abp.Zero/Auditing/AuditLog.cs b/src/Abp.Zero.Common/Auditing/AuditLog.cs similarity index 84% rename from src/Abp.Zero/Auditing/AuditLog.cs rename to src/Abp.Zero.Common/Auditing/AuditLog.cs index a9c93323..0f4972ad 100644 --- a/src/Abp.Zero/Auditing/AuditLog.cs +++ b/src/Abp.Zero.Common/Auditing/AuditLog.cs @@ -1,168 +1,159 @@ -using System; -using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; -using Abp.Domain.Entities; -using Abp.Extensions; - -namespace Abp.Auditing -{ - /// - /// Used to store audit logs. - /// - [Table("AbpAuditLogs")] - public class AuditLog : Entity, IMayHaveTenant - { - /// - /// Maximum length of property. - /// - public const int MaxServiceNameLength = 256; - - /// - /// Maximum length of property. - /// - public const int MaxMethodNameLength = 256; - - /// - /// Maximum length of property. - /// - public const int MaxParametersLength = 1024; - - /// - /// Maximum length of property. - /// - public const int MaxClientIpAddressLength = 64; - - /// - /// Maximum length of property. - /// - public const int MaxClientNameLength = 128; - - /// - /// Maximum length of property. - /// - public const int MaxBrowserInfoLength = 256; - - /// - /// Maximum length of property. - /// - public const int MaxExceptionLength = 2000; - - /// - /// Maximum length of property. - /// - public const int MaxCustomDataLength = 2000; - - /// - /// TenantId. - /// - public virtual int? TenantId { get; set; } - - /// - /// UserId. - /// - public virtual long? UserId { get; set; } - - /// - /// Service (class/interface) name. - /// - [MaxLength(MaxServiceNameLength)] - public virtual string ServiceName { get; set; } - - /// - /// Executed method name. - /// - [MaxLength(MaxMethodNameLength)] - public virtual string MethodName { get; set; } - - /// - /// Calling parameters. - /// - [MaxLength(MaxParametersLength)] - public virtual string Parameters { get; set; } - - /// - /// Start time of the method execution. - /// - public virtual DateTime ExecutionTime { get; set; } - - /// - /// Total duration of the method call as milliseconds. - /// - public virtual int ExecutionDuration { get; set; } - - /// - /// IP address of the client. - /// - [MaxLength(MaxClientIpAddressLength)] - public virtual string ClientIpAddress { get; set; } - - /// - /// Name (generally computer name) of the client. - /// - [MaxLength(MaxClientNameLength)] - public virtual string ClientName { get; set; } - - /// - /// Browser information if this method is called in a web request. - /// - [MaxLength(MaxBrowserInfoLength)] - public virtual string BrowserInfo { get; set; } - - /// - /// Exception object, if an exception occured during execution of the method. - /// - [MaxLength(MaxExceptionLength)] - public virtual string Exception { get; set; } - - /// - /// . - /// - public virtual long? ImpersonatorUserId { get; set; } - - /// - /// . - /// - public virtual int? ImpersonatorTenantId { get; set; } - - /// - /// . - /// - [MaxLength(MaxCustomDataLength)] - public virtual string CustomData { get; set; } - - /// - /// Creates a new CreateFromAuditInfo from given . - /// - /// Source object - /// The object that is created using - public static AuditLog CreateFromAuditInfo(AuditInfo auditInfo) - { - var exceptionMessage = auditInfo.Exception != null ? auditInfo.Exception.ToString() : null; - return new AuditLog - { - TenantId = auditInfo.TenantId, - UserId = auditInfo.UserId, - ServiceName = auditInfo.ServiceName.TruncateWithPostfix(MaxServiceNameLength), - MethodName = auditInfo.MethodName.TruncateWithPostfix(MaxMethodNameLength), - Parameters = auditInfo.Parameters.TruncateWithPostfix(MaxParametersLength), - ExecutionTime = auditInfo.ExecutionTime, - ExecutionDuration = auditInfo.ExecutionDuration, - ClientIpAddress = auditInfo.ClientIpAddress.TruncateWithPostfix(MaxClientIpAddressLength), - ClientName = auditInfo.ClientName.TruncateWithPostfix(MaxClientNameLength), - BrowserInfo = auditInfo.BrowserInfo.TruncateWithPostfix(MaxBrowserInfoLength), - Exception = exceptionMessage.TruncateWithPostfix(MaxExceptionLength), - ImpersonatorUserId = auditInfo.ImpersonatorUserId, - ImpersonatorTenantId = auditInfo.ImpersonatorTenantId, - CustomData = auditInfo.CustomData.TruncateWithPostfix(MaxCustomDataLength) - }; - } - - public override string ToString() - { - return string.Format( - "AUDIT LOG: {0}.{1} is executed by user {2} in {3} ms from {4} IP address.", - ServiceName, MethodName, UserId, ExecutionDuration, ClientIpAddress - ); - } - } -} +using System; +using System.ComponentModel.DataAnnotations.Schema; +using Abp.Domain.Entities; +using Abp.Extensions; + +namespace Abp.Auditing +{ + /// + /// Used to store audit logs. + /// + [Table("AbpAuditLogs")] + public class AuditLog : Entity, IMayHaveTenant + { + /// + /// Maximum length of property. + /// + public static int MaxServiceNameLength = 256; + + /// + /// Maximum length of property. + /// + public static int MaxMethodNameLength = 256; + + /// + /// Maximum length of property. + /// + public static int MaxParametersLength = 1024; + + /// + /// Maximum length of property. + /// + public static int MaxClientIpAddressLength = 64; + + /// + /// Maximum length of property. + /// + public static int MaxClientNameLength = 128; + + /// + /// Maximum length of property. + /// + public static int MaxBrowserInfoLength = 256; + + /// + /// Maximum length of property. + /// + public static int MaxExceptionLength = 2000; + + /// + /// Maximum length of property. + /// + public static int MaxCustomDataLength = 2000; + + /// + /// TenantId. + /// + public virtual int? TenantId { get; set; } + + /// + /// UserId. + /// + public virtual long? UserId { get; set; } + + /// + /// Service (class/interface) name. + /// + public virtual string ServiceName { get; set; } + + /// + /// Executed method name. + /// + public virtual string MethodName { get; set; } + + /// + /// Calling parameters. + /// + public virtual string Parameters { get; set; } + + /// + /// Start time of the method execution. + /// + public virtual DateTime ExecutionTime { get; set; } + + /// + /// Total duration of the method call as milliseconds. + /// + public virtual int ExecutionDuration { get; set; } + + /// + /// IP address of the client. + /// + public virtual string ClientIpAddress { get; set; } + + /// + /// Name (generally computer name) of the client. + /// + public virtual string ClientName { get; set; } + + /// + /// Browser information if this method is called in a web request. + /// + public virtual string BrowserInfo { get; set; } + + /// + /// Exception object, if an exception occured during execution of the method. + /// + public virtual string Exception { get; set; } + + /// + /// . + /// + public virtual long? ImpersonatorUserId { get; set; } + + /// + /// . + /// + public virtual int? ImpersonatorTenantId { get; set; } + + /// + /// . + /// + public virtual string CustomData { get; set; } + + /// + /// Creates a new CreateFromAuditInfo from given . + /// + /// Source object + /// The object that is created using + public static AuditLog CreateFromAuditInfo(AuditInfo auditInfo) + { + var exceptionMessage = auditInfo.Exception != null ? auditInfo.Exception.ToString() : null; + return new AuditLog + { + TenantId = auditInfo.TenantId, + UserId = auditInfo.UserId, + ServiceName = auditInfo.ServiceName.TruncateWithPostfix(MaxServiceNameLength), + MethodName = auditInfo.MethodName.TruncateWithPostfix(MaxMethodNameLength), + Parameters = auditInfo.Parameters.TruncateWithPostfix(MaxParametersLength), + ExecutionTime = auditInfo.ExecutionTime, + ExecutionDuration = auditInfo.ExecutionDuration, + ClientIpAddress = auditInfo.ClientIpAddress.TruncateWithPostfix(MaxClientIpAddressLength), + ClientName = auditInfo.ClientName.TruncateWithPostfix(MaxClientNameLength), + BrowserInfo = auditInfo.BrowserInfo.TruncateWithPostfix(MaxBrowserInfoLength), + Exception = exceptionMessage.TruncateWithPostfix(MaxExceptionLength), + ImpersonatorUserId = auditInfo.ImpersonatorUserId, + ImpersonatorTenantId = auditInfo.ImpersonatorTenantId, + CustomData = auditInfo.CustomData.TruncateWithPostfix(MaxCustomDataLength) + }; + } + + public override string ToString() + { + return string.Format( + "AUDIT LOG: {0}.{1} is executed by user {2} in {3} ms from {4} IP address.", + ServiceName, MethodName, UserId, ExecutionDuration, ClientIpAddress + ); + } + } +} diff --git a/src/Abp.Zero/Auditing/AuditingStore.cs b/src/Abp.Zero.Common/Auditing/AuditingStore.cs similarity index 92% rename from src/Abp.Zero/Auditing/AuditingStore.cs rename to src/Abp.Zero.Common/Auditing/AuditingStore.cs index 84d21db6..2c0a399b 100644 --- a/src/Abp.Zero/Auditing/AuditingStore.cs +++ b/src/Abp.Zero.Common/Auditing/AuditingStore.cs @@ -19,7 +19,7 @@ public AuditingStore(IRepository auditLogRepository) _auditLogRepository = auditLogRepository; } - public Task SaveAsync(AuditInfo auditInfo) + public virtual Task SaveAsync(AuditInfo auditInfo) { return _auditLogRepository.InsertAsync(AuditLog.CreateFromAuditInfo(auditInfo)); } diff --git a/src/Abp.Zero/Authorization/Users/AbpLoginResultType.cs b/src/Abp.Zero.Common/Authorization/AbpLoginResultType.cs similarity index 76% rename from src/Abp.Zero/Authorization/Users/AbpLoginResultType.cs rename to src/Abp.Zero.Common/Authorization/AbpLoginResultType.cs index d0e5dfaf..7becff79 100644 --- a/src/Abp.Zero/Authorization/Users/AbpLoginResultType.cs +++ b/src/Abp.Zero.Common/Authorization/AbpLoginResultType.cs @@ -1,23 +1,25 @@ -namespace Abp.Authorization.Users -{ - public enum AbpLoginResultType : byte - { - Success = 1, - - InvalidUserNameOrEmailAddress, - - InvalidPassword, - - UserIsNotActive, - - InvalidTenancyName, - - TenantIsNotActive, - - UserEmailIsNotConfirmed, - - UnknownExternalLogin, - - LockedOut - } +namespace Abp.Authorization +{ + public enum AbpLoginResultType : byte + { + Success = 1, + + InvalidUserNameOrEmailAddress, + + InvalidPassword, + + UserIsNotActive, + + InvalidTenancyName, + + TenantIsNotActive, + + UserEmailIsNotConfirmed, + + UnknownExternalLogin, + + LockedOut, + + UserPhoneNumberIsNotConfirmed, + } } \ No newline at end of file diff --git a/src/Abp.Zero/Authorization/PermissionGrantInfo.cs b/src/Abp.Zero.Common/Authorization/PermissionGrantInfo.cs similarity index 100% rename from src/Abp.Zero/Authorization/PermissionGrantInfo.cs rename to src/Abp.Zero.Common/Authorization/PermissionGrantInfo.cs diff --git a/src/Abp.Zero/Authorization/PermissionSetting.cs b/src/Abp.Zero.Common/Authorization/PermissionSetting.cs similarity index 100% rename from src/Abp.Zero/Authorization/PermissionSetting.cs rename to src/Abp.Zero.Common/Authorization/PermissionSetting.cs diff --git a/src/Abp.Zero.Common/Authorization/Roles/AbpRoleBase.cs b/src/Abp.Zero.Common/Authorization/Roles/AbpRoleBase.cs new file mode 100644 index 00000000..07ba941a --- /dev/null +++ b/src/Abp.Zero.Common/Authorization/Roles/AbpRoleBase.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using Abp.Authorization.Users; +using Abp.Domain.Entities; +using Abp.Domain.Entities.Auditing; + +namespace Abp.Authorization.Roles +{ + /// + /// Base class for role. + /// + [Table("AbpRoles")] + public abstract class AbpRoleBase : FullAuditedEntity, IMayHaveTenant + { + /// + /// Maximum length of the property. + /// + public const int MaxDisplayNameLength = 64; + + /// + /// Maximum length of the property. + /// + public const int MaxNameLength = 32; + + /// + /// Tenant's Id, if this role is a tenant-level role. Null, if not. + /// + public virtual int? TenantId { get; set; } + + /// + /// Unique name of this role. + /// + [Required] + [StringLength(MaxNameLength)] + public virtual string Name { get; set; } + + /// + /// Display name of this role. + /// + [Required] + [StringLength(MaxDisplayNameLength)] + public virtual string DisplayName { get; set; } + + /// + /// Is this a static role? + /// Static roles can not be deleted, can not change their name. + /// They can be used programmatically. + /// + public virtual bool IsStatic { get; set; } + + /// + /// Is this role will be assigned to new users as default? + /// + public virtual bool IsDefault { get; set; } + + /// + /// List of permissions of the role. + /// + [ForeignKey("RoleId")] + public virtual ICollection Permissions { get; set; } + + protected AbpRoleBase() + { + Name = Guid.NewGuid().ToString("N"); + } + + protected AbpRoleBase(int? tenantId, string displayName) + : this() + { + TenantId = tenantId; + DisplayName = displayName; + } + + protected AbpRoleBase(int? tenantId, string name, string displayName) + : this(tenantId, displayName) + { + Name = name; + } + + public override string ToString() + { + return $"[Role {Id}, Name={Name}]"; + } + } +} \ No newline at end of file diff --git a/src/Abp.Zero/Authorization/Roles/AbpRolePermissionCacheItemInvalidator.cs b/src/Abp.Zero.Common/Authorization/Roles/AbpRolePermissionCacheItemInvalidator.cs similarity index 100% rename from src/Abp.Zero/Authorization/Roles/AbpRolePermissionCacheItemInvalidator.cs rename to src/Abp.Zero.Common/Authorization/Roles/AbpRolePermissionCacheItemInvalidator.cs diff --git a/src/Abp.Zero/Authorization/Roles/IRolePermissionStore.cs b/src/Abp.Zero.Common/Authorization/Roles/IRolePermissionStore.cs similarity index 90% rename from src/Abp.Zero/Authorization/Roles/IRolePermissionStore.cs rename to src/Abp.Zero.Common/Authorization/Roles/IRolePermissionStore.cs index acd3baac..d6b6b2f7 100644 --- a/src/Abp.Zero/Authorization/Roles/IRolePermissionStore.cs +++ b/src/Abp.Zero.Common/Authorization/Roles/IRolePermissionStore.cs @@ -1,56 +1,54 @@ -using System.Collections.Generic; -using System.Threading.Tasks; -using Abp.Authorization.Users; - -namespace Abp.Authorization.Roles -{ - /// - /// Used to perform permission database operations for a role. - /// - public interface IRolePermissionStore - where TRole : AbpRole - where TUser : AbpUser - { - /// - /// Adds a permission grant setting to a role. - /// - /// Role - /// Permission grant setting info - Task AddPermissionAsync(TRole role, PermissionGrantInfo permissionGrant); - - /// - /// Removes a permission grant setting from a role. - /// - /// Role - /// Permission grant setting info - Task RemovePermissionAsync(TRole role, PermissionGrantInfo permissionGrant); - - /// - /// Gets permission grant setting informations for a role. - /// - /// Role - /// List of permission setting informations - Task> GetPermissionsAsync(TRole role); - - /// - /// Gets permission grant setting informations for a role. - /// - /// Role id - /// List of permission setting informations - Task> GetPermissionsAsync(int roleId); - - /// - /// Checks whether a role has a permission grant setting info. - /// - /// Role id - /// Permission grant setting info - /// - Task HasPermissionAsync(int roleId, PermissionGrantInfo permissionGrant); - - /// - /// Deleted all permission settings for a role. - /// - /// Role - Task RemoveAllPermissionSettingsAsync(TRole role); - } +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Abp.Authorization.Roles +{ + /// + /// Used to perform permission database operations for a role. + /// + public interface IRolePermissionStore + where TRole : AbpRoleBase + { + /// + /// Adds a permission grant setting to a role. + /// + /// Role + /// Permission grant setting info + Task AddPermissionAsync(TRole role, PermissionGrantInfo permissionGrant); + + /// + /// Removes a permission grant setting from a role. + /// + /// Role + /// Permission grant setting info + Task RemovePermissionAsync(TRole role, PermissionGrantInfo permissionGrant); + + /// + /// Gets permission grant setting informations for a role. + /// + /// Role + /// List of permission setting informations + Task> GetPermissionsAsync(TRole role); + + /// + /// Gets permission grant setting informations for a role. + /// + /// Role id + /// List of permission setting informations + Task> GetPermissionsAsync(int roleId); + + /// + /// Checks whether a role has a permission grant setting info. + /// + /// Role id + /// Permission grant setting info + /// + Task HasPermissionAsync(int roleId, PermissionGrantInfo permissionGrant); + + /// + /// Deleted all permission settings for a role. + /// + /// Role + Task RemoveAllPermissionSettingsAsync(TRole role); + } } \ No newline at end of file diff --git a/src/Abp.Zero/Authorization/Roles/PermissionEqualityComparer.cs b/src/Abp.Zero.Common/Authorization/Roles/PermissionEqualityComparer.cs similarity index 100% rename from src/Abp.Zero/Authorization/Roles/PermissionEqualityComparer.cs rename to src/Abp.Zero.Common/Authorization/Roles/PermissionEqualityComparer.cs diff --git a/src/Abp.Zero.Common/Authorization/Roles/RoleClaim.cs b/src/Abp.Zero.Common/Authorization/Roles/RoleClaim.cs new file mode 100644 index 00000000..41ffded0 --- /dev/null +++ b/src/Abp.Zero.Common/Authorization/Roles/RoleClaim.cs @@ -0,0 +1,32 @@ +using System.ComponentModel.DataAnnotations.Schema; +using System.Security.Claims; +using Abp.Domain.Entities; +using Abp.Domain.Entities.Auditing; + +namespace Abp.Authorization.Roles +{ + [Table("AbpRoleClaims")] + public class RoleClaim : CreationAuditedEntity, IMayHaveTenant + { + public virtual int? TenantId { get; set; } + + public virtual int RoleId { get; set; } + + public virtual string ClaimType { get; set; } + + public virtual string ClaimValue { get; set; } + + public RoleClaim() + { + + } + + public RoleClaim(AbpRoleBase role, Claim claim) + { + TenantId = role.TenantId; + RoleId = role.Id; + ClaimType = claim.Type; + ClaimValue = claim.Value; + } + } +} diff --git a/src/Abp.Zero/Authorization/Roles/RolePermissionCacheItem.cs b/src/Abp.Zero.Common/Authorization/Roles/RolePermissionCacheItem.cs similarity index 95% rename from src/Abp.Zero/Authorization/Roles/RolePermissionCacheItem.cs rename to src/Abp.Zero.Common/Authorization/Roles/RolePermissionCacheItem.cs index f7e82701..b5d7b0b4 100644 --- a/src/Abp.Zero/Authorization/Roles/RolePermissionCacheItem.cs +++ b/src/Abp.Zero.Common/Authorization/Roles/RolePermissionCacheItem.cs @@ -1,29 +1,29 @@ -using System; -using System.Collections.Generic; - -namespace Abp.Authorization.Roles -{ - /// - /// Used to cache permissions of a role. - /// - [Serializable] - public class RolePermissionCacheItem - { - public const string CacheStoreName = "AbpZeroRolePermissions"; - - public long RoleId { get; set; } - - public HashSet GrantedPermissions { get; set; } - - public RolePermissionCacheItem() - { - GrantedPermissions = new HashSet(); - } - - public RolePermissionCacheItem(int roleId) - : this() - { - RoleId = roleId; - } - } +using System; +using System.Collections.Generic; + +namespace Abp.Authorization.Roles +{ + /// + /// Used to cache permissions of a role. + /// + [Serializable] + public class RolePermissionCacheItem + { + public const string CacheStoreName = "AbpZeroRolePermissions"; + + public long RoleId { get; set; } + + public HashSet GrantedPermissions { get; set; } + + public RolePermissionCacheItem() + { + GrantedPermissions = new HashSet(); + } + + public RolePermissionCacheItem(int roleId) + : this() + { + RoleId = roleId; + } + } } \ No newline at end of file diff --git a/src/Abp.Zero/Authorization/Roles/RolePermissionSetting.cs b/src/Abp.Zero.Common/Authorization/Roles/RolePermissionSetting.cs similarity index 100% rename from src/Abp.Zero/Authorization/Roles/RolePermissionSetting.cs rename to src/Abp.Zero.Common/Authorization/Roles/RolePermissionSetting.cs diff --git a/src/Abp.Zero.Common/Authorization/Users/AbpUserBase.cs b/src/Abp.Zero.Common/Authorization/Users/AbpUserBase.cs new file mode 100644 index 00000000..4c30cf74 --- /dev/null +++ b/src/Abp.Zero.Common/Authorization/Users/AbpUserBase.cs @@ -0,0 +1,251 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using Abp.Configuration; +using Abp.Domain.Entities; +using Abp.Domain.Entities.Auditing; +using Abp.Extensions; + +namespace Abp.Authorization.Users +{ + /// + /// Base class for user. + /// + [Table("AbpUsers")] + public abstract class AbpUserBase : FullAuditedEntity, IMayHaveTenant, IPassivable + { + /// + /// Maximum length of the property. + /// + public const int MaxUserNameLength = 32; + + /// + /// Maximum length of the property. + /// + public const int MaxEmailAddressLength = 256; + + /// + /// Maximum length of the property. + /// + public const int MaxNameLength = 32; + + /// + /// Maximum length of the property. + /// + public const int MaxSurnameLength = 32; + + /// + /// Maximum length of the property. + /// + public const int MaxAuthenticationSourceLength = 64; + + /// + /// UserName of the admin. + /// admin can not be deleted and UserName of the admin can not be changed. + /// + public const string AdminUserName = "admin"; + + /// + /// Maximum length of the property. + /// + public const int MaxPasswordLength = 128; + + /// + /// Maximum length of the without hashed. + /// + public const int MaxPlainPasswordLength = 32; + + /// + /// Maximum length of the property. + /// + public const int MaxEmailConfirmationCodeLength = 328; + + /// + /// Maximum length of the property. + /// + public const int MaxPasswordResetCodeLength = 328; + + /// + /// Authorization source name. + /// It's set to external authentication source name if created by an external source. + /// Default: null. + /// + [MaxLength(MaxAuthenticationSourceLength)] + public virtual string AuthenticationSource { get; set; } + + /// + /// User name. + /// User name must be unique for it's tenant. + /// + [Required] + [StringLength(MaxUserNameLength)] + public virtual string UserName { get; set; } + + /// + /// Tenant Id of this user. + /// + public virtual int? TenantId { get; set; } + + /// + /// Email address of the user. + /// Email address must be unique for it's tenant. + /// + [Required] + [StringLength(MaxEmailAddressLength)] + public virtual string EmailAddress { get; set; } + + /// + /// Name of the user. + /// + [Required] + [StringLength(MaxNameLength)] + public virtual string Name { get; set; } + + /// + /// Surname of the user. + /// + [Required] + [StringLength(MaxSurnameLength)] + public virtual string Surname { get; set; } + + /// + /// Return full name (Name Surname ) + /// + [NotMapped] + public virtual string FullName { get { return this.Name + " " + this.Surname; } } + + /// + /// Password of the user. + /// + [Required] + [StringLength(MaxPasswordLength)] + public virtual string Password { get; set; } + + /// + /// Confirmation code for email. + /// + [StringLength(MaxEmailConfirmationCodeLength)] + public virtual string EmailConfirmationCode { get; set; } + + /// + /// Reset code for password. + /// It's not valid if it's null. + /// It's for one usage and must be set to null after reset. + /// + [StringLength(MaxPasswordResetCodeLength)] + public virtual string PasswordResetCode { get; set; } + + /// + /// Lockout end date. + /// + public virtual DateTime? LockoutEndDateUtc { get; set; } + + /// + /// Gets or sets the access failed count. + /// + public virtual int AccessFailedCount { get; set; } + + /// + /// Gets or sets the lockout enabled. + /// + public virtual bool IsLockoutEnabled { get; set; } + + /// + /// Gets or sets the phone number. + /// + public virtual string PhoneNumber { get; set; } + + /// + /// Is the confirmed. + /// + public virtual bool IsPhoneNumberConfirmed { get; set; } + + /// + /// Gets or sets the security stamp. + /// + public virtual string SecurityStamp { get; set; } + + /// + /// Is two factor auth enabled. + /// + public virtual bool IsTwoFactorEnabled { get; set; } + + /// + /// Login definitions for this user. + /// + [ForeignKey("UserId")] + public virtual ICollection Logins { get; set; } + + /// + /// Roles of this user. + /// + [ForeignKey("UserId")] + public virtual ICollection Roles { get; set; } + + /// + /// Claims of this user. + /// + [ForeignKey("UserId")] + public virtual ICollection Claims { get; set; } + + /// + /// Permission definitions for this user. + /// + [ForeignKey("UserId")] + public virtual ICollection Permissions { get; set; } + + /// + /// Settings for this user. + /// + [ForeignKey("UserId")] + public virtual ICollection Settings { get; set; } + + /// + /// Is the confirmed. + /// + public virtual bool IsEmailConfirmed { get; set; } + + /// + /// Is this user active? + /// If as user is not active, he/she can not use the application. + /// + public virtual bool IsActive { get; set; } + + /// + /// The last time this user entered to the system. + /// + public virtual DateTime? LastLoginTime { get; set; } + + protected AbpUserBase() + { + IsActive = true; + IsLockoutEnabled = true; + SecurityStamp = SequentialGuidGenerator.Instance.Create().ToString(); + } + + public virtual void SetNewPasswordResetCode() + { + PasswordResetCode = Guid.NewGuid().ToString("N").Truncate(MaxPasswordResetCodeLength); + } + + public virtual void SetNewEmailConfirmationCode() + { + EmailConfirmationCode = Guid.NewGuid().ToString("N").Truncate(MaxEmailConfirmationCodeLength); + } + + /// + /// Creates from this User. + /// + /// + public virtual UserIdentifier ToUserIdentifier() + { + return new UserIdentifier(TenantId, Id); + } + + public override string ToString() + { + return $"[User {Id}] {UserName}"; + } + } +} \ No newline at end of file diff --git a/src/Abp.Zero/Authorization/Users/AbpUserPermissionCacheItemInvalidator.cs b/src/Abp.Zero.Common/Authorization/Users/AbpUserPermissionCacheItemInvalidator.cs similarity index 100% rename from src/Abp.Zero/Authorization/Users/AbpUserPermissionCacheItemInvalidator.cs rename to src/Abp.Zero.Common/Authorization/Users/AbpUserPermissionCacheItemInvalidator.cs diff --git a/src/Abp.Zero/Authorization/Users/DefaultExternalAuthenticationSource.cs b/src/Abp.Zero.Common/Authorization/Users/DefaultExternalAuthenticationSource.cs similarity index 97% rename from src/Abp.Zero/Authorization/Users/DefaultExternalAuthenticationSource.cs rename to src/Abp.Zero.Common/Authorization/Users/DefaultExternalAuthenticationSource.cs index e45b39f4..e465a7f3 100644 --- a/src/Abp.Zero/Authorization/Users/DefaultExternalAuthenticationSource.cs +++ b/src/Abp.Zero.Common/Authorization/Users/DefaultExternalAuthenticationSource.cs @@ -11,7 +11,7 @@ namespace Abp.Authorization.Users /// User type public abstract class DefaultExternalAuthenticationSource : IExternalAuthenticationSource where TTenant : AbpTenant - where TUser : AbpUser, new() + where TUser : AbpUserBase, new() { /// public abstract string Name { get; } diff --git a/src/Abp.Zero/Authorization/Users/IExternalAuthenticationSource.cs b/src/Abp.Zero.Common/Authorization/Users/IExternalAuthenticationSource.cs similarity index 89% rename from src/Abp.Zero/Authorization/Users/IExternalAuthenticationSource.cs rename to src/Abp.Zero.Common/Authorization/Users/IExternalAuthenticationSource.cs index d6adb2c6..cfd6dea0 100644 --- a/src/Abp.Zero/Authorization/Users/IExternalAuthenticationSource.cs +++ b/src/Abp.Zero.Common/Authorization/Users/IExternalAuthenticationSource.cs @@ -4,17 +4,17 @@ namespace Abp.Authorization.Users { /// - /// Defines an authorization source to be used by method. + /// Defines an external authorization source. /// /// Tenant type /// User type public interface IExternalAuthenticationSource where TTenant : AbpTenant - where TUser : AbpUser + where TUser : AbpUserBase { /// /// Unique name of the authentication source. - /// This source name is set to + /// This source name is set to /// if the user authenticated by this authentication source /// string Name { get; } diff --git a/src/Abp.Zero/Authorization/Users/IUserPermissionStore.cs b/src/Abp.Zero.Common/Authorization/Users/IUserPermissionStore.cs similarity index 95% rename from src/Abp.Zero/Authorization/Users/IUserPermissionStore.cs rename to src/Abp.Zero.Common/Authorization/Users/IUserPermissionStore.cs index 808418d2..8bf692fe 100644 --- a/src/Abp.Zero/Authorization/Users/IUserPermissionStore.cs +++ b/src/Abp.Zero.Common/Authorization/Users/IUserPermissionStore.cs @@ -1,47 +1,47 @@ -using System.Collections.Generic; -using System.Threading.Tasks; - -namespace Abp.Authorization.Users -{ - /// - /// Used to perform permission database operations for a user. - /// - public interface IUserPermissionStore - where TUser : AbpUser - { - /// - /// Adds a permission grant setting to a user. - /// - /// User - /// Permission grant setting info - Task AddPermissionAsync(TUser user, PermissionGrantInfo permissionGrant); - - /// - /// Removes a permission grant setting from a user. - /// - /// User - /// Permission grant setting info - Task RemovePermissionAsync(TUser user, PermissionGrantInfo permissionGrant); - - /// - /// Gets permission grant setting informations for a user. - /// - /// User id - /// List of permission setting informations - Task> GetPermissionsAsync(long userId); - - /// - /// Checks whether a role has a permission grant setting info. - /// - /// User id - /// Permission grant setting info - /// - Task HasPermissionAsync(long userId, PermissionGrantInfo permissionGrant); - - /// - /// Deleted all permission settings for a role. - /// - /// User - Task RemoveAllPermissionSettingsAsync(TUser user); - } +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Abp.Authorization.Users +{ + /// + /// Used to perform permission database operations for a user. + /// + public interface IUserPermissionStore + where TUser : AbpUserBase + { + /// + /// Adds a permission grant setting to a user. + /// + /// User + /// Permission grant setting info + Task AddPermissionAsync(TUser user, PermissionGrantInfo permissionGrant); + + /// + /// Removes a permission grant setting from a user. + /// + /// User + /// Permission grant setting info + Task RemovePermissionAsync(TUser user, PermissionGrantInfo permissionGrant); + + /// + /// Gets permission grant setting informations for a user. + /// + /// User id + /// List of permission setting informations + Task> GetPermissionsAsync(long userId); + + /// + /// Checks whether a role has a permission grant setting info. + /// + /// User id + /// Permission grant setting info + /// + Task HasPermissionAsync(long userId, PermissionGrantInfo permissionGrant); + + /// + /// Deleted all permission settings for a role. + /// + /// User + Task RemoveAllPermissionSettingsAsync(TUser user); + } } \ No newline at end of file diff --git a/src/Abp.Zero/Authorization/Users/UserAccount.cs b/src/Abp.Zero.Common/Authorization/Users/UserAccount.cs similarity index 96% rename from src/Abp.Zero/Authorization/Users/UserAccount.cs rename to src/Abp.Zero.Common/Authorization/Users/UserAccount.cs index a7864da9..c027cfe3 100644 --- a/src/Abp.Zero/Authorization/Users/UserAccount.cs +++ b/src/Abp.Zero.Common/Authorization/Users/UserAccount.cs @@ -1,27 +1,27 @@ -using System; -using System.ComponentModel.DataAnnotations.Schema; -using Abp.Domain.Entities.Auditing; -using Abp.MultiTenancy; - -namespace Abp.Authorization.Users -{ - /// - /// Represents a summary user - /// - [Table("AbpUserAccounts")] - [MultiTenancySide(MultiTenancySides.Host)] - public class UserAccount : FullAuditedEntity - { - public virtual int? TenantId { get; set; } - - public virtual long UserId { get; set; } - - public virtual long? UserLinkId { get; set; } - - public virtual string UserName { get; set; } - - public virtual string EmailAddress { get; set; } - - public virtual DateTime? LastLoginTime { get; set; } - } +using System; +using System.ComponentModel.DataAnnotations.Schema; +using Abp.Domain.Entities.Auditing; +using Abp.MultiTenancy; + +namespace Abp.Authorization.Users +{ + /// + /// Represents a summary user + /// + [Table("AbpUserAccounts")] + [MultiTenancySide(MultiTenancySides.Host)] + public class UserAccount : FullAuditedEntity + { + public virtual int? TenantId { get; set; } + + public virtual long UserId { get; set; } + + public virtual long? UserLinkId { get; set; } + + public virtual string UserName { get; set; } + + public virtual string EmailAddress { get; set; } + + public virtual DateTime? LastLoginTime { get; set; } + } } \ No newline at end of file diff --git a/src/Abp.Zero/Authorization/Users/UserAccountSynchronizer.cs b/src/Abp.Zero.Common/Authorization/Users/UserAccountSynchronizer.cs similarity index 97% rename from src/Abp.Zero/Authorization/Users/UserAccountSynchronizer.cs rename to src/Abp.Zero.Common/Authorization/Users/UserAccountSynchronizer.cs index 3cd059cf..42a89140 100644 --- a/src/Abp.Zero/Authorization/Users/UserAccountSynchronizer.cs +++ b/src/Abp.Zero.Common/Authorization/Users/UserAccountSynchronizer.cs @@ -1,90 +1,90 @@ -using Abp.Dependency; -using Abp.Domain.Repositories; -using Abp.Domain.Uow; -using Abp.Events.Bus.Entities; -using Abp.Events.Bus.Handlers; - -namespace Abp.Authorization.Users -{ - /// - /// Synchronizes a user's information to user account. - /// - public class UserAccountSynchronizer : - IEventHandler>, - IEventHandler>, - IEventHandler>, - ITransientDependency - { - private readonly IRepository _userAccountRepository; - private readonly IUnitOfWorkManager _unitOfWorkManager; - - /// - /// Constructor - /// - public UserAccountSynchronizer( - IRepository userAccountRepository, - IUnitOfWorkManager unitOfWorkManager) - { - _userAccountRepository = userAccountRepository; - _unitOfWorkManager = unitOfWorkManager; - } - - /// - /// Handles creation event of user - /// - [UnitOfWork] - public virtual void HandleEvent(EntityCreatedEventData eventData) - { - using (_unitOfWorkManager.Current.SetTenantId(null)) - { - _userAccountRepository.Insert(new UserAccount - { - TenantId = eventData.Entity.TenantId, - UserName = eventData.Entity.UserName, - UserId = eventData.Entity.Id, - EmailAddress = eventData.Entity.EmailAddress, - LastLoginTime = eventData.Entity.LastLoginTime - }); - } - } - - /// - /// Handles deletion event of user - /// - /// - [UnitOfWork] - public virtual void HandleEvent(EntityDeletedEventData eventData) - { - using (_unitOfWorkManager.Current.SetTenantId(null)) - { - var userAccount = - _userAccountRepository.FirstOrDefault( - ua => ua.TenantId == eventData.Entity.TenantId && ua.UserId == eventData.Entity.Id); - if (userAccount != null) - { - _userAccountRepository.Delete(userAccount); - } - } - } - - /// - /// Handles update event of user - /// - /// - [UnitOfWork] - public virtual void HandleEvent(EntityUpdatedEventData eventData) - { - using (_unitOfWorkManager.Current.SetTenantId(null)) - { - var userAccount = _userAccountRepository.FirstOrDefault(ua => ua.TenantId == eventData.Entity.TenantId && ua.UserId == eventData.Entity.Id); - if (userAccount != null) - { - userAccount.UserName = eventData.Entity.UserName; - userAccount.EmailAddress = eventData.Entity.EmailAddress; - userAccount.LastLoginTime = eventData.Entity.LastLoginTime; - _userAccountRepository.Update(userAccount); - } - } - } - } +using Abp.Dependency; +using Abp.Domain.Repositories; +using Abp.Domain.Uow; +using Abp.Events.Bus.Entities; +using Abp.Events.Bus.Handlers; + +namespace Abp.Authorization.Users +{ + /// + /// Synchronizes a user's information to user account. + /// + public class UserAccountSynchronizer : + IEventHandler>, + IEventHandler>, + IEventHandler>, + ITransientDependency + { + private readonly IRepository _userAccountRepository; + private readonly IUnitOfWorkManager _unitOfWorkManager; + + /// + /// Constructor + /// + public UserAccountSynchronizer( + IRepository userAccountRepository, + IUnitOfWorkManager unitOfWorkManager) + { + _userAccountRepository = userAccountRepository; + _unitOfWorkManager = unitOfWorkManager; + } + + /// + /// Handles creation event of user + /// + [UnitOfWork] + public virtual void HandleEvent(EntityCreatedEventData eventData) + { + using (_unitOfWorkManager.Current.SetTenantId(null)) + { + _userAccountRepository.Insert(new UserAccount + { + TenantId = eventData.Entity.TenantId, + UserName = eventData.Entity.UserName, + UserId = eventData.Entity.Id, + EmailAddress = eventData.Entity.EmailAddress, + LastLoginTime = eventData.Entity.LastLoginTime + }); + } + } + + /// + /// Handles deletion event of user + /// + /// + [UnitOfWork] + public virtual void HandleEvent(EntityDeletedEventData eventData) + { + using (_unitOfWorkManager.Current.SetTenantId(null)) + { + var userAccount = + _userAccountRepository.FirstOrDefault( + ua => ua.TenantId == eventData.Entity.TenantId && ua.UserId == eventData.Entity.Id); + if (userAccount != null) + { + _userAccountRepository.Delete(userAccount); + } + } + } + + /// + /// Handles update event of user + /// + /// + [UnitOfWork] + public virtual void HandleEvent(EntityUpdatedEventData eventData) + { + using (_unitOfWorkManager.Current.SetTenantId(null)) + { + var userAccount = _userAccountRepository.FirstOrDefault(ua => ua.TenantId == eventData.Entity.TenantId && ua.UserId == eventData.Entity.Id); + if (userAccount != null) + { + userAccount.UserName = eventData.Entity.UserName; + userAccount.EmailAddress = eventData.Entity.EmailAddress; + userAccount.LastLoginTime = eventData.Entity.LastLoginTime; + _userAccountRepository.Update(userAccount); + } + } + } + } } \ No newline at end of file diff --git a/src/Abp.Zero/Authorization/Users/UserClaim.cs b/src/Abp.Zero.Common/Authorization/Users/UserClaim.cs similarity index 95% rename from src/Abp.Zero/Authorization/Users/UserClaim.cs rename to src/Abp.Zero.Common/Authorization/Users/UserClaim.cs index 7964afda..b328a17d 100644 --- a/src/Abp.Zero/Authorization/Users/UserClaim.cs +++ b/src/Abp.Zero.Common/Authorization/Users/UserClaim.cs @@ -26,7 +26,7 @@ public UserClaim(AbpUserBase user, Claim claim) TenantId = user.TenantId; UserId = user.Id; ClaimType = claim.Type; - ClaimType = claim.Value; + ClaimValue = claim.Value; } } } diff --git a/src/Abp.Zero/Authorization/Users/UserLogin.cs b/src/Abp.Zero.Common/Authorization/Users/UserLogin.cs similarity index 80% rename from src/Abp.Zero/Authorization/Users/UserLogin.cs rename to src/Abp.Zero.Common/Authorization/Users/UserLogin.cs index ac9d6da6..a3ca3ce9 100644 --- a/src/Abp.Zero/Authorization/Users/UserLogin.cs +++ b/src/Abp.Zero.Common/Authorization/Users/UserLogin.cs @@ -40,5 +40,18 @@ public class UserLogin : Entity, IMayHaveTenant [Required] [MaxLength(MaxProviderKeyLength)] public virtual string ProviderKey { get; set; } + + public UserLogin() + { + + } + + public UserLogin(int? tenantId, long userId, string loginProvider, string providerKey) + { + TenantId = tenantId; + UserId = userId; + LoginProvider = loginProvider; + ProviderKey = providerKey; + } } } diff --git a/src/Abp.Zero/Authorization/Users/UserLoginAttempt.cs b/src/Abp.Zero.Common/Authorization/Users/UserLoginAttempt.cs similarity index 100% rename from src/Abp.Zero/Authorization/Users/UserLoginAttempt.cs rename to src/Abp.Zero.Common/Authorization/Users/UserLoginAttempt.cs diff --git a/src/Abp.Zero/Authorization/Users/UserOrganizationUnit.cs b/src/Abp.Zero.Common/Authorization/Users/UserOrganizationUnit.cs similarity index 88% rename from src/Abp.Zero/Authorization/Users/UserOrganizationUnit.cs rename to src/Abp.Zero.Common/Authorization/Users/UserOrganizationUnit.cs index 0733b102..98168237 100644 --- a/src/Abp.Zero/Authorization/Users/UserOrganizationUnit.cs +++ b/src/Abp.Zero.Common/Authorization/Users/UserOrganizationUnit.cs @@ -9,7 +9,7 @@ namespace Abp.Authorization.Users /// Represents membership of a User to an OU. /// [Table("AbpUserOrganizationUnits")] - public class UserOrganizationUnit : CreationAuditedEntity, IMayHaveTenant + public class UserOrganizationUnit : CreationAuditedEntity, IMayHaveTenant, ISoftDelete { /// /// TenantId of this entity. @@ -26,6 +26,11 @@ public class UserOrganizationUnit : CreationAuditedEntity, IMayHaveTenant /// public virtual long OrganizationUnitId { get; set; } + /// + /// Specifies if the organization is soft deleted or not. + /// + public virtual bool IsDeleted { get; set; } + /// /// Initializes a new instance of the class. /// diff --git a/src/Abp.Zero/Authorization/Users/UserOrganizationUnitRemover.cs b/src/Abp.Zero.Common/Authorization/Users/UserOrganizationUnitRemover.cs similarity index 100% rename from src/Abp.Zero/Authorization/Users/UserOrganizationUnitRemover.cs rename to src/Abp.Zero.Common/Authorization/Users/UserOrganizationUnitRemover.cs diff --git a/src/Abp.Zero/Authorization/Users/UserPermissionCacheItem.cs b/src/Abp.Zero.Common/Authorization/Users/UserPermissionCacheItem.cs similarity index 96% rename from src/Abp.Zero/Authorization/Users/UserPermissionCacheItem.cs rename to src/Abp.Zero.Common/Authorization/Users/UserPermissionCacheItem.cs index ed198ab8..ebf2f995 100644 --- a/src/Abp.Zero/Authorization/Users/UserPermissionCacheItem.cs +++ b/src/Abp.Zero.Common/Authorization/Users/UserPermissionCacheItem.cs @@ -1,35 +1,35 @@ -using System; -using System.Collections.Generic; - -namespace Abp.Authorization.Users -{ - /// - /// Used to cache roles and permissions of a user. - /// - [Serializable] - public class UserPermissionCacheItem - { - public const string CacheStoreName = "AbpZeroUserPermissions"; - - public long UserId { get; set; } - - public List RoleIds { get; set; } - - public HashSet GrantedPermissions { get; set; } - - public HashSet ProhibitedPermissions { get; set; } - - public UserPermissionCacheItem() - { - RoleIds = new List(); - GrantedPermissions = new HashSet(); - ProhibitedPermissions = new HashSet(); - } - - public UserPermissionCacheItem(long userId) - : this() - { - UserId = userId; - } - } -} +using System; +using System.Collections.Generic; + +namespace Abp.Authorization.Users +{ + /// + /// Used to cache roles and permissions of a user. + /// + [Serializable] + public class UserPermissionCacheItem + { + public const string CacheStoreName = "AbpZeroUserPermissions"; + + public long UserId { get; set; } + + public List RoleIds { get; set; } + + public HashSet GrantedPermissions { get; set; } + + public HashSet ProhibitedPermissions { get; set; } + + public UserPermissionCacheItem() + { + RoleIds = new List(); + GrantedPermissions = new HashSet(); + ProhibitedPermissions = new HashSet(); + } + + public UserPermissionCacheItem(long userId) + : this() + { + UserId = userId; + } + } +} diff --git a/src/Abp.Zero/Authorization/Users/UserPermissionSetting.cs b/src/Abp.Zero.Common/Authorization/Users/UserPermissionSetting.cs similarity index 100% rename from src/Abp.Zero/Authorization/Users/UserPermissionSetting.cs rename to src/Abp.Zero.Common/Authorization/Users/UserPermissionSetting.cs diff --git a/src/Abp.Zero/Authorization/Users/UserRole.cs b/src/Abp.Zero.Common/Authorization/Users/UserRole.cs similarity index 100% rename from src/Abp.Zero/Authorization/Users/UserRole.cs rename to src/Abp.Zero.Common/Authorization/Users/UserRole.cs diff --git a/src/Abp.Zero/Authorization/Users/UserRoleRemover.cs b/src/Abp.Zero.Common/Authorization/Users/UserRoleRemover.cs similarity index 93% rename from src/Abp.Zero/Authorization/Users/UserRoleRemover.cs rename to src/Abp.Zero.Common/Authorization/Users/UserRoleRemover.cs index 6fcea0c1..0c477965 100644 --- a/src/Abp.Zero/Authorization/Users/UserRoleRemover.cs +++ b/src/Abp.Zero.Common/Authorization/Users/UserRoleRemover.cs @@ -7,7 +7,7 @@ namespace Abp.Authorization.Users { /// - /// Removes the user from all organization units when a user is deleted. + /// Removes the user from all user roles when a user is deleted. /// public class UserRoleRemover : IEventHandler>, @@ -35,4 +35,4 @@ public virtual void HandleEvent(EntityDeletedEventData eventData) } } } -} \ No newline at end of file +} diff --git a/src/Abp.Zero/BackgroundJobs/BackgroundJobStore.cs b/src/Abp.Zero.Common/BackgroundJobs/BackgroundJobStore.cs similarity index 100% rename from src/Abp.Zero/BackgroundJobs/BackgroundJobStore.cs rename to src/Abp.Zero.Common/BackgroundJobs/BackgroundJobStore.cs diff --git a/src/Abp.Zero/Configuration/Setting.cs b/src/Abp.Zero.Common/Configuration/Setting.cs similarity index 100% rename from src/Abp.Zero/Configuration/Setting.cs rename to src/Abp.Zero.Common/Configuration/Setting.cs diff --git a/src/Abp.Zero/Configuration/SettingExtensions.cs b/src/Abp.Zero.Common/Configuration/SettingExtensions.cs similarity index 100% rename from src/Abp.Zero/Configuration/SettingExtensions.cs rename to src/Abp.Zero.Common/Configuration/SettingExtensions.cs diff --git a/src/Abp.Zero/Configuration/SettingStore.cs b/src/Abp.Zero.Common/Configuration/SettingStore.cs similarity index 100% rename from src/Abp.Zero/Configuration/SettingStore.cs rename to src/Abp.Zero.Common/Configuration/SettingStore.cs diff --git a/src/Abp.Zero/Localization/ApplicationLanguage.cs b/src/Abp.Zero.Common/Localization/ApplicationLanguage.cs similarity index 83% rename from src/Abp.Zero/Localization/ApplicationLanguage.cs rename to src/Abp.Zero.Common/Localization/ApplicationLanguage.cs index c4a4bd2c..50c79432 100644 --- a/src/Abp.Zero/Localization/ApplicationLanguage.cs +++ b/src/Abp.Zero.Common/Localization/ApplicationLanguage.cs @@ -53,6 +53,11 @@ public class ApplicationLanguage : FullAuditedEntity, IMayHaveTenant [StringLength(MaxIconLength)] public virtual string Icon { get; set; } + /// + /// Is this language active. Inactive languages are not get by . + /// + public bool IsDisabled { get; set; } + /// /// Creates a new object. /// @@ -60,17 +65,18 @@ public ApplicationLanguage() { } - public ApplicationLanguage(int? tenantId, string name, string displayName, string icon = null) + public ApplicationLanguage(int? tenantId, string name, string displayName, string icon = null, bool isDisabled = false) { TenantId = tenantId; Name = name; DisplayName = displayName; Icon = icon; + IsDisabled = isDisabled; } public virtual LanguageInfo ToLanguageInfo() { - return new LanguageInfo(Name, DisplayName, Icon); + return new LanguageInfo(Name, DisplayName, Icon, isDisabled: IsDisabled); } } } diff --git a/src/Abp.Zero/Localization/ApplicationLanguageManager.cs b/src/Abp.Zero.Common/Localization/ApplicationLanguageManager.cs similarity index 92% rename from src/Abp.Zero/Localization/ApplicationLanguageManager.cs rename to src/Abp.Zero.Common/Localization/ApplicationLanguageManager.cs index beb72a01..138cdb01 100644 --- a/src/Abp.Zero/Localization/ApplicationLanguageManager.cs +++ b/src/Abp.Zero.Common/Localization/ApplicationLanguageManager.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using System.Collections.Immutable; -using System.Globalization; using System.Linq; using System.Threading.Tasks; using Abp.Configuration; @@ -26,10 +25,7 @@ public class ApplicationLanguageManager : /// public const string CacheName = "AbpZeroLanguages"; - private ITypedCache> LanguageListCache - { - get { return _cacheManager.GetCache>(CacheName); } - } + private ITypedCache> LanguageListCache => _cacheManager.GetCache>(CacheName); private readonly IRepository _languageRepository; private readonly ICacheManager _cacheManager; @@ -55,7 +51,7 @@ public ApplicationLanguageManager( /// Gets list of all languages available to given tenant (or null for host) /// /// TenantId or null for host - public async Task> GetLanguagesAsync(int? tenantId) + public virtual async Task> GetLanguagesAsync(int? tenantId) { return (await GetLanguageDictionary(tenantId)).Values.ToImmutableList(); } @@ -136,7 +132,7 @@ public virtual async Task UpdateAsync(int? tenantId, ApplicationLanguage languag /// Gets the default language or null for a tenant or the host. /// /// Tenant Id of null for host - public async Task GetDefaultLanguageOrNullAsync(int? tenantId) + public virtual async Task GetDefaultLanguageOrNullAsync(int? tenantId) { var defaultLanguageName = tenantId.HasValue ? await _settingManager.GetSettingValueForTenantAsync(LocalizationSettingNames.DefaultLanguage, tenantId.Value) @@ -150,9 +146,9 @@ public async Task GetDefaultLanguageOrNullAsync(int? tenant /// /// Tenant Id of null for host /// Name of the language. - public async Task SetDefaultLanguageAsync(int? tenantId, string languageName) + public virtual async Task SetDefaultLanguageAsync(int? tenantId, string languageName) { - var cultureInfo = CultureInfo.GetCultureInfo(languageName); + var cultureInfo = CultureInfoHelper.Get(languageName); if (tenantId.HasValue) { await _settingManager.ChangeSettingForTenantAsync(tenantId.Value, LocalizationSettingNames.DefaultLanguage, cultureInfo.Name); @@ -171,7 +167,7 @@ public void HandleEvent(EntityChangedEventData eventData) _cacheManager.GetCache("AbpLocalizationScripts").Clear(); } - private async Task> GetLanguageDictionary(int? tenantId) + protected virtual async Task> GetLanguageDictionary(int? tenantId) { //Creates a copy of the cached dictionary (to not modify it) var languageDictionary = new Dictionary(await GetLanguageDictionaryFromCacheAsync(null)); diff --git a/src/Abp.Zero/Localization/ApplicationLanguageProvider.cs b/src/Abp.Zero.Common/Localization/ApplicationLanguageProvider.cs similarity index 100% rename from src/Abp.Zero/Localization/ApplicationLanguageProvider.cs rename to src/Abp.Zero.Common/Localization/ApplicationLanguageProvider.cs diff --git a/src/Abp.Zero/Localization/ApplicationLanguageText.cs b/src/Abp.Zero.Common/Localization/ApplicationLanguageText.cs similarity index 100% rename from src/Abp.Zero/Localization/ApplicationLanguageText.cs rename to src/Abp.Zero.Common/Localization/ApplicationLanguageText.cs diff --git a/src/Abp.Zero/Localization/ApplicationLanguageTextManager.cs b/src/Abp.Zero.Common/Localization/ApplicationLanguageTextManager.cs similarity index 100% rename from src/Abp.Zero/Localization/ApplicationLanguageTextManager.cs rename to src/Abp.Zero.Common/Localization/ApplicationLanguageTextManager.cs diff --git a/src/Abp.Zero/Localization/EmptyDictionary.cs b/src/Abp.Zero.Common/Localization/EmptyDictionary.cs similarity index 100% rename from src/Abp.Zero/Localization/EmptyDictionary.cs rename to src/Abp.Zero.Common/Localization/EmptyDictionary.cs diff --git a/src/Abp.Zero/Localization/IApplicationLanguageManager.cs b/src/Abp.Zero.Common/Localization/IApplicationLanguageManager.cs similarity index 100% rename from src/Abp.Zero/Localization/IApplicationLanguageManager.cs rename to src/Abp.Zero.Common/Localization/IApplicationLanguageManager.cs diff --git a/src/Abp.Zero/Localization/IApplicationLanguageTextManager.cs b/src/Abp.Zero.Common/Localization/IApplicationLanguageTextManager.cs similarity index 100% rename from src/Abp.Zero/Localization/IApplicationLanguageTextManager.cs rename to src/Abp.Zero.Common/Localization/IApplicationLanguageTextManager.cs diff --git a/src/Abp.Zero/Localization/IMultiTenantLocalizationDictionary.cs b/src/Abp.Zero.Common/Localization/IMultiTenantLocalizationDictionary.cs similarity index 100% rename from src/Abp.Zero/Localization/IMultiTenantLocalizationDictionary.cs rename to src/Abp.Zero.Common/Localization/IMultiTenantLocalizationDictionary.cs diff --git a/src/Abp.Zero/Localization/IMultiTenantLocalizationSource.cs b/src/Abp.Zero.Common/Localization/IMultiTenantLocalizationSource.cs similarity index 100% rename from src/Abp.Zero/Localization/IMultiTenantLocalizationSource.cs rename to src/Abp.Zero.Common/Localization/IMultiTenantLocalizationSource.cs diff --git a/src/Abp.Zero/Localization/MultiTenantLocalizationDictionary.cs b/src/Abp.Zero.Common/Localization/MultiTenantLocalizationDictionary.cs similarity index 100% rename from src/Abp.Zero/Localization/MultiTenantLocalizationDictionary.cs rename to src/Abp.Zero.Common/Localization/MultiTenantLocalizationDictionary.cs diff --git a/src/Abp.Zero/Localization/MultiTenantLocalizationDictionaryCacheCleaner.cs b/src/Abp.Zero.Common/Localization/MultiTenantLocalizationDictionaryCacheCleaner.cs similarity index 100% rename from src/Abp.Zero/Localization/MultiTenantLocalizationDictionaryCacheCleaner.cs rename to src/Abp.Zero.Common/Localization/MultiTenantLocalizationDictionaryCacheCleaner.cs diff --git a/src/Abp.Zero/Localization/MultiTenantLocalizationDictionaryCacheHelper.cs b/src/Abp.Zero.Common/Localization/MultiTenantLocalizationDictionaryCacheHelper.cs similarity index 100% rename from src/Abp.Zero/Localization/MultiTenantLocalizationDictionaryCacheHelper.cs rename to src/Abp.Zero.Common/Localization/MultiTenantLocalizationDictionaryCacheHelper.cs diff --git a/src/Abp.Zero/Localization/MultiTenantLocalizationDictionaryProvider.cs b/src/Abp.Zero.Common/Localization/MultiTenantLocalizationDictionaryProvider.cs similarity index 93% rename from src/Abp.Zero/Localization/MultiTenantLocalizationDictionaryProvider.cs rename to src/Abp.Zero.Common/Localization/MultiTenantLocalizationDictionaryProvider.cs index 7bc343be..a7a1274b 100644 --- a/src/Abp.Zero/Localization/MultiTenantLocalizationDictionaryProvider.cs +++ b/src/Abp.Zero.Common/Localization/MultiTenantLocalizationDictionaryProvider.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Globalization; @@ -6,7 +5,6 @@ using Abp.Collections.Extensions; using Abp.Dependency; using Abp.Localization.Dictionaries; -using Abp.Collections.Extensions; namespace Abp.Localization { @@ -68,13 +66,13 @@ protected virtual ILocalizationDictionary GetDefaultDictionary() var languages = _languageManager.GetLanguages(); if (!languages.Any()) { - throw new ApplicationException("No language defined!"); + throw new AbpException("No language defined!"); } var defaultLanguage = languages.FirstOrDefault(l => l.IsDefault); if (defaultLanguage == null) { - throw new ApplicationException("Default language is not defined!"); + throw new AbpException("Default language is not defined!"); } return _dictionaries.GetOrAdd(defaultLanguage.Name, s => CreateLocalizationDictionary(defaultLanguage)); @@ -84,7 +82,7 @@ protected virtual IMultiTenantLocalizationDictionary CreateLocalizationDictionar { var internalDictionary = _internalProvider.Dictionaries.GetOrDefault(language.Name) ?? - new EmptyDictionary(CultureInfo.GetCultureInfo(language.Name)); + new EmptyDictionary(CultureInfoHelper.Get(language.Name)); var dictionary = _iocManager.Resolve(new { diff --git a/src/Abp.Zero/Localization/MultiTenantLocalizationSource.cs b/src/Abp.Zero.Common/Localization/MultiTenantLocalizationSource.cs similarity index 99% rename from src/Abp.Zero/Localization/MultiTenantLocalizationSource.cs rename to src/Abp.Zero.Common/Localization/MultiTenantLocalizationSource.cs index d3487005..c91a87a2 100644 --- a/src/Abp.Zero/Localization/MultiTenantLocalizationSource.cs +++ b/src/Abp.Zero.Common/Localization/MultiTenantLocalizationSource.cs @@ -107,7 +107,7 @@ public override void Extend(ILocalizationDictionary dictionary) private static string GetBaseCultureName(string cultureName) { return cultureName.Contains("-") - ? cultureName.Left(cultureName.IndexOf("-", StringComparison.InvariantCulture)) + ? cultureName.Left(cultureName.IndexOf("-", StringComparison.Ordinal)) : cultureName; } } diff --git a/src/Abp.Zero/MultiTenancy/AbpTenant.cs b/src/Abp.Zero.Common/MultiTenancy/AbpTenant.cs similarity index 85% rename from src/Abp.Zero/MultiTenancy/AbpTenant.cs rename to src/Abp.Zero.Common/MultiTenancy/AbpTenant.cs index ae41ca01..86742e4a 100644 --- a/src/Abp.Zero/MultiTenancy/AbpTenant.cs +++ b/src/Abp.Zero.Common/MultiTenancy/AbpTenant.cs @@ -1,84 +1,78 @@ -using System.ComponentModel.DataAnnotations; -using Abp.Application.Editions; -using Abp.Authorization.Users; -using Abp.Domain.Entities; -using Abp.Domain.Entities.Auditing; - -namespace Abp.MultiTenancy -{ - /// - /// Represents a Tenant of the application. - /// - public abstract class AbpTenant : AbpTenantBase, IFullAudited, IPassivable - where TUser : AbpUser - { - /// - /// "Default". - /// - public const string DefaultTenantName = "Default"; - - /// - /// "^[a-zA-Z][a-zA-Z0-9_-]{1,}$". - /// - public const string TenancyNameRegex = "^[a-zA-Z][a-zA-Z0-9_-]{1,}$"; - - /// - /// Max length of the property. - /// - public const int MaxNameLength = 128; - - /// - /// Current of the Tenant. - /// - public virtual Edition Edition { get; set; } - public virtual int? EditionId { get; set; } - - /// - /// Display name of the Tenant. - /// - [Required] - [StringLength(MaxNameLength)] - public virtual string Name { get; set; } - - /// - /// Is this tenant active? - /// If as tenant is not active, no user of this tenant can use the application. - /// - public virtual bool IsActive { get; set; } - - /// - /// Reference to the creator user of this entity. - /// - public virtual TUser CreatorUser { get; set; } - - /// - /// Reference to the last modifier user of this entity. - /// - public virtual TUser LastModifierUser { get; set; } - - /// - /// Reference to the deleter user of this entity. - /// - public virtual TUser DeleterUser { get; set; } - - /// - /// Creates a new tenant. - /// - protected AbpTenant() - { - IsActive = true; - } - - /// - /// Creates a new tenant. - /// - /// UNIQUE name of this Tenant - /// Display name of the Tenant - protected AbpTenant(string tenancyName, string name) - : this() - { - TenancyName = tenancyName; - Name = name; - } - } -} +using System.ComponentModel.DataAnnotations; +using Abp.Application.Editions; +using Abp.Authorization.Users; +using Abp.Domain.Entities; +using Abp.Domain.Entities.Auditing; + +namespace Abp.MultiTenancy +{ + /// + /// Represents a Tenant of the application. + /// + public abstract class AbpTenant : AbpTenantBase, IFullAudited + where TUser : AbpUserBase + { + /// + /// "Default". + /// + public const string DefaultTenantName = "Default"; + + /// + /// "^[a-zA-Z][a-zA-Z0-9_-]{1,}$". + /// + public const string TenancyNameRegex = "^[a-zA-Z][a-zA-Z0-9_-]{1,}$"; + + /// + /// Max length of the property. + /// + public const int MaxNameLength = 128; + + /// + /// Current of the Tenant. + /// + public virtual Edition Edition { get; set; } + public virtual int? EditionId { get; set; } + + /// + /// Display name of the Tenant. + /// + [Required] + [StringLength(MaxNameLength)] + public virtual string Name { get; set; } + + /// + /// Reference to the creator user of this entity. + /// + public virtual TUser CreatorUser { get; set; } + + /// + /// Reference to the last modifier user of this entity. + /// + public virtual TUser LastModifierUser { get; set; } + + /// + /// Reference to the deleter user of this entity. + /// + public virtual TUser DeleterUser { get; set; } + + /// + /// Creates a new tenant. + /// + protected AbpTenant() + { + IsActive = true; + } + + /// + /// Creates a new tenant. + /// + /// UNIQUE name of this Tenant + /// Display name of the Tenant + protected AbpTenant(string tenancyName, string name) + : this() + { + TenancyName = tenancyName; + Name = name; + } + } +} diff --git a/src/Abp.Zero/MultiTenancy/AbpTenantBase.cs b/src/Abp.Zero.Common/MultiTenancy/AbpTenantBase.cs similarity index 84% rename from src/Abp.Zero/MultiTenancy/AbpTenantBase.cs rename to src/Abp.Zero.Common/MultiTenancy/AbpTenantBase.cs index 2e035f0f..e5937ab0 100644 --- a/src/Abp.Zero/MultiTenancy/AbpTenantBase.cs +++ b/src/Abp.Zero.Common/MultiTenancy/AbpTenantBase.cs @@ -1,5 +1,6 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; +using Abp.Domain.Entities; using Abp.Domain.Entities.Auditing; using Abp.Runtime.Security; @@ -10,7 +11,7 @@ namespace Abp.MultiTenancy /// [Table("AbpTenants")] [MultiTenancySide(MultiTenancySides.Host)] - public abstract class AbpTenantBase : FullAuditedEntity + public abstract class AbpTenantBase : FullAuditedEntity, IPassivable { /// /// Max length of the property. @@ -37,5 +38,11 @@ public abstract class AbpTenantBase : FullAuditedEntity /// [StringLength(MaxConnectionStringLength)] public virtual string ConnectionString { get; set; } + + /// + /// Is this tenant active? + /// If as tenant is not active, no user of this tenant can use the application. + /// + public virtual bool IsActive { get; set; } } } \ No newline at end of file diff --git a/src/Abp.Zero/MultiTenancy/AbpTenantManager.cs b/src/Abp.Zero.Common/MultiTenancy/AbpTenantManager.cs similarity index 81% rename from src/Abp.Zero/MultiTenancy/AbpTenantManager.cs rename to src/Abp.Zero.Common/MultiTenancy/AbpTenantManager.cs index 31f2c853..c5fb2872 100644 --- a/src/Abp.Zero/MultiTenancy/AbpTenantManager.cs +++ b/src/Abp.Zero.Common/MultiTenancy/AbpTenantManager.cs @@ -1,261 +1,247 @@ -using System.Collections.Generic; -using System.Linq; -using System.Text.RegularExpressions; -using System.Threading.Tasks; -using Abp.Application.Editions; -using Abp.Application.Features; -using Abp.Authorization.Users; -using Abp.Collections.Extensions; -using Abp.Domain.Repositories; -using Abp.Domain.Services; -using Abp.Domain.Uow; -using Abp.Events.Bus.Entities; -using Abp.Events.Bus.Handlers; -using Abp.IdentityFramework; -using Abp.Localization; -using Abp.Runtime.Caching; -using Abp.Zero; -using Microsoft.AspNet.Identity; - -namespace Abp.MultiTenancy -{ - /// - /// Tenant manager. - /// Implements domain logic for . - /// - /// Type of the application Tenant - /// Type of the application User - public abstract class AbpTenantManager : IDomainService, - IEventHandler>, - IEventHandler> - where TTenant : AbpTenant - where TUser : AbpUser - { - public AbpEditionManager EditionManager { get; set; } - - public ILocalizationManager LocalizationManager { get; set; } - - public ICacheManager CacheManager { get; set; } - - public IFeatureManager FeatureManager { get; set; } - - protected IRepository TenantRepository { get; set; } - - protected IRepository TenantFeatureRepository { get; set; } - - private readonly IAbpZeroFeatureValueStore _featureValueStore; - - protected AbpTenantManager( - IRepository tenantRepository, - IRepository tenantFeatureRepository, - AbpEditionManager editionManager, - IAbpZeroFeatureValueStore featureValueStore) - { - _featureValueStore = featureValueStore; - TenantRepository = tenantRepository; - TenantFeatureRepository = tenantFeatureRepository; - EditionManager = editionManager; - LocalizationManager = NullLocalizationManager.Instance; - } - - public virtual IQueryable Tenants { get { return TenantRepository.GetAll(); } } - - public virtual async Task CreateAsync(TTenant tenant) - { - if (await TenantRepository.FirstOrDefaultAsync(t => t.TenancyName == tenant.TenancyName) != null) - { - return AbpIdentityResult.Failed(string.Format(L("TenancyNameIsAlreadyTaken"), tenant.TenancyName)); - } - - var validationResult = await ValidateTenantAsync(tenant); - if (!validationResult.Succeeded) - { - return validationResult; - } - - await TenantRepository.InsertAsync(tenant); - return IdentityResult.Success; - } - - public async Task UpdateAsync(TTenant tenant) - { - if (await TenantRepository.FirstOrDefaultAsync(t => t.TenancyName == tenant.TenancyName && t.Id != tenant.Id) != null) - { - return AbpIdentityResult.Failed(string.Format(L("TenancyNameIsAlreadyTaken"), tenant.TenancyName)); - } - - await TenantRepository.UpdateAsync(tenant); - return IdentityResult.Success; - } - - public virtual async Task FindByIdAsync(int id) - { - return await TenantRepository.FirstOrDefaultAsync(id); - } - - public virtual async Task GetByIdAsync(int id) - { - var tenant = await FindByIdAsync(id); - if (tenant == null) - { - throw new AbpException("There is no tenant with id: " + id); - } - - return tenant; - } - - public virtual Task FindByTenancyNameAsync(string tenancyName) - { - return TenantRepository.FirstOrDefaultAsync(t => t.TenancyName == tenancyName); - } - - public virtual async Task DeleteAsync(TTenant tenant) - { - await TenantRepository.DeleteAsync(tenant); - return IdentityResult.Success; - } - - public Task GetFeatureValueOrNullAsync(int tenantId, string featureName) - { - return _featureValueStore.GetValueOrNullAsync(tenantId, featureName); - } - - public virtual async Task> GetFeatureValuesAsync(int tenantId) - { - var values = new List(); - - foreach (var feature in FeatureManager.GetAll()) - { - values.Add(new NameValue(feature.Name, await GetFeatureValueOrNullAsync(tenantId, feature.Name) ?? feature.DefaultValue)); - } - - return values; - } - - public virtual async Task SetFeatureValuesAsync(int tenantId, params NameValue[] values) - { - if (values.IsNullOrEmpty()) - { - return; - } - - foreach (var value in values) - { - await SetFeatureValueAsync(tenantId, value.Name, value.Value); - } - } - - [UnitOfWork] - public virtual async Task SetFeatureValueAsync(int tenantId, string featureName, string value) - { - await SetFeatureValueAsync(await GetByIdAsync(tenantId), featureName, value); - } - - [UnitOfWork] - public virtual async Task SetFeatureValueAsync(TTenant tenant, string featureName, string value) - { - //No need to change if it's already equals to the current value - if (await GetFeatureValueOrNullAsync(tenant.Id, featureName) == value) - { - return; - } - - //Get the current feature setting - var currentSetting = await TenantFeatureRepository.FirstOrDefaultAsync(f => f.TenantId == tenant.Id && f.Name == featureName); - - //Get the feature - var feature = FeatureManager.GetOrNull(featureName); - if (feature == null) - { - if (currentSetting != null) - { - await TenantFeatureRepository.DeleteAsync(currentSetting); - } - - return; - } - - //Determine default value - var defaultValue = tenant.EditionId.HasValue - ? (await EditionManager.GetFeatureValueOrNullAsync(tenant.EditionId.Value, featureName) ?? feature.DefaultValue) - : feature.DefaultValue; - - //No need to store value if it's default - if (value == defaultValue) - { - if (currentSetting != null) - { - await TenantFeatureRepository.DeleteAsync(currentSetting); - } - - return; - } - - //Insert/update the feature value - if (currentSetting == null) - { - await TenantFeatureRepository.InsertAsync(new TenantFeatureSetting(tenant.Id, featureName, value)); - } - else - { - currentSetting.Value = value; - } - } - - /// - /// Resets all custom feature settings for a tenant. - /// Tenant will have features according to it's edition. - /// - /// Tenant Id - public async Task ResetAllFeaturesAsync(int tenantId) - { - await TenantFeatureRepository.DeleteAsync(f => f.TenantId == tenantId); - } - - protected virtual async Task ValidateTenantAsync(TTenant tenant) - { - var nameValidationResult = await ValidateTenancyNameAsync(tenant.TenancyName); - if (!nameValidationResult.Succeeded) - { - return nameValidationResult; - } - - return IdentityResult.Success; - } - - protected virtual async Task ValidateTenancyNameAsync(string tenancyName) - { - if (!Regex.IsMatch(tenancyName, AbpTenant.TenancyNameRegex)) - { - return AbpIdentityResult.Failed(L("InvalidTenancyName")); - } - - return IdentityResult.Success; - } - - private string L(string name) - { - return LocalizationManager.GetString(AbpZeroConsts.LocalizationSourceName, name); - } - - public void HandleEvent(EntityChangedEventData eventData) - { - if (eventData.Entity.IsTransient()) - { - return; - } - - CacheManager.GetTenantFeatureCache().Remove(eventData.Entity.Id); - } - - [UnitOfWork] - public virtual void HandleEvent(EntityDeletedEventData eventData) - { - var relatedTenants = TenantRepository.GetAllList(t => t.EditionId == eventData.Entity.Id); - foreach (var relatedTenant in relatedTenants) - { - relatedTenant.EditionId = null; - } - } - } +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using Abp.Application.Editions; +using Abp.Application.Features; +using Abp.Authorization.Users; +using Abp.Collections.Extensions; +using Abp.Domain.Repositories; +using Abp.Domain.Services; +using Abp.Domain.Uow; +using Abp.Events.Bus.Entities; +using Abp.Events.Bus.Handlers; +using Abp.Localization; +using Abp.Runtime.Caching; +using Abp.UI; +using Abp.Zero; + +namespace Abp.MultiTenancy +{ + /// + /// Tenant manager. + /// Implements domain logic for . + /// + /// Type of the application Tenant + /// Type of the application User + public class AbpTenantManager : IDomainService, + IEventHandler>, + IEventHandler> + where TTenant : AbpTenant + where TUser : AbpUserBase + { + public AbpEditionManager EditionManager { get; set; } + + public ILocalizationManager LocalizationManager { get; set; } + + public ICacheManager CacheManager { get; set; } + + public IFeatureManager FeatureManager { get; set; } + + protected IRepository TenantRepository { get; set; } + + protected IRepository TenantFeatureRepository { get; set; } + + private readonly IAbpZeroFeatureValueStore _featureValueStore; + + public AbpTenantManager( + IRepository tenantRepository, + IRepository tenantFeatureRepository, + AbpEditionManager editionManager, + IAbpZeroFeatureValueStore featureValueStore) + { + _featureValueStore = featureValueStore; + TenantRepository = tenantRepository; + TenantFeatureRepository = tenantFeatureRepository; + EditionManager = editionManager; + LocalizationManager = NullLocalizationManager.Instance; + } + + public virtual IQueryable Tenants { get { return TenantRepository.GetAll(); } } + + public virtual async Task CreateAsync(TTenant tenant) + { + await ValidateTenantAsync(tenant); + + if (await TenantRepository.FirstOrDefaultAsync(t => t.TenancyName == tenant.TenancyName) != null) + { + throw new UserFriendlyException(string.Format(L("TenancyNameIsAlreadyTaken"), tenant.TenancyName)); + } + + await TenantRepository.InsertAsync(tenant); + } + + public async Task UpdateAsync(TTenant tenant) + { + if (await TenantRepository.FirstOrDefaultAsync(t => t.TenancyName == tenant.TenancyName && t.Id != tenant.Id) != null) + { + throw new UserFriendlyException(string.Format(L("TenancyNameIsAlreadyTaken"), tenant.TenancyName)); + } + + await TenantRepository.UpdateAsync(tenant); + } + + public virtual async Task FindByIdAsync(int id) + { + return await TenantRepository.FirstOrDefaultAsync(id); + } + + public virtual async Task GetByIdAsync(int id) + { + var tenant = await FindByIdAsync(id); + if (tenant == null) + { + throw new AbpException("There is no tenant with id: " + id); + } + + return tenant; + } + + public virtual Task FindByTenancyNameAsync(string tenancyName) + { + return TenantRepository.FirstOrDefaultAsync(t => t.TenancyName == tenancyName); + } + + public virtual async Task DeleteAsync(TTenant tenant) + { + await TenantRepository.DeleteAsync(tenant); + } + + public Task GetFeatureValueOrNullAsync(int tenantId, string featureName) + { + return _featureValueStore.GetValueOrNullAsync(tenantId, featureName); + } + + public virtual async Task> GetFeatureValuesAsync(int tenantId) + { + var values = new List(); + + foreach (var feature in FeatureManager.GetAll()) + { + values.Add(new NameValue(feature.Name, await GetFeatureValueOrNullAsync(tenantId, feature.Name) ?? feature.DefaultValue)); + } + + return values; + } + + public virtual async Task SetFeatureValuesAsync(int tenantId, params NameValue[] values) + { + if (values.IsNullOrEmpty()) + { + return; + } + + foreach (var value in values) + { + await SetFeatureValueAsync(tenantId, value.Name, value.Value); + } + } + + [UnitOfWork] + public virtual async Task SetFeatureValueAsync(int tenantId, string featureName, string value) + { + await SetFeatureValueAsync(await GetByIdAsync(tenantId), featureName, value); + } + + [UnitOfWork] + public virtual async Task SetFeatureValueAsync(TTenant tenant, string featureName, string value) + { + //No need to change if it's already equals to the current value + if (await GetFeatureValueOrNullAsync(tenant.Id, featureName) == value) + { + return; + } + + //Get the current feature setting + var currentSetting = await TenantFeatureRepository.FirstOrDefaultAsync(f => f.TenantId == tenant.Id && f.Name == featureName); + + //Get the feature + var feature = FeatureManager.GetOrNull(featureName); + if (feature == null) + { + if (currentSetting != null) + { + await TenantFeatureRepository.DeleteAsync(currentSetting); + } + + return; + } + + //Determine default value + var defaultValue = tenant.EditionId.HasValue + ? (await EditionManager.GetFeatureValueOrNullAsync(tenant.EditionId.Value, featureName) ?? feature.DefaultValue) + : feature.DefaultValue; + + //No need to store value if it's default + if (value == defaultValue) + { + if (currentSetting != null) + { + await TenantFeatureRepository.DeleteAsync(currentSetting); + } + + return; + } + + //Insert/update the feature value + if (currentSetting == null) + { + await TenantFeatureRepository.InsertAsync(new TenantFeatureSetting(tenant.Id, featureName, value)); + } + else + { + currentSetting.Value = value; + } + } + + /// + /// Resets all custom feature settings for a tenant. + /// Tenant will have features according to it's edition. + /// + /// Tenant Id + public async Task ResetAllFeaturesAsync(int tenantId) + { + await TenantFeatureRepository.DeleteAsync(f => f.TenantId == tenantId); + } + + protected virtual async Task ValidateTenantAsync(TTenant tenant) + { + await ValidateTenancyNameAsync(tenant.TenancyName); + } + + protected virtual Task ValidateTenancyNameAsync(string tenancyName) + { + if (!Regex.IsMatch(tenancyName, AbpTenant.TenancyNameRegex)) + { + throw new UserFriendlyException(L("InvalidTenancyName")); + } + + return Task.FromResult(0); + } + + private string L(string name) + { + return LocalizationManager.GetString(AbpZeroConsts.LocalizationSourceName, name); + } + + public void HandleEvent(EntityChangedEventData eventData) + { + if (eventData.Entity.IsTransient()) + { + return; + } + + CacheManager.GetTenantFeatureCache().Remove(eventData.Entity.Id); + } + + [UnitOfWork] + public virtual void HandleEvent(EntityDeletedEventData eventData) + { + var relatedTenants = TenantRepository.GetAllList(t => t.EditionId == eventData.Entity.Id); + foreach (var relatedTenant in relatedTenants) + { + relatedTenant.EditionId = null; + } + } + } } \ No newline at end of file diff --git a/src/Abp.Zero/MultiTenancy/AbpTenantManagerExtensions.cs b/src/Abp.Zero.Common/MultiTenancy/AbpTenantManagerExtensions.cs similarity index 72% rename from src/Abp.Zero/MultiTenancy/AbpTenantManagerExtensions.cs rename to src/Abp.Zero.Common/MultiTenancy/AbpTenantManagerExtensions.cs index 8f6bb93d..1873e755 100644 --- a/src/Abp.Zero/MultiTenancy/AbpTenantManagerExtensions.cs +++ b/src/Abp.Zero.Common/MultiTenancy/AbpTenantManagerExtensions.cs @@ -1,92 +1,91 @@ using System.Collections.Generic; using Abp.Authorization.Users; using Abp.Threading; -using Microsoft.AspNet.Identity; namespace Abp.MultiTenancy { public static class AbpTenantManagerExtensions { - public static IdentityResult Create(this AbpTenantManager tenantManager, TTenant tenant) + public static void Create(this AbpTenantManager tenantManager, TTenant tenant) where TTenant : AbpTenant - where TUser : AbpUser + where TUser : AbpUserBase { - return AsyncHelper.RunSync(() => tenantManager.CreateAsync(tenant)); + AsyncHelper.RunSync(() => tenantManager.CreateAsync(tenant)); } - public static IdentityResult Update(this AbpTenantManager tenantManager, TTenant tenant) + public static void Update(this AbpTenantManager tenantManager, TTenant tenant) where TTenant : AbpTenant - where TUser : AbpUser + where TUser : AbpUserBase { - return AsyncHelper.RunSync(() => tenantManager.UpdateAsync(tenant)); + AsyncHelper.RunSync(() => tenantManager.UpdateAsync(tenant)); } public static TTenant FindById(this AbpTenantManager tenantManager, int id) where TTenant : AbpTenant - where TUser : AbpUser + where TUser : AbpUserBase { return AsyncHelper.RunSync(() => tenantManager.FindByIdAsync(id)); } public static TTenant GetById(this AbpTenantManager tenantManager, int id) where TTenant : AbpTenant - where TUser : AbpUser + where TUser : AbpUserBase { return AsyncHelper.RunSync(() => tenantManager.GetByIdAsync(id)); } public static TTenant FindByTenancyName(this AbpTenantManager tenantManager, string tenancyName) where TTenant : AbpTenant - where TUser : AbpUser + where TUser : AbpUserBase { return AsyncHelper.RunSync(() => tenantManager.FindByTenancyNameAsync(tenancyName)); } - public static IdentityResult Delete(this AbpTenantManager tenantManager, TTenant tenant) + public static void Delete(this AbpTenantManager tenantManager, TTenant tenant) where TTenant : AbpTenant - where TUser : AbpUser + where TUser : AbpUserBase { - return AsyncHelper.RunSync(() => tenantManager.DeleteAsync(tenant)); + AsyncHelper.RunSync(() => tenantManager.DeleteAsync(tenant)); } public static string GetFeatureValueOrNull(this AbpTenantManager tenantManager, int tenantId, string featureName) where TTenant : AbpTenant - where TUser : AbpUser + where TUser : AbpUserBase { return AsyncHelper.RunSync(() => tenantManager.GetFeatureValueOrNullAsync(tenantId, featureName)); } public static IReadOnlyList GetFeatureValues(this AbpTenantManager tenantManager, int tenantId) where TTenant : AbpTenant - where TUser : AbpUser + where TUser : AbpUserBase { return AsyncHelper.RunSync(() => tenantManager.GetFeatureValuesAsync(tenantId)); } public static void SetFeatureValues(this AbpTenantManager tenantManager, int tenantId, params NameValue[] values) where TTenant : AbpTenant - where TUser : AbpUser + where TUser : AbpUserBase { AsyncHelper.RunSync(() => tenantManager.SetFeatureValuesAsync(tenantId, values)); } public static void SetFeatureValue(this AbpTenantManager tenantManager, int tenantId, string featureName, string value) where TTenant : AbpTenant - where TUser : AbpUser + where TUser : AbpUserBase { AsyncHelper.RunSync(() => tenantManager.SetFeatureValueAsync(tenantId, featureName, value)); } public static void SetFeatureValue(this AbpTenantManager tenantManager, TTenant tenant, string featureName, string value) where TTenant : AbpTenant - where TUser : AbpUser + where TUser : AbpUserBase { AsyncHelper.RunSync(() => tenantManager.SetFeatureValueAsync(tenant, featureName, value)); } public static void ResetAllFeatures(this AbpTenantManager tenantManager, int tenantId) where TTenant : AbpTenant - where TUser : AbpUser + where TUser : AbpUserBase { AsyncHelper.RunSync(() => tenantManager.ResetAllFeaturesAsync(tenantId)); } diff --git a/src/Abp.Zero/MultiTenancy/DbPerTenantConnectionStringResolveArgs.cs b/src/Abp.Zero.Common/MultiTenancy/DbPerTenantConnectionStringResolveArgs.cs similarity index 100% rename from src/Abp.Zero/MultiTenancy/DbPerTenantConnectionStringResolveArgs.cs rename to src/Abp.Zero.Common/MultiTenancy/DbPerTenantConnectionStringResolveArgs.cs diff --git a/src/Abp.Zero/MultiTenancy/IAbpZeroDbMigrator.cs b/src/Abp.Zero.Common/MultiTenancy/IAbpZeroDbMigrator.cs similarity index 100% rename from src/Abp.Zero/MultiTenancy/IAbpZeroDbMigrator.cs rename to src/Abp.Zero.Common/MultiTenancy/IAbpZeroDbMigrator.cs diff --git a/src/Abp.Zero/MultiTenancy/IDbPerTenantConnectionStringResolver.cs b/src/Abp.Zero.Common/MultiTenancy/IDbPerTenantConnectionStringResolver.cs similarity index 100% rename from src/Abp.Zero/MultiTenancy/IDbPerTenantConnectionStringResolver.cs rename to src/Abp.Zero.Common/MultiTenancy/IDbPerTenantConnectionStringResolver.cs diff --git a/src/Abp.Zero/MultiTenancy/ITenantCache.cs b/src/Abp.Zero.Common/MultiTenancy/ITenantCache.cs similarity index 100% rename from src/Abp.Zero/MultiTenancy/ITenantCache.cs rename to src/Abp.Zero.Common/MultiTenancy/ITenantCache.cs diff --git a/src/Abp.Zero/MultiTenancy/TenantCache.cs b/src/Abp.Zero.Common/MultiTenancy/TenantCache.cs similarity index 90% rename from src/Abp.Zero/MultiTenancy/TenantCache.cs rename to src/Abp.Zero.Common/MultiTenancy/TenantCache.cs index 0562dd25..70dabed7 100644 --- a/src/Abp.Zero/MultiTenancy/TenantCache.cs +++ b/src/Abp.Zero.Common/MultiTenancy/TenantCache.cs @@ -10,7 +10,7 @@ namespace Abp.MultiTenancy { public class TenantCache : ITenantCache, IEventHandler> where TTenant : AbpTenant - where TUser : AbpUser + where TUser : AbpUserBase { private readonly ICacheManager _cacheManager; private readonly IRepository _tenantRepository; @@ -52,8 +52,12 @@ public virtual TenantCacheItem Get(string tenancyName) public virtual TenantCacheItem GetOrNull(string tenancyName) { - var tenantId = _cacheManager.GetTenantByNameCache() - .Get(tenancyName, () => GetTenantOrNull(tenancyName)?.Id); + var tenantId = _cacheManager + .GetTenantByNameCache() + .Get( + tenancyName.ToLowerInvariant(), + () => GetTenantOrNull(tenancyName)?.Id + ); if (tenantId == null) { @@ -121,8 +125,8 @@ public void HandleEvent(EntityChangedEventData eventData) .GetTenantByNameCache() .Remove( existingCacheItem != null - ? existingCacheItem.TenancyName - : eventData.Entity.TenancyName + ? existingCacheItem.TenancyName.ToLowerInvariant() + : eventData.Entity.TenancyName.ToLowerInvariant() ); _cacheManager diff --git a/src/Abp.Zero/MultiTenancy/TenantCacheItem.cs b/src/Abp.Zero.Common/MultiTenancy/TenantCacheItem.cs similarity index 100% rename from src/Abp.Zero/MultiTenancy/TenantCacheItem.cs rename to src/Abp.Zero.Common/MultiTenancy/TenantCacheItem.cs diff --git a/src/Abp.Zero/MultiTenancy/TenantCacheManagerExtensions.cs b/src/Abp.Zero.Common/MultiTenancy/TenantCacheManagerExtensions.cs similarity index 100% rename from src/Abp.Zero/MultiTenancy/TenantCacheManagerExtensions.cs rename to src/Abp.Zero.Common/MultiTenancy/TenantCacheManagerExtensions.cs diff --git a/src/Abp.Zero/MultiTenancy/TenantFeatureCacheItem.cs b/src/Abp.Zero.Common/MultiTenancy/TenantFeatureCacheItem.cs similarity index 96% rename from src/Abp.Zero/MultiTenancy/TenantFeatureCacheItem.cs rename to src/Abp.Zero.Common/MultiTenancy/TenantFeatureCacheItem.cs index 1e5ca843..10f6f50c 100644 --- a/src/Abp.Zero/MultiTenancy/TenantFeatureCacheItem.cs +++ b/src/Abp.Zero.Common/MultiTenancy/TenantFeatureCacheItem.cs @@ -1,35 +1,35 @@ -using System; -using System.Collections.Generic; - -namespace Abp.MultiTenancy -{ - /// - /// Used to store features of a Tenant in the cache. - /// - [Serializable] - public class TenantFeatureCacheItem - { - /// - /// The cache store name. - /// - public const string CacheStoreName = "AbpZeroTenantFeatures"; - - /// - /// Edition of the tenant. - /// - public int? EditionId { get; set; } - - /// - /// Feature values. - /// - public IDictionary FeatureValues { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - public TenantFeatureCacheItem() - { - FeatureValues = new Dictionary(); - } - } +using System; +using System.Collections.Generic; + +namespace Abp.MultiTenancy +{ + /// + /// Used to store features of a Tenant in the cache. + /// + [Serializable] + public class TenantFeatureCacheItem + { + /// + /// The cache store name. + /// + public const string CacheStoreName = "AbpZeroTenantFeatures"; + + /// + /// Edition of the tenant. + /// + public int? EditionId { get; set; } + + /// + /// Feature values. + /// + public IDictionary FeatureValues { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + public TenantFeatureCacheItem() + { + FeatureValues = new Dictionary(); + } + } } \ No newline at end of file diff --git a/src/Abp.Zero/MultiTenancy/TenantFeatureCacheItemInvalidator.cs b/src/Abp.Zero.Common/MultiTenancy/TenantFeatureCacheItemInvalidator.cs similarity index 97% rename from src/Abp.Zero/MultiTenancy/TenantFeatureCacheItemInvalidator.cs rename to src/Abp.Zero.Common/MultiTenancy/TenantFeatureCacheItemInvalidator.cs index 4f66a1a9..a8ea2499 100644 --- a/src/Abp.Zero/MultiTenancy/TenantFeatureCacheItemInvalidator.cs +++ b/src/Abp.Zero.Common/MultiTenancy/TenantFeatureCacheItemInvalidator.cs @@ -1,31 +1,31 @@ -using Abp.Dependency; -using Abp.Events.Bus.Entities; -using Abp.Events.Bus.Handlers; -using Abp.Runtime.Caching; - -namespace Abp.MultiTenancy -{ - /// - /// This class handles related events and invalidated tenant feature cache items if needed. - /// - public class TenantFeatureCacheItemInvalidator : - IEventHandler>, - ITransientDependency - { - private readonly ICacheManager _cacheManager; - - /// - /// Initializes a new instance of the class. - /// - /// The cache manager. - public TenantFeatureCacheItemInvalidator(ICacheManager cacheManager) - { - _cacheManager = cacheManager; - } - - public void HandleEvent(EntityChangedEventData eventData) - { - _cacheManager.GetTenantFeatureCache().Remove(eventData.Entity.TenantId); - } - } +using Abp.Dependency; +using Abp.Events.Bus.Entities; +using Abp.Events.Bus.Handlers; +using Abp.Runtime.Caching; + +namespace Abp.MultiTenancy +{ + /// + /// This class handles related events and invalidated tenant feature cache items if needed. + /// + public class TenantFeatureCacheItemInvalidator : + IEventHandler>, + ITransientDependency + { + private readonly ICacheManager _cacheManager; + + /// + /// Initializes a new instance of the class. + /// + /// The cache manager. + public TenantFeatureCacheItemInvalidator(ICacheManager cacheManager) + { + _cacheManager = cacheManager; + } + + public void HandleEvent(EntityChangedEventData eventData) + { + _cacheManager.GetTenantFeatureCache().Remove(eventData.Entity.TenantId); + } + } } \ No newline at end of file diff --git a/src/Abp.Zero/MultiTenancy/TenantFeatureSetting.cs b/src/Abp.Zero.Common/MultiTenancy/TenantFeatureSetting.cs similarity index 96% rename from src/Abp.Zero/MultiTenancy/TenantFeatureSetting.cs rename to src/Abp.Zero.Common/MultiTenancy/TenantFeatureSetting.cs index 41e11faf..a832aea6 100644 --- a/src/Abp.Zero/MultiTenancy/TenantFeatureSetting.cs +++ b/src/Abp.Zero.Common/MultiTenancy/TenantFeatureSetting.cs @@ -1,36 +1,36 @@ -using Abp.Application.Features; -using Abp.Domain.Entities; - -namespace Abp.MultiTenancy -{ - /// - /// Feature setting for a Tenant (). - /// - public class TenantFeatureSetting : FeatureSetting, IMustHaveTenant - { - /// - /// Tenant's Id. - /// - public virtual int TenantId { get; set; } - - /// - /// Initializes a new instance of the class. - /// - public TenantFeatureSetting() - { - - } - - /// - /// Initializes a new instance of the class. - /// - /// The tenant identifier. - /// Feature name. - /// Feature value. - public TenantFeatureSetting(int tenantId, string name, string value) - :base(name, value) - { - TenantId = tenantId; - } - } +using Abp.Application.Features; +using Abp.Domain.Entities; + +namespace Abp.MultiTenancy +{ + /// + /// Feature setting for a Tenant (). + /// + public class TenantFeatureSetting : FeatureSetting, IMustHaveTenant + { + /// + /// Tenant's Id. + /// + public virtual int TenantId { get; set; } + + /// + /// Initializes a new instance of the class. + /// + public TenantFeatureSetting() + { + + } + + /// + /// Initializes a new instance of the class. + /// + /// The tenant identifier. + /// Feature name. + /// Feature value. + public TenantFeatureSetting(int tenantId, string name, string value) + :base(name, value) + { + TenantId = tenantId; + } + } } \ No newline at end of file diff --git a/src/Abp.Zero.Common/MultiTenancy/TenantStore.cs b/src/Abp.Zero.Common/MultiTenancy/TenantStore.cs new file mode 100644 index 00000000..5d91c4cb --- /dev/null +++ b/src/Abp.Zero.Common/MultiTenancy/TenantStore.cs @@ -0,0 +1,34 @@ +namespace Abp.MultiTenancy +{ + public class TenantStore : ITenantStore + { + private readonly ITenantCache _tenantCache; + + public TenantStore(ITenantCache tenantCache) + { + _tenantCache = tenantCache; + } + + public TenantInfo Find(int tenantId) + { + var tenant = _tenantCache.GetOrNull(tenantId); + if (tenant == null) + { + return null; + } + + return new TenantInfo(tenant.Id, tenant.TenancyName); + } + + public TenantInfo Find(string tenancyName) + { + var tenant = _tenantCache.GetOrNull(tenancyName); + if (tenant == null) + { + return null; + } + + return new TenantInfo(tenant.Id, tenant.TenancyName); + } + } +} diff --git a/src/Abp.Zero/Notifications/NotificationStore.cs b/src/Abp.Zero.Common/Notifications/NotificationStore.cs similarity index 89% rename from src/Abp.Zero/Notifications/NotificationStore.cs rename to src/Abp.Zero.Common/Notifications/NotificationStore.cs index 4b390bce..210692e2 100644 --- a/src/Abp.Zero/Notifications/NotificationStore.cs +++ b/src/Abp.Zero.Common/Notifications/NotificationStore.cs @@ -73,11 +73,11 @@ public virtual async Task InsertNotificationAsync(NotificationInfo notification) } [UnitOfWork] - public virtual Task GetNotificationOrNullAsync(Guid notificationId) + public virtual async Task GetNotificationOrNullAsync(Guid notificationId) { using (_unitOfWorkManager.Current.SetTenantId(null)) { - return _notificationRepository.FirstOrDefaultAsync(notificationId); + return await _notificationRepository.FirstOrDefaultAsync(notificationId); } } @@ -118,23 +118,23 @@ public virtual async Task> GetSubscriptionsAs } [UnitOfWork] - public virtual Task> GetSubscriptionsAsync(UserIdentifier user) + public virtual async Task> GetSubscriptionsAsync(UserIdentifier user) { using (_unitOfWorkManager.Current.SetTenantId(user.TenantId)) { - return _notificationSubscriptionRepository.GetAllListAsync(s => s.UserId == user.UserId); + return await _notificationSubscriptionRepository.GetAllListAsync(s => s.UserId == user.UserId); } } [UnitOfWork] - protected virtual Task> GetSubscriptionsAsync(int? tenantId, string notificationName, string entityTypeName, string entityId) + protected virtual async Task> GetSubscriptionsAsync(int? tenantId, string notificationName, string entityTypeName, string entityId) { using (_unitOfWorkManager.Current.SetTenantId(tenantId)) { - return _notificationSubscriptionRepository.GetAllListAsync(s => - s.NotificationName == notificationName && - s.EntityTypeName == entityTypeName && - s.EntityId == entityId + return await _notificationSubscriptionRepository.GetAllListAsync(s => + s.NotificationName == notificationName && + s.EntityTypeName == entityTypeName && + s.EntityId == entityId ); } } @@ -227,11 +227,11 @@ orderby tenantNotificationInfo.CreationTime descending } [UnitOfWork] - public virtual Task GetUserNotificationCountAsync(UserIdentifier user, UserNotificationState? state = null) + public virtual async Task GetUserNotificationCountAsync(UserIdentifier user, UserNotificationState? state = null) { using (_unitOfWorkManager.Current.SetTenantId(user.TenantId)) { - return _userNotificationRepository.CountAsync(un => un.UserId == user.UserId && (state == null || un.State == state.Value)); + return await _userNotificationRepository.CountAsync(un => un.UserId == user.UserId && (state == null || un.State == state.Value)); } } @@ -256,11 +256,11 @@ join tenantNotificationInfo in _tenantNotificationRepository.GetAll() on userNot } [UnitOfWork] - public virtual Task InsertTenantNotificationAsync(TenantNotificationInfo tenantNotificationInfo) + public virtual async Task InsertTenantNotificationAsync(TenantNotificationInfo tenantNotificationInfo) { using (_unitOfWorkManager.Current.SetTenantId(tenantNotificationInfo.TenantId)) { - return _tenantNotificationRepository.InsertAsync(tenantNotificationInfo); + await _tenantNotificationRepository.InsertAsync(tenantNotificationInfo); } } diff --git a/src/Abp.Zero/Organizations/IMayHaveOrganizationUnit.cs b/src/Abp.Zero.Common/Organizations/IMayHaveOrganizationUnit.cs similarity index 100% rename from src/Abp.Zero/Organizations/IMayHaveOrganizationUnit.cs rename to src/Abp.Zero.Common/Organizations/IMayHaveOrganizationUnit.cs diff --git a/src/Abp.Zero/Organizations/IMustHaveOrganizationUnit.cs b/src/Abp.Zero.Common/Organizations/IMustHaveOrganizationUnit.cs similarity index 100% rename from src/Abp.Zero/Organizations/IMustHaveOrganizationUnit.cs rename to src/Abp.Zero.Common/Organizations/IMustHaveOrganizationUnit.cs diff --git a/src/Abp.Zero/Organizations/IOrganizationUnitSettings.cs b/src/Abp.Zero.Common/Organizations/IOrganizationUnitSettings.cs similarity index 100% rename from src/Abp.Zero/Organizations/IOrganizationUnitSettings.cs rename to src/Abp.Zero.Common/Organizations/IOrganizationUnitSettings.cs diff --git a/src/Abp.Zero/Organizations/OrganizationUnit.cs b/src/Abp.Zero.Common/Organizations/OrganizationUnit.cs similarity index 100% rename from src/Abp.Zero/Organizations/OrganizationUnit.cs rename to src/Abp.Zero.Common/Organizations/OrganizationUnit.cs diff --git a/src/Abp.Zero/Organizations/OrganizationUnitManager.cs b/src/Abp.Zero.Common/Organizations/OrganizationUnitManager.cs similarity index 100% rename from src/Abp.Zero/Organizations/OrganizationUnitManager.cs rename to src/Abp.Zero.Common/Organizations/OrganizationUnitManager.cs diff --git a/src/Abp.Zero/Organizations/OrganizationUnitManagerExtensions.cs b/src/Abp.Zero.Common/Organizations/OrganizationUnitManagerExtensions.cs similarity index 100% rename from src/Abp.Zero/Organizations/OrganizationUnitManagerExtensions.cs rename to src/Abp.Zero.Common/Organizations/OrganizationUnitManagerExtensions.cs diff --git a/src/Abp.Zero/Organizations/OrganizationUnitSettings.cs b/src/Abp.Zero.Common/Organizations/OrganizationUnitSettings.cs similarity index 100% rename from src/Abp.Zero/Organizations/OrganizationUnitSettings.cs rename to src/Abp.Zero.Common/Organizations/OrganizationUnitSettings.cs diff --git a/src/Abp.Zero.Common/Properties/AssemblyInfo.cs b/src/Abp.Zero.Common/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..c133eeed --- /dev/null +++ b/src/Abp.Zero.Common/Properties/AssemblyInfo.cs @@ -0,0 +1,4 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Abp.Zero")] +[assembly: InternalsVisibleTo("Abp.ZeroCore")] \ No newline at end of file diff --git a/src/Abp.Zero/Runtime/Caching/AbpZeroCacheManagerExtensions.cs b/src/Abp.Zero.Common/Runtime/Caching/AbpZeroCacheManagerExtensions.cs similarity index 97% rename from src/Abp.Zero/Runtime/Caching/AbpZeroCacheManagerExtensions.cs rename to src/Abp.Zero.Common/Runtime/Caching/AbpZeroCacheManagerExtensions.cs index e8f329cc..954105f5 100644 --- a/src/Abp.Zero/Runtime/Caching/AbpZeroCacheManagerExtensions.cs +++ b/src/Abp.Zero.Common/Runtime/Caching/AbpZeroCacheManagerExtensions.cs @@ -1,30 +1,30 @@ -using Abp.Application.Editions; -using Abp.Authorization.Roles; -using Abp.Authorization.Users; -using Abp.MultiTenancy; - -namespace Abp.Runtime.Caching -{ - public static class AbpZeroCacheManagerExtensions - { - public static ITypedCache GetUserPermissionCache(this ICacheManager cacheManager) - { - return cacheManager.GetCache(UserPermissionCacheItem.CacheStoreName); - } - - public static ITypedCache GetRolePermissionCache(this ICacheManager cacheManager) - { - return cacheManager.GetCache(RolePermissionCacheItem.CacheStoreName); - } - - public static ITypedCache GetTenantFeatureCache(this ICacheManager cacheManager) - { - return cacheManager.GetCache(TenantFeatureCacheItem.CacheStoreName); - } - - public static ITypedCache GetEditionFeatureCache(this ICacheManager cacheManager) - { - return cacheManager.GetCache(EditionfeatureCacheItem.CacheStoreName); - } - } -} +using Abp.Application.Editions; +using Abp.Authorization.Roles; +using Abp.Authorization.Users; +using Abp.MultiTenancy; + +namespace Abp.Runtime.Caching +{ + public static class AbpZeroCacheManagerExtensions + { + public static ITypedCache GetUserPermissionCache(this ICacheManager cacheManager) + { + return cacheManager.GetCache(UserPermissionCacheItem.CacheStoreName); + } + + public static ITypedCache GetRolePermissionCache(this ICacheManager cacheManager) + { + return cacheManager.GetCache(RolePermissionCacheItem.CacheStoreName); + } + + public static ITypedCache GetTenantFeatureCache(this ICacheManager cacheManager) + { + return cacheManager.GetCache(TenantFeatureCacheItem.CacheStoreName); + } + + public static ITypedCache GetEditionFeatureCache(this ICacheManager cacheManager) + { + return cacheManager.GetCache(EditionfeatureCacheItem.CacheStoreName); + } + } +} diff --git a/src/Abp.Zero/Runtime/Session/AbpSessionExtensions.cs b/src/Abp.Zero.Common/Runtime/Session/AbpSessionExtensions.cs similarity index 100% rename from src/Abp.Zero/Runtime/Session/AbpSessionExtensions.cs rename to src/Abp.Zero.Common/Runtime/Session/AbpSessionExtensions.cs diff --git a/src/Abp.Zero.Common/Zero/AbpZeroCommonModule.cs b/src/Abp.Zero.Common/Zero/AbpZeroCommonModule.cs new file mode 100644 index 00000000..9c0ee0e1 --- /dev/null +++ b/src/Abp.Zero.Common/Zero/AbpZeroCommonModule.cs @@ -0,0 +1,106 @@ +using System.Linq; +using System.Reflection; +using Abp.Application.Features; +using Abp.Authorization.Roles; +using Abp.Authorization.Users; +using Abp.Dependency; +using Abp.Localization; +using Abp.Localization.Dictionaries; +using Abp.Localization.Dictionaries.Xml; +using Abp.Modules; +using Abp.MultiTenancy; +using Abp.Reflection; +using Abp.Zero.Configuration; +using Abp.Configuration.Startup; +using Abp.Reflection.Extensions; +using Castle.MicroKernel.Registration; + +namespace Abp.Zero +{ + /// + /// ABP zero core module. + /// + [DependsOn(typeof(AbpKernelModule))] + public class AbpZeroCommonModule : AbpModule + { + public override void PreInitialize() + { + IocManager.RegisterIfNot(); //Registered on services.AddAbpIdentity() for Abp.ZeroCore. + + IocManager.Register(); + IocManager.Register(); + IocManager.Register(); + IocManager.Register(); + + Configuration.ReplaceService(DependencyLifeStyle.Transient); + + Configuration.Settings.Providers.Add(); + + Configuration.Localization.Sources.Add( + new DictionaryBasedLocalizationSource( + AbpZeroConsts.LocalizationSourceName, + new XmlEmbeddedFileLocalizationDictionaryProvider( + typeof(AbpZeroCommonModule).GetAssembly(), "Abp.Zero.Localization.Source" + ))); + + IocManager.IocContainer.Kernel.ComponentRegistered += Kernel_ComponentRegistered; + } + + public override void Initialize() + { + FillMissingEntityTypes(); + + IocManager.Register(DependencyLifeStyle.Transient); + IocManager.RegisterAssemblyByConvention(typeof(AbpZeroCommonModule).GetAssembly()); + + RegisterTenantCache(); + } + + private void Kernel_ComponentRegistered(string key, Castle.MicroKernel.IHandler handler) + { + if (typeof(IAbpZeroFeatureValueStore).IsAssignableFrom(handler.ComponentModel.Implementation) && !IocManager.IsRegistered()) + { + IocManager.IocContainer.Register( + Component.For().ImplementedBy(handler.ComponentModel.Implementation).Named("AbpZeroFeatureValueStore").LifestyleTransient() + ); + } + } + + private void FillMissingEntityTypes() + { + using (var entityTypes = IocManager.ResolveAsDisposable()) + { + if (entityTypes.Object.User != null && + entityTypes.Object.Role != null && + entityTypes.Object.Tenant != null) + { + return; + } + + using (var typeFinder = IocManager.ResolveAsDisposable()) + { + var types = typeFinder.Object.FindAll(); + entityTypes.Object.Tenant = types.FirstOrDefault(t => typeof(AbpTenantBase).IsAssignableFrom(t) && !t.GetTypeInfo().IsAbstract); + entityTypes.Object.Role = types.FirstOrDefault(t => typeof(AbpRoleBase).IsAssignableFrom(t) && !t.GetTypeInfo().IsAbstract); + entityTypes.Object.User = types.FirstOrDefault(t => typeof(AbpUserBase).IsAssignableFrom(t) && !t.GetTypeInfo().IsAbstract); + } + } + } + + private void RegisterTenantCache() + { + if (IocManager.IsRegistered()) + { + return; + } + + using (var entityTypes = IocManager.ResolveAsDisposable()) + { + var implType = typeof (TenantCache<,>) + .MakeGenericType(entityTypes.Object.Tenant, entityTypes.Object.User); + + IocManager.Register(typeof (ITenantCache), implType, DependencyLifeStyle.Transient); + } + } + } +} diff --git a/src/Abp.Zero/Zero/AbpZeroConsts.cs b/src/Abp.Zero.Common/Zero/AbpZeroConsts.cs similarity index 100% rename from src/Abp.Zero/Zero/AbpZeroConsts.cs rename to src/Abp.Zero.Common/Zero/AbpZeroConsts.cs diff --git a/src/Abp.Zero/Zero/Configuration/AbpZeroConfig.cs b/src/Abp.Zero.Common/Zero/Configuration/AbpZeroConfig.cs similarity index 100% rename from src/Abp.Zero/Zero/Configuration/AbpZeroConfig.cs rename to src/Abp.Zero.Common/Zero/Configuration/AbpZeroConfig.cs diff --git a/src/Abp.Zero/Zero/Configuration/AbpZeroEntityTypes.cs b/src/Abp.Zero.Common/Zero/Configuration/AbpZeroEntityTypes.cs similarity index 98% rename from src/Abp.Zero/Zero/Configuration/AbpZeroEntityTypes.cs rename to src/Abp.Zero.Common/Zero/Configuration/AbpZeroEntityTypes.cs index d070c67c..8752858c 100644 --- a/src/Abp.Zero/Zero/Configuration/AbpZeroEntityTypes.cs +++ b/src/Abp.Zero.Common/Zero/Configuration/AbpZeroEntityTypes.cs @@ -1,4 +1,5 @@ using System; +using System.Reflection; using Abp.Authorization.Roles; using Abp.Authorization.Users; using Abp.MultiTenancy; diff --git a/src/Abp.Zero/Zero/Configuration/AbpZeroSettingNames.cs b/src/Abp.Zero.Common/Zero/Configuration/AbpZeroSettingNames.cs similarity index 66% rename from src/Abp.Zero/Zero/Configuration/AbpZeroSettingNames.cs rename to src/Abp.Zero.Common/Zero/Configuration/AbpZeroSettingNames.cs index 44e97286..759a1276 100644 --- a/src/Abp.Zero/Zero/Configuration/AbpZeroSettingNames.cs +++ b/src/Abp.Zero.Common/Zero/Configuration/AbpZeroSettingNames.cs @@ -49,6 +49,34 @@ public static class TwoFactorLogin /// public const string IsRememberBrowserEnabled = "Abp.Zero.UserManagement.TwoFactorLogin.IsRememberBrowserEnabled"; } + + public static class PasswordComplexity + { + /// + /// "Abp.Zero.UserManagement.PasswordComplexity.RequiredLength" + /// + public const string RequiredLength = "Abp.Zero.UserManagement.PasswordComplexity.RequiredLength"; + + /// + /// "Abp.Zero.UserManagement.PasswordComplexity.RequireNonAlphanumeric" + /// + public const string RequireNonAlphanumeric = "Abp.Zero.UserManagement.PasswordComplexity.RequireNonAlphanumeric"; + + /// + /// "Abp.Zero.UserManagement.PasswordComplexity.RequireLowercase" + /// + public const string RequireLowercase = "Abp.Zero.UserManagement.PasswordComplexity.RequireLowercase"; + + /// + /// "Abp.Zero.UserManagement.PasswordComplexity.RequireUppercase" + /// + public const string RequireUppercase = "Abp.Zero.UserManagement.PasswordComplexity.RequireUppercase"; + + /// + /// "Abp.Zero.UserManagement.PasswordComplexity.RequireDigit" + /// + public const string RequireDigit = "Abp.Zero.UserManagement.PasswordComplexity.RequireDigit"; + } } public static class OrganizationUnits diff --git a/src/Abp.Zero/Zero/Configuration/AbpZeroSettingProvider.cs b/src/Abp.Zero.Common/Zero/Configuration/AbpZeroSettingProvider.cs similarity index 67% rename from src/Abp.Zero/Zero/Configuration/AbpZeroSettingProvider.cs rename to src/Abp.Zero.Common/Zero/Configuration/AbpZeroSettingProvider.cs index 81e4c652..b9036a6a 100644 --- a/src/Abp.Zero/Zero/Configuration/AbpZeroSettingProvider.cs +++ b/src/Abp.Zero.Common/Zero/Configuration/AbpZeroSettingProvider.cs @@ -81,6 +81,46 @@ public override IEnumerable GetSettingDefinitions(SettingDefi scopes: SettingScopes.Application | SettingScopes.Tenant, isVisibleToClients: true ), + + new SettingDefinition( + AbpZeroSettingNames.UserManagement.PasswordComplexity.RequireDigit, + "false", + new FixedLocalizableString("Require digit."), + scopes: SettingScopes.Application | SettingScopes.Tenant, + isVisibleToClients: true + ), + + new SettingDefinition( + AbpZeroSettingNames.UserManagement.PasswordComplexity.RequireLowercase, + "false", + new FixedLocalizableString("Require lowercase."), + scopes: SettingScopes.Application | SettingScopes.Tenant, + isVisibleToClients: true + ), + + new SettingDefinition( + AbpZeroSettingNames.UserManagement.PasswordComplexity.RequireNonAlphanumeric, + "false", + new FixedLocalizableString("Require non alphanumeric."), + scopes: SettingScopes.Application | SettingScopes.Tenant, + isVisibleToClients: true + ), + + new SettingDefinition( + AbpZeroSettingNames.UserManagement.PasswordComplexity.RequireUppercase, + "false", + new FixedLocalizableString("Require upper case."), + scopes: SettingScopes.Application | SettingScopes.Tenant, + isVisibleToClients: true + ), + + new SettingDefinition( + AbpZeroSettingNames.UserManagement.PasswordComplexity.RequiredLength, + "3", + new FixedLocalizableString("Required length."), + scopes: SettingScopes.Application | SettingScopes.Tenant, + isVisibleToClients: true + ) }; } } diff --git a/src/Abp.Zero/Zero/Configuration/IAbpZeroConfig.cs b/src/Abp.Zero.Common/Zero/Configuration/IAbpZeroConfig.cs similarity index 100% rename from src/Abp.Zero/Zero/Configuration/IAbpZeroConfig.cs rename to src/Abp.Zero.Common/Zero/Configuration/IAbpZeroConfig.cs diff --git a/src/Abp.Zero/Zero/Configuration/IAbpZeroEntityTypes.cs b/src/Abp.Zero.Common/Zero/Configuration/IAbpZeroEntityTypes.cs similarity index 100% rename from src/Abp.Zero/Zero/Configuration/IAbpZeroEntityTypes.cs rename to src/Abp.Zero.Common/Zero/Configuration/IAbpZeroEntityTypes.cs diff --git a/src/Abp.Zero/Zero/Configuration/ILanguageManagementConfig.cs b/src/Abp.Zero.Common/Zero/Configuration/ILanguageManagementConfig.cs similarity index 100% rename from src/Abp.Zero/Zero/Configuration/ILanguageManagementConfig.cs rename to src/Abp.Zero.Common/Zero/Configuration/ILanguageManagementConfig.cs diff --git a/src/Abp.Zero/Zero/Configuration/IRoleManagementConfig.cs b/src/Abp.Zero.Common/Zero/Configuration/IRoleManagementConfig.cs similarity index 100% rename from src/Abp.Zero/Zero/Configuration/IRoleManagementConfig.cs rename to src/Abp.Zero.Common/Zero/Configuration/IRoleManagementConfig.cs diff --git a/src/Abp.Zero/Zero/Configuration/IUserManagementConfig.cs b/src/Abp.Zero.Common/Zero/Configuration/IUserManagementConfig.cs similarity index 100% rename from src/Abp.Zero/Zero/Configuration/IUserManagementConfig.cs rename to src/Abp.Zero.Common/Zero/Configuration/IUserManagementConfig.cs diff --git a/src/Abp.Zero/Zero/Configuration/LanguageManagementConfig.cs b/src/Abp.Zero.Common/Zero/Configuration/LanguageManagementConfig.cs similarity index 100% rename from src/Abp.Zero/Zero/Configuration/LanguageManagementConfig.cs rename to src/Abp.Zero.Common/Zero/Configuration/LanguageManagementConfig.cs diff --git a/src/Abp.Zero/Zero/Configuration/ModuleZeroConfigurationExtensions.cs b/src/Abp.Zero.Common/Zero/Configuration/ModuleZeroConfigurationExtensions.cs similarity index 100% rename from src/Abp.Zero/Zero/Configuration/ModuleZeroConfigurationExtensions.cs rename to src/Abp.Zero.Common/Zero/Configuration/ModuleZeroConfigurationExtensions.cs diff --git a/src/Abp.Zero/Zero/Configuration/RoleManagementConfig.cs b/src/Abp.Zero.Common/Zero/Configuration/RoleManagementConfig.cs similarity index 100% rename from src/Abp.Zero/Zero/Configuration/RoleManagementConfig.cs rename to src/Abp.Zero.Common/Zero/Configuration/RoleManagementConfig.cs diff --git a/src/Abp.Zero/Zero/Configuration/StaticRoleDefinition.cs b/src/Abp.Zero.Common/Zero/Configuration/StaticRoleDefinition.cs similarity index 100% rename from src/Abp.Zero/Zero/Configuration/StaticRoleDefinition.cs rename to src/Abp.Zero.Common/Zero/Configuration/StaticRoleDefinition.cs diff --git a/src/Abp.Zero/Zero/Configuration/UserManagementConfig.cs b/src/Abp.Zero.Common/Zero/Configuration/UserManagementConfig.cs similarity index 100% rename from src/Abp.Zero/Zero/Configuration/UserManagementConfig.cs rename to src/Abp.Zero.Common/Zero/Configuration/UserManagementConfig.cs diff --git a/src/Abp.Zero.Common/Zero/Localization/Source/AbpZero-ar.xml b/src/Abp.Zero.Common/Zero/Localization/Source/AbpZero-ar.xml new file mode 100644 index 00000000..57fcdc0b --- /dev/null +++ b/src/Abp.Zero.Common/Zero/Localization/Source/AbpZero-ar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/Abp.Zero.Common/Zero/Localization/Source/AbpZero-de.xml b/src/Abp.Zero.Common/Zero/Localization/Source/AbpZero-de.xml new file mode 100644 index 00000000..423e4e1b --- /dev/null +++ b/src/Abp.Zero.Common/Zero/Localization/Source/AbpZero-de.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Abp.Zero.Common/Zero/Localization/Source/AbpZero-es.xml b/src/Abp.Zero.Common/Zero/Localization/Source/AbpZero-es.xml new file mode 100644 index 00000000..bab50856 --- /dev/null +++ b/src/Abp.Zero.Common/Zero/Localization/Source/AbpZero-es.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Abp.Zero.Common/Zero/Localization/Source/AbpZero-fa.xml b/src/Abp.Zero.Common/Zero/Localization/Source/AbpZero-fa.xml new file mode 100644 index 00000000..38e1e1a9 --- /dev/null +++ b/src/Abp.Zero.Common/Zero/Localization/Source/AbpZero-fa.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/Abp.Zero.Common/Zero/Localization/Source/AbpZero-fr.xml b/src/Abp.Zero.Common/Zero/Localization/Source/AbpZero-fr.xml new file mode 100644 index 00000000..34437bec --- /dev/null +++ b/src/Abp.Zero.Common/Zero/Localization/Source/AbpZero-fr.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Abp.Zero.Common/Zero/Localization/Source/AbpZero-it.xml b/src/Abp.Zero.Common/Zero/Localization/Source/AbpZero-it.xml new file mode 100644 index 00000000..22e6f2e0 --- /dev/null +++ b/src/Abp.Zero.Common/Zero/Localization/Source/AbpZero-it.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Abp.Zero.Common/Zero/Localization/Source/AbpZero-lt.xml b/src/Abp.Zero.Common/Zero/Localization/Source/AbpZero-lt.xml new file mode 100644 index 00000000..8b59443e --- /dev/null +++ b/src/Abp.Zero.Common/Zero/Localization/Source/AbpZero-lt.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/Abp.Zero.Common/Zero/Localization/Source/AbpZero-lv.xml b/src/Abp.Zero.Common/Zero/Localization/Source/AbpZero-lv.xml new file mode 100644 index 00000000..12444bac --- /dev/null +++ b/src/Abp.Zero.Common/Zero/Localization/Source/AbpZero-lv.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/Abp.Zero.Common/Zero/Localization/Source/AbpZero-pt-BR.xml b/src/Abp.Zero.Common/Zero/Localization/Source/AbpZero-pt-BR.xml new file mode 100644 index 00000000..ac8913e6 --- /dev/null +++ b/src/Abp.Zero.Common/Zero/Localization/Source/AbpZero-pt-BR.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/Abp.Zero.Common/Zero/Localization/Source/AbpZero-ru.xml b/src/Abp.Zero.Common/Zero/Localization/Source/AbpZero-ru.xml new file mode 100644 index 00000000..24249d71 --- /dev/null +++ b/src/Abp.Zero.Common/Zero/Localization/Source/AbpZero-ru.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/Abp.Zero.Common/Zero/Localization/Source/AbpZero-tr.xml b/src/Abp.Zero.Common/Zero/Localization/Source/AbpZero-tr.xml new file mode 100644 index 00000000..155208ba --- /dev/null +++ b/src/Abp.Zero.Common/Zero/Localization/Source/AbpZero-tr.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Abp.Zero.Common/Zero/Localization/Source/AbpZero-zh-CN.xml b/src/Abp.Zero.Common/Zero/Localization/Source/AbpZero-zh-CN.xml new file mode 100644 index 00000000..c4054e6f --- /dev/null +++ b/src/Abp.Zero.Common/Zero/Localization/Source/AbpZero-zh-CN.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Abp.Zero.Common/Zero/Localization/Source/AbpZero.xml b/src/Abp.Zero.Common/Zero/Localization/Source/AbpZero.xml new file mode 100644 index 00000000..70165656 --- /dev/null +++ b/src/Abp.Zero.Common/Zero/Localization/Source/AbpZero.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Abp.Zero.EntityFramework/Abp.Zero.EntityFramework.csproj b/src/Abp.Zero.EntityFramework/Abp.Zero.EntityFramework.csproj new file mode 100644 index 00000000..2026b16b --- /dev/null +++ b/src/Abp.Zero.EntityFramework/Abp.Zero.EntityFramework.csproj @@ -0,0 +1,42 @@ + + + + + + net46 + true + Abp.Zero.EntityFramework + Abp.Zero.EntityFramework + asp.net;asp.net mvc;boilerplate;application framework;web framework;framework;domain driven design;multitenancy;user management;role management;identity;entity framework + false + false + false + false + false + false + Abp + + + + + + + + + lib/net46/ + true + + + + + + + + + + + + + + + diff --git a/src/Abp.Zero.EntityFramework/Abp.Zero.EntityFramework.xproj b/src/Abp.Zero.EntityFramework/Abp.Zero.EntityFramework.xproj deleted file mode 100644 index 136fd4e7..00000000 --- a/src/Abp.Zero.EntityFramework/Abp.Zero.EntityFramework.xproj +++ /dev/null @@ -1,19 +0,0 @@ - - - - 14.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - - - - a57787c3-ad9a-40a8-b955-eda596397429 - Abp - .\obj - .\bin\ - v4.5.2 - - - 2.0 - - - \ No newline at end of file diff --git a/src/Abp.Zero.EntityFramework/EntityFramework/Extensions/EntityFrameworkModelBuilderExtensions.cs b/src/Abp.Zero.EntityFramework/EntityFramework/Extensions/EntityFrameworkModelBuilderExtensions.cs deleted file mode 100644 index 70970e88..00000000 --- a/src/Abp.Zero.EntityFramework/EntityFramework/Extensions/EntityFrameworkModelBuilderExtensions.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System.ComponentModel.DataAnnotations.Schema; -using System.Data.Entity.Infrastructure.Annotations; -using System.Data.Entity.ModelConfiguration.Configuration; - -namespace Abp.EntityFramework.Extensions -{ - //TODO: MOVE TO ABP - //TODO: We can create simpler extension methods to create indexes - //TODO: Check https://github.com/mj1856/EntityFramework.IndexingExtensions for example - internal static class EntityFrameworkModelBuilderExtensions - { - public static PrimitivePropertyConfiguration CreateIndex(this PrimitivePropertyConfiguration propertyConfiguration) - { - return propertyConfiguration.HasColumnAnnotation( - IndexAnnotation.AnnotationName, - new IndexAnnotation( - new IndexAttribute() - ) - ); - } - - public static PrimitivePropertyConfiguration CreateIndex(this PrimitivePropertyConfiguration propertyConfiguration, string name) - { - return propertyConfiguration.HasColumnAnnotation( - IndexAnnotation.AnnotationName, - new IndexAnnotation( - new IndexAttribute(name) - ) - ); - } - - public static PrimitivePropertyConfiguration CreateIndex(this PrimitivePropertyConfiguration propertyConfiguration, string name, int order) - { - return propertyConfiguration.HasColumnAnnotation( - IndexAnnotation.AnnotationName, - new IndexAnnotation( - new IndexAttribute(name, order) - ) - ); - } - } -} \ No newline at end of file diff --git a/src/Abp.Zero.EntityFramework/Zero/EntityFramework/AbpZeroCommonDbContext.cs b/src/Abp.Zero.EntityFramework/Zero/EntityFramework/AbpZeroCommonDbContext.cs index 908c681a..22814f08 100644 --- a/src/Abp.Zero.EntityFramework/Zero/EntityFramework/AbpZeroCommonDbContext.cs +++ b/src/Abp.Zero.EntityFramework/Zero/EntityFramework/AbpZeroCommonDbContext.cs @@ -1,5 +1,7 @@ using System.Data.Common; using System.Data.Entity; +using System.Data.Entity.Core.Objects; +using System.Data.Entity.Infrastructure; using Abp.Auditing; using Abp.Authorization; using Abp.Authorization.Roles; @@ -130,13 +132,38 @@ protected AbpZeroCommonDbContext(string nameOrConnectionString) } + protected AbpZeroCommonDbContext(DbCompiledModel model) + : base(model) + { + + } + /// /// This constructor can be used for unit tests. /// - protected AbpZeroCommonDbContext(DbConnection dbConnection, bool contextOwnsConnection) - : base(dbConnection, contextOwnsConnection) + protected AbpZeroCommonDbContext(DbConnection existingConnection, bool contextOwnsConnection) + : base(existingConnection, contextOwnsConnection) + { + + } + + protected AbpZeroCommonDbContext(string nameOrConnectionString, DbCompiledModel model) + : base(nameOrConnectionString, model) { } + + protected AbpZeroCommonDbContext(ObjectContext objectContext, bool dbContextOwnsObjectContext) + : base(objectContext, dbContextOwnsObjectContext) + { + } + + /// + /// Constructor. + /// + protected AbpZeroCommonDbContext(DbConnection existingConnection, DbCompiledModel model, bool contextOwnsConnection) + : base(existingConnection, model, contextOwnsConnection) + { + } } } \ No newline at end of file diff --git a/src/Abp.Zero.EntityFramework/Zero/EntityFramework/AbpZeroDbContext.cs b/src/Abp.Zero.EntityFramework/Zero/EntityFramework/AbpZeroDbContext.cs index 38482c19..f51c90ff 100644 --- a/src/Abp.Zero.EntityFramework/Zero/EntityFramework/AbpZeroDbContext.cs +++ b/src/Abp.Zero.EntityFramework/Zero/EntityFramework/AbpZeroDbContext.cs @@ -1,7 +1,10 @@ using System.Data.Common; using System.Data.Entity; +using System.Data.Entity.Core.Objects; +using System.Data.Entity.Infrastructure; using Abp.Application.Editions; using Abp.Application.Features; +using Abp.Auditing; using Abp.Authorization.Roles; using Abp.Authorization.Users; using Abp.BackgroundJobs; @@ -55,32 +58,42 @@ public abstract class AbpZeroDbContext : AbpZeroCommonDbC /// public virtual IDbSet UserAccounts { get; set; } - /// - /// Default constructor. - /// Do not directly instantiate this class. Instead, use dependency injection! - /// protected AbpZeroDbContext() { } - /// - /// Constructor with connection string parameter. - /// - /// Connection string or a name in connection strings in configuration file protected AbpZeroDbContext(string nameOrConnectionString) : base(nameOrConnectionString) { } - /// - /// This constructor can be used for unit tests. - /// - protected AbpZeroDbContext(DbConnection dbConnection, bool contextOwnsConnection) - : base(dbConnection, contextOwnsConnection) + protected AbpZeroDbContext(DbCompiledModel model) + : base(model) + { + + } + + protected AbpZeroDbContext(DbConnection existingConnection, bool contextOwnsConnection) + : base(existingConnection, contextOwnsConnection) + { + + } + + protected AbpZeroDbContext(string nameOrConnectionString, DbCompiledModel model) + : base(nameOrConnectionString, model) { + } + protected AbpZeroDbContext(ObjectContext objectContext, bool dbContextOwnsObjectContext) + : base(objectContext, dbContextOwnsObjectContext) + { + } + + protected AbpZeroDbContext(DbConnection existingConnection, DbCompiledModel model, bool contextOwnsConnection) + : base(existingConnection, model, contextOwnsConnection) + { } protected override void OnModelCreating(DbModelBuilder modelBuilder) @@ -162,6 +175,43 @@ protected override void OnModelCreating(DbModelBuilder modelBuilder) .CreateIndex("IX_UserId_TenantId", 2); #endregion + + #region AuditLog.Set_MaxLengths + + modelBuilder.Entity() + .Property(e => e.ServiceName) + .HasMaxLength(AuditLog.MaxServiceNameLength); + + modelBuilder.Entity() + .Property(e => e.MethodName) + .HasMaxLength(AuditLog.MaxMethodNameLength); + + modelBuilder.Entity() + .Property(e => e.Parameters) + .HasMaxLength(AuditLog.MaxParametersLength); + + modelBuilder.Entity() + .Property(e => e.ClientIpAddress) + .HasMaxLength(AuditLog.MaxClientIpAddressLength); + + modelBuilder.Entity() + .Property(e => e.ClientName) + .HasMaxLength(AuditLog.MaxClientNameLength); + + modelBuilder.Entity() + .Property(e => e.BrowserInfo) + .HasMaxLength(AuditLog.MaxBrowserInfoLength); + + modelBuilder.Entity() + .Property(e => e.Exception) + .HasMaxLength(AuditLog.MaxExceptionLength); + + modelBuilder.Entity() + .Property(e => e.CustomData) + .HasMaxLength(AuditLog.MaxCustomDataLength); + + #endregion + } } } diff --git a/src/Abp.Zero.EntityFramework/Zero/EntityFramework/AbpZeroHostDbContext.cs b/src/Abp.Zero.EntityFramework/Zero/EntityFramework/AbpZeroHostDbContext.cs index 1a0bd5a8..a408f67c 100644 --- a/src/Abp.Zero.EntityFramework/Zero/EntityFramework/AbpZeroHostDbContext.cs +++ b/src/Abp.Zero.EntityFramework/Zero/EntityFramework/AbpZeroHostDbContext.cs @@ -1,5 +1,7 @@ using System.Data.Common; using System.Data.Entity; +using System.Data.Entity.Core.Objects; +using System.Data.Entity.Infrastructure; using Abp.Application.Editions; using Abp.Application.Features; using Abp.Authorization.Roles; @@ -50,32 +52,42 @@ public abstract class AbpZeroHostDbContext : AbpZeroCommo /// public virtual IDbSet UserAccounts { get; set; } - /// - /// Default constructor. - /// Do not directly instantiate this class. Instead, use dependency injection! - /// protected AbpZeroHostDbContext() { } - /// - /// Constructor with connection string parameter. - /// - /// Connection string or a name in connection strings in configuration file protected AbpZeroHostDbContext(string nameOrConnectionString) : base(nameOrConnectionString) { } - /// - /// This constructor can be used for unit tests. - /// - protected AbpZeroHostDbContext(DbConnection dbConnection, bool contextOwnsConnection) - : base(dbConnection, contextOwnsConnection) + protected AbpZeroHostDbContext(DbCompiledModel model) + : base(model) + { + + } + + protected AbpZeroHostDbContext(DbConnection existingConnection, bool contextOwnsConnection) + : base(existingConnection, contextOwnsConnection) { } + + protected AbpZeroHostDbContext(string nameOrConnectionString, DbCompiledModel model) + : base(nameOrConnectionString, model) + { + } + + protected AbpZeroHostDbContext(ObjectContext objectContext, bool dbContextOwnsObjectContext) + : base(objectContext, dbContextOwnsObjectContext) + { + } + + protected AbpZeroHostDbContext(DbConnection existingConnection, DbCompiledModel model, bool contextOwnsConnection) + : base(existingConnection, model, contextOwnsConnection) + { + } } } \ No newline at end of file diff --git a/src/Abp.Zero.EntityFramework/Zero/EntityFramework/AbpZeroTenantDbContext.cs b/src/Abp.Zero.EntityFramework/Zero/EntityFramework/AbpZeroTenantDbContext.cs index 7649fc97..76f44c8b 100644 --- a/src/Abp.Zero.EntityFramework/Zero/EntityFramework/AbpZeroTenantDbContext.cs +++ b/src/Abp.Zero.EntityFramework/Zero/EntityFramework/AbpZeroTenantDbContext.cs @@ -1,4 +1,6 @@ using System.Data.Common; +using System.Data.Entity.Core.Objects; +using System.Data.Entity.Infrastructure; using Abp.Authorization.Roles; using Abp.Authorization.Users; using Abp.MultiTenancy; @@ -29,13 +31,34 @@ protected AbpZeroTenantDbContext(string nameOrConnectionString) } + protected AbpZeroTenantDbContext(DbCompiledModel model) + : base(model) + { + + } + /// /// This constructor can be used for unit tests. /// - protected AbpZeroTenantDbContext(DbConnection dbConnection, bool contextOwnsConnection) - : base(dbConnection, contextOwnsConnection) + protected AbpZeroTenantDbContext(DbConnection existingConnection, bool contextOwnsConnection) + : base(existingConnection, contextOwnsConnection) { } + + protected AbpZeroTenantDbContext(string nameOrConnectionString, DbCompiledModel model) + : base(nameOrConnectionString, model) + { + } + + protected AbpZeroTenantDbContext(ObjectContext objectContext, bool dbContextOwnsObjectContext) + : base(objectContext, dbContextOwnsObjectContext) + { + } + + protected AbpZeroTenantDbContext(DbConnection existingConnection, DbCompiledModel model, bool contextOwnsConnection) + : base(existingConnection, model, contextOwnsConnection) + { + } } } \ No newline at end of file diff --git a/src/Abp.Zero.EntityFramework/project.json b/src/Abp.Zero.EntityFramework/project.json deleted file mode 100644 index 1e58a744..00000000 --- a/src/Abp.Zero.EntityFramework/project.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "version": "1.0.0.0-*", - - "dependencies": { - "Abp.EntityFramework": "1.0.0", - "Abp.Zero": "1.0.0.0-*" - }, - - "frameworks": { - "net452": { - "frameworkAssemblies": { - } - } - }, - - "buildOptions": { - "nowarn": [ - "CS1591" - ], - "xmlDoc": true, - "embed": { - "include": [ - ] - } - }, - - "packOptions": { - "summary": "ASP.NET Boilerplate - Module Zero - Entity Framework Integration.", - "tags": [ "asp.net", "asp.net mvc", "boilerplate", "application framework", "web framework", "framework", "domain driven design", "multitenancy", "user management", "role management", "identity", "entity framework" ], - "owners": [ "Halil İbrahim Kalkan" ], - "iconUrl": "http://www.aspnetboilerplate.com/images/abp_nupkg.png", - "projectUrl": "http://www.aspnetboilerplate.com/", - "licenseUrl": "https://github.com/aspnetboilerplate/module-zero/blob/master/LICENSE", - "requireLicenseAcceptance": false, - "repository": { - "type": "git", - "url": "https://github.com/aspnetboilerplate/module-zero" - }, - "files": { - "mappings": { - "lib/net452/": "bin/Release/net452/Abp.Zero.EntityFramework.pdb" - } - } - } -} diff --git a/src/Abp.Zero.EntityFrameworkCore/Abp.Zero.EntityFrameworkCore.csproj b/src/Abp.Zero.EntityFrameworkCore/Abp.Zero.EntityFrameworkCore.csproj new file mode 100644 index 00000000..10ecebdd --- /dev/null +++ b/src/Abp.Zero.EntityFrameworkCore/Abp.Zero.EntityFrameworkCore.csproj @@ -0,0 +1,41 @@ + + + + + + net46 + true + Abp.Zero.EntityFrameworkCore + Abp.Zero.EntityFrameworkCore + asp.net;asp.net mvc;boilerplate;application framework;web framework;framework;domain driven design;multitenancy;user management;role management;identity;entity framework core + false + false + false + false + false + false + Abp + + + + + lib/net46/ + true + + + + + + + + + + + + + + + + + + diff --git a/src/Abp.Zero.EntityFrameworkCore/Abp.Zero.EntityFrameworkCore.xproj b/src/Abp.Zero.EntityFrameworkCore/Abp.Zero.EntityFrameworkCore.xproj deleted file mode 100644 index 5dab24e4..00000000 --- a/src/Abp.Zero.EntityFrameworkCore/Abp.Zero.EntityFrameworkCore.xproj +++ /dev/null @@ -1,19 +0,0 @@ - - - - 14.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - - - - 83108e5e-fd31-4762-8854-d795a8fb482f - Abp - .\obj - .\bin\ - v4.5.2 - - - 2.0 - - - \ No newline at end of file diff --git a/src/Abp.Zero.EntityFrameworkCore/Zero/EntityFrameworkCore/AbpZeroCommonDbContext.cs b/src/Abp.Zero.EntityFrameworkCore/Zero/EntityFrameworkCore/AbpZeroCommonDbContext.cs index 67d826d4..38f45f7c 100644 --- a/src/Abp.Zero.EntityFrameworkCore/Zero/EntityFrameworkCore/AbpZeroCommonDbContext.cs +++ b/src/Abp.Zero.EntityFrameworkCore/Zero/EntityFrameworkCore/AbpZeroCommonDbContext.cs @@ -128,17 +128,20 @@ protected AbpZeroCommonDbContext(DbContextOptions options) protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); - modelBuilder.Entity() - .Ignore(p => p.DeleterUser); - + modelBuilder.Entity(u => { + u.HasOne(p => p.DeleterUser) + .WithMany() + .HasForeignKey(p => p.DeleterUserId); + u.HasOne(p => p.CreatorUser) - .WithOne() - .HasForeignKey(p => p.CreatorUserId); + .WithMany() + .HasForeignKey(p => p.CreatorUserId); + u.HasOne(p => p.LastModifierUser) - .WithOne() - .HasForeignKey(p => p.LastModifierUserId); + .WithMany() + .HasForeignKey(p => p.LastModifierUserId); }); } } diff --git a/src/Abp.Zero.EntityFrameworkCore/Zero/EntityFrameworkCore/AbpZeroDbMigrator.cs b/src/Abp.Zero.EntityFrameworkCore/Zero/EntityFrameworkCore/AbpZeroDbMigrator.cs index c5363f2e..743f9ac2 100644 --- a/src/Abp.Zero.EntityFrameworkCore/Zero/EntityFrameworkCore/AbpZeroDbMigrator.cs +++ b/src/Abp.Zero.EntityFrameworkCore/Zero/EntityFrameworkCore/AbpZeroDbMigrator.cs @@ -27,7 +27,7 @@ protected AbpZeroDbMigrator( _dbContextResolver = dbContextResolver; } - public void CreateOrMigrateForHost() + public virtual void CreateOrMigrateForHost() { CreateOrMigrateForHost(null); } @@ -37,7 +37,7 @@ public virtual void CreateOrMigrateForHost(Action seedAction) CreateOrMigrate(null, seedAction); } - public void CreateOrMigrateForTenant(AbpTenantBase tenant) + public virtual void CreateOrMigrateForTenant(AbpTenantBase tenant) { CreateOrMigrateForTenant(tenant, null); } @@ -68,7 +68,7 @@ protected virtual void CreateOrMigrate(AbpTenantBase tenant, Action using (var uow = _unitOfWorkManager.Begin(TransactionScopeOption.Suppress)) { - using (var dbContext = _dbContextResolver.Resolve(nameOrConnectionString)) + using (var dbContext = _dbContextResolver.Resolve(nameOrConnectionString, null)) { dbContext.Database.Migrate(); seedAction?.Invoke(dbContext); diff --git a/src/Abp.Zero.EntityFrameworkCore/project.json b/src/Abp.Zero.EntityFrameworkCore/project.json deleted file mode 100644 index 9f6df0cb..00000000 --- a/src/Abp.Zero.EntityFrameworkCore/project.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "version": "1.0.0.0-*", - - "dependencies": { - "Abp.EntityFrameworkCore": "1.0.0", - "Abp.Zero": "1.0.0.0-*", - "Microsoft.EntityFrameworkCore.Relational": "1.0.1" - }, - - "frameworks": { - "net461": { - "frameworkAssemblies": { - } - } - }, - - "buildOptions": { - "nowarn": [ - "CS1591" - ], - "xmlDoc": true, - "embed": { - "include": [ - ] - } - }, - - "packOptions": { - "summary": "ASP.NET Boilerplate - Module Zero - Entity Framework Core Integration.", - "tags": [ "asp.net", "asp.net mvc", "boilerplate", "application framework", "web framework", "framework", "domain driven design", "multitenancy", "user management", "role management", "identity", "entity framework core" ], - "owners": [ "Halil İbrahim Kalkan" ], - "iconUrl": "http://www.aspnetboilerplate.com/images/abp_nupkg.png", - "projectUrl": "http://www.aspnetboilerplate.com/", - "licenseUrl": "https://github.com/aspnetboilerplate/module-zero/blob/master/LICENSE", - "requireLicenseAcceptance": false, - "repository": { - "type": "git", - "url": "https://github.com/aspnetboilerplate/module-zero" - }, - "files": { - "mappings": { - "lib/net461/": "bin/Release/net461/Abp.Zero.EntityFrameworkCore.pdb" - } - } - } -} diff --git a/src/Abp.Zero.Ldap/Abp.Zero.Ldap.csproj b/src/Abp.Zero.Ldap/Abp.Zero.Ldap.csproj new file mode 100644 index 00000000..fcb38c3a --- /dev/null +++ b/src/Abp.Zero.Ldap/Abp.Zero.Ldap.csproj @@ -0,0 +1,36 @@ + + + + + + net46 + true + Abp.Zero.Ldap + Abp.Zero.Ldap + asp.net;asp.net mvc;boilerplate;application framework;web framework;framework;domain driven design;multitenancy;user management;role management;identity;ldap;active directory + false + false + false + false + false + false + Abp.Zero + + + + + + + lib/net46/ + true + + + + + + + + + + + diff --git a/src/Abp.Zero.Ldap/Abp.Zero.Ldap.xproj b/src/Abp.Zero.Ldap/Abp.Zero.Ldap.xproj deleted file mode 100644 index 80c5bc5a..00000000 --- a/src/Abp.Zero.Ldap/Abp.Zero.Ldap.xproj +++ /dev/null @@ -1,19 +0,0 @@ - - - - 14.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - - - - c28f608b-f34e-411c-8d88-c5d566141735 - Abp.Zero - .\obj - .\bin\ - v4.5.2 - - - 2.0 - - - \ No newline at end of file diff --git a/src/Abp.Zero.Ldap/Ldap/AbpZeroLdapModule.cs b/src/Abp.Zero.Ldap/Ldap/AbpZeroLdapModule.cs index bbea4052..948499e1 100644 --- a/src/Abp.Zero.Ldap/Ldap/AbpZeroLdapModule.cs +++ b/src/Abp.Zero.Ldap/Ldap/AbpZeroLdapModule.cs @@ -9,7 +9,7 @@ namespace Abp.Zero.Ldap /// /// This module extends module zero to add LDAP authentication. /// - [DependsOn(typeof (AbpZeroCoreModule))] + [DependsOn(typeof (AbpZeroCommonModule))] public class AbpZeroLdapModule : AbpModule { public override void PreInitialize() @@ -21,7 +21,7 @@ public override void PreInitialize() AbpZeroConsts.LocalizationSourceName, new XmlEmbeddedFileLocalizationDictionaryProvider( Assembly.GetExecutingAssembly(), - "Abp.Zero.Ldap.Ldap.Localization.Source") + "Abp.Zero.Ldap.Localization.Source") ) ); diff --git a/src/Abp.Zero.Ldap/Ldap/Authentication/LdapAuthenticationSource.cs b/src/Abp.Zero.Ldap/Ldap/Authentication/LdapAuthenticationSource.cs index 93013f06..2e59cc2e 100644 --- a/src/Abp.Zero.Ldap/Ldap/Authentication/LdapAuthenticationSource.cs +++ b/src/Abp.Zero.Ldap/Ldap/Authentication/LdapAuthenticationSource.cs @@ -17,7 +17,7 @@ namespace Abp.Zero.Ldap.Authentication /// User type public abstract class LdapAuthenticationSource : DefaultExternalAuthenticationSource, ITransientDependency where TTenant : AbpTenant - where TUser : AbpUser, new() + where TUser : AbpUserBase, new() { /// /// LDAP diff --git a/src/Abp.Zero.Ldap/Ldap/Configuration/LdapSettings.cs b/src/Abp.Zero.Ldap/Ldap/Configuration/LdapSettings.cs index e38cac09..e71111ac 100644 --- a/src/Abp.Zero.Ldap/Ldap/Configuration/LdapSettings.cs +++ b/src/Abp.Zero.Ldap/Ldap/Configuration/LdapSettings.cs @@ -12,53 +12,53 @@ namespace Abp.Zero.Ldap.Configuration public class LdapSettings : ILdapSettings, ITransientDependency { - private readonly ISettingManager _settingManager; + protected ISettingManager SettingManager { get; } public LdapSettings(ISettingManager settingManager) { - _settingManager = settingManager; + SettingManager = settingManager; } - public Task GetIsEnabled(int? tenantId) + public virtual Task GetIsEnabled(int? tenantId) { return tenantId.HasValue - ? _settingManager.GetSettingValueForTenantAsync(LdapSettingNames.IsEnabled, tenantId.Value) - : _settingManager.GetSettingValueForApplicationAsync(LdapSettingNames.IsEnabled); + ? SettingManager.GetSettingValueForTenantAsync(LdapSettingNames.IsEnabled, tenantId.Value) + : SettingManager.GetSettingValueForApplicationAsync(LdapSettingNames.IsEnabled); } - public async Task GetContextType(int? tenantId) + public virtual async Task GetContextType(int? tenantId) { return tenantId.HasValue - ? (await _settingManager.GetSettingValueForTenantAsync(LdapSettingNames.ContextType, tenantId.Value)).ToEnum() - : (await _settingManager.GetSettingValueForApplicationAsync(LdapSettingNames.ContextType)).ToEnum(); + ? (await SettingManager.GetSettingValueForTenantAsync(LdapSettingNames.ContextType, tenantId.Value)).ToEnum() + : (await SettingManager.GetSettingValueForApplicationAsync(LdapSettingNames.ContextType)).ToEnum(); } - public Task GetContainer(int? tenantId) + public virtual Task GetContainer(int? tenantId) { return tenantId.HasValue - ? _settingManager.GetSettingValueForTenantAsync(LdapSettingNames.Container, tenantId.Value) - : _settingManager.GetSettingValueForApplicationAsync(LdapSettingNames.Container); + ? SettingManager.GetSettingValueForTenantAsync(LdapSettingNames.Container, tenantId.Value) + : SettingManager.GetSettingValueForApplicationAsync(LdapSettingNames.Container); } - public Task GetDomain(int? tenantId) + public virtual Task GetDomain(int? tenantId) { return tenantId.HasValue - ? _settingManager.GetSettingValueForTenantAsync(LdapSettingNames.Domain, tenantId.Value) - : _settingManager.GetSettingValueForApplicationAsync(LdapSettingNames.Domain); + ? SettingManager.GetSettingValueForTenantAsync(LdapSettingNames.Domain, tenantId.Value) + : SettingManager.GetSettingValueForApplicationAsync(LdapSettingNames.Domain); } - public Task GetUserName(int? tenantId) + public virtual Task GetUserName(int? tenantId) { return tenantId.HasValue - ? _settingManager.GetSettingValueForTenantAsync(LdapSettingNames.UserName, tenantId.Value) - : _settingManager.GetSettingValueForApplicationAsync(LdapSettingNames.UserName); + ? SettingManager.GetSettingValueForTenantAsync(LdapSettingNames.UserName, tenantId.Value) + : SettingManager.GetSettingValueForApplicationAsync(LdapSettingNames.UserName); } - public Task GetPassword(int? tenantId) + public virtual Task GetPassword(int? tenantId) { return tenantId.HasValue - ? _settingManager.GetSettingValueForTenantAsync(LdapSettingNames.Password, tenantId.Value) - : _settingManager.GetSettingValueForApplicationAsync(LdapSettingNames.Password); + ? SettingManager.GetSettingValueForTenantAsync(LdapSettingNames.Password, tenantId.Value) + : SettingManager.GetSettingValueForApplicationAsync(LdapSettingNames.Password); } } } \ No newline at end of file diff --git a/src/Abp.Zero.Ldap/Ldap/Localization/Source/AbpZero-ar.xml b/src/Abp.Zero.Ldap/Ldap/Localization/Source/AbpZero-ar.xml new file mode 100644 index 00000000..2807b0fa --- /dev/null +++ b/src/Abp.Zero.Ldap/Ldap/Localization/Source/AbpZero-ar.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/Abp.Zero.Ldap/Ldap/Localization/Source/AbpZero-es.xml b/src/Abp.Zero.Ldap/Ldap/Localization/Source/AbpZero-es.xml new file mode 100644 index 00000000..6a34e8d2 --- /dev/null +++ b/src/Abp.Zero.Ldap/Ldap/Localization/Source/AbpZero-es.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/src/Abp.Zero.Ldap/Ldap/Localization/Source/AbpZero-fa.xml b/src/Abp.Zero.Ldap/Ldap/Localization/Source/AbpZero-fa.xml new file mode 100644 index 00000000..be29426c --- /dev/null +++ b/src/Abp.Zero.Ldap/Ldap/Localization/Source/AbpZero-fa.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/Abp.Zero.Ldap/project.json b/src/Abp.Zero.Ldap/project.json deleted file mode 100644 index be3f299d..00000000 --- a/src/Abp.Zero.Ldap/project.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "version": "1.0.0.0-*", - - "dependencies": { - "Abp.Zero": "1.0.0.0-*" - }, - - "frameworks": { - "net452": { - "frameworkAssemblies": { - "System.DirectoryServices.AccountManagement": "4.0.0.0", - "System.DirectoryServices.Protocols": "4.0.0.0" - } - } - }, - - "buildOptions": { - "nowarn": [ - "CS1591" - ], - "xmlDoc": true, - "embed": { - "include": [ - "Ldap/Localization/Source/*.xml" - ] - } - }, - - "packOptions": { - "summary": "ASP.NET Boilerplate - Module Zero.", - "tags": [ "asp.net", "asp.net mvc", "boilerplate", "application framework", "web framework", "framework", "domain driven design", "multitenancy", "user management", "role management", "identity", "ldap", "active directory" ], - "owners": [ "Halil İbrahim Kalkan" ], - "iconUrl": "http://www.aspnetboilerplate.com/images/abp_nupkg.png", - "projectUrl": "http://www.aspnetboilerplate.com/", - "licenseUrl": "https://github.com/aspnetboilerplate/module-zero/blob/master/LICENSE", - "requireLicenseAcceptance": false, - "repository": { - "type": "git", - "url": "https://github.com/aspnetboilerplate/module-zero" - }, - "files": { - "mappings": { - "lib/net452/": "bin/Release/net452/Abp.Zero.Ldap.pdb" - } - } - } -} diff --git a/src/Abp.Zero.NHibernate/Abp.Zero.NHibernate.csproj b/src/Abp.Zero.NHibernate/Abp.Zero.NHibernate.csproj new file mode 100644 index 00000000..38f69a0d --- /dev/null +++ b/src/Abp.Zero.NHibernate/Abp.Zero.NHibernate.csproj @@ -0,0 +1,41 @@ + + + + + + net46 + true + Abp.Zero.NHibernate + Abp.Zero.NHibernate + asp.net;asp.net mvc;boilerplate;application framework;web framework;framework;domain driven design;multitenancy;user management;role management;identity;nhibernate;fluentmigrator + false + false + false + false + false + false + Abp + + + + + lib/net46/ + true + + + + + + + + + + + + + + + + + + diff --git a/src/Abp.Zero.NHibernate/Abp.Zero.NHibernate.xproj b/src/Abp.Zero.NHibernate/Abp.Zero.NHibernate.xproj deleted file mode 100644 index 60c45ebf..00000000 --- a/src/Abp.Zero.NHibernate/Abp.Zero.NHibernate.xproj +++ /dev/null @@ -1,19 +0,0 @@ - - - - 14.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - - - - c64c24cd-74ce-47a2-a03c-06f4f78413f1 - Abp - .\obj - .\bin\ - v4.5.2 - - - 2.0 - - - \ No newline at end of file diff --git a/src/Abp.Zero.NHibernate/Zero/NHibernate/EntityMappings/EditionFeatureSettingMap.cs b/src/Abp.Zero.NHibernate/Zero/NHibernate/EntityMappings/EditionFeatureSettingMap.cs index 08c4232d..f230a349 100644 --- a/src/Abp.Zero.NHibernate/Zero/NHibernate/EntityMappings/EditionFeatureSettingMap.cs +++ b/src/Abp.Zero.NHibernate/Zero/NHibernate/EntityMappings/EditionFeatureSettingMap.cs @@ -9,7 +9,7 @@ public EditionFeatureSettingMap() { DiscriminatorValue("EditionFeatureSetting"); - References(x => x.Edition).Column("EditionId").Not.Nullable(); //TODO: Need to Map EditionId column? + References(x => x.Edition).Column("EditionId").Not.Nullable(); } } } \ No newline at end of file diff --git a/src/Abp.Zero.NHibernate/Zero/NHibernate/EntityMappings/UserLoginAttemptMap.cs b/src/Abp.Zero.NHibernate/Zero/NHibernate/EntityMappings/UserLoginAttemptMap.cs index e26d205a..6f0a0798 100644 --- a/src/Abp.Zero.NHibernate/Zero/NHibernate/EntityMappings/UserLoginAttemptMap.cs +++ b/src/Abp.Zero.NHibernate/Zero/NHibernate/EntityMappings/UserLoginAttemptMap.cs @@ -1,3 +1,4 @@ +using Abp.Authorization; using Abp.Authorization.Users; using Abp.NHibernate.EntityMappings; diff --git a/src/Abp.Zero.NHibernate/project.json b/src/Abp.Zero.NHibernate/project.json deleted file mode 100644 index c27b39b1..00000000 --- a/src/Abp.Zero.NHibernate/project.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "version": "1.0.0.0-*", - - "dependencies": { - "Abp.FluentMigrator": "1.0.0", - "Abp.NHibernate": "1.0.0", - "Abp.Zero": "1.0.0.0-*" - }, - - "frameworks": { - "net452": { - "frameworkAssemblies": { - } - } - }, - - "buildOptions": { - "nowarn": [ - "CS1591" - ], - "xmlDoc": true, - "embed": { - "include": [ - ] - } - }, - - "packOptions": { - "summary": "ASP.NET Boilerplate - Module Zero.", - "tags": [ "asp.net", "asp.net mvc", "boilerplate", "application framework", "web framework", "framework", "domain driven design", "multitenancy", "user management", "role management", "identity", "nhibernate", "fluentmigrator" ], - "owners": [ "Halil İbrahim Kalkan" ], - "iconUrl": "http://www.aspnetboilerplate.com/images/abp_nupkg.png", - "projectUrl": "http://www.aspnetboilerplate.com/", - "licenseUrl": "https://github.com/aspnetboilerplate/module-zero/blob/master/LICENSE", - "requireLicenseAcceptance": false, - "repository": { - "type": "git", - "url": "https://github.com/aspnetboilerplate/module-zero" - }, - "files": { - "mappings": { - "lib/net452/": "bin/Release/net452/Abp.Zero.NHibernate.pdb" - } - } - } -} diff --git a/src/Abp.Zero.Owin/Abp.Zero.Owin.csproj b/src/Abp.Zero.Owin/Abp.Zero.Owin.csproj new file mode 100644 index 00000000..261de531 --- /dev/null +++ b/src/Abp.Zero.Owin/Abp.Zero.Owin.csproj @@ -0,0 +1,37 @@ + + + + + + net46 + true + Abp.Zero.Owin + Abp.Zero.Owin + asp.net;asp.net mvc;boilerplate;application framework;web framework;framework;domain driven design;multitenancy;user management;role management;identity;owin + false + false + false + Abp + + + + + lib/net46/ + true + + + + + + + + + + + + + + + + + diff --git a/src/Abp.Zero.Owin/Abp.Zero.Owin.xproj b/src/Abp.Zero.Owin/Abp.Zero.Owin.xproj deleted file mode 100644 index 1b3146d7..00000000 --- a/src/Abp.Zero.Owin/Abp.Zero.Owin.xproj +++ /dev/null @@ -1,19 +0,0 @@ - - - - 14.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - - - - 81d2351f-6ca1-4976-86eb-63946ebd0a36 - Abp - .\obj - .\bin\ - v4.5.2 - - - 2.0 - - - \ No newline at end of file diff --git a/src/Abp.Zero.Owin/ClaimsIdentityExtensions.cs b/src/Abp.Zero.Owin/ClaimsIdentityExtensions.cs deleted file mode 100644 index 340901b1..00000000 --- a/src/Abp.Zero.Owin/ClaimsIdentityExtensions.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using System.Security.Claims; -using System.Security.Principal; -using Abp.Runtime.Security; -using Microsoft.AspNet.Identity; - -namespace Abp -{ - //TODO: Use from ABP after ABP v1.0! - public static class ClaimsIdentityExtensions - { - public static int? GetTenantId(this IIdentity identity) - { - var claimsIdentity = identity as ClaimsIdentity; - - var tenantIdOrNull = claimsIdentity?.FindFirstValue(AbpClaimTypes.TenantId); - if (tenantIdOrNull == null) - { - return null; - } - - return Convert.ToInt32(tenantIdOrNull); - } - } -} \ No newline at end of file diff --git a/src/Abp.Zero.Owin/project.json b/src/Abp.Zero.Owin/project.json deleted file mode 100644 index c8b1496b..00000000 --- a/src/Abp.Zero.Owin/project.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "version": "1.0.0.0-*", - - "dependencies": { - "Abp.Zero": "1.0.0.0-*", - "Microsoft.AspNet.Identity.Owin": "2.2.1" - }, - - "frameworks": { - "net452": { - } - }, - - "buildOptions": { - "nowarn": [ - "CS1591" - ], - "xmlDoc": true - }, - - "packOptions": { - "summary": "ASP.NET Boilerplate - Module Zero - OWIN.", - "tags": [ "asp.net", "asp.net mvc", "boilerplate", "application framework", "web framework", "framework", "domain driven design", "multitenancy", "user management", "role management", "identity", "owin" ], - "owners": [ "Halil İbrahim Kalkan" ], - "iconUrl": "http://www.aspnetboilerplate.com/images/abp_nupkg.png", - "projectUrl": "http://www.aspnetboilerplate.com/", - "licenseUrl": "https://github.com/aspnetboilerplate/module-zero/blob/master/LICENSE", - "requireLicenseAcceptance": false, - "repository": { - "type": "git", - "url": "https://github.com/aspnetboilerplate/module-zero" - }, - "files": { - "mappings": { - "lib/net452/": "bin/Release/net452/Abp.Zero.Owin.pdb" - } - } - } -} diff --git a/src/Abp.Zero/Abp.Zero.csproj b/src/Abp.Zero/Abp.Zero.csproj new file mode 100644 index 00000000..32fb3f22 --- /dev/null +++ b/src/Abp.Zero/Abp.Zero.csproj @@ -0,0 +1,49 @@ + + + + + + net46 + true + Abp.Zero + Abp.Zero + asp.net;asp.net mvc;boilerplate;application framework;web framework;framework;domain driven design;multitenancy;user management;role management;identity + false + false + false + false + false + false + Abp + + + + + + + + + lib/net46/ + true + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Abp.Zero/Abp.Zero.xproj b/src/Abp.Zero/Abp.Zero.xproj deleted file mode 100644 index 97f95199..00000000 --- a/src/Abp.Zero/Abp.Zero.xproj +++ /dev/null @@ -1,24 +0,0 @@ - - - - 14.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - - - - e06353a0-8403-44a0-8d57-200d55e2f2c5 - Abp - .\obj - .\bin\ - v4.5.2 - - - 2.0 - - - - - - - - \ No newline at end of file diff --git a/src/Abp.Zero/Authorization/AbpLoginManager.cs b/src/Abp.Zero/Authorization/AbpLoginManager.cs index 5703ebaf..2b6d1c98 100644 --- a/src/Abp.Zero/Authorization/AbpLoginManager.cs +++ b/src/Abp.Zero/Authorization/AbpLoginManager.cs @@ -20,25 +20,24 @@ namespace Abp.Authorization { - //SignInManager, - public class AbpLogInManager : ITransientDependency + public abstract class AbpLogInManager : ITransientDependency where TTenant : AbpTenant where TRole : AbpRole, new() where TUser : AbpUser { public IClientInfoProvider ClientInfoProvider { get; set; } - private readonly IMultiTenancyConfig _multiTenancyConfig; - private readonly IRepository _tenantRepository; - private readonly IUnitOfWorkManager _unitOfWorkManager; - private readonly AbpUserManager _userManager; - private readonly ISettingManager _settingManager; - private readonly IRepository _userLoginAttemptRepository; - private readonly IUserManagementConfig _userManagementConfig; - private readonly IIocResolver _iocResolver; - private readonly AbpRoleManager _roleManager; - - public AbpLogInManager( + protected IMultiTenancyConfig MultiTenancyConfig { get; } + protected IRepository TenantRepository { get; } + protected IUnitOfWorkManager UnitOfWorkManager { get; } + protected AbpUserManager UserManager { get; } + protected ISettingManager SettingManager { get; } + protected IRepository UserLoginAttemptRepository { get; } + protected IUserManagementConfig UserManagementConfig { get; } + protected IIocResolver IocResolver { get; } + protected AbpRoleManager RoleManager { get; } + + protected AbpLogInManager( AbpUserManager userManager, IMultiTenancyConfig multiTenancyConfig, IRepository tenantRepository, @@ -46,18 +45,18 @@ public AbpLogInManager( ISettingManager settingManager, IRepository userLoginAttemptRepository, IUserManagementConfig userManagementConfig, - IIocResolver iocResolver, + IIocResolver iocResolver, AbpRoleManager roleManager) { - _multiTenancyConfig = multiTenancyConfig; - _tenantRepository = tenantRepository; - _unitOfWorkManager = unitOfWorkManager; - _settingManager = settingManager; - _userLoginAttemptRepository = userLoginAttemptRepository; - _userManagementConfig = userManagementConfig; - _iocResolver = iocResolver; - _roleManager = roleManager; - _userManager = userManager; + MultiTenancyConfig = multiTenancyConfig; + TenantRepository = tenantRepository; + UnitOfWorkManager = unitOfWorkManager; + SettingManager = settingManager; + UserLoginAttemptRepository = userLoginAttemptRepository; + UserManagementConfig = userManagementConfig; + IocResolver = iocResolver; + RoleManager = roleManager; + UserManager = userManager; ClientInfoProvider = NullClientInfoProvider.Instance; } @@ -70,7 +69,7 @@ public virtual async Task> LoginAsync(UserLoginIn return result; } - private async Task> LoginAsyncInternal(UserLoginInfo login, string tenancyName) + protected virtual async Task> LoginAsyncInternal(UserLoginInfo login, string tenancyName) { if (login == null || login.LoginProvider.IsNullOrEmpty() || login.ProviderKey.IsNullOrEmpty()) { @@ -79,13 +78,13 @@ private async Task> LoginAsyncInternal(UserLoginI //Get and check tenant TTenant tenant = null; - if (!_multiTenancyConfig.IsEnabled) + if (!MultiTenancyConfig.IsEnabled) { tenant = await GetDefaultTenantAsync(); } else if (!string.IsNullOrWhiteSpace(tenancyName)) { - tenant = await _tenantRepository.FirstOrDefaultAsync(t => t.TenancyName == tenancyName); + tenant = await TenantRepository.FirstOrDefaultAsync(t => t.TenancyName == tenancyName); if (tenant == null) { return new AbpLoginResult(AbpLoginResultType.InvalidTenancyName); @@ -98,9 +97,9 @@ private async Task> LoginAsyncInternal(UserLoginI } int? tenantId = tenant == null ? (int?)null : tenant.Id; - using (_unitOfWorkManager.Current.SetTenantId(tenantId)) + using (UnitOfWorkManager.Current.SetTenantId(tenantId)) { - var user = await _userManager.AbpStore.FindAsync(tenantId, login); + var user = await UserManager.AbpStore.FindAsync(tenantId, login); if (user == null) { return new AbpLoginResult(AbpLoginResultType.UnknownExternalLogin, tenant); @@ -118,7 +117,7 @@ public virtual async Task> LoginAsync(string user return result; } - private async Task> LoginAsyncInternal(string userNameOrEmailAddress, string plainPassword, string tenancyName, bool shouldLockout) + protected virtual async Task> LoginAsyncInternal(string userNameOrEmailAddress, string plainPassword, string tenancyName, bool shouldLockout) { if (userNameOrEmailAddress.IsNullOrEmpty()) { @@ -132,15 +131,15 @@ private async Task> LoginAsyncInternal(string use //Get and check tenant TTenant tenant = null; - using (_unitOfWorkManager.Current.SetTenantId(null)) + using (UnitOfWorkManager.Current.SetTenantId(null)) { - if (!_multiTenancyConfig.IsEnabled) + if (!MultiTenancyConfig.IsEnabled) { tenant = await GetDefaultTenantAsync(); } else if (!string.IsNullOrWhiteSpace(tenancyName)) { - tenant = await _tenantRepository.FirstOrDefaultAsync(t => t.TenancyName == tenancyName); + tenant = await TenantRepository.FirstOrDefaultAsync(t => t.TenancyName == tenancyName); if (tenant == null) { return new AbpLoginResult(AbpLoginResultType.InvalidTenancyName); @@ -154,12 +153,12 @@ private async Task> LoginAsyncInternal(string use } var tenantId = tenant == null ? (int?)null : tenant.Id; - using (_unitOfWorkManager.Current.SetTenantId(tenantId)) + using (UnitOfWorkManager.Current.SetTenantId(tenantId)) { //TryLoginFromExternalAuthenticationSources method may create the user, that's why we are calling it before AbpStore.FindByNameOrEmailAsync var loggedInFromExternalSource = await TryLoginFromExternalAuthenticationSources(userNameOrEmailAddress, plainPassword, tenant); - var user = await _userManager.AbpStore.FindByNameOrEmailAsync(tenantId, userNameOrEmailAddress); + var user = await UserManager.AbpStore.FindByNameOrEmailAsync(tenantId, userNameOrEmailAddress); if (user == null) { return new AbpLoginResult(AbpLoginResultType.InvalidUserNameOrEmailAddress, tenant); @@ -167,35 +166,50 @@ private async Task> LoginAsyncInternal(string use if (!loggedInFromExternalSource) { - _userManager.InitializeLockoutSettings(tenantId); + UserManager.InitializeLockoutSettings(tenantId); - if (await _userManager.IsLockedOutAsync(user.Id)) + if (await UserManager.IsLockedOutAsync(user.Id)) { return new AbpLoginResult(AbpLoginResultType.LockedOut, tenant, user); } - var verificationResult = _userManager.PasswordHasher.VerifyHashedPassword(user.Password, plainPassword); - if (verificationResult != PasswordVerificationResult.Success) + var verificationResult = UserManager.PasswordHasher.VerifyHashedPassword(user.Password, plainPassword); + if (verificationResult == PasswordVerificationResult.Failed) { - if (shouldLockout) - { - if (await TryLockOutAsync(tenantId, user.Id)) - { - return new AbpLoginResult(AbpLoginResultType.LockedOut, tenant, user); - } - } + return await GetFailedPasswordValidationAsLoginResultAsync(user, tenant, shouldLockout); + } - return new AbpLoginResult(AbpLoginResultType.InvalidPassword, tenant, user); + if (verificationResult == PasswordVerificationResult.SuccessRehashNeeded) + { + return await GetSuccessRehashNeededAsLoginResultAsync(user, tenant); } - await _userManager.ResetAccessFailedCountAsync(user.Id); + await UserManager.ResetAccessFailedCountAsync(user.Id); } return await CreateLoginResultAsync(user, tenant); } } - private async Task> CreateLoginResultAsync(TUser user, TTenant tenant = null) + protected virtual async Task> GetFailedPasswordValidationAsLoginResultAsync(TUser user, TTenant tenant = null, bool shouldLockout = false) + { + if (shouldLockout) + { + if (await TryLockOutAsync(user.TenantId, user.Id)) + { + return new AbpLoginResult(AbpLoginResultType.LockedOut, tenant, user); + } + } + + return new AbpLoginResult(AbpLoginResultType.InvalidPassword, tenant, user); + } + + protected virtual async Task> GetSuccessRehashNeededAsLoginResultAsync(TUser user, TTenant tenant = null, bool shouldLockout = false) + { + return await GetFailedPasswordValidationAsLoginResultAsync(user, tenant, shouldLockout); + } + + protected virtual async Task> CreateLoginResultAsync(TUser user, TTenant tenant = null) { if (!user.IsActive) { @@ -209,30 +223,30 @@ private async Task> CreateLoginResultAsync(TUser user.LastLoginTime = Clock.Now; - await _userManager.AbpStore.UpdateAsync(user); + await UserManager.AbpStore.UpdateAsync(user); - await _unitOfWorkManager.Current.SaveChangesAsync(); + await UnitOfWorkManager.Current.SaveChangesAsync(); return new AbpLoginResult( tenant, user, - await _userManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie) + await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie) ); } - private async Task SaveLoginAttempt(AbpLoginResult loginResult, string tenancyName, string userNameOrEmailAddress) + protected virtual async Task SaveLoginAttempt(AbpLoginResult loginResult, string tenancyName, string userNameOrEmailAddress) { - using (var uow = _unitOfWorkManager.Begin(TransactionScopeOption.Suppress)) + using (var uow = UnitOfWorkManager.Begin(TransactionScopeOption.Suppress)) { var tenantId = loginResult.Tenant != null ? loginResult.Tenant.Id : (int?)null; - using (_unitOfWorkManager.Current.SetTenantId(tenantId)) + using (UnitOfWorkManager.Current.SetTenantId(tenantId)) { var loginAttempt = new UserLoginAttempt { TenantId = tenantId, TenancyName = tenancyName, - UserId = loginResult.User != null ? loginResult.User.Id : (long?) null, + UserId = loginResult.User != null ? loginResult.User.Id : (long?)null, UserNameOrEmailAddress = userNameOrEmailAddress, Result = loginResult.Result, @@ -242,25 +256,25 @@ private async Task SaveLoginAttempt(AbpLoginResult loginResult, ClientName = ClientInfoProvider.ComputerName, }; - await _userLoginAttemptRepository.InsertAsync(loginAttempt); - await _unitOfWorkManager.Current.SaveChangesAsync(); + await UserLoginAttemptRepository.InsertAsync(loginAttempt); + await UnitOfWorkManager.Current.SaveChangesAsync(); await uow.CompleteAsync(); } } } - private async Task TryLockOutAsync(int? tenantId, long userId) + protected virtual async Task TryLockOutAsync(int? tenantId, long userId) { - using (var uow = _unitOfWorkManager.Begin(TransactionScopeOption.Suppress)) + using (var uow = UnitOfWorkManager.Begin(TransactionScopeOption.Suppress)) { - using (_unitOfWorkManager.Current.SetTenantId(tenantId)) + using (UnitOfWorkManager.Current.SetTenantId(tenantId)) { - (await _userManager.AccessFailedAsync(userId)).CheckErrors(); + (await UserManager.AccessFailedAsync(userId)).CheckErrors(); - var isLockOut = await _userManager.IsLockedOutAsync(userId); + var isLockOut = await UserManager.IsLockedOutAsync(userId); - await _unitOfWorkManager.Current.SaveChangesAsync(); + await UnitOfWorkManager.Current.SaveChangesAsync(); await uow.CompleteAsync(); @@ -269,38 +283,41 @@ private async Task TryLockOutAsync(int? tenantId, long userId) } } - private async Task TryLoginFromExternalAuthenticationSources(string userNameOrEmailAddress, string plainPassword, TTenant tenant) + protected virtual async Task TryLoginFromExternalAuthenticationSources(string userNameOrEmailAddress, string plainPassword, TTenant tenant) { - if (!_userManagementConfig.ExternalAuthenticationSources.Any()) + if (!UserManagementConfig.ExternalAuthenticationSources.Any()) { return false; } - foreach (var sourceType in _userManagementConfig.ExternalAuthenticationSources) + foreach (var sourceType in UserManagementConfig.ExternalAuthenticationSources) { - using (var source = _iocResolver.ResolveAsDisposable>(sourceType)) + using (var source = IocResolver.ResolveAsDisposable>(sourceType)) { if (await source.Object.TryAuthenticateAsync(userNameOrEmailAddress, plainPassword, tenant)) { var tenantId = tenant == null ? (int?)null : tenant.Id; - using (_unitOfWorkManager.Current.SetTenantId(tenantId)) + using (UnitOfWorkManager.Current.SetTenantId(tenantId)) { - var user = await _userManager.AbpStore.FindByNameOrEmailAsync(tenantId, userNameOrEmailAddress); + var user = await UserManager.AbpStore.FindByNameOrEmailAsync(tenantId, userNameOrEmailAddress); if (user == null) { user = await source.Object.CreateUserAsync(userNameOrEmailAddress, tenant); user.TenantId = tenantId; user.AuthenticationSource = source.Object.Name; - user.Password = _userManager.PasswordHasher.HashPassword(Guid.NewGuid().ToString("N").Left(16)); //Setting a random password since it will not be used + user.Password = UserManager.PasswordHasher.HashPassword(Guid.NewGuid().ToString("N").Left(16)); //Setting a random password since it will not be used - user.Roles = new List(); - foreach (var defaultRole in _roleManager.Roles.Where(r => r.TenantId == tenantId && r.IsDefault).ToList()) + if (user.Roles == null) { - user.Roles.Add(new UserRole(tenantId, user.Id, defaultRole.Id)); + user.Roles = new List(); + foreach (var defaultRole in RoleManager.Roles.Where(r => r.TenantId == tenantId && r.IsDefault).ToList()) + { + user.Roles.Add(new UserRole(tenantId, user.Id, defaultRole.Id)); + } } - await _userManager.AbpStore.CreateAsync(user); + await UserManager.AbpStore.CreateAsync(user); } else { @@ -308,10 +325,10 @@ private async Task TryLoginFromExternalAuthenticationSources(string userNa user.AuthenticationSource = source.Object.Name; - await _userManager.AbpStore.UpdateAsync(user); + await UserManager.AbpStore.UpdateAsync(user); } - await _unitOfWorkManager.Current.SaveChangesAsync(); + await UnitOfWorkManager.Current.SaveChangesAsync(); return true; } @@ -322,9 +339,9 @@ private async Task TryLoginFromExternalAuthenticationSources(string userNa return false; } - private async Task GetDefaultTenantAsync() + protected virtual async Task GetDefaultTenantAsync() { - var tenant = await _tenantRepository.FirstOrDefaultAsync(t => t.TenancyName == AbpTenant.DefaultTenantName); + var tenant = await TenantRepository.FirstOrDefaultAsync(t => t.TenancyName == AbpTenant.DefaultTenantName); if (tenant == null) { throw new AbpException("There should be a 'Default' tenant if multi-tenancy is disabled!"); @@ -333,14 +350,14 @@ private async Task GetDefaultTenantAsync() return tenant; } - private async Task IsEmailConfirmationRequiredForLoginAsync(int? tenantId) + protected virtual async Task IsEmailConfirmationRequiredForLoginAsync(int? tenantId) { if (tenantId.HasValue) { - return await _settingManager.GetSettingValueForTenantAsync(AbpZeroSettingNames.UserManagement.IsEmailConfirmationRequiredForLogin, tenantId.Value); + return await SettingManager.GetSettingValueForTenantAsync(AbpZeroSettingNames.UserManagement.IsEmailConfirmationRequiredForLogin, tenantId.Value); } - return await _settingManager.GetSettingValueForApplicationAsync(AbpZeroSettingNames.UserManagement.IsEmailConfirmationRequiredForLogin); + return await SettingManager.GetSettingValueForApplicationAsync(AbpZeroSettingNames.UserManagement.IsEmailConfirmationRequiredForLogin); } } } diff --git a/src/Abp.Zero/Authorization/PermissionChecker.cs b/src/Abp.Zero/Authorization/PermissionChecker.cs index 2c862546..0769906b 100644 --- a/src/Abp.Zero/Authorization/PermissionChecker.cs +++ b/src/Abp.Zero/Authorization/PermissionChecker.cs @@ -3,7 +3,6 @@ using Abp.Authorization.Users; using Abp.Dependency; using Abp.Domain.Uow; -using Abp.MultiTenancy; using Abp.Runtime.Session; using Castle.Core.Logging; @@ -12,13 +11,11 @@ namespace Abp.Authorization /// /// Application should inherit this class to implement . /// - /// /// /// - public abstract class PermissionChecker : IPermissionChecker, ITransientDependency, IIocManagerAccessor + public abstract class PermissionChecker : IPermissionChecker, ITransientDependency, IIocManagerAccessor where TRole : AbpRole, new() where TUser : AbpUser - where TTenant : AbpTenant { private readonly AbpUserManager _userManager; diff --git a/src/Abp.Zero/Authorization/Roles/AbpRole.cs b/src/Abp.Zero/Authorization/Roles/AbpRole.cs index 91b5f6a5..36553661 100644 --- a/src/Abp.Zero/Authorization/Roles/AbpRole.cs +++ b/src/Abp.Zero/Authorization/Roles/AbpRole.cs @@ -1,9 +1,6 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; -using Abp.Authorization.Users; +using Abp.Authorization.Users; using Abp.Domain.Entities.Auditing; +using Microsoft.AspNet.Identity; namespace Abp.Authorization.Roles { @@ -12,85 +9,43 @@ namespace Abp.Authorization.Roles /// /// /// Application should use permissions to check if user is granted to perform an operation. - /// Checking 'if a user has a role' is not possible until the role is static (). + /// Checking 'if a user has a role' is not possible until the role is static (). /// Static roles can be used in the code and can not be deleted by users. /// Non-static (dynamic) roles can be added/removed by users and we can not know their name while coding. /// A user can have multiple roles. Thus, user will have all permissions of all assigned roles. /// - public abstract class AbpRole : AbpRoleBase, IFullAudited + public abstract class AbpRole : AbpRoleBase, IRole, IFullAudited where TUser : AbpUser { - /// - /// Maximum length of the property. - /// - public const int MaxDisplayNameLength = 64; - - /// - /// Display name of this role. - /// - [Required] - [StringLength(MaxDisplayNameLength)] - public virtual string DisplayName { get; set; } - - /// - /// Is this a static role? - /// Static roles can not be deleted, can not change their name. - /// They can be used programmatically. - /// - public virtual bool IsStatic { get; set; } - - /// - /// Is this role will be assigned to new users as default? - /// - public virtual bool IsDefault { get; set; } - - /// - /// List of permissions of the role. - /// - [ForeignKey("RoleId")] - public virtual ICollection Permissions { get; set; } - public virtual TUser DeleterUser { get; set; } public virtual TUser CreatorUser { get; set; } public virtual TUser LastModifierUser { get; set; } - /// - /// Creates a new object. - /// - public AbpRole() + protected AbpRole() { - Name = Guid.NewGuid().ToString("N"); } /// - /// Creates a new object. + /// Creates a new object. /// /// TenantId or null (if this is not a tenant-level role) /// Display name of the role - public AbpRole(int? tenantId, string displayName) - : this() + protected AbpRole(int? tenantId, string displayName) + : base(tenantId, displayName) { - TenantId = tenantId; - DisplayName = displayName; } /// - /// Creates a new object. + /// Creates a new object. /// /// TenantId or null (if this is not a tenant-level role) /// Unique role name /// Display name of the role - public AbpRole(int? tenantId, string name, string displayName) - : this(tenantId, displayName) - { - Name = name; - } - - public override string ToString() + protected AbpRole(int? tenantId, string name, string displayName) + : base(tenantId, name, displayName) { - return string.Format("[Role {0}, Name={1}]", Id, Name); } } -} +} \ No newline at end of file diff --git a/src/Abp.Zero/Authorization/Roles/AbpRoleBase.cs b/src/Abp.Zero/Authorization/Roles/AbpRoleBase.cs deleted file mode 100644 index a8a25d5f..00000000 --- a/src/Abp.Zero/Authorization/Roles/AbpRoleBase.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; -using Abp.Domain.Entities; -using Abp.Domain.Entities.Auditing; -using Microsoft.AspNet.Identity; - -namespace Abp.Authorization.Roles -{ - /// - /// Base class for role. - /// - [Table("AbpRoles")] - public abstract class AbpRoleBase : FullAuditedEntity, IRole, IMayHaveTenant - { - /// - /// Maximum length of the property. - /// - public const int MaxNameLength = 32; - - /// - /// Tenant's Id, if this role is a tenant-level role. Null, if not. - /// - public virtual int? TenantId { get; set; } - - /// - /// Unique name of this role. - /// - [Required] - [StringLength(MaxNameLength)] - public virtual string Name { get; set; } - } -} \ No newline at end of file diff --git a/src/Abp.Zero/Authorization/Roles/AbpRoleManager.cs b/src/Abp.Zero/Authorization/Roles/AbpRoleManager.cs index 9e0394d7..f3578dfb 100644 --- a/src/Abp.Zero/Authorization/Roles/AbpRoleManager.cs +++ b/src/Abp.Zero/Authorization/Roles/AbpRoleManager.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; @@ -32,16 +31,16 @@ public abstract class AbpRoleManager public IRoleManagementConfig RoleManagementConfig { get; private set; } - private IRolePermissionStore RolePermissionStore + private IRolePermissionStore RolePermissionStore { get { - if (!(Store is IRolePermissionStore)) + if (!(Store is IRolePermissionStore)) { throw new AbpException("Store is not IRolePermissionStore"); } - return Store as IRolePermissionStore; + return Store as IRolePermissionStore; } } @@ -72,58 +71,6 @@ protected AbpRoleManager( LocalizationManager = NullLocalizationManager.Instance; } - #region Obsolete methods - - /// - /// Checks if a role has a permission. - /// - /// The role's name to check it's permission - /// Name of the permission - /// True, if the role has the permission - [Obsolete("Use IsGrantedAsync instead.")] - public virtual async Task HasPermissionAsync(string roleName, string permissionName) - { - return await IsGrantedAsync((await GetRoleByNameAsync(roleName)).Id, _permissionManager.GetPermission(permissionName)); - } - - /// - /// Checks if a role has a permission. - /// - /// The role's id to check it's permission - /// Name of the permission - /// True, if the role has the permission - [Obsolete("Use IsGrantedAsync instead.")] - public virtual async Task HasPermissionAsync(int roleId, string permissionName) - { - return await IsGrantedAsync(roleId, _permissionManager.GetPermission(permissionName)); - } - - /// - /// Checks if a role has a permission. - /// - /// The role - /// The permission - /// True, if the role has the permission - [Obsolete("Use IsGrantedAsync instead.")] - public Task HasPermissionAsync(TRole role, Permission permission) - { - return IsGrantedAsync(role.Id, permission); - } - - /// - /// Checks if a role has a permission. - /// - /// role id - /// The permission - /// True, if the role has the permission - [Obsolete("Use IsGrantedAsync instead.")] - public Task HasPermissionAsync(int roleId, Permission permission) - { - return IsGrantedAsync(roleId, permission); - } - - #endregion - /// /// Checks if a role is granted for a permission. /// diff --git a/src/Abp.Zero/Authorization/Roles/AbpRoleStore.cs b/src/Abp.Zero/Authorization/Roles/AbpRoleStore.cs index a8db7b93..29c07199 100644 --- a/src/Abp.Zero/Authorization/Roles/AbpRoleStore.cs +++ b/src/Abp.Zero/Authorization/Roles/AbpRoleStore.cs @@ -13,7 +13,7 @@ namespace Abp.Authorization.Roles /// public abstract class AbpRoleStore : IQueryableRoleStore, - IRolePermissionStore, + IRolePermissionStore, ITransientDependency diff --git a/src/Abp.Zero/Authorization/Users/AbpLoginResult.cs b/src/Abp.Zero/Authorization/Users/AbpLoginResult.cs index c04ee795..31c90a94 100644 --- a/src/Abp.Zero/Authorization/Users/AbpLoginResult.cs +++ b/src/Abp.Zero/Authorization/Users/AbpLoginResult.cs @@ -5,7 +5,7 @@ namespace Abp.Authorization.Users { public class AbpLoginResult where TTenant : AbpTenant - where TUser : AbpUser + where TUser : AbpUserBase { public AbpLoginResultType Result { get; private set; } diff --git a/src/Abp.Zero/Authorization/Users/AbpUser.cs b/src/Abp.Zero/Authorization/Users/AbpUser.cs index 4666296e..b5d5eabf 100644 --- a/src/Abp.Zero/Authorization/Users/AbpUser.cs +++ b/src/Abp.Zero/Authorization/Users/AbpUser.cs @@ -1,211 +1,18 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; -using Abp.Configuration; -using Abp.Domain.Entities; using Abp.Domain.Entities.Auditing; -using Abp.Extensions; +using Microsoft.AspNet.Identity; namespace Abp.Authorization.Users { /// /// Represents a user. /// - public abstract class AbpUser : AbpUserBase, IFullAudited, IPassivable + public abstract class AbpUser : AbpUserBase, IUser, IFullAudited where TUser : AbpUser { - /// - /// UserName of the admin. - /// admin can not be deleted and UserName of the admin can not be changed. - /// - public const string AdminUserName = "admin"; - - /// - /// Maximum length of the property. - /// - public const int MaxNameLength = 32; - - /// - /// Maximum length of the property. - /// - public const int MaxSurnameLength = 32; - - /// - /// Maximum length of the property. - /// - public const int MaxPasswordLength = 128; - - /// - /// Maximum length of the without hashed. - /// - public const int MaxPlainPasswordLength = 32; - - /// - /// Maximum length of the property. - /// - public const int MaxEmailConfirmationCodeLength = 128; - - /// - /// Maximum length of the property. - /// - public const int MaxPasswordResetCodeLength = 328; - - /// - /// Maximum length of the property. - /// - public const int MaxAuthenticationSourceLength = 64; - - /// - /// Authorization source name. - /// It's set to external authentication source name if created by an external source. - /// Default: null. - /// - [MaxLength(MaxAuthenticationSourceLength)] - public virtual string AuthenticationSource { get; set; } - - /// - /// Name of the user. - /// - [Required] - [StringLength(MaxNameLength)] - public virtual string Name { get; set; } - - /// - /// Surname of the user. - /// - [Required] - [StringLength(MaxSurnameLength)] - public virtual string Surname { get; set; } - - /// - /// Return full name (Name Surname ) - /// - [NotMapped] - public virtual string FullName { get { return this.Name + " " + this.Surname; } } - - /// - /// Password of the user. - /// - [Required] - [StringLength(MaxPasswordLength)] - public virtual string Password { get; set; } - - /// - /// Is the confirmed. - /// - public virtual bool IsEmailConfirmed { get; set; } - - /// - /// Confirmation code for email. - /// - [StringLength(MaxEmailConfirmationCodeLength)] - public virtual string EmailConfirmationCode { get; set; } - - /// - /// Reset code for password. - /// It's not valid if it's null. - /// It's for one usage and must be set to null after reset. - /// - [StringLength(MaxPasswordResetCodeLength)] - public virtual string PasswordResetCode { get; set; } - - /// - /// Lockout end date. - /// - public virtual DateTime? LockoutEndDateUtc { get; set; } - - /// - /// Gets or sets the access failed count. - /// - public virtual int AccessFailedCount { get; set; } - - /// - /// Gets or sets the lockout enabled. - /// - public virtual bool IsLockoutEnabled { get; set; } - - /// - /// Gets or sets the phone number. - /// - public virtual string PhoneNumber {get; set; } - - /// - /// Is the confirmed. - /// - public virtual bool IsPhoneNumberConfirmed { get; set; } - - /// - /// Gets or sets the security stamp. - /// - public virtual string SecurityStamp { get; set; } - - /// - /// Is two factor auth enabled. - /// - public virtual bool IsTwoFactorEnabled { get; set; } - - /// - /// Is this user active? - /// If as user is not active, he/she can not use the application. - /// - public virtual bool IsActive { get; set; } - - /// - /// Login definitions for this user. - /// - [ForeignKey("UserId")] - public virtual ICollection Logins { get; set; } - - /// - /// Roles of this user. - /// - [ForeignKey("UserId")] - public virtual ICollection Roles { get; set; } - - /// - /// Claims of this user. - /// - [ForeignKey("UserId")] - public virtual ICollection Claims { get; set; } - - /// - /// Permission definitions for this user. - /// - [ForeignKey("UserId")] - public virtual ICollection Permissions { get; set; } - - /// - /// Settings for this user. - /// - [ForeignKey("UserId")] - public virtual ICollection Settings { get; set; } - public virtual TUser DeleterUser { get; set; } public virtual TUser CreatorUser { get; set; } public virtual TUser LastModifierUser { get; set; } - - protected AbpUser() - { - IsActive = true; - SecurityStamp = SequentialGuidGenerator.Instance.Create().ToString(); - } - - public virtual void SetNewPasswordResetCode() - { - PasswordResetCode = Guid.NewGuid().ToString("N").Truncate(MaxPasswordResetCodeLength); - } - - public virtual void SetNewEmailConfirmationCode() - { - EmailConfirmationCode = Guid.NewGuid().ToString("N").Truncate(MaxEmailConfirmationCodeLength); - } - - public override string ToString() - { - return string.Format("[User {0}] {1}", Id, UserName); - } } } \ No newline at end of file diff --git a/src/Abp.Zero/Authorization/Users/AbpUserBase.cs b/src/Abp.Zero/Authorization/Users/AbpUserBase.cs deleted file mode 100644 index c22e3b6f..00000000 --- a/src/Abp.Zero/Authorization/Users/AbpUserBase.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System; -using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; -using Abp.Domain.Entities; -using Abp.Domain.Entities.Auditing; -using Microsoft.AspNet.Identity; - -namespace Abp.Authorization.Users -{ - /// - /// Base class for user. - /// - [Table("AbpUsers")] - public abstract class AbpUserBase : FullAuditedEntity, IUser, IMayHaveTenant - { - /// - /// Maximum length of the property. - /// - public const int MaxUserNameLength = 32; - - /// - /// Maximum length of the property. - /// - public const int MaxEmailAddressLength = 256; - - /// - /// User name. - /// User name must be unique for it's tenant. - /// - [Required] - [StringLength(MaxUserNameLength)] - public virtual string UserName { get; set; } - - /// - /// Tenant Id of this user. - /// - public virtual int? TenantId { get; set; } - - /// - /// Email address of the user. - /// Email address must be unique for it's tenant. - /// - [Required] - [StringLength(MaxEmailAddressLength)] - public virtual string EmailAddress { get; set; } - - /// - /// The last time this user entered to the system. - /// - public virtual DateTime? LastLoginTime { get; set; } - - /// - /// Creates from this User. - /// - /// - public virtual UserIdentifier ToUserIdentifier() - { - return new UserIdentifier(TenantId, Id); - } - } -} \ No newline at end of file diff --git a/src/Abp.Zero/Authorization/Users/AbpUserManager.cs b/src/Abp.Zero/Authorization/Users/AbpUserManager.cs index 231239b0..53ebc615 100644 --- a/src/Abp.Zero/Authorization/Users/AbpUserManager.cs +++ b/src/Abp.Zero/Authorization/Users/AbpUserManager.cs @@ -162,9 +162,13 @@ public virtual async Task IsGrantedAsync(long userId, Permission permissio return false; } } - + //Get cached user permissions var cacheItem = await GetUserPermissionCacheItemAsync(userId); + if (cacheItem == null) + { + return false; + } //Check for user-specific value if (cacheItem.GrantedPermissions.Contains(permission.Name)) @@ -337,10 +341,13 @@ public async override Task UpdateAsync(TUser user) return result; } - var oldUserName = (await GetUserByIdAsync(user.Id)).UserName; - if (oldUserName == AbpUser.AdminUserName && user.UserName != AbpUser.AdminUserName) + //Admin user's username can not be changed! + if (user.UserName != AbpUser.AdminUserName) { - return AbpIdentityResult.Failed(string.Format(L("CanNotRenameAdminUser"), AbpUser.AdminUserName)); + if ((await GetOldUserNameAsync(user.Id)) == AbpUser.AdminUserName) + { + return AbpIdentityResult.Failed(string.Format(L("CanNotRenameAdminUser"), AbpUser.AdminUserName)); + } } return await base.UpdateAsync(user); @@ -373,7 +380,7 @@ public virtual async Task CheckDuplicateUsernameOrEmailAddressAs var user = (await FindByNameAsync(userName)); if (user != null && user.Id != expectedUserId) { - return AbpIdentityResult.Failed(string.Format(L("Identity.DuplicateName"), userName)); + return AbpIdentityResult.Failed(string.Format(L("Identity.DuplicateUserName"), userName)); } user = (await FindByEmailAsync(emailAddress)); @@ -631,11 +638,22 @@ public override async Task VerifyTwoFactorTokenAsync(long userId, string t return await base.VerifyTwoFactorTokenAsync(userId, twoFactorProvider, token); } + protected virtual Task GetOldUserNameAsync(long userId) + { + return AbpStore.GetUserNameFromDatabaseAsync(userId); + } + private async Task GetUserPermissionCacheItemAsync(long userId) { var cacheKey = userId + "@" + (GetCurrentTenantId() ?? 0); return await _cacheManager.GetUserPermissionCache().GetAsync(cacheKey, async () => { + var user = await FindByIdAsync(userId); + if (user == null) + { + return null; + } + var newCacheItem = new UserPermissionCacheItem(userId); foreach (var roleName in await GetRolesAsync(userId)) diff --git a/src/Abp.Zero/Authorization/Users/AbpUserManagerExtensions.cs b/src/Abp.Zero/Authorization/Users/AbpUserManagerExtensions.cs index 05f0ad82..fb957e68 100644 --- a/src/Abp.Zero/Authorization/Users/AbpUserManagerExtensions.cs +++ b/src/Abp.Zero/Authorization/Users/AbpUserManagerExtensions.cs @@ -1,6 +1,5 @@ using System; using Abp.Authorization.Roles; -using Abp.MultiTenancy; using Abp.Threading; namespace Abp.Authorization.Users diff --git a/src/Abp.Zero/Authorization/Users/AbpUserStore.cs b/src/Abp.Zero/Authorization/Users/AbpUserStore.cs index c9561610..425de423 100644 --- a/src/Abp.Zero/Authorization/Users/AbpUserStore.cs +++ b/src/Abp.Zero/Authorization/Users/AbpUserStore.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Security.Claims; using System.Threading.Tasks; +using System.Transactions; using Abp.Authorization.Roles; using Abp.Dependency; using Abp.Domain.Repositories; @@ -43,7 +44,7 @@ public abstract class AbpUserStore : private readonly IRepository _roleRepository; private readonly IRepository _userPermissionSettingRepository; private readonly IUnitOfWorkManager _unitOfWorkManager; - + /// /// Constructor. /// @@ -137,7 +138,7 @@ public virtual async Task FindByNameOrEmailAsync(int? tenantId, string us } #endregion - + #region IUserPasswordStore public virtual Task SetPasswordHashAsync(TUser user, string passwordHash) @@ -232,9 +233,9 @@ public virtual async Task FindAsync(UserLoginInfo login) public virtual Task> FindAllAsync(UserLoginInfo login) { var query = from userLogin in _userLoginRepository.GetAll() - join user in _userRepository.GetAll() on userLogin.UserId equals user.Id - where userLogin.LoginProvider == login.LoginProvider && userLogin.ProviderKey == login.ProviderKey - select user; + join user in _userRepository.GetAll() on userLogin.UserId equals user.Id + where userLogin.LoginProvider == login.LoginProvider && userLogin.ProviderKey == login.ProviderKey + select user; return Task.FromResult(query.ToList()); } @@ -278,9 +279,9 @@ public virtual async Task RemoveFromRoleAsync(TUser user, string roleName) public virtual async Task> GetRolesAsync(TUser user) { var query = from userRole in _userRoleRepository.GetAll() - join role in _roleRepository.GetAll() on userRole.RoleId equals role.Id - where userRole.UserId == user.Id - select role.Name; + join role in _roleRepository.GetAll() on userRole.RoleId equals role.Id + where userRole.UserId == user.Id + select role.Name; return await AsyncQueryableExecuter.ToListAsync(query); } @@ -489,5 +490,27 @@ public Task GetTwoFactorEnabledAsync(TUser user) { return Task.FromResult(user.IsTwoFactorEnabled); } + + public async Task GetUserNameFromDatabaseAsync(long userId) + { + //note: This workaround will not be needed after fixing https://github.com/aspnetboilerplate/aspnetboilerplate/issues/1828 + var outerUow = _unitOfWorkManager.Current; + using (var uow = _unitOfWorkManager.Begin(new UnitOfWorkOptions + { + Scope = TransactionScopeOption.RequiresNew, + IsTransactional = false, + IsolationLevel = IsolationLevel.ReadUncommitted + })) + { + if (outerUow != null) + { + _unitOfWorkManager.Current.SetTenantId(outerUow.GetTenantId()); + } + + var user = await _userRepository.GetAsync(userId); + await uow.CompleteAsync(); + return user.UserName; + } + } } } diff --git a/src/Abp.Zero/IdentityFramework/IdentityResultExtensions.cs b/src/Abp.Zero/IdentityFramework/IdentityResultExtensions.cs index 2a592744..6ce3d417 100644 --- a/src/Abp.Zero/IdentityFramework/IdentityResultExtensions.cs +++ b/src/Abp.Zero/IdentityFramework/IdentityResultExtensions.cs @@ -23,7 +23,7 @@ private static readonly Dictionary IdentityLocalizations {"User name {0} is invalid, can only contain letters or digits.", "Identity.InvalidUserName"}, {"Passwords must be at least {0} characters.", "Identity.PasswordTooShort"}, {"{0} cannot be null or empty.", "Identity.PropertyTooShort"}, - {"Name {0} is already taken.", "Identity.DuplicateName"}, + {"Name {0} is already taken.", "Identity.DuplicateUserName"}, {"User already has a password set.", "Identity.UserAlreadyHasPassword"}, {"Passwords must have at least one non letter or digit character.", "Identity.PasswordRequireNonLetterOrDigit"}, {"UserId not found.", "Identity.UserIdNotFound"}, diff --git a/src/Abp.Zero/Zero/AbpZeroCoreModule.cs b/src/Abp.Zero/Zero/AbpZeroCoreModule.cs index edca4750..a8888d3e 100644 --- a/src/Abp.Zero/Zero/AbpZeroCoreModule.cs +++ b/src/Abp.Zero/Zero/AbpZeroCoreModule.cs @@ -1,101 +1,28 @@ -using System.Linq; -using System.Reflection; -using Abp.Application.Features; -using Abp.Authorization.Roles; -using Abp.Authorization.Users; -using Abp.Dependency; -using Abp.Localization; -using Abp.Localization.Dictionaries; +using System.Reflection; using Abp.Localization.Dictionaries.Xml; +using Abp.Localization.Sources; using Abp.Modules; -using Abp.MultiTenancy; -using Abp.Reflection; -using Abp.Zero.Configuration; -using Castle.MicroKernel.Registration; namespace Abp.Zero { - /// - /// ABP zero core module. - /// - [DependsOn(typeof(AbpKernelModule))] + [DependsOn(typeof(AbpZeroCommonModule))] public class AbpZeroCoreModule : AbpModule { public override void PreInitialize() { - IocManager.Register(); - IocManager.Register(); - IocManager.Register(); - IocManager.Register(); - IocManager.Register(); - - Configuration.Settings.Providers.Add(); - - Configuration.Localization.Sources.Add( - new DictionaryBasedLocalizationSource( + Configuration.Localization.Sources.Extensions.Add( + new LocalizationSourceExtensionInfo( AbpZeroConsts.LocalizationSourceName, new XmlEmbeddedFileLocalizationDictionaryProvider( - Assembly.GetExecutingAssembly(), "Abp.Zero.Zero.Localization.Source" - ))); - - IocManager.IocContainer.Kernel.ComponentRegistered += Kernel_ComponentRegistered; + Assembly.GetExecutingAssembly(), "Abp.Zero.Localization.SourceExt" + ) + ) + ); } public override void Initialize() { - FillMissingEntityTypes(); - IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly()); - IocManager.Register(DependencyLifeStyle.Transient); //could not register conventionally - - RegisterTenantCache(); - } - - private void Kernel_ComponentRegistered(string key, Castle.MicroKernel.IHandler handler) - { - if (typeof(IAbpZeroFeatureValueStore).IsAssignableFrom(handler.ComponentModel.Implementation) && !IocManager.IsRegistered()) - { - IocManager.IocContainer.Register( - Component.For().ImplementedBy(handler.ComponentModel.Implementation).Named("AbpZeroFeatureValueStore").LifestyleTransient() - ); - } - } - - private void FillMissingEntityTypes() - { - using (var entityTypes = IocManager.ResolveAsDisposable()) - { - if (entityTypes.Object.User != null && - entityTypes.Object.Role != null && - entityTypes.Object.Tenant != null) - { - return; - } - - using (var typeFinder = IocManager.ResolveAsDisposable()) - { - var types = typeFinder.Object.FindAll(); - entityTypes.Object.Tenant = types.FirstOrDefault(t => typeof(AbpTenantBase).IsAssignableFrom(t) && !t.IsAbstract); - entityTypes.Object.Role = types.FirstOrDefault(t => typeof(AbpRoleBase).IsAssignableFrom(t) && !t.IsAbstract); - entityTypes.Object.User = types.FirstOrDefault(t => typeof(AbpUserBase).IsAssignableFrom(t) && !t.IsAbstract); - } - } - } - - private void RegisterTenantCache() - { - if (IocManager.IsRegistered()) - { - return; - } - - using (var entityTypes = IocManager.ResolveAsDisposable()) - { - var implType = typeof (TenantCache<,>) - .MakeGenericType(entityTypes.Object.Tenant, entityTypes.Object.User); - - IocManager.Register(typeof (ITenantCache), implType, DependencyLifeStyle.Transient); - } } } } diff --git a/src/Abp.Zero/Zero/Localization/SourceExt/AbpZero-ar.xml b/src/Abp.Zero/Zero/Localization/SourceExt/AbpZero-ar.xml new file mode 100644 index 00000000..0dae298f --- /dev/null +++ b/src/Abp.Zero/Zero/Localization/SourceExt/AbpZero-ar.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + البريد الإلكتروني + رمز الحماية + رمز الحماية الخاص بك هو: {0} + رسالة قصيرة + رمز الحماية الخاص بك هو: {0} + + diff --git a/src/Abp.Zero/Zero/Localization/Source/AbpZero-de.xml b/src/Abp.Zero/Zero/Localization/SourceExt/AbpZero-de.xml similarity index 72% rename from src/Abp.Zero/Zero/Localization/Source/AbpZero-de.xml rename to src/Abp.Zero/Zero/Localization/SourceExt/AbpZero-de.xml index a6a77072..0b0d6b5b 100644 --- a/src/Abp.Zero/Zero/Localization/Source/AbpZero-de.xml +++ b/src/Abp.Zero/Zero/Localization/SourceExt/AbpZero-de.xml @@ -8,7 +8,7 @@ - + @@ -22,12 +22,5 @@ - - - - - - - \ No newline at end of file diff --git a/src/Abp.Zero/Zero/Localization/SourceExt/AbpZero-es.xml b/src/Abp.Zero/Zero/Localization/SourceExt/AbpZero-es.xml new file mode 100644 index 00000000..3d7e30dc --- /dev/null +++ b/src/Abp.Zero/Zero/Localization/SourceExt/AbpZero-es.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Email + Código de seguridad + Su código de seguridad es: {0} + Sms + Su código de seguridad es: {0} + + \ No newline at end of file diff --git a/src/Abp.Zero/Zero/Localization/SourceExt/AbpZero-fa.xml b/src/Abp.Zero/Zero/Localization/SourceExt/AbpZero-fa.xml new file mode 100644 index 00000000..fd1a4785 --- /dev/null +++ b/src/Abp.Zero/Zero/Localization/SourceExt/AbpZero-fa.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Abp.Zero/Zero/Localization/Source/AbpZero-fr.xml b/src/Abp.Zero/Zero/Localization/SourceExt/AbpZero-fr.xml similarity index 65% rename from src/Abp.Zero/Zero/Localization/Source/AbpZero-fr.xml rename to src/Abp.Zero/Zero/Localization/SourceExt/AbpZero-fr.xml index e105ee0e..a5cf27b0 100644 --- a/src/Abp.Zero/Zero/Localization/Source/AbpZero-fr.xml +++ b/src/Abp.Zero/Zero/Localization/SourceExt/AbpZero-fr.xml @@ -8,7 +8,7 @@ - + @@ -22,13 +22,5 @@ - - - - - - - - \ No newline at end of file diff --git a/src/Abp.Zero/Zero/Localization/Source/AbpZero-it.xml b/src/Abp.Zero/Zero/Localization/SourceExt/AbpZero-it.xml similarity index 62% rename from src/Abp.Zero/Zero/Localization/Source/AbpZero-it.xml rename to src/Abp.Zero/Zero/Localization/SourceExt/AbpZero-it.xml index 9224eacb..4938526c 100644 --- a/src/Abp.Zero/Zero/Localization/Source/AbpZero-it.xml +++ b/src/Abp.Zero/Zero/Localization/SourceExt/AbpZero-it.xml @@ -9,8 +9,8 @@ - - + + @@ -21,14 +21,6 @@ - - - - - - - - - + \ No newline at end of file diff --git a/src/Abp.Zero/Zero/Localization/Source/AbpZero-lt.xml b/src/Abp.Zero/Zero/Localization/SourceExt/AbpZero-lt.xml similarity index 58% rename from src/Abp.Zero/Zero/Localization/Source/AbpZero-lt.xml rename to src/Abp.Zero/Zero/Localization/SourceExt/AbpZero-lt.xml index 815b3999..74700b9c 100644 --- a/src/Abp.Zero/Zero/Localization/Source/AbpZero-lt.xml +++ b/src/Abp.Zero/Zero/Localization/SourceExt/AbpZero-lt.xml @@ -1,34 +1,31 @@ - + - + - + - + - + - + - - - - - - - - + El. paštas + AbpZeroTemplate Saugumo Kodas + Jūsų saugumo kodas: {0} + Sms + Jusu saugumo kodas: {0} \ No newline at end of file diff --git a/src/Abp.Zero/Zero/Localization/Source/AbpZero-lv.xml b/src/Abp.Zero/Zero/Localization/SourceExt/AbpZero-lv.xml similarity index 67% rename from src/Abp.Zero/Zero/Localization/Source/AbpZero-lv.xml rename to src/Abp.Zero/Zero/Localization/SourceExt/AbpZero-lv.xml index 924f3682..9299ae2e 100644 --- a/src/Abp.Zero/Zero/Localization/Source/AbpZero-lv.xml +++ b/src/Abp.Zero/Zero/Localization/SourceExt/AbpZero-lv.xml @@ -8,7 +8,7 @@ - + @@ -22,13 +22,10 @@ - - - - - - - - + + + + + - + \ No newline at end of file diff --git a/src/Abp.Zero/Zero/Localization/Source/AbpZero-pt-BR.xml b/src/Abp.Zero/Zero/Localization/SourceExt/AbpZero-pt-BR.xml similarity index 66% rename from src/Abp.Zero/Zero/Localization/Source/AbpZero-pt-BR.xml rename to src/Abp.Zero/Zero/Localization/SourceExt/AbpZero-pt-BR.xml index a4082a74..4a07bc99 100644 --- a/src/Abp.Zero/Zero/Localization/Source/AbpZero-pt-BR.xml +++ b/src/Abp.Zero/Zero/Localization/SourceExt/AbpZero-pt-BR.xml @@ -8,7 +8,7 @@ - + @@ -22,13 +22,5 @@ - - - - - - - - diff --git a/src/Abp.Zero/Zero/Localization/SourceExt/AbpZero-ru.xml b/src/Abp.Zero/Zero/Localization/SourceExt/AbpZero-ru.xml new file mode 100644 index 00000000..552db759 --- /dev/null +++ b/src/Abp.Zero/Zero/Localization/SourceExt/AbpZero-ru.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Email + Секретный код + Ваш секретный код: {0} + Смс + Ваш секретный код: {0} + + \ No newline at end of file diff --git a/src/Abp.Zero/Zero/Localization/Source/AbpZero-tr.xml b/src/Abp.Zero/Zero/Localization/SourceExt/AbpZero-tr.xml similarity index 68% rename from src/Abp.Zero/Zero/Localization/Source/AbpZero-tr.xml rename to src/Abp.Zero/Zero/Localization/SourceExt/AbpZero-tr.xml index a8a5ef5d..937c9816 100644 --- a/src/Abp.Zero/Zero/Localization/Source/AbpZero-tr.xml +++ b/src/Abp.Zero/Zero/Localization/SourceExt/AbpZero-tr.xml @@ -8,7 +8,7 @@ - + @@ -22,16 +22,8 @@ - - - - - - - - E-posta - AbpZeroTemplate Güvenlik Kodu + Güvenlik Kodu Güvenlik kodunuz: {0} Sms Güvenlik kodunuz: {0} diff --git a/src/Abp.Zero/Zero/Localization/Source/AbpZero-zh-CN.xml b/src/Abp.Zero/Zero/Localization/SourceExt/AbpZero-zh-CN.xml similarity index 73% rename from src/Abp.Zero/Zero/Localization/Source/AbpZero-zh-CN.xml rename to src/Abp.Zero/Zero/Localization/SourceExt/AbpZero-zh-CN.xml index 53ecfbf9..e83fd838 100644 --- a/src/Abp.Zero/Zero/Localization/Source/AbpZero-zh-CN.xml +++ b/src/Abp.Zero/Zero/Localization/SourceExt/AbpZero-zh-CN.xml @@ -8,7 +8,7 @@ - + @@ -22,12 +22,5 @@ - - - - - - - \ No newline at end of file diff --git a/src/Abp.Zero/Zero/Localization/Source/AbpZero.xml b/src/Abp.Zero/Zero/Localization/SourceExt/AbpZero.xml similarity index 66% rename from src/Abp.Zero/Zero/Localization/Source/AbpZero.xml rename to src/Abp.Zero/Zero/Localization/SourceExt/AbpZero.xml index 1bd75cbf..109c937d 100644 --- a/src/Abp.Zero/Zero/Localization/Source/AbpZero.xml +++ b/src/Abp.Zero/Zero/Localization/SourceExt/AbpZero.xml @@ -1,4 +1,4 @@ - + @@ -8,7 +8,7 @@ - + @@ -22,16 +22,8 @@ - - - - - - - - Email - AbpZeroTemplate Security Code + Security Code Your security code is: {0} Sms Your security code is: {0} diff --git a/src/Abp.Zero/project.json b/src/Abp.Zero/project.json deleted file mode 100644 index 172728c6..00000000 --- a/src/Abp.Zero/project.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "version": "1.0.0.0-*", - - "dependencies": { - "Abp": "1.0.0", - "Microsoft.AspNet.Identity.Core": "2.2.1" - }, - - "frameworks": { - "net452": { - } - }, - - "buildOptions": { - "nowarn": [ - "CS1591" - ], - "xmlDoc": true, - "embed": { - "include": [ - "Zero/Localization/Source/*.xml" - ] - } - }, - - "packOptions": { - "summary": "ASP.NET Boilerplate - Module Zero.", - "tags": [ "asp.net", "asp.net mvc", "boilerplate", "application framework", "web framework", "framework", "domain driven design", "multitenancy", "user management", "role management", "identity" ], - "owners": [ "Halil İbrahim Kalkan" ], - "iconUrl": "http://www.aspnetboilerplate.com/images/abp_nupkg.png", - "projectUrl": "http://www.aspnetboilerplate.com/", - "licenseUrl": "https://github.com/aspnetboilerplate/module-zero/blob/master/LICENSE", - "requireLicenseAcceptance": false, - "repository": { - "type": "git", - "url": "https://github.com/aspnetboilerplate/module-zero" - }, - "files": { - "mappings": { - "lib/net452/": "bin/Release/net452/Abp.Zero.pdb" - } - } - } -} diff --git a/src/Abp.ZeroCore.EntityFrameworkCore/Abp.ZeroCore.EntityFrameworkCore.csproj b/src/Abp.ZeroCore.EntityFrameworkCore/Abp.ZeroCore.EntityFrameworkCore.csproj new file mode 100644 index 00000000..eba96eec --- /dev/null +++ b/src/Abp.ZeroCore.EntityFrameworkCore/Abp.ZeroCore.EntityFrameworkCore.csproj @@ -0,0 +1,37 @@ + + + + + + net46;netstandard1.6 + true + Abp + Abp.ZeroCore.EntityFrameworkCore + Abp.ZeroCore.EntityFrameworkCore + asp.net;asp.net mvc;boilerplate;application framework;web framework;framework;domain driven design;multitenancy;user management;role management;identity;entity framework core + + + + + lib/net46/ + true + + + lib/netstandard1.6/ + true + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Abp.ZeroCore.EntityFrameworkCore/Data/ConnectionStringHelper.cs b/src/Abp.ZeroCore.EntityFrameworkCore/Data/ConnectionStringHelper.cs new file mode 100644 index 00000000..c915f39d --- /dev/null +++ b/src/Abp.ZeroCore.EntityFrameworkCore/Data/ConnectionStringHelper.cs @@ -0,0 +1,25 @@ +#if NET46 +using System.Configuration; +#endif + +namespace Abp.Data +{ + public static class ConnectionStringHelper + { + /// + /// Gets connection string from given connection string or name. + /// + public static string GetConnectionString(string nameOrConnectionString) + { +#if NET46 + var connStrSection = ConfigurationManager.ConnectionStrings[nameOrConnectionString]; + if (connStrSection != null) + { + return connStrSection.ConnectionString; + } +#endif + + return nameOrConnectionString; + } + } +} diff --git a/src/Abp.ZeroCore.EntityFrameworkCore/Zero/EntityFrameworkCore/AbpZeroCommonDbContext.cs b/src/Abp.ZeroCore.EntityFrameworkCore/Zero/EntityFrameworkCore/AbpZeroCommonDbContext.cs new file mode 100644 index 00000000..5ce93a15 --- /dev/null +++ b/src/Abp.ZeroCore.EntityFrameworkCore/Zero/EntityFrameworkCore/AbpZeroCommonDbContext.cs @@ -0,0 +1,260 @@ +using Abp.Auditing; +using Abp.Authorization; +using Abp.Authorization.Roles; +using Abp.Authorization.Users; +using Abp.BackgroundJobs; +using Abp.Configuration; +using Abp.EntityFrameworkCore; +using Abp.Localization; +using Abp.Notifications; +using Abp.Organizations; +using Microsoft.EntityFrameworkCore; + +namespace Abp.Zero.EntityFrameworkCore +{ + public abstract class AbpZeroCommonDbContext : AbpDbContext + where TRole : AbpRole + where TUser : AbpUser + where TSelf: AbpZeroCommonDbContext + { + /// + /// Roles. + /// + public virtual DbSet Roles { get; set; } + + /// + /// Users. + /// + public virtual DbSet Users { get; set; } + + /// + /// User logins. + /// + public virtual DbSet UserLogins { get; set; } + + /// + /// User login attempts. + /// + public virtual DbSet UserLoginAttempts { get; set; } + + /// + /// User roles. + /// + public virtual DbSet UserRoles { get; set; } + + /// + /// User claims. + /// + public virtual DbSet UserClaims { get; set; } + + /// + /// User tokens. + /// + public virtual DbSet UserTokens { get; set; } + + /// + /// Role claims. + /// + public virtual DbSet RoleClaims { get; set; } + + /// + /// Permissions. + /// + public virtual DbSet Permissions { get; set; } + + /// + /// Role permissions. + /// + public virtual DbSet RolePermissions { get; set; } + + /// + /// User permissions. + /// + public virtual DbSet UserPermissions { get; set; } + + /// + /// Settings. + /// + public virtual DbSet Settings { get; set; } + + /// + /// Audit logs. + /// + public virtual DbSet AuditLogs { get; set; } + + /// + /// Languages. + /// + public virtual DbSet Languages { get; set; } + + /// + /// LanguageTexts. + /// + public virtual DbSet LanguageTexts { get; set; } + + /// + /// OrganizationUnits. + /// + public virtual DbSet OrganizationUnits { get; set; } + + /// + /// UserOrganizationUnits. + /// + public virtual DbSet UserOrganizationUnits { get; set; } + + /// + /// Tenant notifications. + /// + public virtual DbSet TenantNotifications { get; set; } + + /// + /// User notifications. + /// + public virtual DbSet UserNotifications { get; set; } + + /// + /// Notification subscriptions. + /// + public virtual DbSet NotificationSubscriptions { get; set; } + + /// + /// + /// + /// + protected AbpZeroCommonDbContext(DbContextOptions options) + :base(options) + { + + } + + /// + /// + /// + /// + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + + modelBuilder.Entity(b => + { + b.Property(u => u.ConcurrencyStamp).IsConcurrencyToken(); + + b.HasOne(p => p.DeleterUser) + .WithMany() + .HasForeignKey(p => p.DeleterUserId); + + b.HasOne(p => p.CreatorUser) + .WithMany() + .HasForeignKey(p => p.CreatorUserId); + + b.HasOne(p => p.LastModifierUser) + .WithMany() + .HasForeignKey(p => p.LastModifierUserId); + }); + + modelBuilder.Entity(b => + { + b.Property(r => r.ConcurrencyStamp).IsConcurrencyToken(); + }); + + modelBuilder.Entity(b => + { + b.HasIndex(e => new { e.TenantId, e.UserId }); + b.HasIndex(e => new { e.TenantId, e.ExecutionTime }); + b.HasIndex(e => new { e.TenantId, e.ExecutionDuration }); + }); + + modelBuilder.Entity(b => + { + b.HasIndex(e => new { e.TenantId, e.Name }); + }); + + modelBuilder.Entity(b => + { + b.HasIndex(e => new { e.TenantId, e.Source, e.LanguageName, e.Key }); + }); + + modelBuilder.Entity(b => + { + b.HasIndex(e => new { e.NotificationName, e.EntityTypeName, e.EntityId, e.UserId }); + b.HasIndex(e => new { e.TenantId, e.NotificationName, e.EntityTypeName, e.EntityId, e.UserId }); + }); + + modelBuilder.Entity(b => + { + b.HasIndex(e => new { e.TenantId, e.Code }); + }); + + modelBuilder.Entity(b => + { + b.HasIndex(e => new { e.TenantId, e.Name }); + }); + + modelBuilder.Entity(b => + { + b.HasIndex(e => new { e.RoleId }); + b.HasIndex(e => new { e.TenantId, e.ClaimType }); + }); + + modelBuilder.Entity(b => + { + b.HasIndex(e => new { e.TenantId, e.NormalizedName }); + }); + + modelBuilder.Entity(b => + { + b.HasIndex(e => new { e.TenantId, e.Name }); + }); + + modelBuilder.Entity(b => + { + b.HasIndex(e => new { e.TenantId }); + }); + + modelBuilder.Entity(b => + { + b.HasIndex(e => new { e.TenantId, e.ClaimType }); + }); + + modelBuilder.Entity(b => + { + b.HasIndex(e => new { e.TenancyName, e.UserNameOrEmailAddress, e.Result }); + b.HasIndex(ula => new {ula.UserId, ula.TenantId}); + }); + + modelBuilder.Entity(b => + { + b.HasIndex(e => new { e.TenantId, e.LoginProvider, e.ProviderKey }); + b.HasIndex(e => new { e.TenantId, e.UserId }); + }); + + modelBuilder.Entity(b => + { + b.HasIndex(e => new { e.UserId, e.State, e.CreationTime }); + }); + + modelBuilder.Entity(b => + { + b.HasIndex(e => new { e.TenantId, e.UserId }); + b.HasIndex(e => new { e.TenantId, e.OrganizationUnitId }); + }); + + modelBuilder.Entity(b => + { + b.HasIndex(e => new { e.TenantId, e.UserId }); + b.HasIndex(e => new { e.TenantId, e.RoleId }); + }); + + modelBuilder.Entity(b => + { + b.HasIndex(e => new { e.TenantId, e.NormalizedUserName }); + b.HasIndex(e => new { e.TenantId, e.NormalizedEmailAddress }); + }); + + modelBuilder.Entity(b => + { + b.HasIndex(e => new { e.TenantId, e.UserId }); + }); + } + } +} \ No newline at end of file diff --git a/src/Abp.ZeroCore.EntityFrameworkCore/Zero/EntityFrameworkCore/AbpZeroCoreEntityFrameworkCoreModule.cs b/src/Abp.ZeroCore.EntityFrameworkCore/Zero/EntityFrameworkCore/AbpZeroCoreEntityFrameworkCoreModule.cs new file mode 100644 index 00000000..2a0b72ca --- /dev/null +++ b/src/Abp.ZeroCore.EntityFrameworkCore/Zero/EntityFrameworkCore/AbpZeroCoreEntityFrameworkCoreModule.cs @@ -0,0 +1,33 @@ +using Abp.Domain.Uow; +using Abp.EntityFrameworkCore; +using Abp.Modules; +using Abp.MultiTenancy; +using Abp.Reflection.Extensions; +using Castle.MicroKernel.Registration; + +namespace Abp.Zero.EntityFrameworkCore +{ + /// + /// Entity framework integration module for ASP.NET Boilerplate Zero. + /// + [DependsOn(typeof(AbpZeroCoreModule), typeof(AbpEntityFrameworkCoreModule))] + public class AbpZeroCoreEntityFrameworkCoreModule : AbpModule + { + public override void PreInitialize() + { + Configuration.ReplaceService(typeof(IConnectionStringResolver), () => + { + IocManager.IocContainer.Register( + Component.For() + .ImplementedBy() + .LifestyleTransient() + ); + }); + } + + public override void Initialize() + { + IocManager.RegisterAssemblyByConvention(typeof(AbpZeroCoreEntityFrameworkCoreModule).GetAssembly()); + } + } +} diff --git a/src/Abp.ZeroCore.EntityFrameworkCore/Zero/EntityFrameworkCore/AbpZeroDbContext.cs b/src/Abp.ZeroCore.EntityFrameworkCore/Zero/EntityFrameworkCore/AbpZeroDbContext.cs new file mode 100644 index 00000000..51b4a987 --- /dev/null +++ b/src/Abp.ZeroCore.EntityFrameworkCore/Zero/EntityFrameworkCore/AbpZeroDbContext.cs @@ -0,0 +1,159 @@ +using Abp.Application.Editions; +using Abp.Application.Features; +using Abp.Auditing; +using Abp.Authorization.Roles; +using Abp.Authorization.Users; +using Abp.BackgroundJobs; +using Abp.MultiTenancy; +using Abp.Notifications; +using Microsoft.EntityFrameworkCore; + +namespace Abp.Zero.EntityFrameworkCore +{ + /// + /// Base DbContext for ABP zero. + /// Derive your DbContext from this class to have base entities. + /// + public abstract class AbpZeroDbContext : AbpZeroCommonDbContext + where TTenant : AbpTenant + where TRole : AbpRole + where TUser : AbpUser + where TSelf : AbpZeroDbContext + { + /// + /// Tenants + /// + public virtual DbSet Tenants { get; set; } + + /// + /// Editions. + /// + public virtual DbSet Editions { get; set; } + + /// + /// FeatureSettings. + /// + public virtual DbSet FeatureSettings { get; set; } + + /// + /// TenantFeatureSetting. + /// + public virtual DbSet TenantFeatureSettings { get; set; } + + /// + /// EditionFeatureSettings. + /// + public virtual DbSet EditionFeatureSettings { get; set; } + + /// + /// Background jobs. + /// + public virtual DbSet BackgroundJobs { get; set; } + + /// + /// User accounts + /// + public virtual DbSet UserAccounts { get; set; } + + /// + /// Notifications. + /// + public virtual DbSet Notifications { get; set; } + + /// + /// + /// + /// + protected AbpZeroDbContext(DbContextOptions options) + : base(options) + { + + } + /// + /// + /// + /// + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + + modelBuilder.Entity(b => + { + b.HasIndex(e => new { e.IsAbandoned, e.NextTryTime }); + }); + + modelBuilder.Entity(b => + { + b.HasIndex(e => new { e.TenantId, e.Name }); + }); + + modelBuilder.Entity(b => + { + b.HasIndex(e => new { e.EditionId, e.Name }); + }); + + modelBuilder.Entity(b => + { + b.HasOne(p => p.DeleterUser) + .WithMany() + .HasForeignKey(p => p.DeleterUserId); + + b.HasOne(p => p.CreatorUser) + .WithMany() + .HasForeignKey(p => p.CreatorUserId); + + b.HasOne(p => p.LastModifierUser) + .WithMany() + .HasForeignKey(p => p.LastModifierUserId); + + b.HasIndex(e => e.TenancyName); + }); + + modelBuilder.Entity(b => + { + b.HasIndex(e => new { e.TenantId, e.UserId }); + b.HasIndex(e => new { e.TenantId, e.UserName }); + b.HasIndex(e => new { e.TenantId, e.EmailAddress }); + b.HasIndex(e => new { e.UserName }); + b.HasIndex(e => new { e.EmailAddress }); + }); + + #region AuditLog.Set_MaxLengths + + modelBuilder.Entity() + .Property(e => e.ServiceName) + .HasMaxLength(AuditLog.MaxServiceNameLength); + + modelBuilder.Entity() + .Property(e => e.MethodName) + .HasMaxLength(AuditLog.MaxMethodNameLength); + + modelBuilder.Entity() + .Property(e => e.Parameters) + .HasMaxLength(AuditLog.MaxParametersLength); + + modelBuilder.Entity() + .Property(e => e.ClientIpAddress) + .HasMaxLength(AuditLog.MaxClientIpAddressLength); + + modelBuilder.Entity() + .Property(e => e.ClientName) + .HasMaxLength(AuditLog.MaxClientNameLength); + + modelBuilder.Entity() + .Property(e => e.BrowserInfo) + .HasMaxLength(AuditLog.MaxBrowserInfoLength); + + modelBuilder.Entity() + .Property(e => e.Exception) + .HasMaxLength(AuditLog.MaxExceptionLength); + + modelBuilder.Entity() + .Property(e => e.CustomData) + .HasMaxLength(AuditLog.MaxCustomDataLength); + + #endregion + + } + } +} diff --git a/src/Abp.ZeroCore.EntityFrameworkCore/Zero/EntityFrameworkCore/AbpZeroDbMigrator.cs b/src/Abp.ZeroCore.EntityFrameworkCore/Zero/EntityFrameworkCore/AbpZeroDbMigrator.cs new file mode 100644 index 00000000..743f9ac2 --- /dev/null +++ b/src/Abp.ZeroCore.EntityFrameworkCore/Zero/EntityFrameworkCore/AbpZeroDbMigrator.cs @@ -0,0 +1,81 @@ +using System; +using System.Transactions; +using Abp.Data; +using Abp.Dependency; +using Abp.Domain.Uow; +using Abp.EntityFrameworkCore; +using Abp.Extensions; +using Abp.MultiTenancy; +using Microsoft.EntityFrameworkCore; + +namespace Abp.Zero.EntityFrameworkCore +{ + public abstract class AbpZeroDbMigrator : IAbpZeroDbMigrator, ITransientDependency + where TDbContext : DbContext + { + private readonly IUnitOfWorkManager _unitOfWorkManager; + private readonly IDbPerTenantConnectionStringResolver _connectionStringResolver; + private readonly IDbContextResolver _dbContextResolver; + + protected AbpZeroDbMigrator( + IUnitOfWorkManager unitOfWorkManager, + IDbPerTenantConnectionStringResolver connectionStringResolver, + IDbContextResolver dbContextResolver) + { + _unitOfWorkManager = unitOfWorkManager; + _connectionStringResolver = connectionStringResolver; + _dbContextResolver = dbContextResolver; + } + + public virtual void CreateOrMigrateForHost() + { + CreateOrMigrateForHost(null); + } + + public virtual void CreateOrMigrateForHost(Action seedAction) + { + CreateOrMigrate(null, seedAction); + } + + public virtual void CreateOrMigrateForTenant(AbpTenantBase tenant) + { + CreateOrMigrateForTenant(tenant, null); + } + + public virtual void CreateOrMigrateForTenant(AbpTenantBase tenant, Action seedAction) + { + if (tenant.ConnectionString.IsNullOrEmpty()) + { + return; + } + + CreateOrMigrate(tenant, seedAction); + } + + protected virtual void CreateOrMigrate(AbpTenantBase tenant, Action seedAction) + { + var args = new DbPerTenantConnectionStringResolveArgs( + tenant == null ? (int?) null : (int?) tenant.Id, + tenant == null ? MultiTenancySides.Host : MultiTenancySides.Tenant + ); + + args["DbContextType"] = typeof(TDbContext); + args["DbContextConcreteType"] = typeof(TDbContext); + + var nameOrConnectionString = ConnectionStringHelper.GetConnectionString( + _connectionStringResolver.GetNameOrConnectionString(args) + ); + + using (var uow = _unitOfWorkManager.Begin(TransactionScopeOption.Suppress)) + { + using (var dbContext = _dbContextResolver.Resolve(nameOrConnectionString, null)) + { + dbContext.Database.Migrate(); + seedAction?.Invoke(dbContext); + _unitOfWorkManager.Current.SaveChanges(); + uow.Complete(); + } + } + } + } +} diff --git a/src/Abp.ZeroCore.EntityFrameworkCore/Zero/EntityFrameworkCore/AbpZeroDbModelBuilderExtensions.cs b/src/Abp.ZeroCore.EntityFrameworkCore/Zero/EntityFrameworkCore/AbpZeroDbModelBuilderExtensions.cs new file mode 100644 index 00000000..4c640e11 --- /dev/null +++ b/src/Abp.ZeroCore.EntityFrameworkCore/Zero/EntityFrameworkCore/AbpZeroDbModelBuilderExtensions.cs @@ -0,0 +1,81 @@ +using Abp.Application.Editions; +using Abp.Application.Features; +using Abp.Auditing; +using Abp.Authorization; +using Abp.Authorization.Roles; +using Abp.Authorization.Users; +using Abp.BackgroundJobs; +using Abp.Configuration; +using Abp.Localization; +using Abp.MultiTenancy; +using Abp.Notifications; +using Abp.Organizations; +using Microsoft.EntityFrameworkCore; + +namespace Abp.Zero.EntityFrameworkCore +{ + /// + /// Extension methods for . + /// + public static class AbpZeroDbModelBuilderExtensions + { + /// + /// Changes prefix for ABP tables (which is "Abp" by default). + /// Can be null/empty string to clear the prefix. + /// + /// The type of the tenant entity. + /// The type of the role entity. + /// The type of the user entity. + /// Model builder. + /// Table prefix, or null to clear prefix. + public static void ChangeAbpTablePrefix(this ModelBuilder modelBuilder, string prefix, string schemaName = null) + where TTenant : AbpTenant + where TRole : AbpRole + where TUser : AbpUser + { + prefix = prefix ?? ""; + + SetTableName(modelBuilder, prefix + "AuditLogs", schemaName); + SetTableName(modelBuilder, prefix + "BackgroundJobs", schemaName); + SetTableName(modelBuilder, prefix + "Editions", schemaName); + SetTableName(modelBuilder, prefix + "Features", schemaName); + SetTableName(modelBuilder, prefix + "Features", schemaName); + SetTableName(modelBuilder, prefix + "Features", schemaName); + SetTableName(modelBuilder, prefix + "Languages", schemaName); + SetTableName(modelBuilder, prefix + "LanguageTexts", schemaName); + SetTableName(modelBuilder, prefix + "Notifications", schemaName); + SetTableName(modelBuilder, prefix + "NotificationSubscriptions", schemaName); + SetTableName(modelBuilder, prefix + "OrganizationUnits", schemaName); + SetTableName(modelBuilder, prefix + "Permissions", schemaName); + SetTableName(modelBuilder, prefix + "Permissions", schemaName); + SetTableName(modelBuilder, prefix + "Permissions", schemaName); + SetTableName(modelBuilder, prefix + "Roles", schemaName); + SetTableName(modelBuilder, prefix + "Settings", schemaName); + SetTableName(modelBuilder, prefix + "Tenants", schemaName); + SetTableName(modelBuilder, prefix + "UserLogins", schemaName); + SetTableName(modelBuilder, prefix + "UserLoginAttempts", schemaName); + SetTableName(modelBuilder, prefix + "TenantNotifications", schemaName); + SetTableName(modelBuilder, prefix + "UserNotifications", schemaName); + SetTableName(modelBuilder, prefix + "UserOrganizationUnits", schemaName); + SetTableName(modelBuilder, prefix + "UserRoles", schemaName); + SetTableName(modelBuilder, prefix + "Users", schemaName); + SetTableName(modelBuilder, prefix + "UserAccounts", schemaName); + SetTableName(modelBuilder, prefix + "UserClaims", schemaName); + SetTableName(modelBuilder, prefix + "RoleClaims", schemaName); + SetTableName(modelBuilder, prefix + "UserTokens", schemaName); + } + + internal static void SetTableName(this ModelBuilder modelBuilder, string tableName, string schemaName) + where TEntity : class + { + if (schemaName == null) + { + modelBuilder.Entity().ToTable(tableName); + } + else + { + modelBuilder.Entity().ToTable(tableName, schemaName); + } + } + } +} \ No newline at end of file diff --git a/src/Abp.ZeroCore.EntityFrameworkCore/Zero/EntityFrameworkCore/AbpZeroHostDbContext.cs b/src/Abp.ZeroCore.EntityFrameworkCore/Zero/EntityFrameworkCore/AbpZeroHostDbContext.cs new file mode 100644 index 00000000..5933b7a8 --- /dev/null +++ b/src/Abp.ZeroCore.EntityFrameworkCore/Zero/EntityFrameworkCore/AbpZeroHostDbContext.cs @@ -0,0 +1,130 @@ +using Abp.Application.Editions; +using Abp.Application.Features; +using Abp.Authorization.Roles; +using Abp.Authorization.Users; +using Abp.BackgroundJobs; +using Abp.MultiTenancy; +using Abp.Notifications; +using Microsoft.EntityFrameworkCore; + +namespace Abp.Zero.EntityFrameworkCore +{ + [MultiTenancySide(MultiTenancySides.Host)] + public abstract class AbpZeroHostDbContext : AbpZeroCommonDbContext + where TTenant : AbpTenant + where TRole : AbpRole + where TUser : AbpUser + where TSelf : AbpZeroHostDbContext + { + /// + /// Tenants + /// + public virtual DbSet Tenants { get; set; } + + /// + /// Editions. + /// + public virtual DbSet Editions { get; set; } + + /// + /// FeatureSettings. + /// + public virtual DbSet FeatureSettings { get; set; } + + /// + /// TenantFeatureSetting. + /// + public virtual DbSet TenantFeatureSettings { get; set; } + + /// + /// EditionFeatureSettings. + /// + public virtual DbSet EditionFeatureSettings { get; set; } + + /// + /// Background jobs. + /// + public virtual DbSet BackgroundJobs { get; set; } + + /// + /// User accounts + /// + public virtual DbSet UserAccounts { get; set; } + + /// + /// Notifications. + /// + public virtual DbSet Notifications { get; set; } + + /// + /// + /// + /// + protected AbpZeroHostDbContext(DbContextOptions options) + :base(options) + { + + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + + modelBuilder.Entity(u => + { + u.HasOne(p => p.DeleterUser) + .WithMany() + .HasForeignKey(p => p.DeleterUserId); + + u.HasOne(p => p.CreatorUser) + .WithMany() + .HasForeignKey(p => p.CreatorUserId); + + u.HasOne(p => p.LastModifierUser) + .WithMany() + .HasForeignKey(p => p.LastModifierUserId); + }); + + modelBuilder.Entity(b => + { + b.HasOne(p => p.DeleterUser) + .WithMany() + .HasForeignKey(p => p.DeleterUserId); + + b.HasOne(p => p.CreatorUser) + .WithMany() + .HasForeignKey(p => p.CreatorUserId); + + b.HasOne(p => p.LastModifierUser) + .WithMany() + .HasForeignKey(p => p.LastModifierUserId); + + b.HasIndex(e => e.TenancyName); + }); + + modelBuilder.Entity(b => + { + b.HasIndex(e => new { e.IsAbandoned, e.NextTryTime }); + }); + + modelBuilder.Entity(b => + { + b.HasIndex(e => new { e.TenantId, e.Name }); + }); + + modelBuilder.Entity(b => + { + b.HasIndex(e => new { e.EditionId, e.Name }); + }); + + modelBuilder.Entity(b => + { + b.HasIndex(e => new { e.TenantId, e.UserId }); + b.HasIndex(e => new { e.TenantId, e.UserName }); + b.HasIndex(e => new { e.TenantId, e.EmailAddress }); + b.HasIndex(e => new { e.UserName }); + b.HasIndex(e => new { e.EmailAddress }); + }); + } + } +} \ No newline at end of file diff --git a/src/Abp.ZeroCore.EntityFrameworkCore/Zero/EntityFrameworkCore/AbpZeroTenantDbContext.cs b/src/Abp.ZeroCore.EntityFrameworkCore/Zero/EntityFrameworkCore/AbpZeroTenantDbContext.cs new file mode 100644 index 00000000..8082f37e --- /dev/null +++ b/src/Abp.ZeroCore.EntityFrameworkCore/Zero/EntityFrameworkCore/AbpZeroTenantDbContext.cs @@ -0,0 +1,25 @@ +using Abp.Authorization.Roles; +using Abp.Authorization.Users; +using Abp.MultiTenancy; +using Microsoft.EntityFrameworkCore; + +namespace Abp.Zero.EntityFrameworkCore +{ + [MultiTenancySide(MultiTenancySides.Host)] + public abstract class AbpZeroTenantDbContext : AbpZeroCommonDbContext + where TRole : AbpRole + where TUser : AbpUser + where TSelf: AbpZeroTenantDbContext + { + + /// + /// + /// + /// + protected AbpZeroTenantDbContext(DbContextOptions options) + :base(options) + { + + } + } +} \ No newline at end of file diff --git a/src/Abp.ZeroCore.EntityFrameworkCore/Zero/EntityFrameworkCore/DbPerTenantConnectionStringResolver.cs b/src/Abp.ZeroCore.EntityFrameworkCore/Zero/EntityFrameworkCore/DbPerTenantConnectionStringResolver.cs new file mode 100644 index 00000000..c2d9b17a --- /dev/null +++ b/src/Abp.ZeroCore.EntityFrameworkCore/Zero/EntityFrameworkCore/DbPerTenantConnectionStringResolver.cs @@ -0,0 +1,73 @@ +using Abp.Configuration.Startup; +using Abp.Domain.Uow; +using Abp.Extensions; +using Abp.MultiTenancy; +using Abp.Runtime.Session; + +namespace Abp.Zero.EntityFrameworkCore +{ + /// + /// Implements to dynamically resolve + /// connection string for a multi tenant application. + /// + public class DbPerTenantConnectionStringResolver : DefaultConnectionStringResolver, IDbPerTenantConnectionStringResolver + { + /// + /// Reference to the session. + /// + public IAbpSession AbpSession { get; set; } + + private readonly ICurrentUnitOfWorkProvider _currentUnitOfWorkProvider; + private readonly ITenantCache _tenantCache; + + /// + /// Initializes a new instance of the class. + /// + public DbPerTenantConnectionStringResolver( + IAbpStartupConfiguration configuration, + ICurrentUnitOfWorkProvider currentUnitOfWorkProvider, + ITenantCache tenantCache) + : base(configuration) + { + _currentUnitOfWorkProvider = currentUnitOfWorkProvider; + _tenantCache = tenantCache; + + AbpSession = NullAbpSession.Instance; + } + + public override string GetNameOrConnectionString(ConnectionStringResolveArgs args) + { + if (args.MultiTenancySide == MultiTenancySides.Host) + { + return GetNameOrConnectionString(new DbPerTenantConnectionStringResolveArgs(null, args)); + } + + return GetNameOrConnectionString(new DbPerTenantConnectionStringResolveArgs(GetCurrentTenantId(), args)); + } + + public virtual string GetNameOrConnectionString(DbPerTenantConnectionStringResolveArgs args) + { + if (args.TenantId == null) + { + //Requested for host + return base.GetNameOrConnectionString(args); + } + + var tenantCacheItem = _tenantCache.Get(args.TenantId.Value); + if (tenantCacheItem.ConnectionString.IsNullOrEmpty()) + { + //Tenant has not dedicated database + return base.GetNameOrConnectionString(args); + } + + return tenantCacheItem.ConnectionString; + } + + protected virtual int? GetCurrentTenantId() + { + return _currentUnitOfWorkProvider.Current != null + ? _currentUnitOfWorkProvider.Current.GetTenantId() + : AbpSession.TenantId; + } + } +} diff --git a/src/Abp.ZeroCore.EntityFrameworkCore/Zero/EntityFrameworkCore/IMultiTenantSeed.cs b/src/Abp.ZeroCore.EntityFrameworkCore/Zero/EntityFrameworkCore/IMultiTenantSeed.cs new file mode 100644 index 00000000..6201a18b --- /dev/null +++ b/src/Abp.ZeroCore.EntityFrameworkCore/Zero/EntityFrameworkCore/IMultiTenantSeed.cs @@ -0,0 +1,9 @@ +using Abp.MultiTenancy; + +namespace Abp.Zero.EntityFrameworkCore +{ + public interface IMultiTenantSeed + { + AbpTenantBase Tenant { get; set; } + } +} \ No newline at end of file diff --git a/src/Abp.ZeroCore.IdentityServer4.EntityFrameworkCore/Abp.ZeroCore.IdentityServer4.EntityFrameworkCore.csproj b/src/Abp.ZeroCore.IdentityServer4.EntityFrameworkCore/Abp.ZeroCore.IdentityServer4.EntityFrameworkCore.csproj new file mode 100644 index 00000000..cf266640 --- /dev/null +++ b/src/Abp.ZeroCore.IdentityServer4.EntityFrameworkCore/Abp.ZeroCore.IdentityServer4.EntityFrameworkCore.csproj @@ -0,0 +1,36 @@ + + + + + + net46;netstandard1.6 + true + Abp.ZeroCore.IdentityServer4.EntityFrameworkCore + Abp.ZeroCore.IdentityServer4.EntityFrameworkCore + asp.net;asp.net mvc;boilerplate;application framework;web framework;framework;domain driven design;multitenancy;user management;role management;identity server;entity framework + false + false + false + false + false + false + Abp + + + + + + + + + + lib/net46/ + true + + + lib/netstandard1.6/ + true + + + + \ No newline at end of file diff --git a/src/Abp.ZeroCore.IdentityServer4.EntityFrameworkCore/IdentityServer4/AbpZeroCoreIdentityServerEntityFrameworkCoreConfigurationExtensions.cs b/src/Abp.ZeroCore.IdentityServer4.EntityFrameworkCore/IdentityServer4/AbpZeroCoreIdentityServerEntityFrameworkCoreConfigurationExtensions.cs new file mode 100644 index 00000000..0160d199 --- /dev/null +++ b/src/Abp.ZeroCore.IdentityServer4.EntityFrameworkCore/IdentityServer4/AbpZeroCoreIdentityServerEntityFrameworkCoreConfigurationExtensions.cs @@ -0,0 +1,26 @@ +using Microsoft.EntityFrameworkCore; + +namespace Abp.IdentityServer4 +{ + public static class AbpZeroCoreIdentityServerEntityFrameworkCoreConfigurationExtensions + { + public static void ConfigurePersistedGrantEntity(this ModelBuilder modelBuilder) + { + modelBuilder.Entity(grant => + { + grant.Property(x => x.Id).HasMaxLength(200).ValueGeneratedNever(); + grant.Property(x => x.Type).HasMaxLength(50).IsRequired(); + grant.Property(x => x.SubjectId).HasMaxLength(200); + grant.Property(x => x.ClientId).HasMaxLength(200).IsRequired(); + grant.Property(x => x.CreationTime).IsRequired(); + // 50000 chosen to be explicit to allow enough size to avoid truncation, yet stay beneath the MySql row size limit of ~65K + // apparently anything over 4K converts to nvarchar(max) on SqlServer + grant.Property(x => x.Data).HasMaxLength(50000).IsRequired(); + + grant.HasKey(x => x.Id); + + grant.HasIndex(x => new { x.SubjectId, x.ClientId, x.Type }); + }); + } + } +} \ No newline at end of file diff --git a/src/Abp.ZeroCore.IdentityServer4.EntityFrameworkCore/IdentityServer4/AbpZeroCoreIdentityServerEntityFrameworkCoreModule.cs b/src/Abp.ZeroCore.IdentityServer4.EntityFrameworkCore/IdentityServer4/AbpZeroCoreIdentityServerEntityFrameworkCoreModule.cs new file mode 100644 index 00000000..6c465bdd --- /dev/null +++ b/src/Abp.ZeroCore.IdentityServer4.EntityFrameworkCore/IdentityServer4/AbpZeroCoreIdentityServerEntityFrameworkCoreModule.cs @@ -0,0 +1,15 @@ +using Abp.Modules; +using Abp.Reflection.Extensions; +using Abp.Zero.EntityFrameworkCore; + +namespace Abp.IdentityServer4 +{ + [DependsOn(typeof(AbpZeroCoreIdentityServerModule), typeof(AbpZeroCoreEntityFrameworkCoreModule))] + public class AbpZeroCoreIdentityServerEntityFrameworkCoreModule : AbpModule + { + public override void Initialize() + { + IocManager.RegisterAssemblyByConvention(typeof(AbpZeroCoreIdentityServerEntityFrameworkCoreModule).GetAssembly()); + } + } +} diff --git a/src/Abp.ZeroCore.IdentityServer4.EntityFrameworkCore/IdentityServer4/AbpZeroIdentityServerBuilderEntityFrameworkCoreExtensions.cs b/src/Abp.ZeroCore.IdentityServer4.EntityFrameworkCore/IdentityServer4/AbpZeroIdentityServerBuilderEntityFrameworkCoreExtensions.cs new file mode 100644 index 00000000..a00d3149 --- /dev/null +++ b/src/Abp.ZeroCore.IdentityServer4.EntityFrameworkCore/IdentityServer4/AbpZeroIdentityServerBuilderEntityFrameworkCoreExtensions.cs @@ -0,0 +1,15 @@ +using IdentityServer4.Stores; +using Microsoft.Extensions.DependencyInjection; + +namespace Abp.IdentityServer4 +{ + public static class AbpZeroIdentityServerBuilderEntityFrameworkCoreExtensions + { + public static IIdentityServerBuilder AddAbpPersistedGrants(this IIdentityServerBuilder builder) + where TDbContext : IAbpPersistedGrantDbContext + { + builder.Services.AddTransient(); + return builder; + } + } +} diff --git a/src/Abp.ZeroCore.IdentityServer4.EntityFrameworkCore/IdentityServer4/IAbpPersistedGrantDbContext.cs b/src/Abp.ZeroCore.IdentityServer4.EntityFrameworkCore/IdentityServer4/IAbpPersistedGrantDbContext.cs new file mode 100644 index 00000000..4e2bea12 --- /dev/null +++ b/src/Abp.ZeroCore.IdentityServer4.EntityFrameworkCore/IdentityServer4/IAbpPersistedGrantDbContext.cs @@ -0,0 +1,9 @@ +using Microsoft.EntityFrameworkCore; + +namespace Abp.IdentityServer4 +{ + public interface IAbpPersistedGrantDbContext + { + DbSet PersistedGrants { get; set; } + } +} \ No newline at end of file diff --git a/src/Abp.ZeroCore.IdentityServer4/Abp.ZeroCore.IdentityServer4.csproj b/src/Abp.ZeroCore.IdentityServer4/Abp.ZeroCore.IdentityServer4.csproj new file mode 100644 index 00000000..2d5f13c6 --- /dev/null +++ b/src/Abp.ZeroCore.IdentityServer4/Abp.ZeroCore.IdentityServer4.csproj @@ -0,0 +1,44 @@ + + + + + + net46;netstandard1.6 + true + Abp.ZeroCore.IdentityServer4 + Abp.ZeroCore.IdentityServer4 + asp.net;asp.net mvc;boilerplate;application framework;web framework;framework;domain driven design;multitenancy;user management;role management;identity server + false + false + false + false + false + false + Abp + + + + + + + + + + lib/net46/ + true + + + lib/netstandard1.6/ + true + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Abp.ZeroCore.IdentityServer4/IdentityServer4/AbpClaimsService.cs b/src/Abp.ZeroCore.IdentityServer4/IdentityServer4/AbpClaimsService.cs new file mode 100644 index 00000000..34943d3f --- /dev/null +++ b/src/Abp.ZeroCore.IdentityServer4/IdentityServer4/AbpClaimsService.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; +using System.Linq; +using System.Security.Claims; +using Abp.Runtime.Security; +using IdentityServer4.Services; +using Microsoft.Extensions.Logging; + +namespace Abp.IdentityServer4 +{ + public class AbpClaimsService : DefaultClaimsService + { + public AbpClaimsService(IProfileService profile, ILogger logger) + : base(profile, logger) + { + } + + protected override IEnumerable GetOptionalClaims(ClaimsPrincipal subject) + { + var tenantClaim = subject.FindFirst(AbpClaimTypes.TenantId); + if (tenantClaim == null) + { + return base.GetOptionalClaims(subject); + } + else + { + return base.GetOptionalClaims(subject).Union(new[] { tenantClaim }); + } + } + } +} diff --git a/src/Abp.ZeroCore.IdentityServer4/IdentityServer4/AbpIdentityServerOptions.cs b/src/Abp.ZeroCore.IdentityServer4/IdentityServer4/AbpIdentityServerOptions.cs new file mode 100644 index 00000000..ece9af9f --- /dev/null +++ b/src/Abp.ZeroCore.IdentityServer4/IdentityServer4/AbpIdentityServerOptions.cs @@ -0,0 +1,20 @@ +using System.IdentityModel.Tokens.Jwt; +using Abp.Runtime.Security; + +namespace Abp.IdentityServer4 +{ + public class AbpIdentityServerOptions + { + /// + /// Updates to be compatible with identity server claims. + /// Default: true. + /// + public bool UpdateJwtSecurityTokenHandlerDefaultInboundClaimTypeMap { get; set; } = true; + + /// + /// Updates to be compatible with identity server claims. + /// Default: true. + /// + public bool UpdateAbpClaimTypes { get; set; } = true; + } +} \ No newline at end of file diff --git a/src/Abp.ZeroCore.IdentityServer4/IdentityServer4/AbpPersistentGrantStore.cs b/src/Abp.ZeroCore.IdentityServer4/IdentityServer4/AbpPersistentGrantStore.cs new file mode 100644 index 00000000..d16bd778 --- /dev/null +++ b/src/Abp.ZeroCore.IdentityServer4/IdentityServer4/AbpPersistentGrantStore.cs @@ -0,0 +1,70 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Abp.Domain.Repositories; +using Abp.Domain.Uow; +using IdentityServer4.Models; +using IdentityServer4.Stores; + +namespace Abp.IdentityServer4 +{ + public class AbpPersistedGrantStore : AbpServiceBase, IPersistedGrantStore + { + private readonly IRepository _persistedGrantRepository; + + public AbpPersistedGrantStore(IRepository persistedGrantRepository) + { + _persistedGrantRepository = persistedGrantRepository; + } + + [UnitOfWork] + public virtual async Task StoreAsync(PersistedGrant grant) + { + var entity = await _persistedGrantRepository.FirstOrDefaultAsync(grant.Key); + if (entity == null) + { + await _persistedGrantRepository.InsertAsync(ObjectMapper.Map(grant)); + } + else + { + ObjectMapper.Map(grant, entity); + } + } + + [UnitOfWork] + public virtual async Task GetAsync(string key) + { + var entity = await _persistedGrantRepository.FirstOrDefaultAsync(key); + if (entity == null) + { + return null; + } + + return ObjectMapper.Map(entity); + } + + [UnitOfWork] + public virtual async Task> GetAllAsync(string subjectId) + { + var entities = await _persistedGrantRepository.GetAllListAsync(x => x.SubjectId == subjectId); + return ObjectMapper.Map>(entities); + } + + [UnitOfWork] + public virtual async Task RemoveAsync(string key) + { + await _persistedGrantRepository.DeleteAsync(key); + } + + [UnitOfWork] + public virtual async Task RemoveAllAsync(string subjectId, string clientId) + { + await _persistedGrantRepository.DeleteAsync(x => x.SubjectId == subjectId && x.ClientId == clientId); + } + + [UnitOfWork] + public virtual async Task RemoveAllAsync(string subjectId, string clientId, string type) + { + await _persistedGrantRepository.DeleteAsync(x => x.SubjectId == subjectId && x.ClientId == clientId && x.Type == type); + } + } +} diff --git a/src/Abp.ZeroCore.IdentityServer4/IdentityServer4/AbpProfileService.cs b/src/Abp.ZeroCore.IdentityServer4/IdentityServer4/AbpProfileService.cs new file mode 100644 index 00000000..e39acba9 --- /dev/null +++ b/src/Abp.ZeroCore.IdentityServer4/IdentityServer4/AbpProfileService.cs @@ -0,0 +1,45 @@ +using System.Threading.Tasks; +using Abp.Authorization.Users; +using Abp.Domain.Uow; +using Abp.Runtime.Security; +using IdentityServer4.AspNetIdentity; +using IdentityServer4.Models; +using Microsoft.AspNetCore.Identity; + +namespace Abp.IdentityServer4 +{ + public class AbpProfileService : ProfileService + where TUser : AbpUser + { + private readonly IUnitOfWorkManager _unitOfWorkManager; + + public AbpProfileService( + UserManager userManager, + IUserClaimsPrincipalFactory claimsFactory, + IUnitOfWorkManager unitOfWorkManager + ) : base(userManager, claimsFactory) + { + _unitOfWorkManager = unitOfWorkManager; + } + + [UnitOfWork] + public override async Task GetProfileDataAsync(ProfileDataRequestContext context) + { + var tenantId = context.Subject.Identity.GetTenantId(); + using (_unitOfWorkManager.Current.SetTenantId(tenantId)) + { + await base.GetProfileDataAsync(context); + } + } + + [UnitOfWork] + public override async Task IsActiveAsync(IsActiveContext context) + { + var tenantId = context.Subject.Identity.GetTenantId(); + using (_unitOfWorkManager.Current.SetTenantId(tenantId)) + { + await base.IsActiveAsync(context); + } + } + } +} diff --git a/src/Abp.ZeroCore.IdentityServer4/IdentityServer4/AbpResourceOwnerPasswordValidator.cs b/src/Abp.ZeroCore.IdentityServer4/IdentityServer4/AbpResourceOwnerPasswordValidator.cs new file mode 100644 index 00000000..df36ecc7 --- /dev/null +++ b/src/Abp.ZeroCore.IdentityServer4/IdentityServer4/AbpResourceOwnerPasswordValidator.cs @@ -0,0 +1,81 @@ +using System.Collections.Generic; +using System.Security.Claims; +using System.Threading.Tasks; +using Abp.Authorization.Users; +using Abp.Runtime.Security; +using IdentityModel; +using IdentityServer4.AspNetIdentity; +using IdentityServer4.Models; +using IdentityServer4.Validation; +using Microsoft.AspNetCore.Identity; +using Microsoft.Extensions.Logging; + +namespace Abp.IdentityServer4 +{ + public class AbpResourceOwnerPasswordValidator : ResourceOwnerPasswordValidator + where TUser : AbpUser + { + protected UserManager UserManager { get; } + + protected SignInManager SignInManager { get; } + + protected ILogger> Logger { get; } + + public AbpResourceOwnerPasswordValidator( + UserManager userManager, + SignInManager signInManager, + ILogger> logger + ) + : base(userManager, signInManager, logger) + { + UserManager = userManager; + SignInManager = signInManager; + Logger = logger; + } + + public override async Task ValidateAsync(ResourceOwnerPasswordValidationContext context) + { + var user = await UserManager.FindByNameAsync(context.UserName); + if (user != null) + { + var result = await SignInManager.CheckPasswordSignInAsync(user, context.Password, true); + if (result.Succeeded) + { + Logger.LogInformation("Credentials validated for username: {username}", context.UserName); + + var sub = await UserManager.GetUserIdAsync(user); + context.Result = new GrantValidationResult(sub, OidcConstants.AuthenticationMethods.Password, GetAdditionalClaimsOrNull(user)); + return; + } + else if (result.IsLockedOut) + { + Logger.LogInformation("Authentication failed for username: {username}, reason: locked out", context.UserName); + } + else if (result.IsNotAllowed) + { + Logger.LogInformation("Authentication failed for username: {username}, reason: not allowed", context.UserName); + } + else + { + Logger.LogInformation("Authentication failed for username: {username}, reason: invalid credentials", context.UserName); + } + } + else + { + Logger.LogInformation("No user found matching username: {username}", context.UserName); + } + + context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant); + } + + protected virtual IEnumerable GetAdditionalClaimsOrNull(TUser user) + { + if (!user.TenantId.HasValue) + { + return null; + } + + return new[] { new Claim(AbpClaimTypes.TenantId, user.TenantId?.ToString()) }; + } + } +} diff --git a/src/Abp.ZeroCore.IdentityServer4/IdentityServer4/AbpZeroCoreIdentityServerModule.cs b/src/Abp.ZeroCore.IdentityServer4/IdentityServer4/AbpZeroCoreIdentityServerModule.cs new file mode 100644 index 00000000..8445bab7 --- /dev/null +++ b/src/Abp.ZeroCore.IdentityServer4/IdentityServer4/AbpZeroCoreIdentityServerModule.cs @@ -0,0 +1,31 @@ +using Abp.AutoMapper; +using Abp.Modules; +using Abp.Reflection.Extensions; +using Abp.Zero; +using IdentityServer4.Models; + +namespace Abp.IdentityServer4 +{ + [DependsOn(typeof(AbpZeroCoreModule), typeof(AbpAutoMapperModule))] + public class AbpZeroCoreIdentityServerModule : AbpModule + { + public override void PreInitialize() + { + Configuration.Modules.AbpAutoMapper().Configurators.Add(config => + { + //PersistedGrant -> PersistedGrantEntity + config.CreateMap() + .ForMember(d => d.Id, c => c.MapFrom(s => s.Key)); + + //PersistedGrantEntity -> PersistedGrant + config.CreateMap() + .ForMember(d => d.Key, c => c.MapFrom(s => s.Id)); + }); + } + + public override void Initialize() + { + IocManager.RegisterAssemblyByConvention(typeof(AbpZeroCoreIdentityServerModule).GetAssembly()); + } + } +} diff --git a/src/Abp.ZeroCore.IdentityServer4/IdentityServer4/AbpZeroIdentityServerBuilderExtensions.cs b/src/Abp.ZeroCore.IdentityServer4/IdentityServer4/AbpZeroIdentityServerBuilderExtensions.cs new file mode 100644 index 00000000..1a3ba609 --- /dev/null +++ b/src/Abp.ZeroCore.IdentityServer4/IdentityServer4/AbpZeroIdentityServerBuilderExtensions.cs @@ -0,0 +1,45 @@ +using System; +using System.IdentityModel.Tokens.Jwt; +using Abp.Authorization.Users; +using Abp.IdentityServer4; +using Abp.Runtime.Security; +using IdentityModel; +using IdentityServer4.Services; +using Microsoft.Extensions.DependencyInjection.Extensions; + +// ReSharper disable once CheckNamespace +namespace Microsoft.Extensions.DependencyInjection +{ + public static class AbpZeroIdentityServerBuilderExtensions + { + public static IIdentityServerBuilder AddAbpIdentityServer(this IIdentityServerBuilder builder, Action optionsAction = null) + where TUser : AbpUser + { + var options = new AbpIdentityServerOptions(); + optionsAction?.Invoke(options); + + builder.AddAspNetIdentity(); + + builder.AddProfileService>(); + builder.AddResourceOwnerValidator>(); + + builder.Services.Replace(ServiceDescriptor.Transient()); + + if (options.UpdateAbpClaimTypes) + { + AbpClaimTypes.UserId = JwtClaimTypes.Subject; + AbpClaimTypes.UserName = JwtClaimTypes.Name; + AbpClaimTypes.Role = JwtClaimTypes.Role; + } + + if (options.UpdateJwtSecurityTokenHandlerDefaultInboundClaimTypeMap) + { + JwtSecurityTokenHandler.DefaultInboundClaimTypeMap[AbpClaimTypes.UserId] = AbpClaimTypes.UserId; + JwtSecurityTokenHandler.DefaultInboundClaimTypeMap[AbpClaimTypes.UserName] = AbpClaimTypes.UserName; + JwtSecurityTokenHandler.DefaultInboundClaimTypeMap[AbpClaimTypes.Role] = AbpClaimTypes.Role; + } + + return builder; + } + } +} diff --git a/src/Abp.ZeroCore.IdentityServer4/IdentityServer4/PersistedGrantEntity.cs b/src/Abp.ZeroCore.IdentityServer4/IdentityServer4/PersistedGrantEntity.cs new file mode 100644 index 00000000..c432bfb1 --- /dev/null +++ b/src/Abp.ZeroCore.IdentityServer4/IdentityServer4/PersistedGrantEntity.cs @@ -0,0 +1,22 @@ +using System; +using System.ComponentModel.DataAnnotations.Schema; +using Abp.Domain.Entities; + +namespace Abp.IdentityServer4 +{ + [Table("AbpPersistedGrants")] + public class PersistedGrantEntity : Entity + { + public virtual string Type { get; set; } + + public virtual string SubjectId { get; set; } + + public virtual string ClientId { get; set; } + + public virtual DateTime CreationTime { get; set; } + + public virtual DateTime? Expiration { get; set; } + + public virtual string Data { get; set; } + } +} \ No newline at end of file diff --git a/src/Abp.ZeroCore/Abp.ZeroCore.csproj b/src/Abp.ZeroCore/Abp.ZeroCore.csproj new file mode 100644 index 00000000..73c09408 --- /dev/null +++ b/src/Abp.ZeroCore/Abp.ZeroCore.csproj @@ -0,0 +1,48 @@ + + + + + + net46;netstandard1.6 + true + Abp.ZeroCore + Abp.ZeroCore + asp.net;asp.net mvc;boilerplate;application framework;web framework;framework;domain driven design;multitenancy;user management;role management;identity + false + false + false + false + false + false + Abp + + + + + + + + + lib/net46/ + true + + + lib/netstandard1.6/ + true + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Abp.ZeroCore/Authorization/AbpLoginManager.cs b/src/Abp.ZeroCore/Authorization/AbpLoginManager.cs new file mode 100644 index 00000000..47210feb --- /dev/null +++ b/src/Abp.ZeroCore/Authorization/AbpLoginManager.cs @@ -0,0 +1,370 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Claims; +using System.Threading.Tasks; +using System.Transactions; +using Abp.Auditing; +using Abp.Authorization.Roles; +using Abp.Authorization.Users; +using Abp.Configuration; +using Abp.Configuration.Startup; +using Abp.Dependency; +using Abp.Domain.Repositories; +using Abp.Domain.Uow; +using Abp.Extensions; +using Abp.IdentityFramework; +using Abp.MultiTenancy; +using Abp.Timing; +using Abp.Zero.Configuration; +using Microsoft.AspNetCore.Identity; + +namespace Abp.Authorization +{ + public class AbpLogInManager : ITransientDependency + where TTenant : AbpTenant + where TRole : AbpRole, new() + where TUser : AbpUser + { + public IClientInfoProvider ClientInfoProvider { get; set; } + + protected IMultiTenancyConfig MultiTenancyConfig { get; } + protected IRepository TenantRepository { get; } + protected IUnitOfWorkManager UnitOfWorkManager { get; } + protected AbpUserManager UserManager { get; } + protected ISettingManager SettingManager { get; } + protected IRepository UserLoginAttemptRepository { get; } + protected IUserManagementConfig UserManagementConfig { get; } + protected IIocResolver IocResolver { get; } + protected AbpRoleManager RoleManager { get; } + + private readonly IPasswordHasher _passwordHasher; + + private readonly AbpUserClaimsPrincipalFactory _claimsPrincipalFactory; + + public AbpLogInManager( + AbpUserManager userManager, + IMultiTenancyConfig multiTenancyConfig, + IRepository tenantRepository, + IUnitOfWorkManager unitOfWorkManager, + ISettingManager settingManager, + IRepository userLoginAttemptRepository, + IUserManagementConfig userManagementConfig, + IIocResolver iocResolver, + IPasswordHasher passwordHasher, + AbpRoleManager roleManager, + AbpUserClaimsPrincipalFactory claimsPrincipalFactory) + { + _passwordHasher = passwordHasher; + _claimsPrincipalFactory = claimsPrincipalFactory; + MultiTenancyConfig = multiTenancyConfig; + TenantRepository = tenantRepository; + UnitOfWorkManager = unitOfWorkManager; + SettingManager = settingManager; + UserLoginAttemptRepository = userLoginAttemptRepository; + UserManagementConfig = userManagementConfig; + IocResolver = iocResolver; + RoleManager = roleManager; + UserManager = userManager; + + ClientInfoProvider = NullClientInfoProvider.Instance; + } + + [UnitOfWork] + public virtual async Task> LoginAsync(UserLoginInfo login, string tenancyName = null) + { + var result = await LoginAsyncInternal(login, tenancyName); + await SaveLoginAttempt(result, tenancyName, login.ProviderKey + "@" + login.LoginProvider); + return result; + } + + protected virtual async Task> LoginAsyncInternal(UserLoginInfo login, string tenancyName) + { + if (login == null || login.LoginProvider.IsNullOrEmpty() || login.ProviderKey.IsNullOrEmpty()) + { + throw new ArgumentException("login"); + } + + //Get and check tenant + TTenant tenant = null; + if (!MultiTenancyConfig.IsEnabled) + { + tenant = await GetDefaultTenantAsync(); + } + else if (!string.IsNullOrWhiteSpace(tenancyName)) + { + tenant = await TenantRepository.FirstOrDefaultAsync(t => t.TenancyName == tenancyName); + if (tenant == null) + { + return new AbpLoginResult(AbpLoginResultType.InvalidTenancyName); + } + + if (!tenant.IsActive) + { + return new AbpLoginResult(AbpLoginResultType.TenantIsNotActive, tenant); + } + } + + int? tenantId = tenant == null ? (int?)null : tenant.Id; + using (UnitOfWorkManager.Current.SetTenantId(tenantId)) + { + var user = await UserManager.AbpStore.FindAsync(tenantId, login); + if (user == null) + { + return new AbpLoginResult(AbpLoginResultType.UnknownExternalLogin, tenant); + } + + return await CreateLoginResultAsync(user, tenant); + } + } + + [UnitOfWork] + public virtual async Task> LoginAsync(string userNameOrEmailAddress, string plainPassword, string tenancyName = null, bool shouldLockout = true) + { + var result = await LoginAsyncInternal(userNameOrEmailAddress, plainPassword, tenancyName, shouldLockout); + await SaveLoginAttempt(result, tenancyName, userNameOrEmailAddress); + return result; + } + + protected virtual async Task> LoginAsyncInternal(string userNameOrEmailAddress, string plainPassword, string tenancyName, bool shouldLockout) + { + if (userNameOrEmailAddress.IsNullOrEmpty()) + { + throw new ArgumentNullException(nameof(userNameOrEmailAddress)); + } + + if (plainPassword.IsNullOrEmpty()) + { + throw new ArgumentNullException(nameof(plainPassword)); + } + + //Get and check tenant + TTenant tenant = null; + using (UnitOfWorkManager.Current.SetTenantId(null)) + { + if (!MultiTenancyConfig.IsEnabled) + { + tenant = await GetDefaultTenantAsync(); + } + else if (!string.IsNullOrWhiteSpace(tenancyName)) + { + tenant = await TenantRepository.FirstOrDefaultAsync(t => t.TenancyName == tenancyName); + if (tenant == null) + { + return new AbpLoginResult(AbpLoginResultType.InvalidTenancyName); + } + + if (!tenant.IsActive) + { + return new AbpLoginResult(AbpLoginResultType.TenantIsNotActive, tenant); + } + } + } + + var tenantId = tenant == null ? (int?)null : tenant.Id; + using (UnitOfWorkManager.Current.SetTenantId(tenantId)) + { + await UserManager.InitializeOptionsAsync(tenantId); + + //TryLoginFromExternalAuthenticationSources method may create the user, that's why we are calling it before AbpStore.FindByNameOrEmailAsync + var loggedInFromExternalSource = await TryLoginFromExternalAuthenticationSources(userNameOrEmailAddress, plainPassword, tenant); + + var user = await UserManager.AbpStore.FindByNameOrEmailAsync(tenantId, userNameOrEmailAddress); + if (user == null) + { + return new AbpLoginResult(AbpLoginResultType.InvalidUserNameOrEmailAddress, tenant); + } + + if (!loggedInFromExternalSource) + { + if (await UserManager.IsLockedOutAsync(user)) + { + return new AbpLoginResult(AbpLoginResultType.LockedOut, tenant, user); + } + + if (!await UserManager.CheckPasswordAsync(user, plainPassword)) + { + if (shouldLockout) + { + if (await TryLockOutAsync(tenantId, user.Id)) + { + return new AbpLoginResult(AbpLoginResultType.LockedOut, tenant, user); + } + } + + return new AbpLoginResult(AbpLoginResultType.InvalidPassword, tenant, user); + } + + await UserManager.ResetAccessFailedCountAsync(user); + } + + return await CreateLoginResultAsync(user, tenant); + } + } + + protected virtual async Task> CreateLoginResultAsync(TUser user, TTenant tenant = null) + { + if (!user.IsActive) + { + return new AbpLoginResult(AbpLoginResultType.UserIsNotActive); + } + + if (await IsEmailConfirmationRequiredForLoginAsync(user.TenantId) && !user.IsEmailConfirmed) + { + return new AbpLoginResult(AbpLoginResultType.UserEmailIsNotConfirmed); + } + + if (await IsPhoneConfirmationRequiredForLoginAsync(user.TenantId) && !user.IsPhoneNumberConfirmed) + { + return new AbpLoginResult(AbpLoginResultType.UserPhoneNumberIsNotConfirmed); + } + + user.LastLoginTime = Clock.Now; + + await UserManager.AbpStore.UpdateAsync(user); + + await UnitOfWorkManager.Current.SaveChangesAsync(); + + var principal = await _claimsPrincipalFactory.CreateAsync(user); + + return new AbpLoginResult( + tenant, + user, + principal.Identity as ClaimsIdentity + ); + } + + protected virtual async Task SaveLoginAttempt(AbpLoginResult loginResult, string tenancyName, string userNameOrEmailAddress) + { + using (var uow = UnitOfWorkManager.Begin(TransactionScopeOption.Suppress)) + { + var tenantId = loginResult.Tenant != null ? loginResult.Tenant.Id : (int?)null; + using (UnitOfWorkManager.Current.SetTenantId(tenantId)) + { + var loginAttempt = new UserLoginAttempt + { + TenantId = tenantId, + TenancyName = tenancyName, + + UserId = loginResult.User != null ? loginResult.User.Id : (long?) null, + UserNameOrEmailAddress = userNameOrEmailAddress, + + Result = loginResult.Result, + + BrowserInfo = ClientInfoProvider.BrowserInfo, + ClientIpAddress = ClientInfoProvider.ClientIpAddress, + ClientName = ClientInfoProvider.ComputerName, + }; + + await UserLoginAttemptRepository.InsertAsync(loginAttempt); + await UnitOfWorkManager.Current.SaveChangesAsync(); + + await uow.CompleteAsync(); + } + } + } + + protected virtual async Task TryLockOutAsync(int? tenantId, long userId) + { + using (var uow = UnitOfWorkManager.Begin(TransactionScopeOption.Suppress)) + { + using (UnitOfWorkManager.Current.SetTenantId(tenantId)) + { + var user = await UserManager.FindByIdAsync(userId.ToString()); + + (await UserManager.AccessFailedAsync(user)).CheckErrors(); + + var isLockOut = await UserManager.IsLockedOutAsync(user); + + await UnitOfWorkManager.Current.SaveChangesAsync(); + + await uow.CompleteAsync(); + + return isLockOut; + } + } + } + + protected virtual async Task TryLoginFromExternalAuthenticationSources(string userNameOrEmailAddress, string plainPassword, TTenant tenant) + { + if (!UserManagementConfig.ExternalAuthenticationSources.Any()) + { + return false; + } + + foreach (var sourceType in UserManagementConfig.ExternalAuthenticationSources) + { + using (var source = IocResolver.ResolveAsDisposable>(sourceType)) + { + if (await source.Object.TryAuthenticateAsync(userNameOrEmailAddress, plainPassword, tenant)) + { + var tenantId = tenant == null ? (int?)null : tenant.Id; + using (UnitOfWorkManager.Current.SetTenantId(tenantId)) + { + var user = await UserManager.AbpStore.FindByNameOrEmailAsync(tenantId, userNameOrEmailAddress); + if (user == null) + { + user = await source.Object.CreateUserAsync(userNameOrEmailAddress, tenant); + + user.TenantId = tenantId; + user.AuthenticationSource = source.Object.Name; + user.Password = _passwordHasher.HashPassword(user, Guid.NewGuid().ToString("N").Left(16)); //Setting a random password since it will not be used + + if (user.Roles == null) + { + user.Roles = new List(); + foreach (var defaultRole in RoleManager.Roles.Where(r => r.TenantId == tenantId && r.IsDefault).ToList()) + { + user.Roles.Add(new UserRole(tenantId, user.Id, defaultRole.Id)); + } + } + + await UserManager.AbpStore.CreateAsync(user); + } + else + { + await source.Object.UpdateUserAsync(user, tenant); + + user.AuthenticationSource = source.Object.Name; + + await UserManager.AbpStore.UpdateAsync(user); + } + + await UnitOfWorkManager.Current.SaveChangesAsync(); + + return true; + } + } + } + } + + return false; + } + + protected virtual async Task GetDefaultTenantAsync() + { + var tenant = await TenantRepository.FirstOrDefaultAsync(t => t.TenancyName == AbpTenant.DefaultTenantName); + if (tenant == null) + { + throw new AbpException("There should be a 'Default' tenant if multi-tenancy is disabled!"); + } + + return tenant; + } + + protected virtual async Task IsEmailConfirmationRequiredForLoginAsync(int? tenantId) + { + if (tenantId.HasValue) + { + return await SettingManager.GetSettingValueForTenantAsync(AbpZeroSettingNames.UserManagement.IsEmailConfirmationRequiredForLogin, tenantId.Value); + } + + return await SettingManager.GetSettingValueForApplicationAsync(AbpZeroSettingNames.UserManagement.IsEmailConfirmationRequiredForLogin); + } + + protected virtual Task IsPhoneConfirmationRequiredForLoginAsync(int? tenantId) + { + return Task.FromResult(false); + } + } +} diff --git a/src/Abp.ZeroCore/Authorization/AbpLoginManagerExtensions.cs b/src/Abp.ZeroCore/Authorization/AbpLoginManagerExtensions.cs new file mode 100644 index 00000000..acceb390 --- /dev/null +++ b/src/Abp.ZeroCore/Authorization/AbpLoginManagerExtensions.cs @@ -0,0 +1,30 @@ +using Abp.Authorization.Roles; +using Abp.Authorization.Users; +using Abp.MultiTenancy; +using Abp.Threading; + +namespace Abp.Authorization +{ + public static class AbpLogInManagerExtensions + { + public static AbpLoginResult Login( + this AbpLogInManager logInManager, + string userNameOrEmailAddress, + string plainPassword, + string tenancyName = null, + bool shouldLockout = true) + where TTenant : AbpTenant + where TRole : AbpRole, new() + where TUser : AbpUser + { + return AsyncHelper.RunSync( + () => logInManager.LoginAsync( + userNameOrEmailAddress, + plainPassword, + tenancyName, + shouldLockout + ) + ); + } + } +} diff --git a/src/Abp.ZeroCore/Authorization/AbpSecurityStampValidator.cs b/src/Abp.ZeroCore/Authorization/AbpSecurityStampValidator.cs new file mode 100644 index 00000000..b6b3cdb9 --- /dev/null +++ b/src/Abp.ZeroCore/Authorization/AbpSecurityStampValidator.cs @@ -0,0 +1,33 @@ +using System.Threading.Tasks; +using Abp.Authorization.Roles; +using Abp.Authorization.Users; +using Abp.Domain.Uow; +using Abp.MultiTenancy; +using Microsoft.AspNetCore.Authentication.Cookies; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Identity; +using Microsoft.Extensions.Options; + +namespace Abp.Authorization +{ + public class AbpSecurityStampValidator : SecurityStampValidator + where TTenant : AbpTenant + where TRole : AbpRole, new() + where TUser : AbpUser + { + public AbpSecurityStampValidator( + IOptions options, + AbpSignInManager signInManager) + : base( + options, + signInManager) + { + } + + [UnitOfWork] + public override Task ValidateAsync(CookieValidatePrincipalContext context) + { + return base.ValidateAsync(context); + } + } +} diff --git a/src/Abp.ZeroCore/Authorization/AbpSignInManager.cs b/src/Abp.ZeroCore/Authorization/AbpSignInManager.cs new file mode 100644 index 00000000..ff6db3b7 --- /dev/null +++ b/src/Abp.ZeroCore/Authorization/AbpSignInManager.cs @@ -0,0 +1,172 @@ +using System; +using System.Security.Claims; +using System.Threading.Tasks; +using Abp.Authorization.Roles; +using Abp.Authorization.Users; +using Abp.Configuration; +using Abp.Dependency; +using Abp.Domain.Uow; +using Abp.Extensions; +using Abp.MultiTenancy; +using Abp.Runtime.Security; +using Abp.Zero.Configuration; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Authentication; +using Microsoft.AspNetCore.Identity; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + +namespace Abp.Authorization +{ + public class AbpSignInManager : SignInManager, ITransientDependency + where TTenant : AbpTenant + where TRole : AbpRole, new() + where TUser : AbpUser + { + private readonly IUnitOfWorkManager _unitOfWorkManager; + private readonly ISettingManager _settingManager; + + public AbpSignInManager( + AbpUserManager userManager, + IHttpContextAccessor contextAccessor, + AbpUserClaimsPrincipalFactory claimsFactory, + IOptions optionsAccessor, + ILogger> logger, + IUnitOfWorkManager unitOfWorkManager, + ISettingManager settingManager) + : base( + userManager, + contextAccessor, + claimsFactory, + optionsAccessor, + logger) + { + _unitOfWorkManager = unitOfWorkManager; + _settingManager = settingManager; + } + + public virtual async Task SignInOrTwoFactorAsync(AbpLoginResult loginResult, bool isPersistent, bool? rememberBrowser = null, string loginProvider = null, bool bypassTwoFactor = false) + { + if (loginResult.Result != AbpLoginResultType.Success) + { + throw new ArgumentException("loginResult.Result should be success in order to sign in!"); + } + + using (_unitOfWorkManager.Current.SetTenantId(loginResult.Tenant?.Id)) + { + await UserManager.As>().InitializeOptionsAsync(loginResult.Tenant?.Id); + + if (!bypassTwoFactor && IsTrue(AbpZeroSettingNames.UserManagement.TwoFactorLogin.IsEnabled, loginResult.Tenant?.Id)) + { + if (await UserManager.GetTwoFactorEnabledAsync(loginResult.User)) + { + if ((await UserManager.GetValidTwoFactorProvidersAsync(loginResult.User)).Count > 0) + { + if (!await IsTwoFactorClientRememberedAsync(loginResult.User) || rememberBrowser == false) + { + await Context.Authentication.SignInAsync( + Options.Cookies.TwoFactorUserIdCookieAuthenticationScheme, + StoreTwoFactorInfo(loginResult.User, loginProvider) + ); + + return SignInResult.TwoFactorRequired; + } + } + } + } + + if (loginProvider != null) + { + await Context.Authentication.SignOutAsync(Options.Cookies.ExternalCookieAuthenticationScheme); + } + + await SignInAsync(loginResult.User, isPersistent, loginProvider); + return SignInResult.Success; + } + } + + public virtual async Task SignOutAndSignInAsync(ClaimsIdentity identity, bool isPersistent) + { + await SignOutAsync(); + await SignInAsync(identity, isPersistent); + } + + public virtual async Task SignInAsync(ClaimsIdentity identity, bool isPersistent) + { + await Context.Authentication.SignInAsync(Options.Cookies.ApplicationCookieAuthenticationScheme, + new ClaimsPrincipal(identity), + new AuthenticationProperties {IsPersistent = isPersistent} + ); + } + + [UnitOfWork] + public override Task SignInAsync(TUser user, AuthenticationProperties authenticationProperties, string authenticationMethod = null) + { + return base.SignInAsync(user, authenticationProperties, authenticationMethod); + } + + protected virtual ClaimsPrincipal StoreTwoFactorInfo(TUser user, string loginProvider) + { + var identity = new ClaimsIdentity(Options.Cookies.TwoFactorUserIdCookieAuthenticationScheme); + + identity.AddClaim(new Claim(ClaimTypes.Name, user.Id.ToString())); + + if (user.TenantId.HasValue) + { + identity.AddClaim(new Claim(AbpClaimTypes.TenantId, user.TenantId.Value.ToString())); + } + + if (loginProvider != null) + { + identity.AddClaim(new Claim(ClaimTypes.AuthenticationMethod, loginProvider)); + } + + return new ClaimsPrincipal(identity); + } + + public async Task GetVerifiedTenantIdAsync() + { + var principal = await Context.Authentication.AuthenticateAsync(Options.Cookies.TwoFactorUserIdCookieAuthenticationScheme); + + if (principal == null) + { + return null; + } + + return AbpZeroClaimsIdentityHelper.GetTenantId(principal); + } + + public override async Task IsTwoFactorClientRememberedAsync(TUser user) + { + var result = await Context.Authentication.AuthenticateAsync(Options.Cookies.TwoFactorRememberMeCookieAuthenticationScheme); + + return result != null && + result.FindFirstValue(ClaimTypes.Name) == user.Id.ToString() && + AbpZeroClaimsIdentityHelper.GetTenantId(result) == user.TenantId; + } + + public override async Task RememberTwoFactorClientAsync(TUser user) + { + var rememberBrowserIdentity = new ClaimsIdentity(Options.Cookies.TwoFactorRememberMeCookieAuthenticationScheme); + + rememberBrowserIdentity.AddClaim(new Claim(ClaimTypes.Name, user.Id.ToString())); + + if (user.TenantId.HasValue) + { + rememberBrowserIdentity.AddClaim(new Claim(AbpClaimTypes.TenantId, user.TenantId.Value.ToString())); + } + + await Context.Authentication.SignInAsync(Options.Cookies.TwoFactorRememberMeCookieAuthenticationScheme, + new ClaimsPrincipal(rememberBrowserIdentity), + new AuthenticationProperties { IsPersistent = true }); + } + + private bool IsTrue(string settingName, int? tenantId) + { + return tenantId == null + ? _settingManager.GetSettingValueForApplication(settingName) + : _settingManager.GetSettingValueForTenant(settingName, tenantId.Value); + } + } +} diff --git a/src/Abp.ZeroCore/Authorization/AbpUserClaimsPrincipalFactory.cs b/src/Abp.ZeroCore/Authorization/AbpUserClaimsPrincipalFactory.cs new file mode 100644 index 00000000..141a8a6a --- /dev/null +++ b/src/Abp.ZeroCore/Authorization/AbpUserClaimsPrincipalFactory.cs @@ -0,0 +1,41 @@ +using System.Linq; +using System.Security.Claims; +using System.Threading.Tasks; +using Abp.Authorization.Roles; +using Abp.Authorization.Users; +using Abp.Dependency; +using Abp.Domain.Uow; +using Abp.Runtime.Security; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Identity; +using Microsoft.Extensions.Options; + +namespace Abp.Authorization +{ + public class AbpUserClaimsPrincipalFactory : UserClaimsPrincipalFactory, ITransientDependency + where TRole : AbpRole, new() + where TUser : AbpUser + { + public AbpUserClaimsPrincipalFactory( + AbpUserManager userManager, + AbpRoleManager roleManager, + IOptions optionsAccessor + ) : base(userManager, roleManager, optionsAccessor) + { + + } + + [UnitOfWork] + public override async Task CreateAsync(TUser user) + { + var principal = await base.CreateAsync(user); + + if (user.TenantId.HasValue) + { + principal.Identities.First().AddClaim(new Claim(AbpClaimTypes.TenantId,user.TenantId.ToString())); + } + + return principal; + } + } +} \ No newline at end of file diff --git a/src/Abp.ZeroCore/Authorization/AbpZeroClaimsIdentityHelper.cs b/src/Abp.ZeroCore/Authorization/AbpZeroClaimsIdentityHelper.cs new file mode 100644 index 00000000..fe0d2a1d --- /dev/null +++ b/src/Abp.ZeroCore/Authorization/AbpZeroClaimsIdentityHelper.cs @@ -0,0 +1,20 @@ +using System; +using System.Security.Claims; +using Abp.Runtime.Security; + +namespace Abp.Authorization +{ + internal static class AbpZeroClaimsIdentityHelper + { + public static int? GetTenantId(ClaimsPrincipal principal) + { + var tenantIdOrNull = principal?.FindFirstValue(AbpClaimTypes.TenantId); + if (tenantIdOrNull == null) + { + return null; + } + + return Convert.ToInt32(tenantIdOrNull); + } + } +} \ No newline at end of file diff --git a/src/Abp.ZeroCore/Authorization/PermissionChecker.cs b/src/Abp.ZeroCore/Authorization/PermissionChecker.cs new file mode 100644 index 00000000..57fa0d53 --- /dev/null +++ b/src/Abp.ZeroCore/Authorization/PermissionChecker.cs @@ -0,0 +1,65 @@ +using System.Threading.Tasks; +using Abp.Authorization.Roles; +using Abp.Authorization.Users; +using Abp.Dependency; +using Abp.Domain.Uow; +using Abp.Runtime.Session; +using Castle.Core.Logging; + +namespace Abp.Authorization +{ + /// + /// Application should inherit this class to implement . + /// + /// + /// + public class PermissionChecker : IPermissionChecker, ITransientDependency, IIocManagerAccessor + where TRole : AbpRole, new() + where TUser : AbpUser + { + private readonly AbpUserManager _userManager; + + public IIocManager IocManager { get; set; } + + public ILogger Logger { get; set; } + + public IAbpSession AbpSession { get; set; } + + public ICurrentUnitOfWorkProvider CurrentUnitOfWorkProvider { get; set; } + + /// + /// Constructor. + /// + public PermissionChecker(AbpUserManager userManager) + { + _userManager = userManager; + + Logger = NullLogger.Instance; + AbpSession = NullAbpSession.Instance; + } + + public virtual async Task IsGrantedAsync(string permissionName) + { + return AbpSession.UserId.HasValue && await IsGrantedAsync(AbpSession.UserId.Value, permissionName); + } + + public virtual async Task IsGrantedAsync(long userId, string permissionName) + { + return await _userManager.IsGrantedAsync(userId, permissionName); + } + + [UnitOfWork] + public virtual async Task IsGrantedAsync(UserIdentifier user, string permissionName) + { + if (CurrentUnitOfWorkProvider?.Current == null) + { + return await IsGrantedAsync(user.UserId, permissionName); + } + + using (CurrentUnitOfWorkProvider.Current.SetTenantId(user.TenantId)) + { + return await IsGrantedAsync(user.UserId, permissionName); + } + } + } +} diff --git a/src/Abp.ZeroCore/Authorization/Roles/AbpRole.cs b/src/Abp.ZeroCore/Authorization/Roles/AbpRole.cs new file mode 100644 index 00000000..1154c57f --- /dev/null +++ b/src/Abp.ZeroCore/Authorization/Roles/AbpRole.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using Abp.Authorization.Users; +using Abp.Domain.Entities.Auditing; + +namespace Abp.Authorization.Roles +{ + /// + /// Represents a role in an application. A role is used to group permissions. + /// + /// + /// Application should use permissions to check if user is granted to perform an operation. + /// Checking 'if a user has a role' is not possible until the role is static (). + /// Static roles can be used in the code and can not be deleted by users. + /// Non-static (dynamic) roles can be added/removed by users and we can not know their name while coding. + /// A user can have multiple roles. Thus, user will have all permissions of all assigned roles. + /// + public abstract class AbpRole : AbpRoleBase, IFullAudited + where TUser : AbpUser + { + /// + /// Unique name of this role. + /// + [Required] + [StringLength(MaxNameLength)] + public virtual string NormalizedName { get; set; } + + /// + /// Claims of this user. + /// + [ForeignKey("RoleId")] + public virtual ICollection Claims { get; set; } + + /// + /// A random value that must change whenever a user is persisted to the store + /// + public virtual string ConcurrencyStamp { get; set; } = Guid.NewGuid().ToString(); + + public virtual TUser DeleterUser { get; set; } + + public virtual TUser CreatorUser { get; set; } + + public virtual TUser LastModifierUser { get; set; } + + protected AbpRole() + { + SetNormalizedName(); + } + + /// + /// Creates a new object. + /// + /// TenantId or null (if this is not a tenant-level role) + /// Display name of the role + protected AbpRole(int? tenantId, string displayName) + : base(tenantId, displayName) + { + SetNormalizedName(); + } + + /// + /// Creates a new object. + /// + /// TenantId or null (if this is not a tenant-level role) + /// Unique role name + /// Display name of the role + protected AbpRole(int? tenantId, string name, string displayName) + : base(tenantId, name, displayName) + { + SetNormalizedName(); + } + + public void SetNormalizedName() + { + NormalizedName = Name.ToUpperInvariant(); + } + } +} \ No newline at end of file diff --git a/src/Abp.ZeroCore/Authorization/Roles/AbpRoleManager.cs b/src/Abp.ZeroCore/Authorization/Roles/AbpRoleManager.cs new file mode 100644 index 00000000..30a14ca6 --- /dev/null +++ b/src/Abp.ZeroCore/Authorization/Roles/AbpRoleManager.cs @@ -0,0 +1,424 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Abp.Authorization.Users; +using Abp.Domain.Services; +using Abp.Domain.Uow; +using Abp.Localization; +using Abp.MultiTenancy; +using Abp.Runtime.Caching; +using Abp.Runtime.Session; +using Abp.UI; +using Abp.Zero; +using Abp.Zero.Configuration; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Identity; +using Microsoft.Extensions.Logging; + +namespace Abp.Authorization.Roles +{ + public class AbpRoleManager : RoleManager, IDomainService + where TRole : AbpRole, new() + where TUser : AbpUser + { + public ILocalizationManager LocalizationManager { get; set; } + + public IAbpSession AbpSession { get; set; } + + public IRoleManagementConfig RoleManagementConfig { get; private set; } + + private IRolePermissionStore RolePermissionStore + { + get + { + if (!(Store is IRolePermissionStore)) + { + throw new AbpException("Store is not IRolePermissionStore"); + } + + return Store as IRolePermissionStore; + } + } + + protected AbpRoleStore AbpStore { get; private set; } + + private readonly IPermissionManager _permissionManager; + private readonly ICacheManager _cacheManager; + private readonly IUnitOfWorkManager _unitOfWorkManager; + + public AbpRoleManager( + AbpRoleStore store, + IEnumerable> roleValidators, + ILookupNormalizer keyNormalizer, + IdentityErrorDescriber errors, + ILogger> logger, + IHttpContextAccessor contextAccessor, + IPermissionManager permissionManager, + ICacheManager cacheManager, + IUnitOfWorkManager unitOfWorkManager, + IRoleManagementConfig roleManagementConfig) + : base( + store, + roleValidators, + keyNormalizer, + errors, + logger, + contextAccessor) + { + _permissionManager = permissionManager; + _cacheManager = cacheManager; + _unitOfWorkManager = unitOfWorkManager; + + RoleManagementConfig = roleManagementConfig; + AbpStore = store; + AbpSession = NullAbpSession.Instance; + LocalizationManager = NullLocalizationManager.Instance; + } + + /// + /// Checks if a role is granted for a permission. + /// + /// The role's name to check it's permission + /// Name of the permission + /// True, if the role has the permission + public virtual async Task IsGrantedAsync(string roleName, string permissionName) + { + return await IsGrantedAsync((await GetRoleByNameAsync(roleName)).Id, _permissionManager.GetPermission(permissionName)); + } + + /// + /// Checks if a role has a permission. + /// + /// The role's id to check it's permission + /// Name of the permission + /// True, if the role has the permission + public virtual async Task IsGrantedAsync(int roleId, string permissionName) + { + return await IsGrantedAsync(roleId, _permissionManager.GetPermission(permissionName)); + } + + /// + /// Checks if a role is granted for a permission. + /// + /// The role + /// The permission + /// True, if the role has the permission + public Task IsGrantedAsync(TRole role, Permission permission) + { + return IsGrantedAsync(role.Id, permission); + } + + /// + /// Checks if a role is granted for a permission. + /// + /// role id + /// The permission + /// True, if the role has the permission + public virtual async Task IsGrantedAsync(int roleId, Permission permission) + { + //Get cached role permissions + var cacheItem = await GetRolePermissionCacheItemAsync(roleId); + + //Check the permission + return cacheItem.GrantedPermissions.Contains(permission.Name); + } + + /// + /// Gets granted permission names for a role. + /// + /// Role id + /// List of granted permissions + public virtual async Task> GetGrantedPermissionsAsync(int roleId) + { + return await GetGrantedPermissionsAsync(await GetRoleByIdAsync(roleId)); + } + + /// + /// Gets granted permission names for a role. + /// + /// Role name + /// List of granted permissions + public virtual async Task> GetGrantedPermissionsAsync(string roleName) + { + return await GetGrantedPermissionsAsync(await GetRoleByNameAsync(roleName)); + } + + /// + /// Gets granted permissions for a role. + /// + /// Role + /// List of granted permissions + public virtual async Task> GetGrantedPermissionsAsync(TRole role) + { + var permissionList = new List(); + + foreach (var permission in _permissionManager.GetAllPermissions()) + { + if (await IsGrantedAsync(role.Id, permission)) + { + permissionList.Add(permission); + } + } + + return permissionList; + } + + /// + /// Sets all granted permissions of a role at once. + /// Prohibits all other permissions. + /// + /// Role id + /// Permissions + public virtual async Task SetGrantedPermissionsAsync(int roleId, IEnumerable permissions) + { + await SetGrantedPermissionsAsync(await GetRoleByIdAsync(roleId), permissions); + } + + /// + /// Sets all granted permissions of a role at once. + /// Prohibits all other permissions. + /// + /// The role + /// Permissions + public virtual async Task SetGrantedPermissionsAsync(TRole role, IEnumerable permissions) + { + var oldPermissions = await GetGrantedPermissionsAsync(role); + var newPermissions = permissions.ToArray(); + + foreach (var permission in oldPermissions.Where(p => !newPermissions.Contains(p, PermissionEqualityComparer.Instance))) + { + await ProhibitPermissionAsync(role, permission); + } + + foreach (var permission in newPermissions.Where(p => !oldPermissions.Contains(p, PermissionEqualityComparer.Instance))) + { + await GrantPermissionAsync(role, permission); + } + } + + /// + /// Grants a permission for a role. + /// + /// Role + /// Permission + public async Task GrantPermissionAsync(TRole role, Permission permission) + { + if (await IsGrantedAsync(role.Id, permission)) + { + return; + } + + await RolePermissionStore.AddPermissionAsync(role, new PermissionGrantInfo(permission.Name, true)); + } + + /// + /// Prohibits a permission for a role. + /// + /// Role + /// Permission + public async Task ProhibitPermissionAsync(TRole role, Permission permission) + { + if (!await IsGrantedAsync(role.Id, permission)) + { + return; + } + + await RolePermissionStore.RemovePermissionAsync(role, new PermissionGrantInfo(permission.Name, true)); + } + + /// + /// Prohibits all permissions for a role. + /// + /// Role + public async Task ProhibitAllPermissionsAsync(TRole role) + { + foreach (var permission in _permissionManager.GetAllPermissions()) + { + await ProhibitPermissionAsync(role, permission); + } + } + + /// + /// Resets all permission settings for a role. + /// It removes all permission settings for the role. + /// Role will have permissions those have set to true. + /// + /// Role + public async Task ResetAllPermissionsAsync(TRole role) + { + await RolePermissionStore.RemoveAllPermissionSettingsAsync(role); + } + + /// + /// Creates a role. + /// + /// Role + public override async Task CreateAsync(TRole role) + { + var result = await CheckDuplicateRoleNameAsync(role.Id, role.Name, role.DisplayName); + if (!result.Succeeded) + { + return result; + } + + var tenantId = GetCurrentTenantId(); + if (tenantId.HasValue && !role.TenantId.HasValue) + { + role.TenantId = tenantId.Value; + } + + return await base.CreateAsync(role); + } + + public override async Task UpdateAsync(TRole role) + { + var result = await CheckDuplicateRoleNameAsync(role.Id, role.Name, role.DisplayName); + if (!result.Succeeded) + { + return result; + } + + return await base.UpdateAsync(role); + } + + /// + /// Deletes a role. + /// + /// Role + public override async Task DeleteAsync(TRole role) + { + if (role.IsStatic) + { + throw new UserFriendlyException(string.Format(L("CanNotDeleteStaticRole"), role.Name)); + } + + return await base.DeleteAsync(role); + } + + /// + /// Gets a role by given id. + /// Throws exception if no role with given id. + /// + /// Role id + /// Role + /// Throws exception if no role with given id + public virtual async Task GetRoleByIdAsync(int roleId) + { + var role = await FindByIdAsync(roleId.ToString()); + if (role == null) + { + throw new AbpException("There is no role with id: " + roleId); + } + + return role; + } + + /// + /// Gets a role by given name. + /// Throws exception if no role with given roleName. + /// + /// Role name + /// Role + /// Throws exception if no role with given roleName + public virtual async Task GetRoleByNameAsync(string roleName) + { + var role = await FindByNameAsync(roleName); + if (role == null) + { + throw new AbpException("There is no role with name: " + roleName); + } + + return role; + } + + public async Task GrantAllPermissionsAsync(TRole role) + { + var permissions = _permissionManager.GetAllPermissions(role.GetMultiTenancySide()); + await SetGrantedPermissionsAsync(role, permissions); + } + + [UnitOfWork] + public virtual async Task CreateStaticRoles(int tenantId) + { + var staticRoleDefinitions = RoleManagementConfig.StaticRoles.Where(sr => sr.Side == MultiTenancySides.Tenant); + + using (_unitOfWorkManager.Current.SetTenantId(tenantId)) + { + foreach (var staticRoleDefinition in staticRoleDefinitions) + { + var role = new TRole + { + TenantId = tenantId, + Name = staticRoleDefinition.RoleName, + DisplayName = staticRoleDefinition.RoleName, + IsStatic = true + }; + + var identityResult = await CreateAsync(role); + if (!identityResult.Succeeded) + { + return identityResult; + } + } + } + + return IdentityResult.Success; + } + + public virtual async Task CheckDuplicateRoleNameAsync(int? expectedRoleId, string name, string displayName) + { + var role = await FindByNameAsync(name); + if (role != null && role.Id != expectedRoleId) + { + throw new UserFriendlyException(string.Format(L("RoleNameIsAlreadyTaken"), name)); + } + + role = await FindByDisplayNameAsync(displayName); + if (role != null && role.Id != expectedRoleId) + { + throw new UserFriendlyException(string.Format(L("RoleDisplayNameIsAlreadyTaken"), displayName)); + } + + return IdentityResult.Success; + } + + private Task FindByDisplayNameAsync(string displayName) + { + return AbpStore.FindByDisplayNameAsync(displayName); + } + + private async Task GetRolePermissionCacheItemAsync(int roleId) + { + var cacheKey = roleId + "@" + (GetCurrentTenantId() ?? 0); + return await _cacheManager.GetRolePermissionCache().GetAsync(cacheKey, async () => + { + var newCacheItem = new RolePermissionCacheItem(roleId); + + foreach (var permissionInfo in await RolePermissionStore.GetPermissionsAsync(roleId)) + { + if (permissionInfo.IsGranted) + { + newCacheItem.GrantedPermissions.Add(permissionInfo.Name); + } + } + + return newCacheItem; + }); + } + + private string L(string name) + { + return LocalizationManager.GetString(AbpZeroConsts.LocalizationSourceName, name); + } + + private int? GetCurrentTenantId() + { + if (_unitOfWorkManager.Current != null) + { + return _unitOfWorkManager.Current.GetTenantId(); + } + + return AbpSession.TenantId; + } + } +} diff --git a/src/Abp.ZeroCore/Authorization/Roles/AbpRoleStore.cs b/src/Abp.ZeroCore/Authorization/Roles/AbpRoleStore.cs new file mode 100644 index 00000000..0436f00c --- /dev/null +++ b/src/Abp.ZeroCore/Authorization/Roles/AbpRoleStore.cs @@ -0,0 +1,387 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Claims; +using System.Threading; +using System.Threading.Tasks; +using Abp.Authorization.Users; +using Abp.Dependency; +using Abp.Domain.Repositories; +using Abp.Domain.Uow; +using Abp.Extensions; +using Abp.Zero; +using Castle.Core.Logging; +using JetBrains.Annotations; +using Microsoft.AspNetCore.Identity; + +namespace Abp.Authorization.Roles +{ + /// + /// Creates a new instance of a persistence store for roles. + /// + public class AbpRoleStore : + IRoleStore, + IRoleClaimStore, + IRolePermissionStore, + IQueryableRoleStore, + ITransientDependency + + where TRole : AbpRole + where TUser : AbpUser + { + public ILogger Logger { get; set; } + + /// + /// Gets or sets the for any error that occurred with the current operation. + /// + public IdentityErrorDescriber ErrorDescriber { get; set; } + + /// + /// Gets or sets a flag indicating if changes should be persisted after CreateAsync, UpdateAsync and DeleteAsync are called. + /// + /// + /// True if changes should be automatically persisted, otherwise false. + /// + public bool AutoSaveChanges { get; set; } = true; + + public IQueryable Roles => _roleRepository.GetAll(); + + private readonly IRepository _roleRepository; + private readonly IUnitOfWorkManager _unitOfWorkManager; + private readonly IRepository _rolePermissionSettingRepository; + + public AbpRoleStore( + IUnitOfWorkManager unitOfWorkManager, + IRepository roleRepository, + IRepository rolePermissionSettingRepository) + { + _unitOfWorkManager = unitOfWorkManager; + _roleRepository = roleRepository; + _rolePermissionSettingRepository = rolePermissionSettingRepository; + + ErrorDescriber = new IdentityErrorDescriber(); + Logger = NullLogger.Instance; + } + + /// Saves the current store. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + protected Task SaveChanges(CancellationToken cancellationToken) + { + if (!AutoSaveChanges || _unitOfWorkManager.Current == null) + { + return Task.CompletedTask; + } + + return _unitOfWorkManager.Current.SaveChangesAsync(); + } + + /// + /// Creates a new role in a store as an asynchronous operation. + /// + /// The role to create in the store. + /// The used to propagate notifications that the operation should be canceled. + /// A that represents the of the asynchronous query. + public virtual async Task CreateAsync([NotNull] TRole role, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + + Check.NotNull(role, nameof(role)); + + await _roleRepository.InsertAsync(role); + await SaveChanges(cancellationToken); + + return IdentityResult.Success; + } + + /// + /// Updates a role in a store as an asynchronous operation. + /// + /// The role to update in the store. + /// The used to propagate notifications that the operation should be canceled. + /// A that represents the of the asynchronous query. + public virtual async Task UpdateAsync([NotNull] TRole role, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + + Check.NotNull(role, nameof(role)); + + role.ConcurrencyStamp = Guid.NewGuid().ToString(); + await _roleRepository.UpdateAsync(role); + + try + { + await SaveChanges(cancellationToken); + } + catch (AbpDbConcurrencyException ex) + { + Logger.Warn(ex.ToString(), ex); + return IdentityResult.Failed(ErrorDescriber.ConcurrencyFailure()); + } + + await SaveChanges(cancellationToken); + + return IdentityResult.Success; + } + + /// + /// Deletes a role from the store as an asynchronous operation. + /// + /// The role to delete from the store. + /// The used to propagate notifications that the operation should be canceled. + /// A that represents the of the asynchronous query. + public virtual async Task DeleteAsync([NotNull] TRole role, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + + Check.NotNull(role, nameof(role)); + + await _roleRepository.DeleteAsync(role); + + try + { + await SaveChanges(cancellationToken); + } + catch (AbpDbConcurrencyException ex) + { + Logger.Warn(ex.ToString(), ex); + return IdentityResult.Failed(ErrorDescriber.ConcurrencyFailure()); + } + + await SaveChanges(cancellationToken); + + return IdentityResult.Success; + } + + /// + /// Gets the ID for a role from the store as an asynchronous operation. + /// + /// The role whose ID should be returned. + /// The used to propagate notifications that the operation should be canceled. + /// A that contains the ID of the role. + public Task GetRoleIdAsync([NotNull] TRole role, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + + Check.NotNull(role, nameof(role)); + + return Task.FromResult(role.Id.ToString()); + } + + /// + /// Gets the name of a role from the store as an asynchronous operation. + /// + /// The role whose name should be returned. + /// The used to propagate notifications that the operation should be canceled. + /// A that contains the name of the role. + public Task GetRoleNameAsync([NotNull] TRole role, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + + Check.NotNull(role, nameof(role)); + + return Task.FromResult(role.Name); + } + + /// + /// Sets the name of a role in the store as an asynchronous operation. + /// + /// The role whose name should be set. + /// The name of the role. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + public Task SetRoleNameAsync([NotNull] TRole role, string roleName, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + + Check.NotNull(role, nameof(role)); + + role.Name = roleName; + return Task.CompletedTask; + } + + /// + /// Finds the role who has the specified ID as an asynchronous operation. + /// + /// The role ID to look for. + /// The used to propagate notifications that the operation should be canceled. + /// A that result of the look up. + public virtual Task FindByIdAsync(string id, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + + return _roleRepository.GetAsync(id.To()); + } + + /// + /// Finds the role who has the specified normalized name as an asynchronous operation. + /// + /// The normalized role name to look for. + /// The used to propagate notifications that the operation should be canceled. + /// A that result of the look up. + public virtual Task FindByNameAsync([NotNull] string normalizedName, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + + Check.NotNull(normalizedName, nameof(normalizedName)); + + return _roleRepository.FirstOrDefaultAsync(r => r.NormalizedName == normalizedName); + } + + /// + /// Get a role's normalized name as an asynchronous operation. + /// + /// The role whose normalized name should be retrieved. + /// The used to propagate notifications that the operation should be canceled. + /// A that contains the name of the role. + public virtual Task GetNormalizedRoleNameAsync([NotNull] TRole role, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + + Check.NotNull(role, nameof(role)); + + return Task.FromResult(role.NormalizedName); + } + + /// + /// Set a role's normalized name as an asynchronous operation. + /// + /// The role whose normalized name should be set. + /// The normalized name to set + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + public virtual Task SetNormalizedRoleNameAsync([NotNull] TRole role, string normalizedName, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + + Check.NotNull(role, nameof(role)); + + role.NormalizedName = normalizedName; + + return Task.CompletedTask; + } + + /// + /// Dispose the stores + /// + public void Dispose() + { + } + + /// + /// Get the claims associated with the specified as an asynchronous operation. + /// + /// The role whose claims should be retrieved. + /// The used to propagate notifications that the operation should be canceled. + /// A that contains the claims granted to a role. + public virtual async Task> GetClaimsAsync([NotNull] TRole role, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + + Check.NotNull(role, nameof(role)); + + await _roleRepository.EnsureCollectionLoadedAsync(role, u => u.Claims, cancellationToken); + + return role.Claims.Select(c => new Claim(c.ClaimType, c.ClaimValue)).ToList(); + } + + /// + /// Adds the given to the specified . + /// + /// The role to add the claim to. + /// The claim to add to the role. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + public async Task AddClaimAsync([NotNull] TRole role, [NotNull] Claim claim, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + + Check.NotNull(role, nameof(role)); + Check.NotNull(claim, nameof(claim)); + + await _roleRepository.EnsureCollectionLoadedAsync(role, u => u.Claims, cancellationToken); + + role.Claims.Add(new RoleClaim(role, claim)); + } + + /// + /// Removes the given from the specified . + /// + /// The role to remove the claim from. + /// The claim to remove from the role. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + public async Task RemoveClaimAsync([NotNull] TRole role, [NotNull] Claim claim, CancellationToken cancellationToken = default(CancellationToken)) + { + Check.NotNull(role, nameof(role)); + Check.NotNull(claim, nameof(claim)); + + await _roleRepository.EnsureCollectionLoadedAsync(role, u => u.Claims, cancellationToken); + + role.Claims.RemoveAll(c => c.ClaimValue == claim.Value && c.ClaimType == claim.Type); + } + + public virtual async Task FindByDisplayNameAsync(string displayName) + { + return await _roleRepository.FirstOrDefaultAsync( + role => role.DisplayName == displayName + ); + } + + public virtual async Task AddPermissionAsync(TRole role, PermissionGrantInfo permissionGrant) + { + if (await HasPermissionAsync(role.Id, permissionGrant)) + { + return; + } + + await _rolePermissionSettingRepository.InsertAsync( + new RolePermissionSetting + { + TenantId = role.TenantId, + RoleId = role.Id, + Name = permissionGrant.Name, + IsGranted = permissionGrant.IsGranted + }); + } + + /// + public virtual async Task RemovePermissionAsync(TRole role, PermissionGrantInfo permissionGrant) + { + await _rolePermissionSettingRepository.DeleteAsync( + permissionSetting => permissionSetting.RoleId == role.Id && + permissionSetting.Name == permissionGrant.Name && + permissionSetting.IsGranted == permissionGrant.IsGranted + ); + } + + /// + public virtual Task> GetPermissionsAsync(TRole role) + { + return GetPermissionsAsync(role.Id); + } + + public async Task> GetPermissionsAsync(int roleId) + { + return (await _rolePermissionSettingRepository.GetAllListAsync(p => p.RoleId == roleId)) + .Select(p => new PermissionGrantInfo(p.Name, p.IsGranted)) + .ToList(); + } + + /// + public virtual async Task HasPermissionAsync(int roleId, PermissionGrantInfo permissionGrant) + { + return await _rolePermissionSettingRepository.FirstOrDefaultAsync( + p => p.RoleId == roleId && + p.Name == permissionGrant.Name && + p.IsGranted == permissionGrant.IsGranted + ) != null; + } + + /// + public virtual async Task RemoveAllPermissionSettingsAsync(TRole role) + { + await _rolePermissionSettingRepository.DeleteAsync(s => s.RoleId == role.Id); + } + } +} diff --git a/src/Abp.ZeroCore/Authorization/Users/AbpLoginResult.cs b/src/Abp.ZeroCore/Authorization/Users/AbpLoginResult.cs new file mode 100644 index 00000000..31c90a94 --- /dev/null +++ b/src/Abp.ZeroCore/Authorization/Users/AbpLoginResult.cs @@ -0,0 +1,32 @@ +using System.Security.Claims; +using Abp.MultiTenancy; + +namespace Abp.Authorization.Users +{ + public class AbpLoginResult + where TTenant : AbpTenant + where TUser : AbpUserBase + { + public AbpLoginResultType Result { get; private set; } + + public TTenant Tenant { get; private set; } + + public TUser User { get; private set; } + + public ClaimsIdentity Identity { get; private set; } + + public AbpLoginResult(AbpLoginResultType result, TTenant tenant = null, TUser user = null) + { + Result = result; + Tenant = tenant; + User = user; + } + + public AbpLoginResult(TTenant tenant, TUser user, ClaimsIdentity identity) + : this(AbpLoginResultType.Success, tenant) + { + User = user; + Identity = identity; + } + } +} \ No newline at end of file diff --git a/src/Abp.ZeroCore/Authorization/Users/AbpUser.cs b/src/Abp.ZeroCore/Authorization/Users/AbpUser.cs new file mode 100644 index 00000000..c7527cd8 --- /dev/null +++ b/src/Abp.ZeroCore/Authorization/Users/AbpUser.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel.DataAnnotations; +using Abp.Domain.Entities.Auditing; + +namespace Abp.Authorization.Users +{ + /// + /// Represents a user. + /// + public abstract class AbpUser : AbpUserBase, IFullAudited + where TUser : AbpUser + { + /// + /// User name. + /// User name must be unique for it's tenant. + /// + [Required] + [StringLength(MaxUserNameLength)] + public virtual string NormalizedUserName { get; set; } + + /// + /// Email address of the user. + /// Email address must be unique for it's tenant. + /// + [Required] + [StringLength(MaxEmailAddressLength)] + public virtual string NormalizedEmailAddress { get; set; } + + /// + /// A random value that must change whenever a user is persisted to the store + /// + public virtual string ConcurrencyStamp { get; set; } = Guid.NewGuid().ToString(); + + public virtual ICollection Tokens { get; set; } + + public virtual TUser DeleterUser { get; set; } + + public virtual TUser CreatorUser { get; set; } + + public virtual TUser LastModifierUser { get; set; } + + protected AbpUser() + { + Tokens = new Collection(); + } + + public void SetNormalizedNames() + { + NormalizedUserName = UserName.ToUpperInvariant(); + NormalizedEmailAddress = EmailAddress.ToUpperInvariant(); + } + } +} \ No newline at end of file diff --git a/src/Abp.ZeroCore/Authorization/Users/AbpUserManager.cs b/src/Abp.ZeroCore/Authorization/Users/AbpUserManager.cs new file mode 100644 index 00000000..55c57eb8 --- /dev/null +++ b/src/Abp.ZeroCore/Authorization/Users/AbpUserManager.cs @@ -0,0 +1,681 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Abp.Application.Features; +using Abp.Authorization.Roles; +using Abp.Configuration; +using Abp.Domain.Repositories; +using Abp.Domain.Services; +using Abp.Domain.Uow; +using Abp.Localization; +using Abp.MultiTenancy; +using Abp.Organizations; +using Abp.Runtime.Caching; +using Abp.Runtime.Session; +using Abp.UI; +using Abp.Zero; +using Abp.Zero.Configuration; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Identity; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + +namespace Abp.Authorization.Users +{ + public class AbpUserManager : UserManager, IDomainService + where TRole : AbpRole, new() + where TUser : AbpUser + { + protected IUserPermissionStore UserPermissionStore + { + get + { + if (!(Store is IUserPermissionStore)) + { + throw new AbpException("Store is not IUserPermissionStore"); + } + + return Store as IUserPermissionStore; + } + } + + public ILocalizationManager LocalizationManager { get; set; } + + public IAbpSession AbpSession { get; set; } + + public FeatureDependencyContext FeatureDependencyContext { get; set; } + + protected AbpRoleManager RoleManager { get; } + + public AbpUserStore AbpStore { get; } + + private readonly IPermissionManager _permissionManager; + private readonly IUnitOfWorkManager _unitOfWorkManager; + private readonly ICacheManager _cacheManager; + private readonly IRepository _organizationUnitRepository; + private readonly IRepository _userOrganizationUnitRepository; + private readonly IOrganizationUnitSettings _organizationUnitSettings; + private readonly ISettingManager _settingManager; + + public AbpUserManager( + AbpRoleManager roleManager, + AbpUserStore store, + IOptions optionsAccessor, + IPasswordHasher passwordHasher, + IEnumerable> userValidators, + IEnumerable> passwordValidators, + ILookupNormalizer keyNormalizer, + IdentityErrorDescriber errors, + IServiceProvider services, + ILogger> logger, + IPermissionManager permissionManager, + IUnitOfWorkManager unitOfWorkManager, + ICacheManager cacheManager, + IRepository organizationUnitRepository, + IRepository userOrganizationUnitRepository, + IOrganizationUnitSettings organizationUnitSettings, + ISettingManager settingManager) + : base( + store, + optionsAccessor, + passwordHasher, + userValidators, + passwordValidators, + keyNormalizer, + errors, + services, + logger) + { + _permissionManager = permissionManager; + _unitOfWorkManager = unitOfWorkManager; + _cacheManager = cacheManager; + _organizationUnitRepository = organizationUnitRepository; + _userOrganizationUnitRepository = userOrganizationUnitRepository; + _organizationUnitSettings = organizationUnitSettings; + _settingManager = settingManager; + + AbpStore = store; + RoleManager = roleManager; + } + + public override async Task CreateAsync(TUser user) + { + var result = await CheckDuplicateUsernameOrEmailAddressAsync(user.Id, user.UserName, user.EmailAddress); + if (!result.Succeeded) + { + return result; + } + + var tenantId = GetCurrentTenantId(); + if (tenantId.HasValue && !user.TenantId.HasValue) + { + user.TenantId = tenantId.Value; + } + + return await base.CreateAsync(user); + } + + /// + /// Check whether a user is granted for a permission. + /// + /// User id + /// Permission name + public virtual async Task IsGrantedAsync(long userId, string permissionName) + { + return await IsGrantedAsync( + userId, + _permissionManager.GetPermission(permissionName) + ); + } + + /// + /// Check whether a user is granted for a permission. + /// + /// User + /// Permission + public virtual Task IsGrantedAsync(TUser user, Permission permission) + { + return IsGrantedAsync(user.Id, permission); + } + + /// + /// Check whether a user is granted for a permission. + /// + /// User id + /// Permission + public virtual async Task IsGrantedAsync(long userId, Permission permission) + { + //Check for multi-tenancy side + if (!permission.MultiTenancySides.HasFlag(AbpSession.MultiTenancySide)) + { + return false; + } + + //Check for depended features + if (permission.FeatureDependency != null && AbpSession.MultiTenancySide == MultiTenancySides.Tenant) + { + if (!await permission.FeatureDependency.IsSatisfiedAsync(FeatureDependencyContext)) + { + return false; + } + } + + //Get cached user permissions + var cacheItem = await GetUserPermissionCacheItemAsync(userId); + if (cacheItem == null) + { + return false; + } + + //Check for user-specific value + if (cacheItem.GrantedPermissions.Contains(permission.Name)) + { + return true; + } + + if (cacheItem.ProhibitedPermissions.Contains(permission.Name)) + { + return false; + } + + //Check for roles + foreach (var roleId in cacheItem.RoleIds) + { + if (await RoleManager.IsGrantedAsync(roleId, permission)) + { + return true; + } + } + + return false; + } + + /// + /// Gets granted permissions for a user. + /// + /// Role + /// List of granted permissions + public virtual async Task> GetGrantedPermissionsAsync(TUser user) + { + var permissionList = new List(); + + foreach (var permission in _permissionManager.GetAllPermissions()) + { + if (await IsGrantedAsync(user.Id, permission)) + { + permissionList.Add(permission); + } + } + + return permissionList; + } + + /// + /// Sets all granted permissions of a user at once. + /// Prohibits all other permissions. + /// + /// The user + /// Permissions + public virtual async Task SetGrantedPermissionsAsync(TUser user, IEnumerable permissions) + { + var oldPermissions = await GetGrantedPermissionsAsync(user); + var newPermissions = permissions.ToArray(); + + foreach (var permission in oldPermissions.Where(p => !newPermissions.Contains(p))) + { + await ProhibitPermissionAsync(user, permission); + } + + foreach (var permission in newPermissions.Where(p => !oldPermissions.Contains(p))) + { + await GrantPermissionAsync(user, permission); + } + } + + /// + /// Prohibits all permissions for a user. + /// + /// User + public async Task ProhibitAllPermissionsAsync(TUser user) + { + foreach (var permission in _permissionManager.GetAllPermissions()) + { + await ProhibitPermissionAsync(user, permission); + } + } + + /// + /// Resets all permission settings for a user. + /// It removes all permission settings for the user. + /// User will have permissions according to his roles. + /// This method does not prohibit all permissions. + /// For that, use . + /// + /// User + public async Task ResetAllPermissionsAsync(TUser user) + { + await UserPermissionStore.RemoveAllPermissionSettingsAsync(user); + } + + /// + /// Grants a permission for a user if not already granted. + /// + /// User + /// Permission + public virtual async Task GrantPermissionAsync(TUser user, Permission permission) + { + await UserPermissionStore.RemovePermissionAsync(user, new PermissionGrantInfo(permission.Name, false)); + + if (await IsGrantedAsync(user.Id, permission)) + { + return; + } + + await UserPermissionStore.AddPermissionAsync(user, new PermissionGrantInfo(permission.Name, true)); + } + + /// + /// Prohibits a permission for a user if it's granted. + /// + /// User + /// Permission + public virtual async Task ProhibitPermissionAsync(TUser user, Permission permission) + { + await UserPermissionStore.RemovePermissionAsync(user, new PermissionGrantInfo(permission.Name, true)); + + if (!await IsGrantedAsync(user.Id, permission)) + { + return; + } + + await UserPermissionStore.AddPermissionAsync(user, new PermissionGrantInfo(permission.Name, false)); + } + + public virtual async Task FindByNameOrEmailAsync(string userNameOrEmailAddress) + { + return await AbpStore.FindByNameOrEmailAsync(userNameOrEmailAddress); + } + + public virtual Task> FindAllAsync(UserLoginInfo login) + { + return AbpStore.FindAllAsync(login); + } + + /// + /// Gets a user by given id. + /// Throws exception if no user found with given id. + /// + /// User id + /// User + /// Throws exception if no user found with given id + public virtual async Task GetUserByIdAsync(long userId) + { + var user = await FindByIdAsync(userId.ToString()); + if (user == null) + { + throw new AbpException("There is no user with id: " + userId); + } + + return user; + } + + public override async Task UpdateAsync(TUser user) + { + var result = await CheckDuplicateUsernameOrEmailAddressAsync(user.Id, user.UserName, user.EmailAddress); + if (!result.Succeeded) + { + return result; + } + + //Admin user's username can not be changed! + if (user.UserName != AbpUserBase.AdminUserName) + { + if ((await GetOldUserNameAsync(user.Id)) == AbpUserBase.AdminUserName) + { + throw new UserFriendlyException(string.Format(L("CanNotRenameAdminUser"), AbpUserBase.AdminUserName)); + } + } + + return await base.UpdateAsync(user); + } + + public override async Task DeleteAsync(TUser user) + { + if (user.UserName == AbpUserBase.AdminUserName) + { + throw new UserFriendlyException(string.Format(L("CanNotDeleteAdminUser"), AbpUserBase.AdminUserName)); + } + + return await base.DeleteAsync(user); + } + + public virtual async Task ChangePasswordAsync(TUser user, string newPassword) + { + var errors = new List(); + + foreach (var validator in PasswordValidators) + { + var validationResult = await validator.ValidateAsync(this, user, newPassword); + if (!validationResult.Succeeded) + { + errors.AddRange(validationResult.Errors); + } + } + + if (errors.Any()) + { + return IdentityResult.Failed(errors.ToArray()); + } + + await AbpStore.SetPasswordHashAsync(user, PasswordHasher.HashPassword(user, newPassword)); + return IdentityResult.Success; + } + + public virtual async Task CheckDuplicateUsernameOrEmailAddressAsync(long? expectedUserId, string userName, string emailAddress) + { + var user = (await FindByNameAsync(userName)); + if (user != null && user.Id != expectedUserId) + { + throw new UserFriendlyException(string.Format(L("Identity.DuplicateUserName"), userName)); + } + + user = (await FindByEmailAsync(emailAddress)); + if (user != null && user.Id != expectedUserId) + { + throw new UserFriendlyException(string.Format(L("Identity.DuplicateEmail"), emailAddress)); + } + + return IdentityResult.Success; + } + + public virtual async Task SetRoles(TUser user, string[] roleNames) + { + await AbpStore.UserRepository.EnsureCollectionLoadedAsync(user, u => u.Roles); + + //Remove from removed roles + foreach (var userRole in user.Roles.ToList()) + { + var role = await RoleManager.FindByIdAsync(userRole.RoleId.ToString()); + if (roleNames.All(roleName => role.Name != roleName)) + { + var result = await RemoveFromRoleAsync(user, role.Name); + if (!result.Succeeded) + { + return result; + } + } + } + + //Add to added roles + foreach (var roleName in roleNames) + { + var role = await RoleManager.GetRoleByNameAsync(roleName); + if (user.Roles.All(ur => ur.RoleId != role.Id)) + { + var result = await AddToRoleAsync(user, roleName); + if (!result.Succeeded) + { + return result; + } + } + } + + return IdentityResult.Success; + } + + public virtual async Task IsInOrganizationUnitAsync(long userId, long ouId) + { + return await IsInOrganizationUnitAsync( + await GetUserByIdAsync(userId), + await _organizationUnitRepository.GetAsync(ouId) + ); + } + + public virtual async Task IsInOrganizationUnitAsync(TUser user, OrganizationUnit ou) + { + return await _userOrganizationUnitRepository.CountAsync(uou => + uou.UserId == user.Id && uou.OrganizationUnitId == ou.Id + ) > 0; + } + + public virtual async Task AddToOrganizationUnitAsync(long userId, long ouId) + { + await AddToOrganizationUnitAsync( + await GetUserByIdAsync(userId), + await _organizationUnitRepository.GetAsync(ouId) + ); + } + + public virtual async Task AddToOrganizationUnitAsync(TUser user, OrganizationUnit ou) + { + var currentOus = await GetOrganizationUnitsAsync(user); + + if (currentOus.Any(cou => cou.Id == ou.Id)) + { + return; + } + + await CheckMaxUserOrganizationUnitMembershipCountAsync(user.TenantId, currentOus.Count + 1); + + await _userOrganizationUnitRepository.InsertAsync(new UserOrganizationUnit(user.TenantId, user.Id, ou.Id)); + } + + public virtual async Task RemoveFromOrganizationUnitAsync(long userId, long ouId) + { + await RemoveFromOrganizationUnitAsync( + await GetUserByIdAsync(userId), + await _organizationUnitRepository.GetAsync(ouId) + ); + } + + public virtual async Task RemoveFromOrganizationUnitAsync(TUser user, OrganizationUnit ou) + { + await _userOrganizationUnitRepository.DeleteAsync(uou => uou.UserId == user.Id && uou.OrganizationUnitId == ou.Id); + } + + public virtual async Task SetOrganizationUnitsAsync(long userId, params long[] organizationUnitIds) + { + await SetOrganizationUnitsAsync( + await GetUserByIdAsync(userId), + organizationUnitIds + ); + } + + private async Task CheckMaxUserOrganizationUnitMembershipCountAsync(int? tenantId, int requestedCount) + { + var maxCount = await _organizationUnitSettings.GetMaxUserMembershipCountAsync(tenantId); + if (requestedCount > maxCount) + { + throw new AbpException($"Can not set more than {maxCount} organization unit for a user!"); + } + } + + public virtual async Task SetOrganizationUnitsAsync(TUser user, params long[] organizationUnitIds) + { + if (organizationUnitIds == null) + { + organizationUnitIds = new long[0]; + } + + await CheckMaxUserOrganizationUnitMembershipCountAsync(user.TenantId, organizationUnitIds.Length); + + var currentOus = await GetOrganizationUnitsAsync(user); + + //Remove from removed OUs + foreach (var currentOu in currentOus) + { + if (!organizationUnitIds.Contains(currentOu.Id)) + { + await RemoveFromOrganizationUnitAsync(user, currentOu); + } + } + + //Add to added OUs + foreach (var organizationUnitId in organizationUnitIds) + { + if (currentOus.All(ou => ou.Id != organizationUnitId)) + { + await AddToOrganizationUnitAsync( + user, + await _organizationUnitRepository.GetAsync(organizationUnitId) + ); + } + } + } + + [UnitOfWork] + public virtual Task> GetOrganizationUnitsAsync(TUser user) + { + var query = from uou in _userOrganizationUnitRepository.GetAll() + join ou in _organizationUnitRepository.GetAll() on uou.OrganizationUnitId equals ou.Id + where uou.UserId == user.Id + select ou; + + return Task.FromResult(query.ToList()); + } + + [UnitOfWork] + public virtual Task> GetUsersInOrganizationUnit(OrganizationUnit organizationUnit, bool includeChildren = false) + { + if (!includeChildren) + { + var query = from uou in _userOrganizationUnitRepository.GetAll() + join user in Users on uou.UserId equals user.Id + where uou.OrganizationUnitId == organizationUnit.Id + select user; + + return Task.FromResult(query.ToList()); + } + else + { + var query = from uou in _userOrganizationUnitRepository.GetAll() + join user in Users on uou.UserId equals user.Id + join ou in _organizationUnitRepository.GetAll() on uou.OrganizationUnitId equals ou.Id + where ou.Code.StartsWith(organizationUnit.Code) + select user; + + return Task.FromResult(query.ToList()); + } + } + + public virtual async Task InitializeOptionsAsync(int? tenantId) + { + Options = new IdentityOptions(); + + //Lockout + Options.Lockout.AllowedForNewUsers = await IsTrueAsync(AbpZeroSettingNames.UserManagement.UserLockOut.IsEnabled, tenantId); + Options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromSeconds(await GetSettingValueAsync(AbpZeroSettingNames.UserManagement.UserLockOut.DefaultAccountLockoutSeconds, tenantId)); + Options.Lockout.MaxFailedAccessAttempts = await GetSettingValueAsync(AbpZeroSettingNames.UserManagement.UserLockOut.MaxFailedAccessAttemptsBeforeLockout, tenantId); + + //Password complexity + Options.Password.RequireDigit = await GetSettingValueAsync(AbpZeroSettingNames.UserManagement.PasswordComplexity.RequireDigit, tenantId); + Options.Password.RequireLowercase = await GetSettingValueAsync(AbpZeroSettingNames.UserManagement.PasswordComplexity.RequireLowercase, tenantId); + Options.Password.RequireNonAlphanumeric = await GetSettingValueAsync(AbpZeroSettingNames.UserManagement.PasswordComplexity.RequireNonAlphanumeric, tenantId); + Options.Password.RequireUppercase = await GetSettingValueAsync(AbpZeroSettingNames.UserManagement.PasswordComplexity.RequireUppercase, tenantId); + Options.Password.RequiredLength = await GetSettingValueAsync(AbpZeroSettingNames.UserManagement.PasswordComplexity.RequiredLength, tenantId); + } + + protected virtual Task GetOldUserNameAsync(long userId) + { + return AbpStore.GetUserNameFromDatabaseAsync(userId); + } + + private async Task GetUserPermissionCacheItemAsync(long userId) + { + var cacheKey = userId + "@" + (GetCurrentTenantId() ?? 0); + return await _cacheManager.GetUserPermissionCache().GetAsync(cacheKey, async () => + { + var user = await FindByIdAsync(userId.ToString()); + if (user == null) + { + return null; + } + + var newCacheItem = new UserPermissionCacheItem(userId); + + foreach (var roleName in await GetRolesAsync(user)) + { + newCacheItem.RoleIds.Add((await RoleManager.GetRoleByNameAsync(roleName)).Id); + } + + foreach (var permissionInfo in await UserPermissionStore.GetPermissionsAsync(userId)) + { + if (permissionInfo.IsGranted) + { + newCacheItem.GrantedPermissions.Add(permissionInfo.Name); + } + else + { + newCacheItem.ProhibitedPermissions.Add(permissionInfo.Name); + } + } + + return newCacheItem; + }); + } + + public override async Task> GetValidTwoFactorProvidersAsync(TUser user) + { + var providers = new List(); + + foreach (var provider in await base.GetValidTwoFactorProvidersAsync(user)) + { + if (provider == "Email" && + !await IsTrueAsync(AbpZeroSettingNames.UserManagement.TwoFactorLogin.IsEmailProviderEnabled, user.TenantId)) + { + continue; + } + + if (provider == "Phone" && + !await IsTrueAsync(AbpZeroSettingNames.UserManagement.TwoFactorLogin.IsSmsProviderEnabled, user.TenantId)) + { + continue; + } + + providers.Add(provider); + } + + return providers; + } + + private bool IsTrue(string settingName, int? tenantId) + { + return GetSettingValue(settingName, tenantId); + } + + private Task IsTrueAsync(string settingName, int? tenantId) + { + return GetSettingValueAsync(settingName, tenantId); + } + + private T GetSettingValue(string settingName, int? tenantId) where T : struct + { + return tenantId == null + ? _settingManager.GetSettingValueForApplication(settingName) + : _settingManager.GetSettingValueForTenant(settingName, tenantId.Value); + } + + private Task GetSettingValueAsync(string settingName, int? tenantId) where T : struct + { + return tenantId == null + ? _settingManager.GetSettingValueForApplicationAsync(settingName) + : _settingManager.GetSettingValueForTenantAsync(settingName, tenantId.Value); + } + + private string L(string name) + { + return LocalizationManager.GetString(AbpZeroConsts.LocalizationSourceName, name); + } + + private int? GetCurrentTenantId() + { + if (_unitOfWorkManager.Current != null) + { + return _unitOfWorkManager.Current.GetTenantId(); + } + + return AbpSession.TenantId; + } + } +} \ No newline at end of file diff --git a/src/Abp.ZeroCore/Authorization/Users/AbpUserManagerExtensions.cs b/src/Abp.ZeroCore/Authorization/Users/AbpUserManagerExtensions.cs new file mode 100644 index 00000000..ffabab85 --- /dev/null +++ b/src/Abp.ZeroCore/Authorization/Users/AbpUserManagerExtensions.cs @@ -0,0 +1,30 @@ +using System; +using Abp.Authorization.Roles; +using Abp.Threading; + +namespace Abp.Authorization.Users +{ + /// + /// Extension methods for . + /// + public static class AbpUserManagerExtensions + { + /// + /// Check whether a user is granted for a permission. + /// + /// User manager + /// User id + /// Permission name + public static bool IsGranted(AbpUserManager manager, long userId, string permissionName) + where TRole : AbpRole, new() + where TUser : AbpUser + { + if (manager == null) + { + throw new ArgumentNullException(nameof(manager)); + } + + return AsyncHelper.RunSync(() => manager.IsGrantedAsync(userId, permissionName)); + } + } +} \ No newline at end of file diff --git a/src/Abp.ZeroCore/Authorization/Users/AbpUserStore.cs b/src/Abp.ZeroCore/Authorization/Users/AbpUserStore.cs new file mode 100644 index 00000000..dc4f6041 --- /dev/null +++ b/src/Abp.ZeroCore/Authorization/Users/AbpUserStore.cs @@ -0,0 +1,1289 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Security.Claims; +using System.Threading; +using System.Threading.Tasks; +using System.Transactions; +using Abp.Authorization.Roles; +using Abp.Dependency; +using Abp.Domain.Repositories; +using Abp.Domain.Uow; +using Abp.Extensions; +using Abp.Linq; +using Abp.Runtime.Session; +using Abp.Zero; +using Castle.Core.Logging; +using JetBrains.Annotations; +using Microsoft.AspNetCore.Identity; + +namespace Abp.Authorization.Users +{ + /// + /// Represents a new instance of a persistence store for the specified user and role types. + /// + public class AbpUserStore : + IUserLoginStore, + IUserRoleStore, + IUserClaimStore, + IUserPasswordStore, + IUserSecurityStampStore, + IUserEmailStore, + IUserLockoutStore, + IUserPhoneNumberStore, + IUserTwoFactorStore, + IUserAuthenticationTokenStore, + IUserPermissionStore, + IQueryableUserStore, + ITransientDependency + + where TRole : AbpRole + where TUser : AbpUser + { + public ILogger Logger { get; set; } + + /// + /// Gets or sets the for any error that occurred with the current operation. + /// + public IdentityErrorDescriber ErrorDescriber { get; set; } + + /// + /// Gets or sets a flag indicating if changes should be persisted after CreateAsync, UpdateAsync and DeleteAsync are called. + /// + /// + /// True if changes should be automatically persisted, otherwise false. + /// + public bool AutoSaveChanges { get; set; } = true; + + public IAbpSession AbpSession { get; set; } + + public IQueryable Users => UserRepository.GetAll(); + + public IRepository UserRepository { get; } + + private readonly IRepository _roleRepository; + private readonly IRepository _userRoleRepository; + private readonly IAsyncQueryableExecuter _asyncQueryableExecuter; + private readonly IRepository _userLoginRepository; + private readonly IRepository _userClaimRepository; + private readonly IRepository _userPermissionSettingRepository; + + private readonly IUnitOfWorkManager _unitOfWorkManager; + + public AbpUserStore( + IUnitOfWorkManager unitOfWorkManager, + IRepository userRepository, + IRepository roleRepository, + IAsyncQueryableExecuter asyncQueryableExecuter, + IRepository userRoleRepository, + IRepository userLoginRepository, + IRepository userClaimRepository, + IRepository userPermissionSettingRepository) + { + _unitOfWorkManager = unitOfWorkManager; + UserRepository = userRepository; + _roleRepository = roleRepository; + _asyncQueryableExecuter = asyncQueryableExecuter; + _userRoleRepository = userRoleRepository; + _userLoginRepository = userLoginRepository; + _userClaimRepository = userClaimRepository; + _userPermissionSettingRepository = userPermissionSettingRepository; + + AbpSession = NullAbpSession.Instance; + ErrorDescriber = new IdentityErrorDescriber(); + Logger = NullLogger.Instance; + } + + /// Saves the current store. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + protected Task SaveChanges(CancellationToken cancellationToken) + { + if (!AutoSaveChanges || _unitOfWorkManager.Current == null) + { + return Task.CompletedTask; + } + + return _unitOfWorkManager.Current.SaveChangesAsync(); + } + + /// + /// Gets the user identifier for the specified . + /// + /// The user whose identifier should be retrieved. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation, containing the identifier for the specified . + public virtual Task GetUserIdAsync([NotNull] TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + + Check.NotNull(user, nameof(user)); + + return Task.FromResult(user.Id.ToString()); + } + + /// + /// Gets the user name for the specified . + /// + /// The user whose name should be retrieved. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation, containing the name for the specified . + public virtual Task GetUserNameAsync([NotNull] TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + + Check.NotNull(user, nameof(user)); + + return Task.FromResult(user.UserName); + } + + /// + /// Sets the given for the specified . + /// + /// The user whose name should be set. + /// The user name to set. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + public virtual Task SetUserNameAsync([NotNull] TUser user, string userName, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + + Check.NotNull(user, nameof(user)); + + user.UserName = userName; + + return Task.CompletedTask; + } + + /// + /// Gets the normalized user name for the specified . + /// + /// The user whose normalized name should be retrieved. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation, containing the normalized user name for the specified . + public virtual Task GetNormalizedUserNameAsync([NotNull] TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + + Check.NotNull(user, nameof(user)); + + return Task.FromResult(user.NormalizedUserName); + } + + /// + /// Sets the given normalized name for the specified . + /// + /// The user whose name should be set. + /// The normalized name to set. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + public virtual Task SetNormalizedUserNameAsync([NotNull] TUser user, string normalizedName, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + + Check.NotNull(user, nameof(user)); + + user.NormalizedUserName = normalizedName; + + return Task.CompletedTask; + } + + /// + /// Creates the specified in the user store. + /// + /// The user to create. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation, containing the of the creation operation. + public virtual async Task CreateAsync([NotNull] TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + + Check.NotNull(user, nameof(user)); + + await UserRepository.InsertAsync(user); + await SaveChanges(cancellationToken); + + return IdentityResult.Success; + } + + /// + /// Updates the specified in the user store. + /// + /// The user to update. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation, containing the of the update operation. + public virtual async Task UpdateAsync([NotNull] TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + + Check.NotNull(user, nameof(user)); + + user.ConcurrencyStamp = Guid.NewGuid().ToString(); + await UserRepository.UpdateAsync(user); + + try + { + await SaveChanges(cancellationToken); + } + catch (AbpDbConcurrencyException ex) + { + Logger.Warn(ex.ToString(), ex); + return IdentityResult.Failed(ErrorDescriber.ConcurrencyFailure()); + } + + await SaveChanges(cancellationToken); + + return IdentityResult.Success; + } + + /// + /// Deletes the specified from the user store. + /// + /// The user to delete. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation, containing the of the update operation. + public virtual async Task DeleteAsync([NotNull] TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + + Check.NotNull(user, nameof(user)); + + await UserRepository.DeleteAsync(user); + + try + { + await SaveChanges(cancellationToken); + } + catch (AbpDbConcurrencyException ex) + { + Logger.Warn(ex.ToString(), ex); + return IdentityResult.Failed(ErrorDescriber.ConcurrencyFailure()); + } + + await SaveChanges(cancellationToken); + + return IdentityResult.Success; + } + + /// + /// Finds and returns a user, if any, who has the specified . + /// + /// The user ID to search for. + /// The used to propagate notifications that the operation should be canceled. + /// + /// The that represents the asynchronous operation, containing the user matching the specified if it exists. + /// + public virtual Task FindByIdAsync(string userId, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + + return UserRepository.FirstOrDefaultAsync(userId.To()); + } + + /// + /// Finds and returns a user, if any, who has the specified normalized user name. + /// + /// The normalized user name to search for. + /// The used to propagate notifications that the operation should be canceled. + /// + /// The that represents the asynchronous operation, containing the user matching the specified if it exists. + /// + public virtual Task FindByNameAsync([NotNull] string normalizedUserName, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + + Check.NotNull(normalizedUserName, nameof(normalizedUserName)); + + return UserRepository.FirstOrDefaultAsync(u => u.NormalizedUserName == normalizedUserName); + } + + /// + /// Sets the password hash for a user. + /// + /// The user to set the password hash for. + /// The password hash to set. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + public virtual Task SetPasswordHashAsync([NotNull] TUser user, string passwordHash, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + + Check.NotNull(user, nameof(user)); + + user.Password = passwordHash; + + return Task.CompletedTask; + } + + /// + /// Gets the password hash for a user. + /// + /// The user to retrieve the password hash for. + /// The used to propagate notifications that the operation should be canceled. + /// A that contains the password hash for the user. + public virtual Task GetPasswordHashAsync([NotNull] TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + + Check.NotNull(user, nameof(user)); + + return Task.FromResult(user.Password); + } + + /// + /// Returns a flag indicating if the specified user has a password. + /// + /// The user to retrieve the password hash for. + /// The used to propagate notifications that the operation should be canceled. + /// A containing a flag indicating if the specified user has a password. If the + /// user has a password the returned value with be true, otherwise it will be false. + public virtual Task HasPasswordAsync([NotNull] TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + + Check.NotNull(user, nameof(user)); + + return Task.FromResult(user.Password != null); + } + + /// + /// Adds the given to the specified . + /// + /// The user to add the role to. + /// The role to add. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + public virtual async Task AddToRoleAsync([NotNull] TUser user, [NotNull] string normalizedRoleName, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + + Check.NotNull(user, nameof(user)); + Check.NotNull(normalizedRoleName, nameof(normalizedRoleName)); + + if (await IsInRoleAsync(user, normalizedRoleName, cancellationToken)) + { + return; + } + + var role = await _roleRepository.FirstOrDefaultAsync(r => r.NormalizedName== normalizedRoleName); + + if (role == null) + { + throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "Role {0} does not exist!", normalizedRoleName)); + } + + user.Roles.Add(new UserRole(user.TenantId, user.Id, role.Id)); + } + + /// + /// Removes the given from the specified . + /// + /// The user to remove the role from. + /// The role to remove. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + public virtual async Task RemoveFromRoleAsync([NotNull] TUser user, [NotNull] string normalizedRoleName, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + + Check.NotNull(user, nameof(user)); + + if (string.IsNullOrWhiteSpace(normalizedRoleName)) + { + throw new ArgumentException(nameof(normalizedRoleName) + " can not be null or whitespace"); + } + + if (!await IsInRoleAsync(user, normalizedRoleName, cancellationToken)) + { + return; + } + + var role = await _roleRepository.FirstOrDefaultAsync(r => r.NormalizedName == normalizedRoleName); + if (role == null) + { + return; + } + + user.Roles.RemoveAll(r => r.RoleId == role.Id); + } + + /// + /// Retrieves the roles the specified is a member of. + /// + /// The user whose roles should be retrieved. + /// The used to propagate notifications that the operation should be canceled. + /// A that contains the roles the user is a member of. + [UnitOfWork] + public virtual async Task> GetRolesAsync([NotNull] TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + + Check.NotNull(user, nameof(user)); + + var query = from userRole in _userRoleRepository.GetAll() + join role in _roleRepository.GetAll() on userRole.RoleId equals role.Id + where userRole.UserId == user.Id + select role.Name; + + return await _asyncQueryableExecuter.ToListAsync(query); + } + + /// + /// Returns a flag indicating if the specified user is a member of the give . + /// + /// The user whose role membership should be checked. + /// The role to check membership of + /// The used to propagate notifications that the operation should be canceled. + /// A containing a flag indicating if the specified user is a member of the given group. If the + /// user is a member of the group the returned value with be true, otherwise it will be false. + public virtual async Task IsInRoleAsync([NotNull] TUser user, [NotNull] string normalizedRoleName, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + + Check.NotNull(user, nameof(user)); + + if (string.IsNullOrWhiteSpace(normalizedRoleName)) + { + throw new ArgumentException(nameof(normalizedRoleName) + " can not be null or whitespace"); + } + + var role = await _roleRepository.FirstOrDefaultAsync(r => r.NormalizedName == normalizedRoleName); + if (role == null) + { + return false; + } + + await UserRepository.EnsureCollectionLoadedAsync(user, u => u.Roles, cancellationToken); + + return user.Roles.Any(r => r.RoleId == role.Id); + } + + /// + /// Dispose the store + /// + public void Dispose() + { + + } + + /// + /// Get the claims associated with the specified as an asynchronous operation. + /// + /// The user whose claims should be retrieved. + /// The used to propagate notifications that the operation should be canceled. + /// A that contains the claims granted to a user. + public virtual async Task> GetClaimsAsync([NotNull] TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + + Check.NotNull(user, nameof(user)); + + await UserRepository.EnsureCollectionLoadedAsync(user, u => u.Claims, cancellationToken); + + return user.Claims.Select(c => new Claim(c.ClaimType, c.ClaimValue)).ToList(); + } + + /// + /// Adds the given to the specified . + /// + /// The user to add the claim to. + /// The claim to add to the user. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + public virtual async Task AddClaimsAsync([NotNull] TUser user, [NotNull] IEnumerable claims, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + + Check.NotNull(user, nameof(user)); + Check.NotNull(claims, nameof(claims)); + + await UserRepository.EnsureCollectionLoadedAsync(user, u => u.Claims, cancellationToken); + + foreach (var claim in claims) + { + user.Claims.Add(new UserClaim(user, claim)); + } + } + + /// + /// Replaces the on the specified , with the . + /// + /// The user to replace the claim on. + /// The claim replace. + /// The new claim replacing the . + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + public virtual async Task ReplaceClaimAsync([NotNull] TUser user, [NotNull] Claim claim, [NotNull] Claim newClaim, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + + Check.NotNull(user, nameof(user)); + Check.NotNull(claim, nameof(claim)); + Check.NotNull(newClaim, nameof(newClaim)); + + await UserRepository.EnsureCollectionLoadedAsync(user, u => u.Claims, cancellationToken); + + var userClaims = user.Claims.Where(uc => uc.ClaimValue == claim.Value && uc.ClaimType == claim.Type); + foreach (var userClaim in userClaims) + { + userClaim.ClaimType = claim.Type; + userClaim.ClaimValue = claim.Value; + } + } + + /// + /// Removes the given from the specified . + /// + /// The user to remove the claims from. + /// The claim to remove. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + public virtual async Task RemoveClaimsAsync([NotNull] TUser user, [NotNull] IEnumerable claims, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + + Check.NotNull(user, nameof(user)); + Check.NotNull(claims, nameof(claims)); + + await UserRepository.EnsureCollectionLoadedAsync(user, u => u.Claims, cancellationToken); + + foreach (var claim in claims) + { + user.Claims.RemoveAll(c => c.ClaimValue == claim.Value && c.ClaimType == claim.Type); + } + } + + /// + /// Adds the given to the specified . + /// + /// The user to add the login to. + /// The login to add to the user. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + public virtual async Task AddLoginAsync([NotNull] TUser user, [NotNull] UserLoginInfo login, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + + Check.NotNull(user, nameof(user)); + Check.NotNull(login, nameof(login)); + + await UserRepository.EnsureCollectionLoadedAsync(user, u => u.Logins, cancellationToken); + + user.Logins.Add(new UserLogin(user.TenantId, user.Id, login.LoginProvider, login.ProviderKey)); + } + + /// + /// Removes the given from the specified . + /// + /// The user to remove the login from. + /// The login to remove from the user. + /// The key provided by the to identify a user. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + public virtual async Task RemoveLoginAsync([NotNull] TUser user, [NotNull] string loginProvider, [NotNull] string providerKey, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + + Check.NotNull(user, nameof(user)); + Check.NotNull(loginProvider, nameof(loginProvider)); + Check.NotNull(providerKey, nameof(providerKey)); + + await UserRepository.EnsureCollectionLoadedAsync(user, u => u.Logins, cancellationToken); + + user.Logins.RemoveAll(userLogin => userLogin.LoginProvider == loginProvider && userLogin.ProviderKey == providerKey); + } + + /// + /// Retrieves the associated logins for the specified . + /// + /// The user whose associated logins to retrieve. + /// The used to propagate notifications that the operation should be canceled. + /// + /// The for the asynchronous operation, containing a list of for the specified , if any. + /// + public virtual async Task> GetLoginsAsync([NotNull] TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + + Check.NotNull(user, nameof(user)); + + await UserRepository.EnsureCollectionLoadedAsync(user, u => u.Logins, cancellationToken); + + return user.Logins.Select(l => new UserLoginInfo(l.LoginProvider, l.ProviderKey, l.LoginProvider)).ToList(); + } + + /// + /// Retrieves the user associated with the specified login provider and login provider key.. + /// + /// The login provider who provided the . + /// The key provided by the to identify a user. + /// The used to propagate notifications that the operation should be canceled. + /// + /// The for the asynchronous operation, containing the user, if any which matched the specified login provider and key. + /// + [UnitOfWork] + public virtual Task FindByLoginAsync([NotNull] string loginProvider, [NotNull] string providerKey, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + + Check.NotNull(loginProvider, nameof(loginProvider)); + Check.NotNull(providerKey, nameof(providerKey)); + + var query = from userLogin in _userLoginRepository.GetAll() + join user in UserRepository.GetAll() on userLogin.UserId equals user.Id + where userLogin.LoginProvider == loginProvider && + userLogin.ProviderKey == providerKey && + userLogin.TenantId == AbpSession.TenantId + select user; + + return _asyncQueryableExecuter.FirstOrDefaultAsync(query); + } + + /// + /// Gets a flag indicating whether the email address for the specified has been verified, true if the email address is verified otherwise + /// false. + /// + /// The user whose email confirmation status should be returned. + /// The used to propagate notifications that the operation should be canceled. + /// + /// The task object containing the results of the asynchronous operation, a flag indicating whether the email address for the specified + /// has been confirmed or not. + /// + public virtual Task GetEmailConfirmedAsync([NotNull] TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + + Check.NotNull(user, nameof(user)); + + return Task.FromResult(user.IsEmailConfirmed); + } + + /// + /// Sets the flag indicating whether the specified 's email address has been confirmed or not. + /// + /// The user whose email confirmation status should be set. + /// A flag indicating if the email address has been confirmed, true if the address is confirmed otherwise false. + /// The used to propagate notifications that the operation should be canceled. + /// The task object representing the asynchronous operation. + public virtual Task SetEmailConfirmedAsync([NotNull] TUser user, bool confirmed, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + + Check.NotNull(user, nameof(user)); + + user.IsEmailConfirmed = confirmed; + + return Task.CompletedTask; + } + + /// + /// Sets the address for a . + /// + /// The user whose email should be set. + /// The email to set. + /// The used to propagate notifications that the operation should be canceled. + /// The task object representing the asynchronous operation. + public virtual Task SetEmailAsync([NotNull] TUser user, string email, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + + Check.NotNull(user, nameof(user)); + + user.EmailAddress = email; + + return Task.CompletedTask; + } + + /// + /// Gets the email address for the specified . + /// + /// The user whose email should be returned. + /// The used to propagate notifications that the operation should be canceled. + /// The task object containing the results of the asynchronous operation, the email address for the specified . + public virtual Task GetEmailAsync([NotNull] TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + + Check.NotNull(user, nameof(user)); + + return Task.FromResult(user.EmailAddress); + } + + /// + /// Returns the normalized email for the specified . + /// + /// The user whose email address to retrieve. + /// The used to propagate notifications that the operation should be canceled. + /// + /// The task object containing the results of the asynchronous lookup operation, the normalized email address if any associated with the specified user. + /// + public virtual Task GetNormalizedEmailAsync([NotNull] TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + + Check.NotNull(user, nameof(user)); + + return Task.FromResult(user.NormalizedEmailAddress); + } + + /// + /// Sets the normalized email for the specified . + /// + /// The user whose email address to set. + /// The normalized email to set for the specified . + /// The used to propagate notifications that the operation should be canceled. + /// The task object representing the asynchronous operation. + public virtual Task SetNormalizedEmailAsync([NotNull] TUser user, string normalizedEmail, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + + Check.NotNull(user, nameof(user)); + + user.NormalizedEmailAddress = normalizedEmail; + + return Task.CompletedTask; + } + + /// + /// Gets the user, if any, associated with the specified, normalized email address. + /// + /// The normalized email address to return the user for. + /// The used to propagate notifications that the operation should be canceled. + /// + /// The task object containing the results of the asynchronous lookup operation, the user if any associated with the specified normalized email address. + /// + public virtual Task FindByEmailAsync(string normalizedEmail, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + + return UserRepository.FirstOrDefaultAsync(u => u.NormalizedEmailAddress == normalizedEmail); + } + + /// + /// Gets the last a user's last lockout expired, if any. + /// Any time in the past should be indicates a user is not locked out. + /// + /// The user whose lockout date should be retrieved. + /// The used to propagate notifications that the operation should be canceled. + /// + /// A that represents the result of the asynchronous query, a containing the last time + /// a user's lockout expired, if any. + /// + public virtual Task GetLockoutEndDateAsync([NotNull] TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + + Check.NotNull(user, nameof(user)); + + if (!user.LockoutEndDateUtc.HasValue) + { + return Task.FromResult(null); + } + + var lockoutEndDate = DateTime.SpecifyKind(user.LockoutEndDateUtc.Value, DateTimeKind.Utc); + + return Task.FromResult(new DateTimeOffset(lockoutEndDate)); + } + + /// + /// Locks out a user until the specified end date has passed. Setting a end date in the past immediately unlocks a user. + /// + /// The user whose lockout date should be set. + /// The after which the 's lockout should end. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + public virtual Task SetLockoutEndDateAsync([NotNull] TUser user, DateTimeOffset? lockoutEnd, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + + Check.NotNull(user, nameof(user)); + + user.LockoutEndDateUtc = lockoutEnd?.UtcDateTime; + + return Task.CompletedTask; + } + + /// + /// Records that a failed access has occurred, incrementing the failed access count. + /// + /// The user whose cancellation count should be incremented. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation, containing the incremented failed access count. + public virtual Task IncrementAccessFailedCountAsync([NotNull] TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + + Check.NotNull(user, nameof(user)); + + user.AccessFailedCount++; + + return Task.FromResult(user.AccessFailedCount); + } + + /// + /// Resets a user's failed access count. + /// + /// The user whose failed access count should be reset. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + /// This is typically called after the account is successfully accessed. + public virtual Task ResetAccessFailedCountAsync([NotNull] TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + + Check.NotNull(user, nameof(user)); + + user.AccessFailedCount = 0; + + return Task.CompletedTask; + } + + /// + /// Retrieves the current failed access count for the specified .. + /// + /// The user whose failed access count should be retrieved. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation, containing the failed access count. + public virtual Task GetAccessFailedCountAsync([NotNull] TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + + Check.NotNull(user, nameof(user)); + + return Task.FromResult(user.AccessFailedCount); + } + + /// + /// Retrieves a flag indicating whether user lockout can enabled for the specified user. + /// + /// The user whose ability to be locked out should be returned. + /// The used to propagate notifications that the operation should be canceled. + /// + /// The that represents the asynchronous operation, true if a user can be locked out, otherwise false. + /// + public virtual Task GetLockoutEnabledAsync([NotNull] TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + + Check.NotNull(user, nameof(user)); + + return Task.FromResult(user.IsLockoutEnabled); + } + + /// + /// Set the flag indicating if the specified can be locked out.. + /// + /// The user whose ability to be locked out should be set. + /// A flag indicating if lock out can be enabled for the specified . + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + public virtual Task SetLockoutEnabledAsync([NotNull] TUser user, bool enabled, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + + Check.NotNull(user, nameof(user)); + + user.IsLockoutEnabled = enabled; + + return Task.CompletedTask; + } + + /// + /// Sets the telephone number for the specified . + /// + /// The user whose telephone number should be set. + /// The telephone number to set. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + public virtual Task SetPhoneNumberAsync([NotNull] TUser user, string phoneNumber, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + + Check.NotNull(user, nameof(user)); + + user.PhoneNumber = phoneNumber; + + return Task.CompletedTask; + } + + /// + /// Gets the telephone number, if any, for the specified . + /// + /// The user whose telephone number should be retrieved. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation, containing the user's telephone number, if any. + public virtual Task GetPhoneNumberAsync([NotNull] TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + + Check.NotNull(user, nameof(user)); + + return Task.FromResult(user.PhoneNumber); + } + + /// + /// Gets a flag indicating whether the specified 's telephone number has been confirmed. + /// + /// The user to return a flag for, indicating whether their telephone number is confirmed. + /// The used to propagate notifications that the operation should be canceled. + /// + /// The that represents the asynchronous operation, returning true if the specified has a confirmed + /// telephone number otherwise false. + /// + public virtual Task GetPhoneNumberConfirmedAsync([NotNull] TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + + Check.NotNull(user, nameof(user)); + + return Task.FromResult(user.IsPhoneNumberConfirmed); + } + + /// + /// Sets a flag indicating if the specified 's phone number has been confirmed.. + /// + /// The user whose telephone number confirmation status should be set. + /// A flag indicating whether the user's telephone number has been confirmed. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + public virtual Task SetPhoneNumberConfirmedAsync([NotNull] TUser user, bool confirmed, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + + Check.NotNull(user, nameof(user)); + + user.IsPhoneNumberConfirmed = confirmed; + + return Task.CompletedTask; + } + + /// + /// Sets the provided security for the specified . + /// + /// The user whose security stamp should be set. + /// The security stamp to set. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + public virtual Task SetSecurityStampAsync([NotNull] TUser user, string stamp, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + + Check.NotNull(user, nameof(user)); + + user.SecurityStamp = stamp; + + return Task.CompletedTask; + } + + /// + /// Get the security stamp for the specified . + /// + /// The user whose security stamp should be set. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation, containing the security stamp for the specified . + public virtual Task GetSecurityStampAsync([NotNull] TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + + Check.NotNull(user, nameof(user)); + + return Task.FromResult(user.SecurityStamp); + } + + /// + /// Sets a flag indicating whether the specified has two factor authentication enabled or not, + /// as an asynchronous operation. + /// + /// The user whose two factor authentication enabled status should be set. + /// A flag indicating whether the specified has two factor authentication enabled. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + public virtual Task SetTwoFactorEnabledAsync([NotNull] TUser user, bool enabled, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + + Check.NotNull(user, nameof(user)); + + user.IsTwoFactorEnabled = enabled; + + return Task.CompletedTask; + } + + /// + /// Returns a flag indicating whether the specified has two factor authentication enabled or not, + /// as an asynchronous operation. + /// + /// The user whose two factor authentication enabled status should be set. + /// The used to propagate notifications that the operation should be canceled. + /// + /// The that represents the asynchronous operation, containing a flag indicating whether the specified + /// has two factor authentication enabled or not. + /// + public virtual Task GetTwoFactorEnabledAsync([NotNull] TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + + Check.NotNull(user, nameof(user)); + + return Task.FromResult(user.IsTwoFactorEnabled); + } + + /// + /// Retrieves all users with the specified claim. + /// + /// The claim whose users should be retrieved. + /// The used to propagate notifications that the operation should be canceled. + /// + /// The contains a list of users, if any, that contain the specified claim. + /// + [UnitOfWork] + public virtual async Task> GetUsersForClaimAsync([NotNull] Claim claim, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + + Check.NotNull(claim, nameof(claim)); + + var query = from userclaims in _userClaimRepository.GetAll() + join user in UserRepository.GetAll() on userclaims.UserId equals user.Id + where userclaims.ClaimValue == claim.Value && userclaims.ClaimType == claim.Type && userclaims.TenantId == AbpSession.TenantId + select user; + + return await _asyncQueryableExecuter.ToListAsync(query); + } + + /// + /// Retrieves all users in the specified role. + /// + /// The role whose users should be retrieved. + /// The used to propagate notifications that the operation should be canceled. + /// + /// The contains a list of users, if any, that are in the specified role. + /// + [UnitOfWork] + public virtual async Task> GetUsersInRoleAsync([NotNull] string normalizedRoleName, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + + if (string.IsNullOrEmpty(normalizedRoleName)) + { + throw new ArgumentNullException(nameof(normalizedRoleName)); + } + + var role = await _roleRepository.FirstOrDefaultAsync(r => r.NormalizedName == normalizedRoleName); + + if (role == null) + { + return new List(); + } + + var query = from userrole in _userRoleRepository.GetAll() + join user in UserRepository.GetAll() on userrole.UserId equals user.Id + where userrole.RoleId.Equals(role.Id) + select user; + + return await _asyncQueryableExecuter.ToListAsync(query); + } + + /// + /// Sets the token value for a particular user. + /// + /// The user. + /// The authentication provider for the token. + /// The name of the token. + /// The value of the token. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + public virtual async Task SetTokenAsync([NotNull] TUser user, string loginProvider, string name, string value, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + Check.NotNull(user, nameof(user)); + + await UserRepository.EnsureCollectionLoadedAsync(user, u => u.Tokens, cancellationToken); + + var token = user.Tokens.FirstOrDefault(t => t.LoginProvider == loginProvider && t.Name == name); + if (token == null) + { + user.Tokens.Add(new UserToken(user, loginProvider, name, value)); + } + else + { + token.Value = value; + } + } + + /// + /// Deletes a token for a user. + /// + /// The user. + /// The authentication provider for the token. + /// The name of the token. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + public async Task RemoveTokenAsync(TUser user, string loginProvider, string name, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + Check.NotNull(user, nameof(user)); + + await UserRepository.EnsureCollectionLoadedAsync(user, u => u.Tokens, cancellationToken); + + user.Tokens.RemoveAll(t => t.LoginProvider == loginProvider && t.Name == name); + } + + /// + /// Returns the token value. + /// + /// The user. + /// The authentication provider for the token. + /// The name of the token. + /// The used to propagate notifications that the operation should be canceled. + /// The that represents the asynchronous operation. + public async Task GetTokenAsync(TUser user, string loginProvider, string name, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + Check.NotNull(user, nameof(user)); + + await UserRepository.EnsureCollectionLoadedAsync(user, u => u.Tokens, cancellationToken); + + return user.Tokens.FirstOrDefault(t => t.LoginProvider == loginProvider && t.Name == name)?.Value; + } + + /// + /// Tries to find a user with user name or email address in current tenant. + /// + /// User name or email address + /// User or null + public virtual async Task FindByNameOrEmailAsync(string userNameOrEmailAddress) + { + return await UserRepository.FirstOrDefaultAsync( + user => (user.UserName == userNameOrEmailAddress || user.EmailAddress == userNameOrEmailAddress) + ); + } + + /// + /// Tries to find a user with user name or email address in given tenant. + /// + /// Tenant Id + /// User name or email address + /// User or null + [UnitOfWork] + public virtual async Task FindByNameOrEmailAsync(int? tenantId, string userNameOrEmailAddress) + { + using (_unitOfWorkManager.Current.SetTenantId(tenantId)) + { + return await FindByNameOrEmailAsync(userNameOrEmailAddress); + } + } + + public virtual async Task FindAsync(UserLoginInfo login) + { + var userLogin = await _userLoginRepository.FirstOrDefaultAsync( + ul => ul.LoginProvider == login.LoginProvider && ul.ProviderKey == login.ProviderKey + ); + + if (userLogin == null) + { + return null; + } + + return await UserRepository.FirstOrDefaultAsync(u => u.Id == userLogin.UserId); + } + + [UnitOfWork] + public virtual Task> FindAllAsync(UserLoginInfo login) + { + var query = from userLogin in _userLoginRepository.GetAll() + join user in UserRepository.GetAll() on userLogin.UserId equals user.Id + where userLogin.LoginProvider == login.LoginProvider && userLogin.ProviderKey == login.ProviderKey + select user; + + return Task.FromResult(query.ToList()); + } + + [UnitOfWork] + public virtual Task FindAsync(int? tenantId, UserLoginInfo login) + { + using (_unitOfWorkManager.Current.SetTenantId(tenantId)) + { + var query = from userLogin in _userLoginRepository.GetAll() + join user in UserRepository.GetAll() on userLogin.UserId equals user.Id + where userLogin.LoginProvider == login.LoginProvider && userLogin.ProviderKey == login.ProviderKey + select user; + + return Task.FromResult(query.FirstOrDefault()); + } + } + + public async Task GetUserNameFromDatabaseAsync(long userId) + { + //note: This workaround will not be needed after fixing https://github.com/aspnetboilerplate/aspnetboilerplate/issues/1828 + var outerUow = _unitOfWorkManager.Current; + using (var uow = _unitOfWorkManager.Begin(new UnitOfWorkOptions + { + Scope = TransactionScopeOption.RequiresNew, + IsTransactional = false, + IsolationLevel = IsolationLevel.ReadUncommitted + })) + { + if (outerUow != null) + { + _unitOfWorkManager.Current.SetTenantId(outerUow.GetTenantId()); + } + + var user = await UserRepository.GetAsync(userId); + await uow.CompleteAsync(); + return user.UserName; + } + } + + public virtual async Task AddPermissionAsync(TUser user, PermissionGrantInfo permissionGrant) + { + if (await HasPermissionAsync(user.Id, permissionGrant)) + { + return; + } + + await _userPermissionSettingRepository.InsertAsync( + new UserPermissionSetting + { + TenantId = user.TenantId, + UserId = user.Id, + Name = permissionGrant.Name, + IsGranted = permissionGrant.IsGranted + }); + } + + public virtual async Task RemovePermissionAsync(TUser user, PermissionGrantInfo permissionGrant) + { + await _userPermissionSettingRepository.DeleteAsync( + permissionSetting => permissionSetting.UserId == user.Id && + permissionSetting.Name == permissionGrant.Name && + permissionSetting.IsGranted == permissionGrant.IsGranted + ); + } + + public virtual async Task> GetPermissionsAsync(long userId) + { + return (await _userPermissionSettingRepository.GetAllListAsync(p => p.UserId == userId)) + .Select(p => new PermissionGrantInfo(p.Name, p.IsGranted)) + .ToList(); + } + + public virtual async Task HasPermissionAsync(long userId, PermissionGrantInfo permissionGrant) + { + return await _userPermissionSettingRepository.FirstOrDefaultAsync( + p => p.UserId == userId && + p.Name == permissionGrant.Name && + p.IsGranted == permissionGrant.IsGranted + ) != null; + } + + public virtual async Task RemoveAllPermissionSettingsAsync(TUser user) + { + await _userPermissionSettingRepository.DeleteAsync(s => s.UserId == user.Id); + } + } +} diff --git a/src/Abp.ZeroCore/Authorization/Users/UserToken.cs b/src/Abp.ZeroCore/Authorization/Users/UserToken.cs new file mode 100644 index 00000000..99c7dca7 --- /dev/null +++ b/src/Abp.ZeroCore/Authorization/Users/UserToken.cs @@ -0,0 +1,54 @@ +using System.ComponentModel.DataAnnotations.Schema; +using Abp.Domain.Entities; +using JetBrains.Annotations; + +namespace Abp.Authorization.Users +{ + /// + /// Represents an authentication token for a user. + /// + [Table("AbpUserTokens")] + public class UserToken : Entity, IMayHaveTenant + { + public const int MaxLoginProviderLength = 64; + + public virtual int? TenantId { get; set; } + + /// + /// Gets or sets the primary key of the user that the token belongs to. + /// + public virtual long UserId { get; set; } + + /// + /// Gets or sets the LoginProvider this token is from. + /// + public virtual string LoginProvider { get; set; } + + /// + /// Gets or sets the name of the token. + /// + public virtual string Name { get; set; } + + /// + /// Gets or sets the token value. + /// + public virtual string Value { get; set; } + + protected UserToken() + { + + } + + protected internal UserToken(AbpUserBase user, [NotNull] string loginProvider, [NotNull] string name, string value) + { + Check.NotNull(loginProvider, nameof(loginProvider)); + Check.NotNull(name, nameof(name)); + + TenantId = user.TenantId; + UserId = user.Id; + LoginProvider = loginProvider; + Name = name; + Value = value; + } + } +} \ No newline at end of file diff --git a/src/Abp.ZeroCore/IdentityFramework/AbpIdentityBuilder.cs b/src/Abp.ZeroCore/IdentityFramework/AbpIdentityBuilder.cs new file mode 100644 index 00000000..97aac7ed --- /dev/null +++ b/src/Abp.ZeroCore/IdentityFramework/AbpIdentityBuilder.cs @@ -0,0 +1,16 @@ +using System; +using Microsoft.AspNetCore.Identity; + +namespace Microsoft.Extensions.DependencyInjection +{ + public class AbpIdentityBuilder : IdentityBuilder + { + public Type TenantType { get; } + + public AbpIdentityBuilder(IdentityBuilder identityBuilder, Type tenantType) + : base(identityBuilder.UserType, identityBuilder.RoleType, identityBuilder.Services) + { + TenantType = tenantType; + } + } +} \ No newline at end of file diff --git a/src/Abp.ZeroCore/IdentityFramework/AbpZeroIdentityBuilderExtensions.cs b/src/Abp.ZeroCore/IdentityFramework/AbpZeroIdentityBuilderExtensions.cs new file mode 100644 index 00000000..25b3ecd5 --- /dev/null +++ b/src/Abp.ZeroCore/IdentityFramework/AbpZeroIdentityBuilderExtensions.cs @@ -0,0 +1,142 @@ +using Abp.Application.Editions; +using Abp.Application.Features; +using Abp.Authorization; +using Microsoft.AspNetCore.Identity; +using Abp.Authorization.Users; +using Abp.Authorization.Roles; +using Abp.MultiTenancy; + +// ReSharper disable once CheckNamespace - This is done to add extension methods to Microsoft.Extensions.DependencyInjection namespace +namespace Microsoft.Extensions.DependencyInjection +{ + public static class AbpZeroIdentityBuilderExtensions + { + public static AbpIdentityBuilder AddAbpTenantManager(this AbpIdentityBuilder builder) + where TTenantManager : class + { + var type = typeof(TTenantManager); + var abpManagerType = typeof(AbpTenantManager<,>).MakeGenericType(builder.TenantType, builder.UserType); + builder.Services.AddTransient(type, provider => provider.GetService(abpManagerType)); + builder.Services.AddTransient(abpManagerType, type); + return builder; + } + + public static AbpIdentityBuilder AddAbpEditionManager(this AbpIdentityBuilder builder) + where TEditionManager : class + { + var type = typeof(TEditionManager); + var abpManagerType = typeof(AbpEditionManager); + builder.Services.AddTransient(type, provider => provider.GetService(abpManagerType)); + builder.Services.AddTransient(abpManagerType, type); + return builder; + } + + public static AbpIdentityBuilder AddAbpRoleManager(this AbpIdentityBuilder builder) + where TRoleManager : class + { + var abpManagerType = typeof(AbpRoleManager<,>).MakeGenericType(builder.RoleType, builder.UserType); + var managerType = typeof(RoleManager<>).MakeGenericType(builder.RoleType); + builder.Services.AddScoped(abpManagerType, services => services.GetRequiredService(managerType)); + builder.AddRoleManager(); + return builder; + } + + public static AbpIdentityBuilder AddAbpUserManager(this AbpIdentityBuilder builder) + where TUserManager : class + { + var abpManagerType = typeof(AbpUserManager<,>).MakeGenericType(builder.RoleType, builder.UserType); + var managerType = typeof(UserManager<>).MakeGenericType(builder.UserType); + builder.Services.AddScoped(abpManagerType, services => services.GetRequiredService(managerType)); + builder.AddUserManager(); + return builder; + } + + public static AbpIdentityBuilder AddAbpSignInManager(this AbpIdentityBuilder builder) + where TSignInManager : class + { + var abpManagerType = typeof(AbpSignInManager<,,>).MakeGenericType(builder.TenantType, builder.RoleType, builder.UserType); + var managerType = typeof(SignInManager<>).MakeGenericType(builder.UserType); + builder.Services.AddScoped(abpManagerType, services => services.GetRequiredService(managerType)); + builder.AddSignInManager(); + return builder; + } + + public static AbpIdentityBuilder AddAbpLogInManager(this AbpIdentityBuilder builder) + where TLogInManager : class + { + var type = typeof(TLogInManager); + var abpManagerType = typeof(AbpLogInManager<,,>).MakeGenericType(builder.TenantType, builder.RoleType, builder.UserType); + builder.Services.AddScoped(type, provider => provider.GetService(abpManagerType)); + builder.Services.AddScoped(abpManagerType, type); + return builder; + } + + public static AbpIdentityBuilder AddAbpUserClaimsPrincipalFactory(this AbpIdentityBuilder builder) + where TUserClaimsPrincipalFactory : class + { + var type = typeof(TUserClaimsPrincipalFactory); + builder.Services.AddScoped(typeof(UserClaimsPrincipalFactory<,>).MakeGenericType(builder.UserType, builder.RoleType), services => services.GetRequiredService(type)); + builder.Services.AddScoped(typeof(AbpUserClaimsPrincipalFactory<,>).MakeGenericType(builder.UserType, builder.RoleType), services => services.GetRequiredService(type)); + builder.Services.AddScoped(typeof(IUserClaimsPrincipalFactory<>).MakeGenericType(builder.UserType), services => services.GetRequiredService(type)); + builder.Services.AddScoped(type); + return builder; + } + + public static AbpIdentityBuilder AddAbpSecurityStampValidator(this AbpIdentityBuilder builder) + where TSecurityStampValidator : class, ISecurityStampValidator + { + var type = typeof(TSecurityStampValidator); + builder.Services.AddScoped(typeof(SecurityStampValidator<>).MakeGenericType(builder.UserType), services => services.GetRequiredService(type)); + builder.Services.AddScoped(typeof(AbpSecurityStampValidator<,,>).MakeGenericType(builder.TenantType, builder.RoleType, builder.UserType), services => services.GetRequiredService(type)); + builder.Services.AddScoped(typeof(ISecurityStampValidator), services => services.GetRequiredService(type)); + builder.Services.AddScoped(type); + return builder; + } + + public static AbpIdentityBuilder AddPermissionChecker(this AbpIdentityBuilder builder) + where TPermissionChecker : class + { + var type = typeof(TPermissionChecker); + var checkerType = typeof(PermissionChecker<,>).MakeGenericType(builder.RoleType, builder.UserType); + builder.Services.AddScoped(type); + builder.Services.AddScoped(checkerType, provider => provider.GetService(type)); + builder.Services.AddScoped(typeof(IPermissionChecker), provider => provider.GetService(type)); + return builder; + } + + public static AbpIdentityBuilder AddAbpUserStore(this AbpIdentityBuilder builder) + where TUserStore : class + { + var type = typeof(TUserStore); + var abpStoreType = typeof(AbpUserStore<,>).MakeGenericType(builder.RoleType, builder.UserType); + var storeType = typeof(IUserStore<>).MakeGenericType(builder.UserType); + builder.Services.AddScoped(type); + builder.Services.AddScoped(abpStoreType, services => services.GetRequiredService(type)); + builder.Services.AddScoped(storeType, services => services.GetRequiredService(type)); + return builder; + } + + public static AbpIdentityBuilder AddAbpRoleStore(this AbpIdentityBuilder builder) + where TRoleStore : class + { + var type = typeof(TRoleStore); + var abpStoreType = typeof(AbpRoleStore<,>).MakeGenericType(builder.RoleType, builder.UserType); + var storeType = typeof(IRoleStore<>).MakeGenericType(builder.RoleType); + builder.Services.AddScoped(type); + builder.Services.AddScoped(abpStoreType, services => services.GetRequiredService(type)); + builder.Services.AddScoped(storeType, services => services.GetRequiredService(type)); + return builder; + } + + public static AbpIdentityBuilder AddFeatureValueStore(this AbpIdentityBuilder builder) + where TFeatureValueStore : class + { + var type = typeof(TFeatureValueStore); + var storeType = typeof(AbpFeatureValueStore<,>).MakeGenericType(builder.TenantType, builder.UserType); + builder.Services.AddScoped(type); + builder.Services.AddScoped(storeType, provider => provider.GetService(type)); + builder.Services.AddScoped(typeof(IFeatureValueStore), provider => provider.GetService(type)); + return builder; + } + } +} diff --git a/src/Abp.ZeroCore/IdentityFramework/AbpZeroServiceCollectionExtensions.cs b/src/Abp.ZeroCore/IdentityFramework/AbpZeroServiceCollectionExtensions.cs new file mode 100644 index 00000000..539595b2 --- /dev/null +++ b/src/Abp.ZeroCore/IdentityFramework/AbpZeroServiceCollectionExtensions.cs @@ -0,0 +1,88 @@ +using System; +using Abp.Application.Editions; +using Abp.Application.Features; +using Abp.Authorization; +using Abp.Authorization.Roles; +using Abp.Authorization.Users; +using Abp.MultiTenancy; +using Abp.Zero.Configuration; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Identity; +using Microsoft.Extensions.DependencyInjection.Extensions; + +// ReSharper disable once CheckNamespace - This is done to add extension methods to Microsoft.Extensions.DependencyInjection namespace +namespace Microsoft.Extensions.DependencyInjection +{ + public static class AbpZeroServiceCollectionExtensions + { + public static AbpIdentityBuilder AddAbpIdentity(this IServiceCollection services) + where TTenant : AbpTenant + where TRole : AbpRole, new() + where TUser : AbpUser + { + return services.AddAbpIdentity(setupAction: null); + } + + public static AbpIdentityBuilder AddAbpIdentity(this IServiceCollection services, Action setupAction) + where TTenant : AbpTenant + where TRole : AbpRole, new() + where TUser : AbpUser + { + services.AddSingleton(new AbpZeroEntityTypes + { + Tenant = typeof(TTenant), + Role = typeof(TRole), + User = typeof(TUser) + }); + + //AbpTenantManager + services.TryAddScoped>(); + + //AbpEditionManager + services.TryAddScoped(); + + //AbpRoleManager + services.TryAddScoped>(); + services.TryAddScoped(typeof(RoleManager), provider => provider.GetService(typeof(AbpRoleManager))); + + //AbpUserManager + services.TryAddScoped>(); + services.TryAddScoped(typeof(UserManager), provider => provider.GetService(typeof(AbpUserManager))); + + //SignInManager + services.TryAddScoped>(); + services.TryAddScoped(typeof(SignInManager), provider => provider.GetService(typeof(AbpSignInManager))); + + //AbpLogInManager + services.TryAddScoped>(); + + //AbpUserClaimsPrincipalFactory + services.TryAddScoped>(); + services.TryAddScoped(typeof(UserClaimsPrincipalFactory), provider => provider.GetService(typeof(AbpUserClaimsPrincipalFactory))); + services.TryAddScoped(typeof(IUserClaimsPrincipalFactory), provider => provider.GetService(typeof(AbpUserClaimsPrincipalFactory))); + + //AbpSecurityStampValidator + services.TryAddScoped>(); + services.TryAddScoped(typeof(SecurityStampValidator), provider => provider.GetService(typeof(AbpSecurityStampValidator))); + services.TryAddScoped(typeof(ISecurityStampValidator), provider => provider.GetService(typeof(AbpSecurityStampValidator))); + + //PermissionChecker + services.TryAddScoped>(); + services.TryAddScoped(typeof(IPermissionChecker), provider => provider.GetService(typeof(PermissionChecker))); + + //AbpUserStore + services.TryAddScoped>(); + services.TryAddScoped(typeof(IUserStore), provider => provider.GetService(typeof(AbpUserStore))); + + //AbpRoleStore + services.TryAddScoped>(); + services.TryAddScoped(typeof(IRoleStore), provider => provider.GetService(typeof(AbpRoleStore))); + + //AbpFeatureValueStore + services.TryAddScoped>(); + services.TryAddScoped(typeof(IFeatureValueStore), provider => provider.GetService(typeof(AbpFeatureValueStore))); + + return new AbpIdentityBuilder(services.AddIdentity(setupAction), typeof(TTenant)); + } + } +} diff --git a/src/Abp.ZeroCore/IdentityFramework/IdentityResultExtensions.cs b/src/Abp.ZeroCore/IdentityFramework/IdentityResultExtensions.cs new file mode 100644 index 00000000..ece3212d --- /dev/null +++ b/src/Abp.ZeroCore/IdentityFramework/IdentityResultExtensions.cs @@ -0,0 +1,106 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Abp.Collections.Extensions; +using Abp.Localization; +using Abp.Localization.Sources; +using Abp.Text; +using Abp.UI; +using Abp.Zero; +using Microsoft.AspNetCore.Identity; + +namespace Abp.IdentityFramework +{ + public static class IdentityResultExtensions + { + private static readonly Dictionary IdentityLocalizations + = new Dictionary + { + {"Optimistic concurrency failure, object has been modified.", "Identity.ConcurrencyFailure"}, + {"An unknown failure has occurred.", "Identity.DefaultError"}, + {"Email '{0}' is already taken.", "Identity.DuplicateEmail"}, + {"Role name '{0}' is already taken.", "Identity.DuplicateRoleName"}, + {"User name '{0}' is already taken.", "Identity.DuplicateUserName"}, + {"Email '{0}' is invalid.", "Identity.InvalidEmail"}, + {"The provided PasswordHasherCompatibilityMode is invalid.", "Identity.InvalidPasswordHasherCompatibilityMode"}, + {"The iteration count must be a positive integer.", "Identity.InvalidPasswordHasherIterationCount"}, + {"Role name '{0}' is invalid.", "Identity.InvalidRoleName"}, + {"Invalid token.", "Identity.InvalidToken"}, + {"User name '{0}' is invalid, can only contain letters or digits.", "Identity.InvalidUserName"}, + {"A user with this login already exists.", "Identity.LoginAlreadyAssociated"}, + {"Incorrect password.", "Identity.PasswordMismatch"}, + {"Passwords must have at least one digit ('0'-'9').", "Identity.PasswordRequiresDigit"}, + {"Passwords must have at least one lowercase ('a'-'z').", "Identity.PasswordRequiresLower"}, + {"Passwords must have at least one non alphanumeric character.", "Identity.PasswordRequiresNonAlphanumeric"}, + {"Passwords must have at least one uppercase ('A'-'Z').", "Identity.PasswordRequiresUpper"}, + {"Passwords must be at least {0} characters.", "Identity.PasswordTooShort"}, + {"Role {0} does not exist.", "Identity.RoleNotFound"}, + {"User already has a password set.", "Identity.UserAlreadyHasPassword"}, + {"User already in role '{0}'.", "Identity.UserAlreadyInRole"}, + {"User is locked out.", "Identity.UserLockedOut"}, + {"Lockout is not enabled for this user.", "Identity.UserLockoutNotEnabled"}, + {"User {0} does not exist.", "Identity.UserNameNotFound"}, + {"User is not in role '{0}'.", "Identity.UserNotInRole"} + }; + + /// + /// Checks errors of given and throws if it's not succeeded. + /// + /// Identity result to check + public static void CheckErrors(this IdentityResult identityResult) + { + if (identityResult.Succeeded) + { + return; + } + + throw new UserFriendlyException(identityResult.Errors.JoinAsString(", ")); + } + + /// + /// Checks errors of given and throws if it's not succeeded. + /// + /// Identity result to check + /// Localization manager to localize error messages + public static void CheckErrors(this IdentityResult identityResult, ILocalizationManager localizationManager) + { + if (identityResult.Succeeded) + { + return; + } + + throw new UserFriendlyException(identityResult.LocalizeErrors(localizationManager)); + } + + public static string LocalizeErrors(this IdentityResult identityResult, ILocalizationManager localizationManager) + { + if (identityResult.Succeeded) + { + throw new ArgumentException("identityResult.Succeeded should be false in order to localize errors."); + } + + if (identityResult.Errors == null) + { + throw new ArgumentException("identityResult.Errors should not be null."); + } + + return identityResult.Errors.Select(err => LocalizeErrorMessage(err.Description, localizationManager)).JoinAsString(" "); + } + + private static string LocalizeErrorMessage(string identityErrorMessage, ILocalizationManager localizationManager) + { + var localizationSource = localizationManager.GetSource(AbpZeroConsts.LocalizationSourceName); + + foreach (var identityLocalization in IdentityLocalizations) + { + string[] values; + if (FormattedStringValueExtracter.IsMatch(identityErrorMessage, identityLocalization.Key, out values)) + { + return localizationSource.GetString(identityLocalization.Value, values.Cast().ToArray()); + } + } + + return localizationSource.GetString("Identity.DefaultError"); + } + } +} \ No newline at end of file diff --git a/src/Abp.ZeroCore/Zero/AbpZeroCoreModule.cs b/src/Abp.ZeroCore/Zero/AbpZeroCoreModule.cs new file mode 100644 index 00000000..cc45b15d --- /dev/null +++ b/src/Abp.ZeroCore/Zero/AbpZeroCoreModule.cs @@ -0,0 +1,28 @@ +using Abp.Localization.Dictionaries.Xml; +using Abp.Localization.Sources; +using Abp.Modules; +using Abp.Reflection.Extensions; + +namespace Abp.Zero +{ + [DependsOn(typeof(AbpZeroCommonModule))] + public class AbpZeroCoreModule : AbpModule + { + public override void PreInitialize() + { + Configuration.Localization.Sources.Extensions.Add( + new LocalizationSourceExtensionInfo( + AbpZeroConsts.LocalizationSourceName, + new XmlEmbeddedFileLocalizationDictionaryProvider( + typeof(AbpZeroCoreModule).GetAssembly(), "Abp.Zero.Localization.SourceExt" + ) + ) + ); + } + + public override void Initialize() + { + IocManager.RegisterAssemblyByConvention(typeof(AbpZeroCoreModule).GetAssembly()); + } + } +} diff --git a/src/Abp.ZeroCore/Zero/CollectionExtensions.cs b/src/Abp.ZeroCore/Zero/CollectionExtensions.cs new file mode 100644 index 00000000..297e33c0 --- /dev/null +++ b/src/Abp.ZeroCore/Zero/CollectionExtensions.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using JetBrains.Annotations; + +namespace Abp.Zero +{ + internal static class CollectionExtensions + { + public static IList RemoveAll([NotNull] this ICollection source, Func predicate) + { + var items = source.Where(predicate).ToList(); + + foreach (var item in items) + { + source.Remove(item); + } + + return items; + } + } +} diff --git a/src/Abp.ZeroCore/Zero/Localization/SourceExt/AbpZero-ar.xml b/src/Abp.ZeroCore/Zero/Localization/SourceExt/AbpZero-ar.xml new file mode 100644 index 00000000..3a07a391 --- /dev/null +++ b/src/Abp.ZeroCore/Zero/Localization/SourceExt/AbpZero-ar.xml @@ -0,0 +1,35 @@ + + + + فشل التزامن الأمثل، تم تعديل الكائن. + حدث فشل غير معروف. + البريد الإلكتروني '{0}' مستخدم بالفعل. + اسم الدور '{0}' مأخوذ بالفعل. + اسم المستخدم '{0}' مأخوذ بالفعل. + البريد الإلكتروني '{0}' غير صالح. + وضع التوافق المتوفر لمشفر كلمة المرور غير صالح. + يجب أن يكون عدد التكرار عددا صحيحا موجبا. + اسم الدور '{0}' غير صالح. + + + + كلمة المرور غير متوافقة + + + + + + + + المستخدم بالفعل منضم للدور '{0}'. + تم غلق المستخدم. + + + + البريد الإلكتروني + رمز الحماية + رمز الحماية الخاص بك هو: {0} + رسالة قصيرة + رمز الحماية الخاص بك هو: {0} + + diff --git a/src/Abp.ZeroCore/Zero/Localization/SourceExt/AbpZero-de.xml b/src/Abp.ZeroCore/Zero/Localization/SourceExt/AbpZero-de.xml new file mode 100644 index 00000000..f64ef332 --- /dev/null +++ b/src/Abp.ZeroCore/Zero/Localization/SourceExt/AbpZero-de.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/Abp.ZeroCore/Zero/Localization/SourceExt/AbpZero-es.xml b/src/Abp.ZeroCore/Zero/Localization/SourceExt/AbpZero-es.xml new file mode 100644 index 00000000..34f1417b --- /dev/null +++ b/src/Abp.ZeroCore/Zero/Localization/SourceExt/AbpZero-es.xml @@ -0,0 +1,35 @@ + + + + Fallo en concurrencia optimista, el objeto ha sido modificado. + Un error desconocido ha ocurrido. + Email '{0}' ya ha sido tomado. + El rol '{0}' ya está tomado. + El nombre {0} ya está tomado. + Email '{0}' no es válido. + El PasswordHasherCompatibilityMode no es válido. + El conteo iterativo debe ser un número positivo. + El rol '{0}' no es válido. + Token no válido. + Nombre de usuario {0} no es válido, solo puede contener letras y dígitos. + Un usuario con esta sesión ya existe. + Contraseña incorrecta. + Las contraseñas deben tener al menos un dígito ('0'-'9'). + Las contraseñas deben tener al menos una minúscula ('a'-'z'). + Las contraseñas deben tener al menos un carácter que no sea letra o dígito. + Las contraseñas deben tener al menos una mayúscula ('A'-'Z'). + Las contraseñas deben ser al menos {0} caracteres. + El rol {0} no existe. + El usuario ya tiene una contraseña definida. + Usuario ya está en rol '{0}'. + El usuario está bloqueado. + Bloqueado no está habilitado para este usuario. + El usuario {0} no existe. + El usuario no está en rol '{0}'. + Email + Código de seguridad + Su código de seguridad es: {0} + Sms + Su código de seguridad es: {0} + + \ No newline at end of file diff --git a/src/Abp.ZeroCore/Zero/Localization/SourceExt/AbpZero-fa.xml b/src/Abp.ZeroCore/Zero/Localization/SourceExt/AbpZero-fa.xml new file mode 100644 index 00000000..c8046a45 --- /dev/null +++ b/src/Abp.ZeroCore/Zero/Localization/SourceExt/AbpZero-fa.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/Abp.ZeroCore/Zero/Localization/SourceExt/AbpZero-fr.xml b/src/Abp.ZeroCore/Zero/Localization/SourceExt/AbpZero-fr.xml new file mode 100644 index 00000000..162abab8 --- /dev/null +++ b/src/Abp.ZeroCore/Zero/Localization/SourceExt/AbpZero-fr.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/Abp.ZeroCore/Zero/Localization/SourceExt/AbpZero-it.xml b/src/Abp.ZeroCore/Zero/Localization/SourceExt/AbpZero-it.xml new file mode 100644 index 00000000..79059fbf --- /dev/null +++ b/src/Abp.ZeroCore/Zero/Localization/SourceExt/AbpZero-it.xml @@ -0,0 +1,18 @@ + + + + Si è verificato un errore non gestito. + Email '{0}' già utilizzata. + Email '{0}' non valida. + Token non valido. + Nome utente {0} non valido, sono ammesse solo lettere e numeri. + Password errata. + La password deve avere almeno {0} caratteri. + Il ruolo {0} non esiste. + Password già impostata per l'utente. + Ruolo già assegnato all'utente. + Blocco non abilitato per questo utente. + L'utente {0} non esiste. + Ruolo mancate per l'utente. + + \ No newline at end of file diff --git a/src/Abp.ZeroCore/Zero/Localization/SourceExt/AbpZero-lt.xml b/src/Abp.ZeroCore/Zero/Localization/SourceExt/AbpZero-lt.xml new file mode 100644 index 00000000..1e6f1c15 --- /dev/null +++ b/src/Abp.ZeroCore/Zero/Localization/SourceExt/AbpZero-lt.xml @@ -0,0 +1,10 @@ + + + + El. paštas + AbpZeroTemplate Saugumo Kodas + Jūsų saugumo kodas: {0} + Sms + Jusu saugumo kodas: {0} + + diff --git a/src/Abp.ZeroCore/Zero/Localization/SourceExt/AbpZero-lv.xml b/src/Abp.ZeroCore/Zero/Localization/SourceExt/AbpZero-lv.xml new file mode 100644 index 00000000..e152d51c --- /dev/null +++ b/src/Abp.ZeroCore/Zero/Localization/SourceExt/AbpZero-lv.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Abp.ZeroCore/Zero/Localization/SourceExt/AbpZero-pt-BR.xml b/src/Abp.ZeroCore/Zero/Localization/SourceExt/AbpZero-pt-BR.xml new file mode 100644 index 00000000..1b0aa21f --- /dev/null +++ b/src/Abp.ZeroCore/Zero/Localization/SourceExt/AbpZero-pt-BR.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Abp.ZeroCore/Zero/Localization/SourceExt/AbpZero-ru.xml b/src/Abp.ZeroCore/Zero/Localization/SourceExt/AbpZero-ru.xml new file mode 100644 index 00000000..2aeac5df --- /dev/null +++ b/src/Abp.ZeroCore/Zero/Localization/SourceExt/AbpZero-ru.xml @@ -0,0 +1,10 @@ + + + + Email + Секретный код + Ваш секретный код: {0} + Смс + Ваш секретный код: {0} + + diff --git a/src/Abp.ZeroCore/Zero/Localization/SourceExt/AbpZero-tr.xml b/src/Abp.ZeroCore/Zero/Localization/SourceExt/AbpZero-tr.xml new file mode 100644 index 00000000..321a207b --- /dev/null +++ b/src/Abp.ZeroCore/Zero/Localization/SourceExt/AbpZero-tr.xml @@ -0,0 +1,35 @@ + + + + İyimser eşzamanlılık hatası. Nesne değiştirilmiş. + Bilinmeyen bir hata oluştu. + '{0}' email adresi zaten alınmış. + '{0}' rol ismi zaten alınmış. + '{0}' kullanıcı adı zaten alınmış. + '{0}' email adresi hatalı. + Belirtilen PasswordHasherCompatibilityMode geçersiz. + Iterasyon sayısı sıfırdan büyük bir sayı olmalı. + '{0}' rol ismi geçersizdir. + Geçersiz token. + '{0}' kullanıcı adı geçersiz, sadece harf ve rakamlardan oluşmalı. + Bu giriş bilgilerine sahip bir kullanıcı zaten var. + Hatalı şifre. + Şifre en az bir sayı içermeli ('0'-'9'). + Şifre en az bir küçük harf içermeli ('a'-'z'). + Şifre en az bir sayı ya da harf olmayan karakter içermeli. + Şifre en az bir büyük harf içermeli ('A'-'Z'). + Şifre en az {0} karakter uzunluğunda olmalı. + {0} rolü bulunamadı. + Kullanıcının zaten bir şifresi var. + Kullanıcı zaten '{0}' rolünde. + Kullanıcı hesabı kilitlenmiş. + Bu kullanıcı için hesap kilitleme etkin değil. + {0} kullanıcısı bulunamadı. + Kullanıcı '{0}' rolünde değil. + E-posta + Güvenlik Kodu + Güvenlik kodunuz: {0} + Sms + Güvenlik kodunuz: {0} + + \ No newline at end of file diff --git a/src/Abp.ZeroCore/Zero/Localization/SourceExt/AbpZero-zh-CN.xml b/src/Abp.ZeroCore/Zero/Localization/SourceExt/AbpZero-zh-CN.xml new file mode 100644 index 00000000..d64d1e9d --- /dev/null +++ b/src/Abp.ZeroCore/Zero/Localization/SourceExt/AbpZero-zh-CN.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/Abp.ZeroCore/Zero/Localization/SourceExt/AbpZero.xml b/src/Abp.ZeroCore/Zero/Localization/SourceExt/AbpZero.xml new file mode 100644 index 00000000..e2ceab28 --- /dev/null +++ b/src/Abp.ZeroCore/Zero/Localization/SourceExt/AbpZero.xml @@ -0,0 +1,35 @@ + + + + Optimistic concurrency failure, object has been modified. + An unknown failure has occurred. + Email '{0}' is already taken. + Role name '{0}' is already taken. + User name '{0}' is already taken. + Email '{0}' is invalid. + The provided PasswordHasherCompatibilityMode is invalid. + The iteration count must be a positive integer. + Role name '{0}' is invalid. + Invalid token. + User name '{0}' is invalid, can only contain letters or digits. + A user with this login already exists. + Incorrect password. + Passwords must have at least one digit ('0'-'9'). + Passwords must have at least one lowercase ('a'-'z'). + Passwords must have at least one non alphanumeric character. + Passwords must have at least one uppercase ('A'-'Z'). + Passwords must be at least {0} characters. + Role {0} does not exist. + User already has a password set. + User already in role '{0}'. + User is locked out. + Lockout is not enabled for this user. + User {0} does not exist. + User is not in role '{0}'. + Email + Security Code + Your security code is: {0} + Sms + Your security code is: {0} + + \ No newline at end of file diff --git a/test/Abp.Zero.SampleApp.EntityFramework/Abp.Zero.SampleApp.EntityFramework.csproj b/test/Abp.Zero.SampleApp.EntityFramework/Abp.Zero.SampleApp.EntityFramework.csproj new file mode 100644 index 00000000..6d57f311 --- /dev/null +++ b/test/Abp.Zero.SampleApp.EntityFramework/Abp.Zero.SampleApp.EntityFramework.csproj @@ -0,0 +1,27 @@ + + + + 1.5.0.0 + net46 + Abp.Zero.SampleApp.EntityFramework + Abp.Zero.SampleApp.EntityFramework + false + false + false + false + false + false + Abp.Zero.SampleApp + + + + + + + + + + + + + diff --git a/test/Abp.Zero.SampleApp.EntityFramework/Abp.Zero.SampleApp.EntityFramework.xproj b/test/Abp.Zero.SampleApp.EntityFramework/Abp.Zero.SampleApp.EntityFramework.xproj deleted file mode 100644 index 09cbf6cc..00000000 --- a/test/Abp.Zero.SampleApp.EntityFramework/Abp.Zero.SampleApp.EntityFramework.xproj +++ /dev/null @@ -1,19 +0,0 @@ - - - - 14.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - - - - 97670397-edf9-4968-9a25-0eb71c68d5f8 - Abp.Zero.SampleApp - .\obj - .\bin\ - v4.5.2 - - - 2.0 - - - \ No newline at end of file diff --git a/test/Abp.Zero.SampleApp.EntityFramework/EntityFramework/AppDbContext.cs b/test/Abp.Zero.SampleApp.EntityFramework/EntityFramework/AppDbContext.cs index 128a4778..36a493f3 100644 --- a/test/Abp.Zero.SampleApp.EntityFramework/EntityFramework/AppDbContext.cs +++ b/test/Abp.Zero.SampleApp.EntityFramework/EntityFramework/AppDbContext.cs @@ -8,8 +8,8 @@ namespace Abp.Zero.SampleApp.EntityFramework { public class AppDbContext : AbpZeroDbContext { - public AppDbContext(DbConnection connection) - : base(connection, true) + public AppDbContext(DbConnection existingConnection) + : base(existingConnection, true) { } diff --git a/test/Abp.Zero.SampleApp.EntityFramework/project.json b/test/Abp.Zero.SampleApp.EntityFramework/project.json deleted file mode 100644 index 918e7570..00000000 --- a/test/Abp.Zero.SampleApp.EntityFramework/project.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "version": "1.0.0.0-*", - - "dependencies": { - "Abp.Zero.SampleApp": "1.0.0.0-*", - "Abp.Zero.EntityFramework": "1.0.0.0-*" - }, - - "frameworks": { - "net452": { - "frameworkAssemblies": { - } - } - } -} diff --git a/test/Abp.Zero.SampleApp.EntityFrameworkCore.ConsoleAppTest/Abp.Zero.SampleApp.EntityFrameworkCore.ConsoleAppTest.csproj b/test/Abp.Zero.SampleApp.EntityFrameworkCore.ConsoleAppTest/Abp.Zero.SampleApp.EntityFrameworkCore.ConsoleAppTest.csproj new file mode 100644 index 00000000..79ea04a6 --- /dev/null +++ b/test/Abp.Zero.SampleApp.EntityFrameworkCore.ConsoleAppTest/Abp.Zero.SampleApp.EntityFrameworkCore.ConsoleAppTest.csproj @@ -0,0 +1,35 @@ + + + + net461 + Abp.Zero.SampleApp.EntityFrameworkCore.ConsoleAppTest + Exe + Abp.Zero.SampleApp.EntityFrameworkCore.ConsoleAppTest + false + false + false + win7-x64 + + + + + + + + + + + PreserveNewest + + + + + + + + + + + + + diff --git a/test/Abp.Zero.SampleApp.EntityFrameworkCore.ConsoleAppTest/Abp.Zero.SampleApp.EntityFrameworkCore.ConsoleAppTest.xproj b/test/Abp.Zero.SampleApp.EntityFrameworkCore.ConsoleAppTest/Abp.Zero.SampleApp.EntityFrameworkCore.ConsoleAppTest.xproj deleted file mode 100644 index 989c9545..00000000 --- a/test/Abp.Zero.SampleApp.EntityFrameworkCore.ConsoleAppTest/Abp.Zero.SampleApp.EntityFrameworkCore.ConsoleAppTest.xproj +++ /dev/null @@ -1,21 +0,0 @@ - - - - 14.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - - - - - 25f73233-3922-4bc0-ac1c-2042613dec5e - Abp.Zero.SampleApp.EntityFrameworkCore.ConsoleAppTest - .\obj - .\bin\ - v4.6.1 - - - - 2.0 - - - diff --git a/test/Abp.Zero.SampleApp.EntityFrameworkCore.ConsoleAppTest/project.json b/test/Abp.Zero.SampleApp.EntityFrameworkCore.ConsoleAppTest/project.json deleted file mode 100644 index f3f8a64f..00000000 --- a/test/Abp.Zero.SampleApp.EntityFrameworkCore.ConsoleAppTest/project.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "version": "1.0.0-*", - "buildOptions": { - "emitEntryPoint": true, - "copyToOutput": "log4net.config" - }, - - "dependencies": { - "Abp.Castle.Log4Net": "1.0.0", - "Abp.Zero.SampleApp.EntityFrameworkCore": "1.0.0-*", - "Castle.LoggingFacility.MsLogging": "1.0.0", - "Castle.Windsor.MsDependencyInjection": "1.1.0" - }, - - "frameworks": { - "net461": { - - } - } -} diff --git a/test/Abp.Zero.SampleApp.EntityFrameworkCore.Tests/Abp.Zero.SampleApp.EntityFrameworkCore.Tests.csproj b/test/Abp.Zero.SampleApp.EntityFrameworkCore.Tests/Abp.Zero.SampleApp.EntityFrameworkCore.Tests.csproj new file mode 100644 index 00000000..8d322e7c --- /dev/null +++ b/test/Abp.Zero.SampleApp.EntityFrameworkCore.Tests/Abp.Zero.SampleApp.EntityFrameworkCore.Tests.csproj @@ -0,0 +1,40 @@ + + + + net461 + Abp.Zero.SampleApp.EntityFrameworkCore.Tests + Abp.Zero.SampleApp.EntityFrameworkCore.Tests + true + false + false + false + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/Abp.Zero.SampleApp.EntityFrameworkCore.Tests/Abp.Zero.SampleApp.EntityFrameworkCore.Tests.xproj b/test/Abp.Zero.SampleApp.EntityFrameworkCore.Tests/Abp.Zero.SampleApp.EntityFrameworkCore.Tests.xproj deleted file mode 100644 index 32ced589..00000000 --- a/test/Abp.Zero.SampleApp.EntityFrameworkCore.Tests/Abp.Zero.SampleApp.EntityFrameworkCore.Tests.xproj +++ /dev/null @@ -1,22 +0,0 @@ - - - - 14.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - - - - 9223f076-ece7-4381-9bbf-f3e33073b91a - Abp.Zero.SampleApp.EntityFrameworkCore.Tests - .\obj - .\bin\ - v4.6.1 - - - 2.0 - - - - - - \ No newline at end of file diff --git a/test/Abp.Zero.SampleApp.EntityFrameworkCore.Tests/project.json b/test/Abp.Zero.SampleApp.EntityFrameworkCore.Tests/project.json deleted file mode 100644 index 358432fb..00000000 --- a/test/Abp.Zero.SampleApp.EntityFrameworkCore.Tests/project.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "version": "1.0.0-*", - - "testRunner": "xunit", - - "dependencies": { - "Abp.Zero.SampleApp.EntityFrameworkCore": "1.0.0-*", - "xunit": "2.2.0-beta2-build3300", - "xunit.extensibility.execution": "2.2.0-beta2-build3300", - "xunit.runner.visualstudio": "2.2.0-beta2-build1149", - "dotnet-test-xunit": "2.2.0-preview2-build1029", - "NSubstitute": "1.10.0", - "Shouldly": "2.8.2", - "Castle.Windsor.MsDependencyInjection": "1.1.0", - "Microsoft.EntityFrameworkCore.InMemory": "1.0.1", - "Abp.TestBase": "1.0.0" - }, - - "frameworks": { - "net461": { - "frameworkAssemblies": { - } - } - } -} diff --git a/test/Abp.Zero.SampleApp.EntityFrameworkCore/Abp.Zero.SampleApp.EntityFrameworkCore.csproj b/test/Abp.Zero.SampleApp.EntityFrameworkCore/Abp.Zero.SampleApp.EntityFrameworkCore.csproj new file mode 100644 index 00000000..e08be5c3 --- /dev/null +++ b/test/Abp.Zero.SampleApp.EntityFrameworkCore/Abp.Zero.SampleApp.EntityFrameworkCore.csproj @@ -0,0 +1,43 @@ + + + + net461 + Abp.Zero.SampleApp.EntityFrameworkCore + Library + Abp.Zero.SampleApp.EntityFrameworkCore + false + false + false + win7-x64 + + library + + + + + AnyCPU + + + + + + + + + + All + + + + + + + + + + + + + + + diff --git a/test/Abp.Zero.SampleApp.EntityFrameworkCore/Abp.Zero.SampleApp.EntityFrameworkCore.xproj b/test/Abp.Zero.SampleApp.EntityFrameworkCore/Abp.Zero.SampleApp.EntityFrameworkCore.xproj deleted file mode 100644 index ec20c1d6..00000000 --- a/test/Abp.Zero.SampleApp.EntityFrameworkCore/Abp.Zero.SampleApp.EntityFrameworkCore.xproj +++ /dev/null @@ -1,21 +0,0 @@ - - - - 14.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - - - - - 76e2cce7-a61f-4700-9054-9923645e41cc - Abp.Zero.SampleApp.EntityFrameworkCore - .\obj - .\bin\ - v4.6.1 - - - - 2.0 - - - diff --git a/test/Abp.Zero.SampleApp.EntityFrameworkCore/Migrations/20160901080154_InitialMigration.cs b/test/Abp.Zero.SampleApp.EntityFrameworkCore/Migrations/20160901080154_InitialMigration.cs index 8b98c72a..7830a04e 100644 --- a/test/Abp.Zero.SampleApp.EntityFrameworkCore/Migrations/20160901080154_InitialMigration.cs +++ b/test/Abp.Zero.SampleApp.EntityFrameworkCore/Migrations/20160901080154_InitialMigration.cs @@ -654,14 +654,12 @@ protected override void Up(MigrationBuilder migrationBuilder) migrationBuilder.CreateIndex( name: "IX_AbpUsers_CreatorUserId", table: "AbpUsers", - column: "CreatorUserId", - unique: true); + column: "CreatorUserId"); migrationBuilder.CreateIndex( name: "IX_AbpUsers_LastModifierUserId", table: "AbpUsers", - column: "LastModifierUserId", - unique: true); + column: "LastModifierUserId"); } protected override void Down(MigrationBuilder migrationBuilder) diff --git a/test/Abp.Zero.SampleApp.EntityFrameworkCore/Migrations/20161207181625_Remove_Unique_Constraint.Designer.cs b/test/Abp.Zero.SampleApp.EntityFrameworkCore/Migrations/20161207181625_Remove_Unique_Constraint.Designer.cs new file mode 100644 index 00000000..32099bbc --- /dev/null +++ b/test/Abp.Zero.SampleApp.EntityFrameworkCore/Migrations/20161207181625_Remove_Unique_Constraint.Designer.cs @@ -0,0 +1,969 @@ +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Abp.Zero.SampleApp.EntityFrameworkCore; +using Abp.Authorization.Users; +using Abp.BackgroundJobs; +using Abp.Notifications; + +namespace Abp.Zero.SampleApp.EntityFrameworkCore.Migrations +{ + [DbContext(typeof(AppDbContext))] + [Migration("20161207181625_Remove_Unique_Constraint")] + partial class Remove_Unique_Constraint + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { + modelBuilder + .HasAnnotation("ProductVersion", "1.1.0-rtm-22752") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("Abp.Application.Editions.Edition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("CreationTime"); + + b.Property("CreatorUserId"); + + b.Property("DeleterUserId"); + + b.Property("DeletionTime"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(64); + + b.Property("IsDeleted"); + + b.Property("LastModificationTime"); + + b.Property("LastModifierUserId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(32); + + b.HasKey("Id"); + + b.ToTable("AbpEditions"); + }); + + modelBuilder.Entity("Abp.Application.Features.FeatureSetting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("CreationTime"); + + b.Property("CreatorUserId"); + + b.Property("Discriminator") + .IsRequired(); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128); + + b.Property("Value") + .IsRequired() + .HasMaxLength(2000); + + b.HasKey("Id"); + + b.ToTable("AbpFeatures"); + + b.HasDiscriminator("Discriminator").HasValue("FeatureSetting"); + }); + + modelBuilder.Entity("Abp.Auditing.AuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("BrowserInfo") + .HasMaxLength(256); + + b.Property("ClientIpAddress") + .HasMaxLength(64); + + b.Property("ClientName") + .HasMaxLength(128); + + b.Property("CustomData") + .HasMaxLength(2000); + + b.Property("Exception") + .HasMaxLength(2000); + + b.Property("ExecutionDuration"); + + b.Property("ExecutionTime"); + + b.Property("ImpersonatorTenantId"); + + b.Property("ImpersonatorUserId"); + + b.Property("MethodName") + .HasMaxLength(256); + + b.Property("Parameters") + .HasMaxLength(1024); + + b.Property("ServiceName") + .HasMaxLength(256); + + b.Property("TenantId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.ToTable("AbpAuditLogs"); + }); + + modelBuilder.Entity("Abp.Authorization.PermissionSetting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("CreationTime"); + + b.Property("CreatorUserId"); + + b.Property("Discriminator") + .IsRequired(); + + b.Property("IsGranted"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128); + + b.Property("TenantId"); + + b.HasKey("Id"); + + b.ToTable("AbpPermissions"); + + b.HasDiscriminator("Discriminator").HasValue("PermissionSetting"); + }); + + modelBuilder.Entity("Abp.Authorization.Users.UserAccount", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("CreationTime"); + + b.Property("CreatorUserId"); + + b.Property("DeleterUserId"); + + b.Property("DeletionTime"); + + b.Property("EmailAddress"); + + b.Property("IsDeleted"); + + b.Property("LastLoginTime"); + + b.Property("LastModificationTime"); + + b.Property("LastModifierUserId"); + + b.Property("TenantId"); + + b.Property("UserId"); + + b.Property("UserLinkId"); + + b.Property("UserName"); + + b.HasKey("Id"); + + b.ToTable("AbpUserAccounts"); + }); + + modelBuilder.Entity("Abp.Authorization.Users.UserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("CreationTime"); + + b.Property("CreatorUserId"); + + b.Property("TenantId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AbpUserClaims"); + }); + + modelBuilder.Entity("Abp.Authorization.Users.UserLogin", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("LoginProvider") + .IsRequired() + .HasMaxLength(128); + + b.Property("ProviderKey") + .IsRequired() + .HasMaxLength(256); + + b.Property("TenantId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AbpUserLogins"); + }); + + modelBuilder.Entity("Abp.Authorization.Users.UserLoginAttempt", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("BrowserInfo") + .HasMaxLength(256); + + b.Property("ClientIpAddress") + .HasMaxLength(64); + + b.Property("ClientName") + .HasMaxLength(128); + + b.Property("CreationTime"); + + b.Property("Result"); + + b.Property("TenancyName") + .HasMaxLength(64); + + b.Property("TenantId"); + + b.Property("UserId"); + + b.Property("UserNameOrEmailAddress") + .HasMaxLength(255); + + b.HasKey("Id"); + + b.HasIndex("UserId", "TenantId"); + + b.HasIndex("TenancyName", "UserNameOrEmailAddress", "Result"); + + b.ToTable("AbpUserLoginAttempts"); + }); + + modelBuilder.Entity("Abp.Authorization.Users.UserOrganizationUnit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("CreationTime"); + + b.Property("CreatorUserId"); + + b.Property("OrganizationUnitId"); + + b.Property("TenantId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.ToTable("AbpUserOrganizationUnits"); + }); + + modelBuilder.Entity("Abp.Authorization.Users.UserRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("CreationTime"); + + b.Property("CreatorUserId"); + + b.Property("RoleId"); + + b.Property("TenantId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AbpUserRoles"); + }); + + modelBuilder.Entity("Abp.BackgroundJobs.BackgroundJobInfo", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("CreationTime"); + + b.Property("CreatorUserId"); + + b.Property("IsAbandoned"); + + b.Property("JobArgs") + .IsRequired() + .HasMaxLength(1048576); + + b.Property("JobType") + .IsRequired() + .HasMaxLength(512); + + b.Property("LastTryTime"); + + b.Property("NextTryTime"); + + b.Property("Priority"); + + b.Property("TryCount"); + + b.HasKey("Id"); + + b.HasIndex("IsAbandoned", "NextTryTime"); + + b.ToTable("AbpBackgroundJobs"); + }); + + modelBuilder.Entity("Abp.Configuration.Setting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("CreationTime"); + + b.Property("CreatorUserId"); + + b.Property("LastModificationTime"); + + b.Property("LastModifierUserId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256); + + b.Property("TenantId"); + + b.Property("UserId"); + + b.Property("Value") + .HasMaxLength(2000); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AbpSettings"); + }); + + modelBuilder.Entity("Abp.Localization.ApplicationLanguage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("CreationTime"); + + b.Property("CreatorUserId"); + + b.Property("DeleterUserId"); + + b.Property("DeletionTime"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(64); + + b.Property("Icon") + .HasMaxLength(128); + + b.Property("IsDeleted"); + + b.Property("LastModificationTime"); + + b.Property("LastModifierUserId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(10); + + b.Property("TenantId"); + + b.HasKey("Id"); + + b.ToTable("AbpLanguages"); + }); + + modelBuilder.Entity("Abp.Localization.ApplicationLanguageText", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("CreationTime"); + + b.Property("CreatorUserId"); + + b.Property("Key") + .IsRequired() + .HasMaxLength(256); + + b.Property("LanguageName") + .IsRequired() + .HasMaxLength(10); + + b.Property("LastModificationTime"); + + b.Property("LastModifierUserId"); + + b.Property("Source") + .IsRequired() + .HasMaxLength(128); + + b.Property("TenantId"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(67108864); + + b.HasKey("Id"); + + b.ToTable("AbpLanguageTexts"); + }); + + modelBuilder.Entity("Abp.Notifications.NotificationInfo", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("CreationTime"); + + b.Property("CreatorUserId"); + + b.Property("Data") + .HasMaxLength(1048576); + + b.Property("DataTypeName") + .HasMaxLength(512); + + b.Property("EntityId") + .HasMaxLength(96); + + b.Property("EntityTypeAssemblyQualifiedName") + .HasMaxLength(512); + + b.Property("EntityTypeName") + .HasMaxLength(250); + + b.Property("ExcludedUserIds") + .HasMaxLength(131072); + + b.Property("NotificationName") + .IsRequired() + .HasMaxLength(96); + + b.Property("Severity"); + + b.Property("TenantIds") + .HasMaxLength(131072); + + b.Property("UserIds") + .HasMaxLength(131072); + + b.HasKey("Id"); + + b.ToTable("AbpNotifications"); + }); + + modelBuilder.Entity("Abp.Notifications.NotificationSubscriptionInfo", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("CreationTime"); + + b.Property("CreatorUserId"); + + b.Property("EntityId") + .HasMaxLength(96); + + b.Property("EntityTypeAssemblyQualifiedName") + .HasMaxLength(512); + + b.Property("EntityTypeName") + .HasMaxLength(250); + + b.Property("NotificationName") + .HasMaxLength(96); + + b.Property("TenantId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("NotificationName", "EntityTypeName", "EntityId", "UserId"); + + b.ToTable("AbpNotificationSubscriptions"); + }); + + modelBuilder.Entity("Abp.Notifications.TenantNotificationInfo", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("CreationTime"); + + b.Property("CreatorUserId"); + + b.Property("Data") + .HasMaxLength(1048576); + + b.Property("DataTypeName") + .HasMaxLength(512); + + b.Property("EntityId") + .HasMaxLength(96); + + b.Property("EntityTypeAssemblyQualifiedName") + .HasMaxLength(512); + + b.Property("EntityTypeName") + .HasMaxLength(250); + + b.Property("NotificationName") + .IsRequired() + .HasMaxLength(96); + + b.Property("Severity"); + + b.Property("TenantId"); + + b.HasKey("Id"); + + b.ToTable("AbpTenantNotifications"); + }); + + modelBuilder.Entity("Abp.Notifications.UserNotificationInfo", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("CreationTime"); + + b.Property("State"); + + b.Property("TenantId"); + + b.Property("TenantNotificationId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId", "State", "CreationTime"); + + b.ToTable("AbpUserNotifications"); + }); + + modelBuilder.Entity("Abp.Organizations.OrganizationUnit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Code") + .IsRequired() + .HasMaxLength(95); + + b.Property("CreationTime"); + + b.Property("CreatorUserId"); + + b.Property("DeleterUserId"); + + b.Property("DeletionTime"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(128); + + b.Property("IsDeleted"); + + b.Property("LastModificationTime"); + + b.Property("LastModifierUserId"); + + b.Property("ParentId"); + + b.Property("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.ToTable("AbpOrganizationUnits"); + }); + + modelBuilder.Entity("Abp.Zero.SampleApp.MultiTenancy.Tenant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConnectionString") + .HasMaxLength(1024); + + b.Property("CreationTime"); + + b.Property("CreatorUserId"); + + b.Property("DeleterUserId"); + + b.Property("DeletionTime"); + + b.Property("EditionId"); + + b.Property("IsActive"); + + b.Property("IsDeleted"); + + b.Property("LastModificationTime"); + + b.Property("LastModifierUserId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128); + + b.Property("TenancyName") + .IsRequired() + .HasMaxLength(64); + + b.Property("UserId"); + + b.Property("UserId1"); + + b.Property("UserId2"); + + b.HasKey("Id"); + + b.HasIndex("EditionId"); + + b.HasIndex("UserId"); + + b.HasIndex("UserId1"); + + b.HasIndex("UserId2"); + + b.ToTable("AbpTenants"); + }); + + modelBuilder.Entity("Abp.Zero.SampleApp.Roles.Role", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("CreationTime"); + + b.Property("CreatorUserId"); + + b.Property("DeleterUserId"); + + b.Property("DeletionTime"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(64); + + b.Property("IsDefault"); + + b.Property("IsDeleted"); + + b.Property("IsStatic"); + + b.Property("LastModificationTime"); + + b.Property("LastModifierUserId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(32); + + b.Property("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("CreatorUserId"); + + b.HasIndex("DeleterUserId"); + + b.HasIndex("LastModifierUserId"); + + b.ToTable("AbpRoles"); + }); + + modelBuilder.Entity("Abp.Zero.SampleApp.Users.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AccessFailedCount"); + + b.Property("AuthenticationSource") + .HasMaxLength(64); + + b.Property("CreationTime"); + + b.Property("CreatorUserId"); + + b.Property("DeleterUserId"); + + b.Property("DeletionTime"); + + b.Property("EmailAddress") + .IsRequired() + .HasMaxLength(256); + + b.Property("EmailConfirmationCode") + .HasMaxLength(328); + + b.Property("IsActive"); + + b.Property("IsDeleted"); + + b.Property("IsEmailConfirmed"); + + b.Property("IsLockoutEnabled"); + + b.Property("IsPhoneNumberConfirmed"); + + b.Property("IsTwoFactorEnabled"); + + b.Property("LastLoginTime"); + + b.Property("LastModificationTime"); + + b.Property("LastModifierUserId"); + + b.Property("LockoutEndDateUtc"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(32); + + b.Property("Password") + .IsRequired() + .HasMaxLength(128); + + b.Property("PasswordResetCode") + .HasMaxLength(328); + + b.Property("PhoneNumber"); + + b.Property("SecurityStamp"); + + b.Property("Surname") + .IsRequired() + .HasMaxLength(32); + + b.Property("TenantId"); + + b.Property("UserName") + .IsRequired() + .HasMaxLength(32); + + b.HasKey("Id"); + + b.HasIndex("CreatorUserId"); + + b.HasIndex("DeleterUserId"); + + b.HasIndex("LastModifierUserId"); + + b.ToTable("AbpUsers"); + }); + + modelBuilder.Entity("Abp.Application.Features.EditionFeatureSetting", b => + { + b.HasBaseType("Abp.Application.Features.FeatureSetting"); + + b.Property("EditionId"); + + b.HasIndex("EditionId"); + + b.ToTable("AbpFeatures"); + + b.HasDiscriminator().HasValue("EditionFeatureSetting"); + }); + + modelBuilder.Entity("Abp.MultiTenancy.TenantFeatureSetting", b => + { + b.HasBaseType("Abp.Application.Features.FeatureSetting"); + + b.Property("TenantId"); + + b.ToTable("AbpFeatures"); + + b.HasDiscriminator().HasValue("TenantFeatureSetting"); + }); + + modelBuilder.Entity("Abp.Authorization.Roles.RolePermissionSetting", b => + { + b.HasBaseType("Abp.Authorization.PermissionSetting"); + + b.Property("RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AbpPermissions"); + + b.HasDiscriminator().HasValue("RolePermissionSetting"); + }); + + modelBuilder.Entity("Abp.Authorization.Users.UserPermissionSetting", b => + { + b.HasBaseType("Abp.Authorization.PermissionSetting"); + + b.Property("UserId"); + + b.HasIndex("UserId"); + + b.ToTable("AbpPermissions"); + + b.HasDiscriminator().HasValue("UserPermissionSetting"); + }); + + modelBuilder.Entity("Abp.Authorization.Users.UserClaim", b => + { + b.HasOne("Abp.Zero.SampleApp.Users.User") + .WithMany("Claims") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Abp.Authorization.Users.UserLogin", b => + { + b.HasOne("Abp.Zero.SampleApp.Users.User") + .WithMany("Logins") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Abp.Authorization.Users.UserRole", b => + { + b.HasOne("Abp.Zero.SampleApp.Users.User") + .WithMany("Roles") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Abp.Configuration.Setting", b => + { + b.HasOne("Abp.Zero.SampleApp.Users.User") + .WithMany("Settings") + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Abp.Organizations.OrganizationUnit", b => + { + b.HasOne("Abp.Organizations.OrganizationUnit", "Parent") + .WithMany("Children") + .HasForeignKey("ParentId"); + }); + + modelBuilder.Entity("Abp.Zero.SampleApp.MultiTenancy.Tenant", b => + { + b.HasOne("Abp.Application.Editions.Edition", "Edition") + .WithMany() + .HasForeignKey("EditionId"); + + b.HasOne("Abp.Zero.SampleApp.Users.User", "CreatorUser") + .WithMany() + .HasForeignKey("UserId"); + + b.HasOne("Abp.Zero.SampleApp.Users.User", "DeleterUser") + .WithMany() + .HasForeignKey("UserId1"); + + b.HasOne("Abp.Zero.SampleApp.Users.User", "LastModifierUser") + .WithMany() + .HasForeignKey("UserId2"); + }); + + modelBuilder.Entity("Abp.Zero.SampleApp.Roles.Role", b => + { + b.HasOne("Abp.Zero.SampleApp.Users.User", "CreatorUser") + .WithMany() + .HasForeignKey("CreatorUserId"); + + b.HasOne("Abp.Zero.SampleApp.Users.User", "DeleterUser") + .WithMany() + .HasForeignKey("DeleterUserId"); + + b.HasOne("Abp.Zero.SampleApp.Users.User", "LastModifierUser") + .WithMany() + .HasForeignKey("LastModifierUserId"); + }); + + modelBuilder.Entity("Abp.Zero.SampleApp.Users.User", b => + { + b.HasOne("Abp.Zero.SampleApp.Users.User", "CreatorUser") + .WithMany() + .HasForeignKey("CreatorUserId"); + + b.HasOne("Abp.Zero.SampleApp.Users.User", "DeleterUser") + .WithMany() + .HasForeignKey("DeleterUserId"); + + b.HasOne("Abp.Zero.SampleApp.Users.User", "LastModifierUser") + .WithMany() + .HasForeignKey("LastModifierUserId"); + }); + + modelBuilder.Entity("Abp.Application.Features.EditionFeatureSetting", b => + { + b.HasOne("Abp.Application.Editions.Edition", "Edition") + .WithMany() + .HasForeignKey("EditionId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Abp.Authorization.Roles.RolePermissionSetting", b => + { + b.HasOne("Abp.Zero.SampleApp.Roles.Role") + .WithMany("Permissions") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Abp.Authorization.Users.UserPermissionSetting", b => + { + b.HasOne("Abp.Zero.SampleApp.Users.User") + .WithMany("Permissions") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + } + } +} diff --git a/test/Abp.Zero.SampleApp.EntityFrameworkCore/Migrations/20161207181625_Remove_Unique_Constraint.cs b/test/Abp.Zero.SampleApp.EntityFrameworkCore/Migrations/20161207181625_Remove_Unique_Constraint.cs new file mode 100644 index 00000000..1a9539f5 --- /dev/null +++ b/test/Abp.Zero.SampleApp.EntityFrameworkCore/Migrations/20161207181625_Remove_Unique_Constraint.cs @@ -0,0 +1,324 @@ +using System; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Metadata; + +namespace Abp.Zero.SampleApp.EntityFrameworkCore.Migrations +{ + public partial class Remove_Unique_Constraint : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_AbpTenants_AbpUsers_CreatorUserId1", + table: "AbpTenants"); + + migrationBuilder.DropForeignKey( + name: "FK_AbpTenants_AbpUsers_DeleterUserId1", + table: "AbpTenants"); + + migrationBuilder.DropForeignKey( + name: "FK_AbpTenants_AbpUsers_LastModifierUserId1", + table: "AbpTenants"); + + migrationBuilder.DropIndex( + name: "IX_AbpUsers_CreatorUserId", + table: "AbpUsers"); + + migrationBuilder.DropIndex( + name: "IX_AbpUsers_LastModifierUserId", + table: "AbpUsers"); + + migrationBuilder.RenameColumn( + name: "LastModifierUserId1", + table: "AbpTenants", + newName: "UserId2"); + + migrationBuilder.RenameColumn( + name: "DeleterUserId1", + table: "AbpTenants", + newName: "UserId1"); + + migrationBuilder.RenameColumn( + name: "CreatorUserId1", + table: "AbpTenants", + newName: "UserId"); + + migrationBuilder.RenameIndex( + name: "IX_AbpTenants_LastModifierUserId1", + table: "AbpTenants", + newName: "IX_AbpTenants_UserId2"); + + migrationBuilder.RenameIndex( + name: "IX_AbpTenants_DeleterUserId1", + table: "AbpTenants", + newName: "IX_AbpTenants_UserId1"); + + migrationBuilder.RenameIndex( + name: "IX_AbpTenants_CreatorUserId1", + table: "AbpTenants", + newName: "IX_AbpTenants_UserId"); + + migrationBuilder.AlterColumn( + name: "EmailConfirmationCode", + table: "AbpUsers", + maxLength: 328, + nullable: true, + oldClrType: typeof(string), + oldMaxLength: 128, + oldNullable: true); + + migrationBuilder.AddColumn( + name: "AccessFailedCount", + table: "AbpUsers", + nullable: false, + defaultValue: 0); + + migrationBuilder.AddColumn( + name: "IsLockoutEnabled", + table: "AbpUsers", + nullable: false, + defaultValue: false); + + migrationBuilder.AddColumn( + name: "IsPhoneNumberConfirmed", + table: "AbpUsers", + nullable: false, + defaultValue: false); + + migrationBuilder.AddColumn( + name: "IsTwoFactorEnabled", + table: "AbpUsers", + nullable: false, + defaultValue: false); + + migrationBuilder.AddColumn( + name: "LockoutEndDateUtc", + table: "AbpUsers", + nullable: true); + + migrationBuilder.AddColumn( + name: "PhoneNumber", + table: "AbpUsers", + nullable: true); + + migrationBuilder.AddColumn( + name: "SecurityStamp", + table: "AbpUsers", + nullable: true); + + migrationBuilder.CreateTable( + name: "AbpUserClaims", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn), + ClaimType = table.Column(nullable: true), + ClaimValue = table.Column(nullable: true), + CreationTime = table.Column(nullable: false), + CreatorUserId = table.Column(nullable: true), + TenantId = table.Column(nullable: true), + UserId = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpUserClaims", x => x.Id); + table.ForeignKey( + name: "FK_AbpUserClaims_AbpUsers_UserId", + column: x => x.UserId, + principalTable: "AbpUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_AbpUsers_CreatorUserId", + table: "AbpUsers", + column: "CreatorUserId"); + + migrationBuilder.CreateIndex( + name: "IX_AbpUsers_DeleterUserId", + table: "AbpUsers", + column: "DeleterUserId"); + + migrationBuilder.CreateIndex( + name: "IX_AbpUsers_LastModifierUserId", + table: "AbpUsers", + column: "LastModifierUserId"); + + migrationBuilder.CreateIndex( + name: "IX_AbpUserClaims_UserId", + table: "AbpUserClaims", + column: "UserId"); + + migrationBuilder.AddForeignKey( + name: "FK_AbpTenants_AbpUsers_UserId", + table: "AbpTenants", + column: "UserId", + principalTable: "AbpUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_AbpTenants_AbpUsers_UserId1", + table: "AbpTenants", + column: "UserId1", + principalTable: "AbpUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_AbpTenants_AbpUsers_UserId2", + table: "AbpTenants", + column: "UserId2", + principalTable: "AbpUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_AbpUsers_AbpUsers_DeleterUserId", + table: "AbpUsers", + column: "DeleterUserId", + principalTable: "AbpUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_AbpTenants_AbpUsers_UserId", + table: "AbpTenants"); + + migrationBuilder.DropForeignKey( + name: "FK_AbpTenants_AbpUsers_UserId1", + table: "AbpTenants"); + + migrationBuilder.DropForeignKey( + name: "FK_AbpTenants_AbpUsers_UserId2", + table: "AbpTenants"); + + migrationBuilder.DropForeignKey( + name: "FK_AbpUsers_AbpUsers_DeleterUserId", + table: "AbpUsers"); + + migrationBuilder.DropTable( + name: "AbpUserClaims"); + + migrationBuilder.DropIndex( + name: "IX_AbpUsers_CreatorUserId", + table: "AbpUsers"); + + migrationBuilder.DropIndex( + name: "IX_AbpUsers_DeleterUserId", + table: "AbpUsers"); + + migrationBuilder.DropIndex( + name: "IX_AbpUsers_LastModifierUserId", + table: "AbpUsers"); + + migrationBuilder.DropColumn( + name: "AccessFailedCount", + table: "AbpUsers"); + + migrationBuilder.DropColumn( + name: "IsLockoutEnabled", + table: "AbpUsers"); + + migrationBuilder.DropColumn( + name: "IsPhoneNumberConfirmed", + table: "AbpUsers"); + + migrationBuilder.DropColumn( + name: "IsTwoFactorEnabled", + table: "AbpUsers"); + + migrationBuilder.DropColumn( + name: "LockoutEndDateUtc", + table: "AbpUsers"); + + migrationBuilder.DropColumn( + name: "PhoneNumber", + table: "AbpUsers"); + + migrationBuilder.DropColumn( + name: "SecurityStamp", + table: "AbpUsers"); + + migrationBuilder.RenameColumn( + name: "UserId2", + table: "AbpTenants", + newName: "LastModifierUserId1"); + + migrationBuilder.RenameColumn( + name: "UserId1", + table: "AbpTenants", + newName: "DeleterUserId1"); + + migrationBuilder.RenameColumn( + name: "UserId", + table: "AbpTenants", + newName: "CreatorUserId1"); + + migrationBuilder.RenameIndex( + name: "IX_AbpTenants_UserId2", + table: "AbpTenants", + newName: "IX_AbpTenants_LastModifierUserId1"); + + migrationBuilder.RenameIndex( + name: "IX_AbpTenants_UserId1", + table: "AbpTenants", + newName: "IX_AbpTenants_DeleterUserId1"); + + migrationBuilder.RenameIndex( + name: "IX_AbpTenants_UserId", + table: "AbpTenants", + newName: "IX_AbpTenants_CreatorUserId1"); + + migrationBuilder.AlterColumn( + name: "EmailConfirmationCode", + table: "AbpUsers", + maxLength: 128, + nullable: true, + oldClrType: typeof(string), + oldMaxLength: 328, + oldNullable: true); + + migrationBuilder.CreateIndex( + name: "IX_AbpUsers_CreatorUserId", + table: "AbpUsers", + column: "CreatorUserId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_AbpUsers_LastModifierUserId", + table: "AbpUsers", + column: "LastModifierUserId", + unique: true); + + migrationBuilder.AddForeignKey( + name: "FK_AbpTenants_AbpUsers_CreatorUserId1", + table: "AbpTenants", + column: "CreatorUserId1", + principalTable: "AbpUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_AbpTenants_AbpUsers_DeleterUserId1", + table: "AbpTenants", + column: "DeleterUserId1", + principalTable: "AbpUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_AbpTenants_AbpUsers_LastModifierUserId1", + table: "AbpTenants", + column: "LastModifierUserId1", + principalTable: "AbpUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + } + } +} diff --git a/test/Abp.Zero.SampleApp.EntityFrameworkCore/Migrations/AppDbContextModelSnapshot.cs b/test/Abp.Zero.SampleApp.EntityFrameworkCore/Migrations/AppDbContextModelSnapshot.cs index 502d99ff..3d719dc7 100644 --- a/test/Abp.Zero.SampleApp.EntityFrameworkCore/Migrations/AppDbContextModelSnapshot.cs +++ b/test/Abp.Zero.SampleApp.EntityFrameworkCore/Migrations/AppDbContextModelSnapshot.cs @@ -4,6 +4,9 @@ using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; using Abp.Zero.SampleApp.EntityFrameworkCore; +using Abp.Authorization.Users; +using Abp.BackgroundJobs; +using Abp.Notifications; namespace Abp.Zero.SampleApp.EntityFrameworkCore.Migrations { @@ -13,7 +16,7 @@ partial class AppDbContextModelSnapshot : ModelSnapshot protected override void BuildModel(ModelBuilder modelBuilder) { modelBuilder - .HasAnnotation("ProductVersion", "1.0.0-rtm-21431") + .HasAnnotation("ProductVersion", "1.1.0-rtm-22752") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); modelBuilder.Entity("Abp.Application.Editions.Edition", b => @@ -31,7 +34,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("DisplayName") .IsRequired() - .HasAnnotation("MaxLength", 64); + .HasMaxLength(64); b.Property("IsDeleted"); @@ -41,7 +44,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Name") .IsRequired() - .HasAnnotation("MaxLength", 32); + .HasMaxLength(32); b.HasKey("Id"); @@ -62,11 +65,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Name") .IsRequired() - .HasAnnotation("MaxLength", 128); + .HasMaxLength(128); b.Property("Value") .IsRequired() - .HasAnnotation("MaxLength", 2000); + .HasMaxLength(2000); b.HasKey("Id"); @@ -81,19 +84,19 @@ protected override void BuildModel(ModelBuilder modelBuilder) .ValueGeneratedOnAdd(); b.Property("BrowserInfo") - .HasAnnotation("MaxLength", 256); + .HasMaxLength(256); b.Property("ClientIpAddress") - .HasAnnotation("MaxLength", 64); + .HasMaxLength(64); b.Property("ClientName") - .HasAnnotation("MaxLength", 128); + .HasMaxLength(128); b.Property("CustomData") - .HasAnnotation("MaxLength", 2000); + .HasMaxLength(2000); b.Property("Exception") - .HasAnnotation("MaxLength", 2000); + .HasMaxLength(2000); b.Property("ExecutionDuration"); @@ -104,13 +107,13 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("ImpersonatorUserId"); b.Property("MethodName") - .HasAnnotation("MaxLength", 256); + .HasMaxLength(256); b.Property("Parameters") - .HasAnnotation("MaxLength", 1024); + .HasMaxLength(1024); b.Property("ServiceName") - .HasAnnotation("MaxLength", 256); + .HasMaxLength(256); b.Property("TenantId"); @@ -137,7 +140,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Name") .IsRequired() - .HasAnnotation("MaxLength", 128); + .HasMaxLength(128); b.Property("TenantId"); @@ -184,6 +187,30 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("AbpUserAccounts"); }); + modelBuilder.Entity("Abp.Authorization.Users.UserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("CreationTime"); + + b.Property("CreatorUserId"); + + b.Property("TenantId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AbpUserClaims"); + }); + modelBuilder.Entity("Abp.Authorization.Users.UserLogin", b => { b.Property("Id") @@ -191,11 +218,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("LoginProvider") .IsRequired() - .HasAnnotation("MaxLength", 128); + .HasMaxLength(128); b.Property("ProviderKey") .IsRequired() - .HasAnnotation("MaxLength", 256); + .HasMaxLength(256); b.Property("TenantId"); @@ -214,27 +241,27 @@ protected override void BuildModel(ModelBuilder modelBuilder) .ValueGeneratedOnAdd(); b.Property("BrowserInfo") - .HasAnnotation("MaxLength", 256); + .HasMaxLength(256); b.Property("ClientIpAddress") - .HasAnnotation("MaxLength", 64); + .HasMaxLength(64); b.Property("ClientName") - .HasAnnotation("MaxLength", 128); + .HasMaxLength(128); b.Property("CreationTime"); b.Property("Result"); b.Property("TenancyName") - .HasAnnotation("MaxLength", 64); + .HasMaxLength(64); b.Property("TenantId"); b.Property("UserId"); b.Property("UserNameOrEmailAddress") - .HasAnnotation("MaxLength", 255); + .HasMaxLength(255); b.HasKey("Id"); @@ -260,6 +287,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("UserId"); + b.Property("IsDeleted"); + b.HasKey("Id"); b.ToTable("AbpUserOrganizationUnits"); @@ -300,11 +329,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("JobArgs") .IsRequired() - .HasAnnotation("MaxLength", 1048576); + .HasMaxLength(1048576); b.Property("JobType") .IsRequired() - .HasAnnotation("MaxLength", 512); + .HasMaxLength(512); b.Property("LastTryTime"); @@ -336,14 +365,14 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Name") .IsRequired() - .HasAnnotation("MaxLength", 256); + .HasMaxLength(256); b.Property("TenantId"); b.Property("UserId"); b.Property("Value") - .HasAnnotation("MaxLength", 2000); + .HasMaxLength(2000); b.HasKey("Id"); @@ -367,10 +396,10 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("DisplayName") .IsRequired() - .HasAnnotation("MaxLength", 64); + .HasMaxLength(64); b.Property("Icon") - .HasAnnotation("MaxLength", 128); + .HasMaxLength(128); b.Property("IsDeleted"); @@ -380,7 +409,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Name") .IsRequired() - .HasAnnotation("MaxLength", 10); + .HasMaxLength(10); b.Property("TenantId"); @@ -400,11 +429,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Key") .IsRequired() - .HasAnnotation("MaxLength", 256); + .HasMaxLength(256); b.Property("LanguageName") .IsRequired() - .HasAnnotation("MaxLength", 10); + .HasMaxLength(10); b.Property("LastModificationTime"); @@ -412,13 +441,13 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Source") .IsRequired() - .HasAnnotation("MaxLength", 128); + .HasMaxLength(128); b.Property("TenantId"); b.Property("Value") .IsRequired() - .HasAnnotation("MaxLength", 67108864); + .HasMaxLength(67108864); b.HasKey("Id"); @@ -435,34 +464,34 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("CreatorUserId"); b.Property("Data") - .HasAnnotation("MaxLength", 1048576); + .HasMaxLength(1048576); b.Property("DataTypeName") - .HasAnnotation("MaxLength", 512); + .HasMaxLength(512); b.Property("EntityId") - .HasAnnotation("MaxLength", 96); + .HasMaxLength(96); b.Property("EntityTypeAssemblyQualifiedName") - .HasAnnotation("MaxLength", 512); + .HasMaxLength(512); b.Property("EntityTypeName") - .HasAnnotation("MaxLength", 250); + .HasMaxLength(250); b.Property("ExcludedUserIds") - .HasAnnotation("MaxLength", 131072); + .HasMaxLength(131072); b.Property("NotificationName") .IsRequired() - .HasAnnotation("MaxLength", 96); + .HasMaxLength(96); b.Property("Severity"); b.Property("TenantIds") - .HasAnnotation("MaxLength", 131072); + .HasMaxLength(131072); b.Property("UserIds") - .HasAnnotation("MaxLength", 131072); + .HasMaxLength(131072); b.HasKey("Id"); @@ -479,16 +508,16 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("CreatorUserId"); b.Property("EntityId") - .HasAnnotation("MaxLength", 96); + .HasMaxLength(96); b.Property("EntityTypeAssemblyQualifiedName") - .HasAnnotation("MaxLength", 512); + .HasMaxLength(512); b.Property("EntityTypeName") - .HasAnnotation("MaxLength", 250); + .HasMaxLength(250); b.Property("NotificationName") - .HasAnnotation("MaxLength", 96); + .HasMaxLength(96); b.Property("TenantId"); @@ -511,23 +540,23 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("CreatorUserId"); b.Property("Data") - .HasAnnotation("MaxLength", 1048576); + .HasMaxLength(1048576); b.Property("DataTypeName") - .HasAnnotation("MaxLength", 512); + .HasMaxLength(512); b.Property("EntityId") - .HasAnnotation("MaxLength", 96); + .HasMaxLength(96); b.Property("EntityTypeAssemblyQualifiedName") - .HasAnnotation("MaxLength", 512); + .HasMaxLength(512); b.Property("EntityTypeName") - .HasAnnotation("MaxLength", 250); + .HasMaxLength(250); b.Property("NotificationName") .IsRequired() - .HasAnnotation("MaxLength", 96); + .HasMaxLength(96); b.Property("Severity"); @@ -567,7 +596,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Code") .IsRequired() - .HasAnnotation("MaxLength", 95); + .HasMaxLength(95); b.Property("CreationTime"); @@ -579,7 +608,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("DisplayName") .IsRequired() - .HasAnnotation("MaxLength", 128); + .HasMaxLength(128); b.Property("IsDeleted"); @@ -604,18 +633,14 @@ protected override void BuildModel(ModelBuilder modelBuilder) .ValueGeneratedOnAdd(); b.Property("ConnectionString") - .HasAnnotation("MaxLength", 1024); + .HasMaxLength(1024); b.Property("CreationTime"); b.Property("CreatorUserId"); - b.Property("CreatorUserId1"); - b.Property("DeleterUserId"); - b.Property("DeleterUserId1"); - b.Property("DeletionTime"); b.Property("EditionId"); @@ -628,25 +653,29 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("LastModifierUserId"); - b.Property("LastModifierUserId1"); - b.Property("Name") .IsRequired() - .HasAnnotation("MaxLength", 128); + .HasMaxLength(128); b.Property("TenancyName") .IsRequired() - .HasAnnotation("MaxLength", 64); + .HasMaxLength(64); - b.HasKey("Id"); + b.Property("UserId"); - b.HasIndex("CreatorUserId1"); + b.Property("UserId1"); - b.HasIndex("DeleterUserId1"); + b.Property("UserId2"); + + b.HasKey("Id"); b.HasIndex("EditionId"); - b.HasIndex("LastModifierUserId1"); + b.HasIndex("UserId"); + + b.HasIndex("UserId1"); + + b.HasIndex("UserId2"); b.ToTable("AbpTenants"); }); @@ -666,7 +695,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("DisplayName") .IsRequired() - .HasAnnotation("MaxLength", 64); + .HasMaxLength(64); b.Property("IsDefault"); @@ -680,7 +709,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Name") .IsRequired() - .HasAnnotation("MaxLength", 32); + .HasMaxLength(32); b.Property("TenantId"); @@ -700,8 +729,10 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Id") .ValueGeneratedOnAdd(); + b.Property("AccessFailedCount"); + b.Property("AuthenticationSource") - .HasAnnotation("MaxLength", 64); + .HasMaxLength(64); b.Property("CreationTime"); @@ -713,10 +744,10 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("EmailAddress") .IsRequired() - .HasAnnotation("MaxLength", 256); + .HasMaxLength(256); b.Property("EmailConfirmationCode") - .HasAnnotation("MaxLength", 128); + .HasMaxLength(328); b.Property("IsActive"); @@ -724,40 +755,52 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("IsEmailConfirmed"); + b.Property("IsLockoutEnabled"); + + b.Property("IsPhoneNumberConfirmed"); + + b.Property("IsTwoFactorEnabled"); + b.Property("LastLoginTime"); b.Property("LastModificationTime"); b.Property("LastModifierUserId"); + b.Property("LockoutEndDateUtc"); + b.Property("Name") .IsRequired() - .HasAnnotation("MaxLength", 32); + .HasMaxLength(32); b.Property("Password") .IsRequired() - .HasAnnotation("MaxLength", 128); + .HasMaxLength(128); b.Property("PasswordResetCode") - .HasAnnotation("MaxLength", 328); + .HasMaxLength(328); + + b.Property("PhoneNumber"); + + b.Property("SecurityStamp"); b.Property("Surname") .IsRequired() - .HasAnnotation("MaxLength", 32); + .HasMaxLength(32); b.Property("TenantId"); b.Property("UserName") .IsRequired() - .HasAnnotation("MaxLength", 32); + .HasMaxLength(32); b.HasKey("Id"); - b.HasIndex("CreatorUserId") - .IsUnique(); + b.HasIndex("CreatorUserId"); - b.HasIndex("LastModifierUserId") - .IsUnique(); + b.HasIndex("DeleterUserId"); + + b.HasIndex("LastModifierUserId"); b.ToTable("AbpUsers"); }); @@ -812,6 +855,14 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasDiscriminator().HasValue("UserPermissionSetting"); }); + modelBuilder.Entity("Abp.Authorization.Users.UserClaim", b => + { + b.HasOne("Abp.Zero.SampleApp.Users.User") + .WithMany("Claims") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + modelBuilder.Entity("Abp.Authorization.Users.UserLogin", b => { b.HasOne("Abp.Zero.SampleApp.Users.User") @@ -844,21 +895,21 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("Abp.Zero.SampleApp.MultiTenancy.Tenant", b => { - b.HasOne("Abp.Zero.SampleApp.Users.User", "CreatorUser") + b.HasOne("Abp.Application.Editions.Edition", "Edition") .WithMany() - .HasForeignKey("CreatorUserId1"); + .HasForeignKey("EditionId"); - b.HasOne("Abp.Zero.SampleApp.Users.User", "DeleterUser") + b.HasOne("Abp.Zero.SampleApp.Users.User", "CreatorUser") .WithMany() - .HasForeignKey("DeleterUserId1"); + .HasForeignKey("UserId"); - b.HasOne("Abp.Application.Editions.Edition", "Edition") + b.HasOne("Abp.Zero.SampleApp.Users.User", "DeleterUser") .WithMany() - .HasForeignKey("EditionId"); + .HasForeignKey("UserId1"); b.HasOne("Abp.Zero.SampleApp.Users.User", "LastModifierUser") .WithMany() - .HasForeignKey("LastModifierUserId1"); + .HasForeignKey("UserId2"); }); modelBuilder.Entity("Abp.Zero.SampleApp.Roles.Role", b => @@ -879,12 +930,16 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("Abp.Zero.SampleApp.Users.User", b => { b.HasOne("Abp.Zero.SampleApp.Users.User", "CreatorUser") - .WithOne() - .HasForeignKey("Abp.Zero.SampleApp.Users.User", "CreatorUserId"); + .WithMany() + .HasForeignKey("CreatorUserId"); + + b.HasOne("Abp.Zero.SampleApp.Users.User", "DeleterUser") + .WithMany() + .HasForeignKey("DeleterUserId"); b.HasOne("Abp.Zero.SampleApp.Users.User", "LastModifierUser") - .WithOne() - .HasForeignKey("Abp.Zero.SampleApp.Users.User", "LastModifierUserId"); + .WithMany() + .HasForeignKey("LastModifierUserId"); }); modelBuilder.Entity("Abp.Application.Features.EditionFeatureSetting", b => diff --git a/test/Abp.Zero.SampleApp.EntityFrameworkCore/project.json b/test/Abp.Zero.SampleApp.EntityFrameworkCore/project.json deleted file mode 100644 index a35c6a47..00000000 --- a/test/Abp.Zero.SampleApp.EntityFrameworkCore/project.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "version": "1.0.0-*", - - "dependencies": { - "Abp.Zero.SampleApp": "1.0.0.0-*", - "Abp.Zero.EntityFrameworkCore": "1.0.0.0-*", - "Microsoft.EntityFrameworkCore.Tools": { - "version": "1.0.0-preview2-final", - "type": "build" - }, - "Microsoft.EntityFrameworkCore.SqlServer": "1.0.1", - "Microsoft.EntityFrameworkCore.SqlServer.Design": "1.0.1" - }, - - "tools": { - "Microsoft.EntityFrameworkCore.Tools": "1.0.0-preview2-final" - }, - - "buildOptions": { - "emitEntryPoint": true - }, - - "frameworks": { - "net461": { - "frameworkAssemblies": { - }, - "Microsoft.NETCore.App": "1.0.0" - } - } -} diff --git a/test/Abp.Zero.SampleApp.NHibernateTests/Abp.Zero.SampleApp.NHibernateTests.csproj b/test/Abp.Zero.SampleApp.NHibernateTests/Abp.Zero.SampleApp.NHibernateTests.csproj new file mode 100644 index 00000000..15eb3756 --- /dev/null +++ b/test/Abp.Zero.SampleApp.NHibernateTests/Abp.Zero.SampleApp.NHibernateTests.csproj @@ -0,0 +1,41 @@ + + + + net46 + Abp.Zero.SampleApp.NHibernateTests + Abp.Zero.SampleApp.NHibernateTests + true + false + false + false + Abp.Zero.SampleApp + + + + + PreserveNewest + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/Abp.Zero.SampleApp.NHibernateTests/Abp.Zero.SampleApp.NHibernateTests.xproj b/test/Abp.Zero.SampleApp.NHibernateTests/Abp.Zero.SampleApp.NHibernateTests.xproj deleted file mode 100644 index 4628913e..00000000 --- a/test/Abp.Zero.SampleApp.NHibernateTests/Abp.Zero.SampleApp.NHibernateTests.xproj +++ /dev/null @@ -1,22 +0,0 @@ - - - - 14.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - - - - fa3e87c5-7273-461c-ae9f-67b416e07949 - Abp.Zero.SampleApp - .\obj - .\bin\ - v4.5.2 - - - 2.0 - - - - - - \ No newline at end of file diff --git a/test/Abp.Zero.SampleApp.NHibernateTests/NHibernate/TestDatas/InitialTestDataBuilder.cs b/test/Abp.Zero.SampleApp.NHibernateTests/NHibernate/TestDatas/InitialTestDataBuilder.cs index 42f95c9e..f79a6cf7 100644 --- a/test/Abp.Zero.SampleApp.NHibernateTests/NHibernate/TestDatas/InitialTestDataBuilder.cs +++ b/test/Abp.Zero.SampleApp.NHibernateTests/NHibernate/TestDatas/InitialTestDataBuilder.cs @@ -13,8 +13,6 @@ public InitialTestDataBuilder(ISession session) public void Build() { - //_session.DisableAllFilters(); //TODO: Needs? - new InitialTenantsBuilder(_session).Build(); new InitialUsersBuilder(_session).Build(); new InitialTestLanguagesBuilder(_session).Build(); diff --git a/test/Abp.Zero.SampleApp.NHibernateTests/project.json b/test/Abp.Zero.SampleApp.NHibernateTests/project.json deleted file mode 100644 index 516cb299..00000000 --- a/test/Abp.Zero.SampleApp.NHibernateTests/project.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "version": "1.0.0-*", - - "testRunner": "xunit", - - "dependencies": { - "xunit": "2.2.0-beta2-build3300", - "xunit.extensibility.execution": "2.2.0-beta2-build3300", - "xunit.runner.visualstudio": "2.2.0-beta2-build1149", - "dotnet-test-xunit": "2.2.0-preview2-build1029", - "NSubstitute": "1.10.0", - "Abp.Zero.SampleApp": "1.0.0.0-*", - "Abp.Zero.NHibernate": "1.0.0.0-*", - "Shouldly": "2.8.2", - "System.Data.SQLite.Core": "1.0.103", - "Abp.TestBase": "1.0.0" - }, - - "frameworks": { - "net452": { - "Microsoft.NETCore.App": "1.0.0" - } - }, - - "buildOptions": { - "copyToOutput": "SQLite.Interop.dll" - } -} diff --git a/test/Abp.Zero.SampleApp.Tests/Abp.Zero.SampleApp.Tests.csproj b/test/Abp.Zero.SampleApp.Tests/Abp.Zero.SampleApp.Tests.csproj new file mode 100644 index 00000000..a307721b --- /dev/null +++ b/test/Abp.Zero.SampleApp.Tests/Abp.Zero.SampleApp.Tests.csproj @@ -0,0 +1,42 @@ + + + + net46 + Abp.Zero.SampleApp.Tests + Abp.Zero.SampleApp.Tests + true + false + false + false + false + false + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/Abp.Zero.SampleApp.Tests/Abp.Zero.SampleApp.Tests.xproj b/test/Abp.Zero.SampleApp.Tests/Abp.Zero.SampleApp.Tests.xproj deleted file mode 100644 index b25e7887..00000000 --- a/test/Abp.Zero.SampleApp.Tests/Abp.Zero.SampleApp.Tests.xproj +++ /dev/null @@ -1,22 +0,0 @@ - - - - 14.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - - - - 41f88d4f-fab9-4d0a-85e5-c71f8b63b973 - Abp.Zero.SampleApp.Tests - .\obj - .\bin\ - v4.5.2 - - - 2.0 - - - - - - \ No newline at end of file diff --git a/test/Abp.Zero.SampleApp.Tests/Ldap/LdapAuthenticationSource_Tests.cs b/test/Abp.Zero.SampleApp.Tests/Ldap/LdapAuthenticationSource_Tests.cs index b9c75182..351588bf 100644 --- a/test/Abp.Zero.SampleApp.Tests/Ldap/LdapAuthenticationSource_Tests.cs +++ b/test/Abp.Zero.SampleApp.Tests/Ldap/LdapAuthenticationSource_Tests.cs @@ -1,5 +1,6 @@ using System; using System.Threading.Tasks; +using Abp.Authorization; using Abp.Authorization.Users; using Abp.Configuration; using Abp.Dependency; diff --git a/test/Abp.Zero.SampleApp.Tests/MultiTenancy/TenantFeature_Tests.cs b/test/Abp.Zero.SampleApp.Tests/MultiTenancy/TenantFeature_Tests.cs new file mode 100644 index 00000000..7207701d --- /dev/null +++ b/test/Abp.Zero.SampleApp.Tests/MultiTenancy/TenantFeature_Tests.cs @@ -0,0 +1,45 @@ +using Abp.Application.Features; +using Abp.MultiTenancy; +using Abp.Zero.SampleApp.Features; +using Abp.Zero.SampleApp.MultiTenancy; +using Shouldly; +using Xunit; + +namespace Abp.Zero.SampleApp.Tests.MultiTenancy +{ + public class TenantFeature_Tests : SampleAppTestBase + { + private readonly TenantManager _tenantManager; + private readonly IFeatureChecker _featureChecker; + + public TenantFeature_Tests() + { + _tenantManager = Resolve(); + _featureChecker = Resolve(); + } + + [Fact] + public void Changing_Tenant_Feature_Should_Not_Effect_Other_Tenants() + { + //Create tenants + var firstTenantId = UsingDbContext(context => + { + var firstTenant = new Tenant("Tenant1", "Tenant1"); + context.Tenants.Add(firstTenant); + context.SaveChanges(); + return firstTenant.Id; + }); + + var secondTenantId = UsingDbContext(context => + { + var secondTenant = new Tenant("Tenant2", "Tenant2"); + context.Tenants.Add(secondTenant); + context.SaveChanges(); + return secondTenant.Id; + }); + + _tenantManager.SetFeatureValue(firstTenantId, AppFeatureProvider.MyBoolFeature, "true"); + _featureChecker.IsEnabled(secondTenantId, AppFeatureProvider.MyBoolFeature).ShouldBe(false); + } + } +} diff --git a/test/Abp.Zero.SampleApp.Tests/MultiTenancy/TenantManager_Tests.cs b/test/Abp.Zero.SampleApp.Tests/MultiTenancy/TenantManager_Tests.cs index 6c18d3a6..96328109 100644 --- a/test/Abp.Zero.SampleApp.Tests/MultiTenancy/TenantManager_Tests.cs +++ b/test/Abp.Zero.SampleApp.Tests/MultiTenancy/TenantManager_Tests.cs @@ -17,12 +17,14 @@ public TenantManager_Tests() [Fact] public async Task Should_Not_Create_Duplicate_Tenant() { - (await _tenantManager.CreateAsync(new Tenant("Tenant-X", "Tenant X"))).Succeeded.ShouldBe(true); + await _tenantManager.CreateAsync(new Tenant("Tenant-X", "Tenant X")); //Trying to re-create with same tenancy name - - var result = (await _tenantManager.CreateAsync(new Tenant("Tenant-X", "Tenant X"))); - result.Succeeded.ShouldBe(false); + + await Assert.ThrowsAnyAsync(async () => + { + await _tenantManager.CreateAsync(new Tenant("Tenant-X", "Tenant X")); + }); } } } diff --git a/test/Abp.Zero.SampleApp.Tests/MultiTenancy/TenantStore_Tests.cs b/test/Abp.Zero.SampleApp.Tests/MultiTenancy/TenantStore_Tests.cs new file mode 100644 index 00000000..37cfc72d --- /dev/null +++ b/test/Abp.Zero.SampleApp.Tests/MultiTenancy/TenantStore_Tests.cs @@ -0,0 +1,46 @@ +using Abp.MultiTenancy; +using Abp.Zero.SampleApp.MultiTenancy; +using Shouldly; +using Xunit; + +namespace Abp.Zero.SampleApp.Tests.MultiTenancy +{ + public class TenantStore_Tests : SampleAppTestBase + { + private readonly ITenantStore _tenantStore; + + public TenantStore_Tests() + { + _tenantStore = Resolve(); + } + + + [Fact] + public void Should_Get_Tenant_By_Id() + { + //Act + var tenant = _tenantStore.Find(1); + + //Assert + Assert.NotNull(tenant); + tenant.TenancyName.ShouldBe(Tenant.DefaultTenantName); + } + + [Fact] + public void Should_Get_Tenant_By_Name() + { + //Act + var tenant = _tenantStore.Find(Tenant.DefaultTenantName); + + //Assert + Assert.NotNull(tenant); + tenant.Id.ShouldBe(1); + } + + [Fact] + public void Should_Not_Get_Unknown_Tenant() + { + Assert.Null(_tenantStore.Find("unknown-tenancy-name")); + } + } +} \ No newline at end of file diff --git a/test/Abp.Zero.SampleApp.Tests/SampleAppTestModule.cs b/test/Abp.Zero.SampleApp.Tests/SampleAppTestModule.cs index 7c60add4..3695ff35 100644 --- a/test/Abp.Zero.SampleApp.Tests/SampleAppTestModule.cs +++ b/test/Abp.Zero.SampleApp.Tests/SampleAppTestModule.cs @@ -1,3 +1,4 @@ +using System; using Abp.Modules; using Abp.TestBase; using Abp.Zero.Ldap; @@ -14,6 +15,11 @@ namespace Abp.Zero.SampleApp.Tests typeof(AbpTestBaseModule))] public class SampleAppTestModule : AbpModule { + public override void PreInitialize() + { + Configuration.UnitOfWork.Timeout = TimeSpan.FromMinutes(2); + } + public override void Initialize() { IocManager.IocContainer.Register( diff --git a/test/Abp.Zero.SampleApp.Tests/TestDatas/InitialUsersBuilder.cs b/test/Abp.Zero.SampleApp.Tests/TestDatas/InitialUsersBuilder.cs index 8812496b..ba4ca302 100644 --- a/test/Abp.Zero.SampleApp.Tests/TestDatas/InitialUsersBuilder.cs +++ b/test/Abp.Zero.SampleApp.Tests/TestDatas/InitialUsersBuilder.cs @@ -27,12 +27,23 @@ private void CreateUsers() var admin = _context.Users.Add( new User { - TenantId = defaultTenant.Id, - Name = "System", - Surname = "Administrator", - UserName = User.AdminUserName, - Password = new PasswordHasher().HashPassword("123qwe"), - EmailAddress = "admin@aspnetboilerplate.com" + TenantId = defaultTenant.Id, + Name = "System", + Surname = "Administrator", + UserName = User.AdminUserName, + Password = new PasswordHasher().HashPassword("123qwe"), + EmailAddress = "admin@aspnetboilerplate.com" + }); + + _context.Users.Add( + new User + { + TenantId = defaultTenant.Id, + Name = "System", + Surname = "Manager", + UserName = "manager", + Password = new PasswordHasher().HashPassword("123qwe"), + EmailAddress = "manager@aspnetboilerplate.com" }); } } diff --git a/test/Abp.Zero.SampleApp.Tests/Users/UserAppService_Tests.cs b/test/Abp.Zero.SampleApp.Tests/Users/UserAppService_Tests.cs index 4b696db8..be1d88a2 100644 --- a/test/Abp.Zero.SampleApp.Tests/Users/UserAppService_Tests.cs +++ b/test/Abp.Zero.SampleApp.Tests/Users/UserAppService_Tests.cs @@ -1,7 +1,9 @@ using System.Linq; +using System.Threading.Tasks; using Abp.Auditing; using Abp.Zero.SampleApp.Users; using Abp.Zero.SampleApp.Users.Dto; +using Microsoft.AspNet.Identity; using Shouldly; using Xunit; @@ -10,10 +12,12 @@ namespace Abp.Zero.SampleApp.Tests.Users public class UserAppService_Tests : SampleAppTestBase { private readonly IUserAppService _userAppService; + private readonly UserManager _userManager; public UserAppService_Tests() { _userAppService = Resolve(); + _userManager = Resolve(); Resolve().IsEnabledForAnonymousUsers = true; } @@ -43,5 +47,33 @@ public void Should_Insert_And_Write_Audit_Logs() auditLog.Exception.ShouldBe(null); }); } + + [Fact] + public async Task Shoudl_Reset_Password() + { + AbpSession.TenantId = 1; //Default tenant + var managerUser = await _userManager.FindByNameAsync("manager"); + managerUser.PasswordResetCode = "fc9640bb73ec40a2b42b479610741a5a"; + _userManager.Update(managerUser); + + AbpSession.TenantId = null; //Default tenant + + await _userAppService.ResetPassword(new ResetPasswordInput + { + TenantId = 1, + UserId = managerUser.Id, + Password = "123qwe", + ResetCode = "fc9640bb73ec40a2b42b479610741a5a" + }); + + var updatedUser = UsingDbContext( + context => + { + return context.Users.FirstOrDefault(u => u.UserName == "manager"); + }); + + updatedUser.UserName.ShouldBe("manager"); + updatedUser.PasswordResetCode.ShouldBe(null); + } } } diff --git a/test/Abp.Zero.SampleApp.Tests/Users/UserLogin_ExternalAuthenticationSources_Test.cs b/test/Abp.Zero.SampleApp.Tests/Users/UserLogin_ExternalAuthenticationSources_Test.cs index 3df700b9..2b1d38b3 100644 --- a/test/Abp.Zero.SampleApp.Tests/Users/UserLogin_ExternalAuthenticationSources_Test.cs +++ b/test/Abp.Zero.SampleApp.Tests/Users/UserLogin_ExternalAuthenticationSources_Test.cs @@ -1,4 +1,5 @@ using System.Threading.Tasks; +using Abp.Authorization; using Abp.Authorization.Users; using Abp.Dependency; using Abp.Modules; diff --git a/test/Abp.Zero.SampleApp.Tests/Users/UserLogin_Tests.cs b/test/Abp.Zero.SampleApp.Tests/Users/UserLogin_Tests.cs index 7cb21403..d4265ba5 100644 --- a/test/Abp.Zero.SampleApp.Tests/Users/UserLogin_Tests.cs +++ b/test/Abp.Zero.SampleApp.Tests/Users/UserLogin_Tests.cs @@ -1,5 +1,6 @@ using System.Linq; using System.Threading.Tasks; +using Abp.Authorization; using Abp.Authorization.Users; using Abp.Configuration; using Abp.Configuration.Startup; diff --git a/test/Abp.Zero.SampleApp.Tests/Users/UserManager_Lockout_Tests.cs b/test/Abp.Zero.SampleApp.Tests/Users/UserManager_Lockout_Tests.cs index de53967d..c7db5cc6 100644 --- a/test/Abp.Zero.SampleApp.Tests/Users/UserManager_Lockout_Tests.cs +++ b/test/Abp.Zero.SampleApp.Tests/Users/UserManager_Lockout_Tests.cs @@ -1,5 +1,6 @@ using System; using System.Threading.Tasks; +using Abp.Authorization; using Abp.Authorization.Users; using Abp.Configuration; using Abp.Threading; diff --git a/test/Abp.Zero.SampleApp.Tests/Users/UserManager_Update_Tests.cs b/test/Abp.Zero.SampleApp.Tests/Users/UserManager_Update_Tests.cs new file mode 100644 index 00000000..97d493ef --- /dev/null +++ b/test/Abp.Zero.SampleApp.Tests/Users/UserManager_Update_Tests.cs @@ -0,0 +1,37 @@ +using Abp.Domain.Uow; +using Abp.Zero.SampleApp.Users; +using Microsoft.AspNet.Identity; +using Shouldly; +using Xunit; + +namespace Abp.Zero.SampleApp.Tests.Users +{ + public class UserManager_Update_Tests : SampleAppTestBase + { + private readonly UserManager _userManager; + private readonly IUnitOfWorkManager _unitOfWorkManager; + + public UserManager_Update_Tests() + { + _userManager = Resolve(); + _unitOfWorkManager = Resolve(); + + AbpSession.TenantId = 1; //Default tenant + } + + [Fact] + public void Should_Not_Change_Admin_UserName() + { + using (var uow = _unitOfWorkManager.Begin()) + { + var adminUser = _userManager.FindByName("admin"); + + adminUser.UserName = "tuana"; + + _userManager.Update(adminUser).Succeeded.ShouldBeFalse(); + + uow.Complete(); + } + } + } +} \ No newline at end of file diff --git a/test/Abp.Zero.SampleApp.Tests/Users/UserOrganizationUnit_Tests.cs b/test/Abp.Zero.SampleApp.Tests/Users/UserOrganizationUnit_Tests.cs index c268893f..7ea56188 100644 --- a/test/Abp.Zero.SampleApp.Tests/Users/UserOrganizationUnit_Tests.cs +++ b/test/Abp.Zero.SampleApp.Tests/Users/UserOrganizationUnit_Tests.cs @@ -61,7 +61,7 @@ public async Task Test_RemoveFromOrganizationUnitAsync() //Assert (await _userManager.IsInOrganizationUnitAsync(_defaultTenantAdmin, ou11)).ShouldBe(false); - UsingDbContext(context => context.UserOrganizationUnits.FirstOrDefault(ou => ou.UserId == _defaultTenantAdmin.Id && ou.OrganizationUnitId == ou11.Id).ShouldBeNull()); + UsingDbContext(context => context.UserOrganizationUnits.FirstOrDefault(ou => ou.UserId == _defaultTenantAdmin.Id && ou.OrganizationUnitId == ou11.Id).IsDeleted.ShouldBeTrue()); } [Fact] diff --git a/test/Abp.Zero.SampleApp.Tests/project.json b/test/Abp.Zero.SampleApp.Tests/project.json deleted file mode 100644 index 4e54ae5f..00000000 --- a/test/Abp.Zero.SampleApp.Tests/project.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "version": "1.0.0-*", - - "testRunner": "xunit", - - "dependencies": { - "xunit": "2.2.0-beta2-build3300", - "xunit.extensibility.execution": "2.2.0-beta2-build3300", - "xunit.runner.visualstudio": "2.2.0-beta2-build1149", - "dotnet-test-xunit": "2.2.0-preview2-build1029", - "NSubstitute": "1.10.0", - "Effort.EF6": "1.1.5", - "Abp.Zero.SampleApp": "1.0.0.0-*", - "Abp.Zero.SampleApp.EntityFramework": "1.0.0.0-*", - "Abp.Zero.Ldap": "1.0.0.0-*", - "Shouldly": "2.8.2", - "Abp.TestBase": "1.0.0" - }, - - "frameworks": { - "net452": { - "Microsoft.NETCore.App": "1.0.0" - } - }, - - "buildOptions": { - "embed": { - "include": [ - ] - } - } -} diff --git a/test/Abp.Zero.SampleApp/Abp.Zero.SampleApp.csproj b/test/Abp.Zero.SampleApp/Abp.Zero.SampleApp.csproj new file mode 100644 index 00000000..736b2012 --- /dev/null +++ b/test/Abp.Zero.SampleApp/Abp.Zero.SampleApp.csproj @@ -0,0 +1,30 @@ + + + + 1.5.0.0 + net46 + Abp.Zero.SampleApp + Abp.Zero.SampleApp + false + false + false + false + false + false + + + + + + + + + + + + + + + + + diff --git a/test/Abp.Zero.SampleApp/Abp.Zero.SampleApp.xproj b/test/Abp.Zero.SampleApp/Abp.Zero.SampleApp.xproj deleted file mode 100644 index cd89dc53..00000000 --- a/test/Abp.Zero.SampleApp/Abp.Zero.SampleApp.xproj +++ /dev/null @@ -1,21 +0,0 @@ - - - - 14.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - - - - - 667801a0-99a5-414a-8685-a035408bf413 - Abp.Zero.SampleApp - .\obj - .\bin\ - v4.5.2 - - - - 2.0 - - - diff --git a/test/Abp.Zero.SampleApp/Authorization/AppPermissionChecker.cs b/test/Abp.Zero.SampleApp/Authorization/AppPermissionChecker.cs index 495d91b8..f6d17d2a 100644 --- a/test/Abp.Zero.SampleApp/Authorization/AppPermissionChecker.cs +++ b/test/Abp.Zero.SampleApp/Authorization/AppPermissionChecker.cs @@ -1,11 +1,10 @@ using Abp.Authorization; -using Abp.Zero.SampleApp.MultiTenancy; using Abp.Zero.SampleApp.Roles; using Abp.Zero.SampleApp.Users; namespace Abp.Zero.SampleApp.Authorization { - public class AppPermissionChecker : PermissionChecker + public class AppPermissionChecker : PermissionChecker { public AppPermissionChecker(UserManager userManager) : base(userManager) diff --git a/test/Abp.Zero.SampleApp/Users/Dto/ResetPasswordInput.cs b/test/Abp.Zero.SampleApp/Users/Dto/ResetPasswordInput.cs new file mode 100644 index 00000000..11ba372f --- /dev/null +++ b/test/Abp.Zero.SampleApp/Users/Dto/ResetPasswordInput.cs @@ -0,0 +1,13 @@ +namespace Abp.Zero.SampleApp.Users.Dto +{ + public class ResetPasswordInput + { + public int? TenantId { get; set; } + + public long UserId { get; set; } + + public string ResetCode { get; set; } + + public string Password { get; set; } + } +} diff --git a/test/Abp.Zero.SampleApp/Users/IUserAppService.cs b/test/Abp.Zero.SampleApp/Users/IUserAppService.cs index a8435941..d5341ca5 100644 --- a/test/Abp.Zero.SampleApp/Users/IUserAppService.cs +++ b/test/Abp.Zero.SampleApp/Users/IUserAppService.cs @@ -1,4 +1,5 @@ -using Abp.Application.Services; +using System.Threading.Tasks; +using Abp.Application.Services; using Abp.Zero.SampleApp.Users.Dto; namespace Abp.Zero.SampleApp.Users @@ -10,5 +11,7 @@ public interface IUserAppService : IApplicationService void UpdateUser(UpdateUserInput input); void DeleteUser(long userId); + + Task ResetPassword(ResetPasswordInput input); } } diff --git a/test/Abp.Zero.SampleApp/Users/UserAppService.cs b/test/Abp.Zero.SampleApp/Users/UserAppService.cs index e5555301..7c48dede 100644 --- a/test/Abp.Zero.SampleApp/Users/UserAppService.cs +++ b/test/Abp.Zero.SampleApp/Users/UserAppService.cs @@ -1,29 +1,43 @@ -using Abp.Domain.Repositories; +using System; +using System.Threading.Tasks; +using Abp.Domain.Repositories; +using Abp.Domain.Uow; +using Abp.Extensions; +using Abp.Runtime.Security; +using Abp.UI; using Abp.Zero.SampleApp.Users.Dto; +using Microsoft.AspNet.Identity; namespace Abp.Zero.SampleApp.Users { public class UserAppService : IUserAppService { private readonly IRepository _userRepository; + private readonly IUnitOfWorkManager _unitOfWorkManager; + private readonly UserManager _userManager; - public UserAppService(IRepository userRepository) + public UserAppService( + IRepository userRepository, + IUnitOfWorkManager unitOfWorkManager, + UserManager userManager) { _userRepository = userRepository; + _unitOfWorkManager = unitOfWorkManager; + _userManager = userManager; } public void CreateUser(CreateUserInput input) { _userRepository.Insert(new User - { - TenantId = null, - UserName = input.UserName, - Name = input.Name, - Surname = input.Surname, - EmailAddress = input.EmailAddress, - IsEmailConfirmed = true, - Password = "AM4OLBpptxBYmM79lGOX9egzZk3vIQU3d/gFCJzaBjAPXzYIK3tQ2N7X4fcrHtElTw==" //123qwe - }); + { + TenantId = null, + UserName = input.UserName, + Name = input.Name, + Surname = input.Surname, + EmailAddress = input.EmailAddress, + IsEmailConfirmed = true, + Password = "AM4OLBpptxBYmM79lGOX9egzZk3vIQU3d/gFCJzaBjAPXzYIK3tQ2N7X4fcrHtElTw==" //123qwe + }); } public void UpdateUser(UpdateUserInput input) @@ -46,5 +60,22 @@ public void DeleteUser(long userId) { _userRepository.Delete(userId); } + + public virtual async Task ResetPassword(ResetPasswordInput input) + { + _unitOfWorkManager.Current.SetTenantId(input.TenantId); + + var user = await _userManager.GetUserByIdAsync(input.UserId); + if (user == null || user.PasswordResetCode.IsNullOrEmpty() || user.PasswordResetCode != input.ResetCode) + { + throw new UserFriendlyException("InvalidPasswordResetCode", "InvalidPasswordResetCode_Detail"); + } + + user.Password = new PasswordHasher().HashPassword(input.Password); + user.PasswordResetCode = null; + user.IsEmailConfirmed = true; + + await _userManager.UpdateAsync(user); + } } } \ No newline at end of file diff --git a/test/Abp.Zero.SampleApp/project.json b/test/Abp.Zero.SampleApp/project.json deleted file mode 100644 index 9d46a295..00000000 --- a/test/Abp.Zero.SampleApp/project.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "version": "1.0.0.0-*", - - "dependencies": { - "Abp.Zero": "1.0.0.0-*", - "Abp.Zero.Owin": "1.0.0.0-*" - }, - - "frameworks": { - "net452": { - "frameworkAssemblies": { - } - } - } -} diff --git a/test/Abp.ZeroCore.IdentityServer4.Tests/Abp.ZeroCore.IdentityServer4.Tests.csproj b/test/Abp.ZeroCore.IdentityServer4.Tests/Abp.ZeroCore.IdentityServer4.Tests.csproj new file mode 100644 index 00000000..9533d193 --- /dev/null +++ b/test/Abp.ZeroCore.IdentityServer4.Tests/Abp.ZeroCore.IdentityServer4.Tests.csproj @@ -0,0 +1,28 @@ + + + + netcoreapp1.0 + Abp + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Abp.ZeroCore.IdentityServer4.Tests/IdentityServer4/AbpZeroIdentityServerTestBase.cs b/test/Abp.ZeroCore.IdentityServer4.Tests/IdentityServer4/AbpZeroIdentityServerTestBase.cs new file mode 100644 index 00000000..6aa2eda9 --- /dev/null +++ b/test/Abp.ZeroCore.IdentityServer4.Tests/IdentityServer4/AbpZeroIdentityServerTestBase.cs @@ -0,0 +1,9 @@ +using Abp.TestBase; + +namespace Abp.IdentityServer4 +{ + public abstract class AbpZeroIdentityServerTestBase : AbpIntegratedTestBase + { + + } +} diff --git a/test/Abp.ZeroCore.IdentityServer4.Tests/IdentityServer4/AbpZeroIdentityServerTestModule.cs b/test/Abp.ZeroCore.IdentityServer4.Tests/IdentityServer4/AbpZeroIdentityServerTestModule.cs new file mode 100644 index 00000000..f82b6702 --- /dev/null +++ b/test/Abp.ZeroCore.IdentityServer4.Tests/IdentityServer4/AbpZeroIdentityServerTestModule.cs @@ -0,0 +1,40 @@ +using Abp.Modules; +using Abp.Reflection.Extensions; +using Abp.Zero; +using Abp.ZeroCore.SampleApp.Core; +using Abp.ZeroCore.SampleApp.EntityFramework; +using Castle.Windsor.MsDependencyInjection; +using Microsoft.Extensions.DependencyInjection; + +namespace Abp.IdentityServer4 +{ + [DependsOn(typeof(AbpZeroCoreIdentityServerEntityFrameworkCoreModule), typeof(AbpZeroTestModule))] + public class AbpZeroIdentityServerTestModule : AbpModule + { + public override void PreInitialize() + { + Configuration.BackgroundJobs.IsJobExecutionEnabled = false; + + var services = new ServiceCollection(); + + services.AddIdentityServer() + .AddTemporarySigningCredential() + .AddAbpPersistedGrants() + .AddInMemoryIdentityResources(IdentityServerConfig.GetIdentityResources()) + .AddInMemoryApiResources(IdentityServerConfig.GetApiResources()) + .AddInMemoryClients(IdentityServerConfig.GetClients()) + .AddAbpIdentityServer() + .AddProfileService>(); + + var serviceProvider = WindsorRegistrationHelper.CreateServiceProvider( + IocManager.IocContainer, + services + ); + } + + public override void Initialize() + { + IocManager.RegisterAssemblyByConvention(typeof(AbpZeroIdentityServerTestModule).GetAssembly()); + } + } +} diff --git a/test/Abp.ZeroCore.IdentityServer4.Tests/IdentityServer4/DependencyInjection_Tests.cs b/test/Abp.ZeroCore.IdentityServer4.Tests/IdentityServer4/DependencyInjection_Tests.cs new file mode 100644 index 00000000..9dd18723 --- /dev/null +++ b/test/Abp.ZeroCore.IdentityServer4.Tests/IdentityServer4/DependencyInjection_Tests.cs @@ -0,0 +1,13 @@ +using Xunit; + +namespace Abp.IdentityServer4 +{ + public class DependencyInjection_Tests: AbpZeroIdentityServerTestBase + { + [Fact] + public void Should_Inject_AbpPersistedGrantStore() + { + Resolve(); + } + } +} diff --git a/test/Abp.ZeroCore.IdentityServer4.Tests/IdentityServer4/IdentityServerConfig.cs b/test/Abp.ZeroCore.IdentityServer4.Tests/IdentityServer4/IdentityServerConfig.cs new file mode 100644 index 00000000..6cdb77cd --- /dev/null +++ b/test/Abp.ZeroCore.IdentityServer4.Tests/IdentityServer4/IdentityServerConfig.cs @@ -0,0 +1,42 @@ +using System.Collections.Generic; +using IdentityServer4.Models; + +namespace Abp.IdentityServer4 +{ + public static class IdentityServerConfig + { + public static IEnumerable GetApiResources() + { + return new List + { + new ApiResource("default-api", "Default (all) API") + }; + } + + public static IEnumerable GetIdentityResources() + { + return new List + { + new IdentityResources.OpenId(), + new IdentityResources.Profile(), + new IdentityResources.Email(), + new IdentityResources.Phone() + }; + } + + public static IEnumerable GetClients() + { + return new List + { + new Client + { + ClientId = "test-client", + ClientName = "My Test Client", + AllowedGrantTypes = new List{ "hybrid", "client_credentials", "password" }, + ClientSecrets = new List {new Secret("secret".Sha256()) }, + AllowedScopes = new List { "default-api" } + } + }; + } + } +} \ No newline at end of file diff --git a/test/Abp.ZeroCore.SampleApp/Abp.ZeroCore.SampleApp.csproj b/test/Abp.ZeroCore.SampleApp/Abp.ZeroCore.SampleApp.csproj new file mode 100644 index 00000000..d9a27242 --- /dev/null +++ b/test/Abp.ZeroCore.SampleApp/Abp.ZeroCore.SampleApp.csproj @@ -0,0 +1,24 @@ + + + + netcoreapp1.0 + Abp.ZeroCore.SampleApp + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Abp.ZeroCore.SampleApp/AbpZeroCoreSampleAppModule.cs b/test/Abp.ZeroCore.SampleApp/AbpZeroCoreSampleAppModule.cs new file mode 100644 index 00000000..c708f384 --- /dev/null +++ b/test/Abp.ZeroCore.SampleApp/AbpZeroCoreSampleAppModule.cs @@ -0,0 +1,41 @@ +using Abp.AutoMapper; +using Abp.EntityFrameworkCore.Configuration; +using Abp.Modules; +using Abp.Reflection.Extensions; +using Abp.Zero.EntityFrameworkCore; +using Abp.ZeroCore.SampleApp.Application; +using Abp.ZeroCore.SampleApp.EntityFramework; +using Abp.ZeroCore.SampleApp.EntityFramework.Seed; + +namespace Abp.ZeroCore.SampleApp +{ + [DependsOn(typeof(AbpZeroCoreEntityFrameworkCoreModule), typeof(AbpAutoMapperModule))] + public class AbpZeroCoreSampleAppModule : AbpModule + { + /* Used it tests to skip dbcontext registration, in order to use in-memory database of EF Core */ + public bool SkipDbContextRegistration { get; set; } + + public override void PreInitialize() + { + if (!SkipDbContextRegistration) + { + Configuration.Modules.AbpEfCore().AddDbContext(configuration => + { + AbpZeroTemplateDbContextConfigurer.Configure(configuration.DbContextOptions, configuration.ConnectionString); + }); + } + + Configuration.Authorization.Providers.Add(); + } + + public override void Initialize() + { + IocManager.RegisterAssemblyByConvention(typeof(AbpZeroCoreSampleAppModule).GetAssembly()); + } + + public override void PostInitialize() + { + SeedHelper.SeedHostDb(IocManager); + } + } +} diff --git a/test/Abp.ZeroCore.SampleApp/Application/AppAuthorizationProvider.cs b/test/Abp.ZeroCore.SampleApp/Application/AppAuthorizationProvider.cs new file mode 100644 index 00000000..1e523248 --- /dev/null +++ b/test/Abp.ZeroCore.SampleApp/Application/AppAuthorizationProvider.cs @@ -0,0 +1,12 @@ +using Abp.Authorization; + +namespace Abp.ZeroCore.SampleApp.Application +{ + public class AppAuthorizationProvider : AuthorizationProvider + { + public override void SetPermissions(IPermissionDefinitionContext context) + { + + } + } +} diff --git a/test/Abp.ZeroCore.SampleApp/Application/AppConsts.cs b/test/Abp.ZeroCore.SampleApp/Application/AppConsts.cs new file mode 100644 index 00000000..76c798e0 --- /dev/null +++ b/test/Abp.ZeroCore.SampleApp/Application/AppConsts.cs @@ -0,0 +1,7 @@ +namespace Abp.ZeroCore.SampleApp.Application +{ + public static class AppConsts + { + public const string LocalizationSourceName = "SampleApp"; + } +} diff --git a/test/Abp.ZeroCore.SampleApp/Application/AppFeatureProvider.cs b/test/Abp.ZeroCore.SampleApp/Application/AppFeatureProvider.cs new file mode 100644 index 00000000..22988236 --- /dev/null +++ b/test/Abp.ZeroCore.SampleApp/Application/AppFeatureProvider.cs @@ -0,0 +1,20 @@ +using Abp.Application.Features; +using Abp.UI.Inputs; + +using static Abp.ZeroCore.SampleApp.Application.AppLocalizationHelper; + +namespace Abp.ZeroCore.SampleApp.Application +{ + public class AppFeatureProvider : FeatureProvider + { + public override void SetFeatures(IFeatureDefinitionContext context) + { + context.Create( + AppFeatures.SimpleBooleanFeature, + defaultValue: "false", + displayName: L("SimpleBooleanFeature"), + inputType: new CheckboxInputType() + ); + } + } +} diff --git a/test/Abp.ZeroCore.SampleApp/Application/AppFeatures.cs b/test/Abp.ZeroCore.SampleApp/Application/AppFeatures.cs new file mode 100644 index 00000000..d0857b4a --- /dev/null +++ b/test/Abp.ZeroCore.SampleApp/Application/AppFeatures.cs @@ -0,0 +1,7 @@ +namespace Abp.ZeroCore.SampleApp.Application +{ + public static class AppFeatures + { + public const string SimpleBooleanFeature = "SimpleBooleanFeature"; + } +} diff --git a/test/Abp.ZeroCore.SampleApp/Application/AppLocalizationHelper.cs b/test/Abp.ZeroCore.SampleApp/Application/AppLocalizationHelper.cs new file mode 100644 index 00000000..50f75f8a --- /dev/null +++ b/test/Abp.ZeroCore.SampleApp/Application/AppLocalizationHelper.cs @@ -0,0 +1,12 @@ +using Abp.Localization; + +namespace Abp.ZeroCore.SampleApp.Application +{ + public static class AppLocalizationHelper + { + public static ILocalizableString L(string name) + { + return new LocalizableString(name, AppConsts.LocalizationSourceName); + } + } +} diff --git a/test/Abp.ZeroCore.SampleApp/Application/AppStaticRoleNames.cs b/test/Abp.ZeroCore.SampleApp/Application/AppStaticRoleNames.cs new file mode 100644 index 00000000..20f6d0af --- /dev/null +++ b/test/Abp.ZeroCore.SampleApp/Application/AppStaticRoleNames.cs @@ -0,0 +1,17 @@ +namespace Abp.ZeroCore.SampleApp.Application +{ + public static class AppStaticRoleNames + { + public static class Host + { + public const string Admin = "Admin"; + } + + public static class Tenants + { + public const string Admin = "Admin"; + + public const string User = "User"; + } + } +} \ No newline at end of file diff --git a/test/Abp.ZeroCore.SampleApp/Application/Users/IUserAppService.cs b/test/Abp.ZeroCore.SampleApp/Application/Users/IUserAppService.cs new file mode 100644 index 00000000..fc45153a --- /dev/null +++ b/test/Abp.ZeroCore.SampleApp/Application/Users/IUserAppService.cs @@ -0,0 +1,9 @@ +using Abp.Application.Services; + +namespace Abp.ZeroCore.SampleApp.Application.Users +{ + public interface IUserAppService : IAsyncCrudAppService + { + + } +} diff --git a/test/Abp.ZeroCore.SampleApp/Application/Users/UserAppService.cs b/test/Abp.ZeroCore.SampleApp/Application/Users/UserAppService.cs new file mode 100644 index 00000000..434e8a89 --- /dev/null +++ b/test/Abp.ZeroCore.SampleApp/Application/Users/UserAppService.cs @@ -0,0 +1,15 @@ +using Abp.Application.Services; +using Abp.Domain.Repositories; +using Abp.ZeroCore.SampleApp.Core; + +namespace Abp.ZeroCore.SampleApp.Application.Users +{ + public class UserAppService : AsyncCrudAppService, IUserAppService + { + public UserAppService(IRepository repository) + : base(repository) + { + + } + } +} \ No newline at end of file diff --git a/test/Abp.ZeroCore.SampleApp/Application/Users/UserDto.cs b/test/Abp.ZeroCore.SampleApp/Application/Users/UserDto.cs new file mode 100644 index 00000000..dad71bae --- /dev/null +++ b/test/Abp.ZeroCore.SampleApp/Application/Users/UserDto.cs @@ -0,0 +1,12 @@ +using Abp.Application.Services.Dto; +using Abp.AutoMapper; +using Abp.ZeroCore.SampleApp.Core; + +namespace Abp.ZeroCore.SampleApp.Application.Users +{ + [AutoMap(typeof(User))] + public class UserDto : EntityDto + { + public string UserName { get; set; } + } +} \ No newline at end of file diff --git a/test/Abp.ZeroCore.SampleApp/Core/Role.cs b/test/Abp.ZeroCore.SampleApp/Core/Role.cs new file mode 100644 index 00000000..a350687b --- /dev/null +++ b/test/Abp.ZeroCore.SampleApp/Core/Role.cs @@ -0,0 +1,18 @@ +using Abp.Authorization.Roles; + +namespace Abp.ZeroCore.SampleApp.Core +{ + public class Role : AbpRole + { + public Role() + { + + } + + public Role(int? tenantId, string name, string displayName) + : base(tenantId, name, displayName) + { + + } + } +} \ No newline at end of file diff --git a/test/Abp.ZeroCore.SampleApp/Core/ServicesCollectionDependencyRegistrar.cs b/test/Abp.ZeroCore.SampleApp/Core/ServicesCollectionDependencyRegistrar.cs new file mode 100644 index 00000000..335ebe2a --- /dev/null +++ b/test/Abp.ZeroCore.SampleApp/Core/ServicesCollectionDependencyRegistrar.cs @@ -0,0 +1,27 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace Abp.ZeroCore.SampleApp.Core +{ + public static class ServicesCollectionDependencyRegistrar + { + public static void Register(ServiceCollection services) + { + services.AddLogging(); + + services.AddAbpIdentity() + .AddAbpTenantManager() + .AddAbpEditionManager() + .AddAbpRoleManager() + .AddAbpUserManager() + .AddAbpSignInManager() + .AddAbpLogInManager() + .AddAbpUserClaimsPrincipalFactory() + .AddAbpSecurityStampValidator() + .AddPermissionChecker() + .AddAbpUserStore() + .AddAbpRoleStore() + .AddFeatureValueStore() + .AddDefaultTokenProviders(); + } + } +} diff --git a/test/Abp.ZeroCore.SampleApp/Core/Tenant.cs b/test/Abp.ZeroCore.SampleApp/Core/Tenant.cs new file mode 100644 index 00000000..857e953f --- /dev/null +++ b/test/Abp.ZeroCore.SampleApp/Core/Tenant.cs @@ -0,0 +1,17 @@ +using Abp.MultiTenancy; + +namespace Abp.ZeroCore.SampleApp.Core +{ + public class Tenant : AbpTenant + { + protected Tenant() + { + + } + + public Tenant(string tenancyName, string name) + : base(tenancyName, name) + { + } + } +} \ No newline at end of file diff --git a/test/Abp.ZeroCore.SampleApp/Core/User.cs b/test/Abp.ZeroCore.SampleApp/Core/User.cs new file mode 100644 index 00000000..77392ce8 --- /dev/null +++ b/test/Abp.ZeroCore.SampleApp/Core/User.cs @@ -0,0 +1,28 @@ +using Abp.Authorization.Users; + +namespace Abp.ZeroCore.SampleApp.Core +{ + public class User : AbpUser + { + public override string ToString() + { + return string.Format("[User {0}] {1}", Id, UserName); + } + + public static User CreateTenantAdminUser(int tenantId, string emailAddress) + { + var user = new User + { + TenantId = tenantId, + UserName = AdminUserName, + Name = AdminUserName, + Surname = AdminUserName, + EmailAddress = emailAddress + }; + + user.SetNormalizedNames(); + + return user; + } + } +} \ No newline at end of file diff --git a/test/Abp.ZeroCore.SampleApp/Core/_Service_Overrides.cs b/test/Abp.ZeroCore.SampleApp/Core/_Service_Overrides.cs new file mode 100644 index 00000000..85edcd7e --- /dev/null +++ b/test/Abp.ZeroCore.SampleApp/Core/_Service_Overrides.cs @@ -0,0 +1,274 @@ +using System; +using System.Collections.Generic; +using System.Security.Claims; +using System.Threading.Tasks; +using Abp.Application.Editions; +using Abp.Application.Features; +using Abp.Authorization; +using Abp.Authorization.Roles; +using Abp.Authorization.Users; +using Abp.Configuration; +using Abp.Configuration.Startup; +using Abp.Dependency; +using Abp.Domain.Repositories; +using Abp.Domain.Uow; +using Abp.Linq; +using Abp.MultiTenancy; +using Abp.Organizations; +using Abp.Runtime.Caching; +using Abp.Zero.Configuration; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Identity; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + +namespace Abp.ZeroCore.SampleApp.Core +{ + public class UserManager : AbpUserManager + { + public UserManager( + RoleManager roleManager, + UserStore store, + IOptions optionsAccessor, + IPasswordHasher passwordHasher, + IEnumerable> userValidators, + IEnumerable> passwordValidators, + ILookupNormalizer keyNormalizer, + IdentityErrorDescriber errors, + IServiceProvider services, + ILogger logger, + IPermissionManager permissionManager, + IUnitOfWorkManager unitOfWorkManager, + ICacheManager cacheManager, + IRepository organizationUnitRepository, + IRepository userOrganizationUnitRepository, + IOrganizationUnitSettings organizationUnitSettings, + ISettingManager settingManager) : base( + roleManager, + store, + optionsAccessor, + passwordHasher, + userValidators, + passwordValidators, + keyNormalizer, + errors, + services, + logger, + permissionManager, + unitOfWorkManager, + cacheManager, + organizationUnitRepository, + userOrganizationUnitRepository, + organizationUnitSettings, + settingManager) + { + } + } + + public class TenantManager : AbpTenantManager + { + public TenantManager( + IRepository tenantRepository, + IRepository tenantFeatureRepository, + EditionManager editionManager, + IAbpZeroFeatureValueStore featureValueStore) : + base( + tenantRepository, + tenantFeatureRepository, + editionManager, + featureValueStore) + { + } + } + + public class EditionManager : AbpEditionManager + { + public const string DefaultEditionName = "Standard"; + + public EditionManager( + IRepository editionRepository, + IAbpZeroFeatureValueStore featureValueStore) + : base( + editionRepository, + featureValueStore) + { + } + } + + public class RoleManager : AbpRoleManager + { + public RoleManager( + RoleStore store, + IEnumerable> roleValidators, + ILookupNormalizer keyNormalizer, + IdentityErrorDescriber errors, + ILogger logger, + IHttpContextAccessor contextAccessor, + IPermissionManager permissionManager, + ICacheManager cacheManager, + IUnitOfWorkManager unitOfWorkManager, + IRoleManagementConfig roleManagementConfig + ) : base( + store, + roleValidators, + keyNormalizer, + errors, + logger, + contextAccessor, + permissionManager, + cacheManager, + unitOfWorkManager, + roleManagementConfig) + { + } + } + + public class LogInManager : AbpLogInManager + { + public LogInManager( + AbpUserManager userManager, + IMultiTenancyConfig multiTenancyConfig, + IRepository tenantRepository, + IUnitOfWorkManager unitOfWorkManager, + ISettingManager settingManager, + IRepository userLoginAttemptRepository, + IUserManagementConfig userManagementConfig, + IIocResolver iocResolver, + IPasswordHasher passwordHasher, + AbpRoleManager roleManager, + UserClaimsPrincipalFactory claimsPrincipalFactory + ) : base( + userManager, + multiTenancyConfig, + tenantRepository, + unitOfWorkManager, + settingManager, + userLoginAttemptRepository, + userManagementConfig, + iocResolver, + passwordHasher, + roleManager, + claimsPrincipalFactory) + { + } + } + + public class PermissionChecker : PermissionChecker + { + public PermissionChecker(UserManager userManager) + : base(userManager) + { + } + } + + public class FeatureValueStore : AbpFeatureValueStore + { + public FeatureValueStore(ICacheManager cacheManager, + IRepository tenantFeatureRepository, + IRepository tenantRepository, + IRepository editionFeatureRepository, + IFeatureManager featureManager, + IUnitOfWorkManager unitOfWorkManager) + : base( + cacheManager, + tenantFeatureRepository, + tenantRepository, + editionFeatureRepository, + featureManager, + unitOfWorkManager) + { + + } + } + + public class RoleStore : AbpRoleStore + { + public RoleStore( + IUnitOfWorkManager unitOfWorkManager, + IRepository roleRepository, + IRepository rolePermissionSettingRepository + ) : base( + unitOfWorkManager, + roleRepository, + rolePermissionSettingRepository) + { + } + } + + public class SecurityStampValidator : AbpSecurityStampValidator + { + public SecurityStampValidator( + IOptions options, + SignInManager signInManager) + : base(options, signInManager) + { + } + } + + public class SignInManager : AbpSignInManager + { + public SignInManager( + UserManager userManager, + IHttpContextAccessor contextAccessor, + UserClaimsPrincipalFactory claimsFactory, + IOptions optionsAccessor, + ILogger> logger, + IUnitOfWorkManager unitOfWorkManager, + ISettingManager settingManager + ) : base( + userManager, + contextAccessor, + claimsFactory, + optionsAccessor, + logger, + unitOfWorkManager, + settingManager) + { + } + } + + public class UserStore : AbpUserStore + { + public UserStore( + IUnitOfWorkManager unitOfWorkManager, + IRepository userRepository, + IRepository roleRepository, + IAsyncQueryableExecuter asyncQueryableExecuter, + IRepository userRoleRepository, + IRepository userLoginRepository, + IRepository userClaimRepository, + IRepository userPermissionSettingRepository + ) : base( + unitOfWorkManager, + userRepository, + roleRepository, + asyncQueryableExecuter, + userRoleRepository, + userLoginRepository, + userClaimRepository, + userPermissionSettingRepository) + { + } + } + + public class UserClaimsPrincipalFactory : AbpUserClaimsPrincipalFactory + { + public UserClaimsPrincipalFactory( + UserManager userManager, + RoleManager roleManager, + IOptions optionsAccessor) + : base( + userManager, + roleManager, + optionsAccessor) + { + } + + [UnitOfWork] + public override async Task CreateAsync(User user) + { + return await base.CreateAsync(user); + } + } +} diff --git a/test/Abp.ZeroCore.SampleApp/EntityFramework/AbpZeroTemplateDbContextConfigurer.cs b/test/Abp.ZeroCore.SampleApp/EntityFramework/AbpZeroTemplateDbContextConfigurer.cs new file mode 100644 index 00000000..78844dd0 --- /dev/null +++ b/test/Abp.ZeroCore.SampleApp/EntityFramework/AbpZeroTemplateDbContextConfigurer.cs @@ -0,0 +1,12 @@ +using Microsoft.EntityFrameworkCore; + +namespace Abp.ZeroCore.SampleApp.EntityFramework +{ + public static class AbpZeroTemplateDbContextConfigurer + { + public static void Configure(DbContextOptionsBuilder builder, string connectionString) + { + builder.UseSqlServer(connectionString); + } + } +} \ No newline at end of file diff --git a/test/Abp.ZeroCore.SampleApp/EntityFramework/SampleAppDbContext.cs b/test/Abp.ZeroCore.SampleApp/EntityFramework/SampleAppDbContext.cs new file mode 100644 index 00000000..9a5f40ca --- /dev/null +++ b/test/Abp.ZeroCore.SampleApp/EntityFramework/SampleAppDbContext.cs @@ -0,0 +1,25 @@ +using Abp.IdentityServer4; +using Abp.Zero.EntityFrameworkCore; +using Abp.ZeroCore.SampleApp.Core; +using Microsoft.EntityFrameworkCore; +using Abp.IdentityServer4; + +namespace Abp.ZeroCore.SampleApp.EntityFramework +{ + public class SampleAppDbContext : AbpZeroDbContext, IAbpPersistedGrantDbContext + { + public DbSet PersistedGrants { get; set; } + + public SampleAppDbContext(DbContextOptions options) + : base(options) + { + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + + modelBuilder.ConfigurePersistedGrantEntity(); + } + } +} diff --git a/test/Abp.ZeroCore.SampleApp/EntityFramework/Seed/Host/DefaultEditionCreator.cs b/test/Abp.ZeroCore.SampleApp/EntityFramework/Seed/Host/DefaultEditionCreator.cs new file mode 100644 index 00000000..abc5c15c --- /dev/null +++ b/test/Abp.ZeroCore.SampleApp/EntityFramework/Seed/Host/DefaultEditionCreator.cs @@ -0,0 +1,55 @@ +using System.Linq; +using Abp.Application.Editions; +using Abp.Application.Features; +using Abp.ZeroCore.SampleApp.Application; +using Abp.ZeroCore.SampleApp.Core; + +namespace Abp.ZeroCore.SampleApp.EntityFramework.Seed.Host +{ + public class DefaultEditionCreator + { + private readonly SampleAppDbContext _context; + + public DefaultEditionCreator(SampleAppDbContext context) + { + _context = context; + } + + public void Create() + { + CreateEditions(); + } + + private void CreateEditions() + { + var defaultEdition = _context.Editions.FirstOrDefault(e => e.Name == EditionManager.DefaultEditionName); + if (defaultEdition == null) + { + defaultEdition = new Edition { Name = EditionManager.DefaultEditionName, DisplayName = EditionManager.DefaultEditionName }; + _context.Editions.Add(defaultEdition); + _context.SaveChanges(); + } + + if (defaultEdition.Id > 0) + { + CreateFeatureIfNotExists(defaultEdition.Id, AppFeatures.SimpleBooleanFeature, true); + } + } + + private void CreateFeatureIfNotExists(int editionId, string featureName, bool isEnabled) + { + var defaultEditionChatFeature = _context.EditionFeatureSettings + .FirstOrDefault(ef => ef.EditionId == editionId && ef.Name == featureName); + + if (defaultEditionChatFeature == null) + { + _context.EditionFeatureSettings.Add(new EditionFeatureSetting + { + Name = featureName, + Value = isEnabled.ToString(), + EditionId = editionId + }); + } + } + } +} \ No newline at end of file diff --git a/test/Abp.ZeroCore.SampleApp/EntityFramework/Seed/Host/DefaultLanguagesCreator.cs b/test/Abp.ZeroCore.SampleApp/EntityFramework/Seed/Host/DefaultLanguagesCreator.cs new file mode 100644 index 00000000..a9db2f09 --- /dev/null +++ b/test/Abp.ZeroCore.SampleApp/EntityFramework/Seed/Host/DefaultLanguagesCreator.cs @@ -0,0 +1,52 @@ +using System.Collections.Generic; +using System.Linq; +using Abp.Localization; + +namespace Abp.ZeroCore.SampleApp.EntityFramework.Seed.Host +{ + public class DefaultLanguagesCreator + { + public static List InitialLanguages => GetInitialLanguages(); + + private readonly SampleAppDbContext _context; + + private static List GetInitialLanguages() + { + return new List + { + new ApplicationLanguage(null, "en", "English", "famfamfam-flags gb"), + new ApplicationLanguage(null, "tr", "Türkçe", "famfamfam-flags tr") + }; + } + + public DefaultLanguagesCreator(SampleAppDbContext context) + { + _context = context; + } + + public void Create() + { + CreateLanguages(); + } + + private void CreateLanguages() + { + foreach (var language in InitialLanguages) + { + AddLanguageIfNotExists(language); + } + } + + private void AddLanguageIfNotExists(ApplicationLanguage language) + { + if (_context.Languages.Any(l => l.TenantId == language.TenantId && l.Name == language.Name)) + { + return; + } + + _context.Languages.Add(language); + + _context.SaveChanges(); + } + } +} \ No newline at end of file diff --git a/test/Abp.ZeroCore.SampleApp/EntityFramework/Seed/Host/DefaultSettingsCreator.cs b/test/Abp.ZeroCore.SampleApp/EntityFramework/Seed/Host/DefaultSettingsCreator.cs new file mode 100644 index 00000000..ffd399f9 --- /dev/null +++ b/test/Abp.ZeroCore.SampleApp/EntityFramework/Seed/Host/DefaultSettingsCreator.cs @@ -0,0 +1,38 @@ +using System.Linq; +using Abp.Configuration; +using Abp.Localization; +using Abp.Net.Mail; + +namespace Abp.ZeroCore.SampleApp.EntityFramework.Seed.Host +{ + public class DefaultSettingsCreator + { + private readonly SampleAppDbContext _context; + + public DefaultSettingsCreator(SampleAppDbContext context) + { + _context = context; + } + + public void Create() + { + //Emailing + AddSettingIfNotExists(EmailSettingNames.DefaultFromAddress, "admin@mydomain.com"); + AddSettingIfNotExists(EmailSettingNames.DefaultFromDisplayName, "mydomain.com mailer"); + + //Languages + AddSettingIfNotExists(LocalizationSettingNames.DefaultLanguage, "en"); + } + + private void AddSettingIfNotExists(string name, string value, int? tenantId = null) + { + if (_context.Settings.Any(s => s.Name == name && s.TenantId == tenantId && s.UserId == null)) + { + return; + } + + _context.Settings.Add(new Setting(tenantId, null, name, value)); + _context.SaveChanges(); + } + } +} \ No newline at end of file diff --git a/test/Abp.ZeroCore.SampleApp/EntityFramework/Seed/Host/HostRoleAndUserCreator.cs b/test/Abp.ZeroCore.SampleApp/EntityFramework/Seed/Host/HostRoleAndUserCreator.cs new file mode 100644 index 00000000..d9b812f9 --- /dev/null +++ b/test/Abp.ZeroCore.SampleApp/EntityFramework/Seed/Host/HostRoleAndUserCreator.cs @@ -0,0 +1,95 @@ +using System.Linq; +using Abp.Authorization; +using Abp.Authorization.Roles; +using Abp.Authorization.Users; +using Abp.MultiTenancy; +using Abp.ZeroCore.SampleApp.Application; +using Abp.ZeroCore.SampleApp.Core; + +namespace Abp.ZeroCore.SampleApp.EntityFramework.Seed.Host +{ + public class HostRoleAndUserCreator + { + private readonly SampleAppDbContext _context; + + public HostRoleAndUserCreator(SampleAppDbContext context) + { + _context = context; + } + + public void Create() + { + CreateHostRoleAndUsers(); + } + + private void CreateHostRoleAndUsers() + { + //Admin role for host + + var adminRoleForHost = _context.Roles.FirstOrDefault(r => r.TenantId == null && r.Name == AppStaticRoleNames.Host.Admin); + if (adminRoleForHost == null) + { + adminRoleForHost = _context.Roles.Add(new Role(null, AppStaticRoleNames.Host.Admin, AppStaticRoleNames.Host.Admin) { IsStatic = true, IsDefault = true }).Entity; + _context.SaveChanges(); + } + + //admin user for host + + var adminUserForHost = _context.Users.FirstOrDefault(u => u.TenantId == null && u.UserName == AbpUserBase.AdminUserName); + if (adminUserForHost == null) + { + var user = new User + { + TenantId = null, + UserName = AbpUserBase.AdminUserName, + Name = "admin", + Surname = "admin", + EmailAddress = "admin@aspnetzero.com", + IsEmailConfirmed = true, + IsActive = true, + Password = "AM4OLBpptxBYmM79lGOX9egzZk3vIQU3d/gFCJzaBjAPXzYIK3tQ2N7X4fcrHtElTw==" //123qwe + }; + + user.SetNormalizedNames(); + + adminUserForHost = _context.Users.Add(user).Entity; + _context.SaveChanges(); + + //Assign Admin role to admin user + _context.UserRoles.Add(new UserRole(null, adminUserForHost.Id, adminRoleForHost.Id)); + _context.SaveChanges(); + + //Grant all permissions + var permissions = PermissionFinder + .GetAllPermissions(new AppAuthorizationProvider()) + .Where(p => p.MultiTenancySides.HasFlag(MultiTenancySides.Host)) + .ToList(); + + foreach (var permission in permissions) + { + _context.Permissions.Add( + new RolePermissionSetting + { + TenantId = null, + Name = permission.Name, + IsGranted = true, + RoleId = adminRoleForHost.Id + }); + } + + _context.SaveChanges(); + + //User account of admin user + _context.UserAccounts.Add(new UserAccount + { + TenantId = null, + UserId = adminUserForHost.Id, + UserName = AbpUserBase.AdminUserName, + EmailAddress = adminUserForHost.EmailAddress + }); + + _context.SaveChanges(); + } + } + } +} \ No newline at end of file diff --git a/test/Abp.ZeroCore.SampleApp/EntityFramework/Seed/Host/InitialHostDbBuilder.cs b/test/Abp.ZeroCore.SampleApp/EntityFramework/Seed/Host/InitialHostDbBuilder.cs new file mode 100644 index 00000000..4470054e --- /dev/null +++ b/test/Abp.ZeroCore.SampleApp/EntityFramework/Seed/Host/InitialHostDbBuilder.cs @@ -0,0 +1,22 @@ +namespace Abp.ZeroCore.SampleApp.EntityFramework.Seed.Host +{ + public class InitialHostDbBuilder + { + private readonly SampleAppDbContext _context; + + public InitialHostDbBuilder(SampleAppDbContext context) + { + _context = context; + } + + public void Create() + { + new DefaultEditionCreator(_context).Create(); + new DefaultLanguagesCreator(_context).Create(); + new HostRoleAndUserCreator(_context).Create(); + new DefaultSettingsCreator(_context).Create(); + + _context.SaveChanges(); + } + } +} diff --git a/test/Abp.ZeroCore.SampleApp/EntityFramework/Seed/SeedHelper.cs b/test/Abp.ZeroCore.SampleApp/EntityFramework/Seed/SeedHelper.cs new file mode 100644 index 00000000..1da93649 --- /dev/null +++ b/test/Abp.ZeroCore.SampleApp/EntityFramework/Seed/SeedHelper.cs @@ -0,0 +1,48 @@ +using System; +using System.Transactions; +using Abp.Dependency; +using Abp.Domain.Uow; +using Abp.EntityFrameworkCore.Uow; +using Abp.MultiTenancy; +using Abp.ZeroCore.SampleApp.EntityFramework.Seed.Host; +using Abp.ZeroCore.SampleApp.EntityFramework.Seed.Tenants; +using Microsoft.EntityFrameworkCore; + +namespace Abp.ZeroCore.SampleApp.EntityFramework.Seed +{ + public static class SeedHelper + { + public static void SeedHostDb(IIocResolver iocResolver) + { + WithDbContext(iocResolver, SeedHostDb); + } + + public static void SeedHostDb(SampleAppDbContext context) + { + context.SuppressAutoSetTenantId = true; + + //Host seed + new InitialHostDbBuilder(context).Create(); + + //Default tenant seed (in host database). + new DefaultTenantBuilder(context).Create(); + new TenantRoleAndUserBuilder(context, 1).Create(); + } + + private static void WithDbContext(IIocResolver iocResolver, Action contextAction) + where TDbContext : DbContext + { + using (var uowManager = iocResolver.ResolveAsDisposable()) + { + using (var uow = uowManager.Object.Begin(TransactionScopeOption.Suppress)) + { + var context = uowManager.Object.Current.GetDbContext(MultiTenancySides.Host); + + contextAction(context); + + uow.Complete(); + } + } + } + } +} diff --git a/test/Abp.ZeroCore.SampleApp/EntityFramework/Seed/Tenants/DefaultTenantBuilder.cs b/test/Abp.ZeroCore.SampleApp/EntityFramework/Seed/Tenants/DefaultTenantBuilder.cs new file mode 100644 index 00000000..608e24b2 --- /dev/null +++ b/test/Abp.ZeroCore.SampleApp/EntityFramework/Seed/Tenants/DefaultTenantBuilder.cs @@ -0,0 +1,40 @@ +using System.Linq; +using Abp.ZeroCore.SampleApp.Core; + +namespace Abp.ZeroCore.SampleApp.EntityFramework.Seed.Tenants +{ + public class DefaultTenantBuilder + { + private readonly SampleAppDbContext _context; + + public DefaultTenantBuilder(SampleAppDbContext context) + { + _context = context; + } + + public void Create() + { + CreateDefaultTenant(); + } + + private void CreateDefaultTenant() + { + //Default tenant + + var defaultTenant = _context.Tenants.FirstOrDefault(t => t.TenancyName == Tenant.DefaultTenantName); + if (defaultTenant == null) + { + defaultTenant = new Tenant(Tenant.DefaultTenantName, Tenant.DefaultTenantName); + + var defaultEdition = _context.Editions.FirstOrDefault(e => e.Name == EditionManager.DefaultEditionName); + if (defaultEdition != null) + { + defaultTenant.EditionId = defaultEdition.Id; + } + + _context.Tenants.Add(defaultTenant); + _context.SaveChanges(); + } + } + } +} diff --git a/test/Abp.ZeroCore.SampleApp/EntityFramework/Seed/Tenants/TenantRoleAndUserBuilder.cs b/test/Abp.ZeroCore.SampleApp/EntityFramework/Seed/Tenants/TenantRoleAndUserBuilder.cs new file mode 100644 index 00000000..6a25116a --- /dev/null +++ b/test/Abp.ZeroCore.SampleApp/EntityFramework/Seed/Tenants/TenantRoleAndUserBuilder.cs @@ -0,0 +1,101 @@ +using System.Linq; +using Abp.Authorization; +using Abp.Authorization.Roles; +using Abp.Authorization.Users; +using Abp.MultiTenancy; +using Abp.ZeroCore.SampleApp.Application; +using Abp.ZeroCore.SampleApp.Core; +using Microsoft.AspNetCore.Identity; +using Microsoft.Extensions.Options; + +namespace Abp.ZeroCore.SampleApp.EntityFramework.Seed.Tenants +{ + public class TenantRoleAndUserBuilder + { + private readonly SampleAppDbContext _context; + private readonly int _tenantId; + + public TenantRoleAndUserBuilder(SampleAppDbContext context, int tenantId) + { + _context = context; + _tenantId = tenantId; + } + + public void Create() + { + CreateRolesAndUsers(); + } + + private void CreateRolesAndUsers() + { + //Admin role + + var adminRole = _context.Roles.FirstOrDefault(r => r.TenantId == _tenantId && r.Name == AppStaticRoleNames.Tenants.Admin); + if (adminRole == null) + { + adminRole = _context.Roles.Add(new Role(_tenantId, AppStaticRoleNames.Tenants.Admin, AppStaticRoleNames.Tenants.Admin) { IsStatic = true }).Entity; + _context.SaveChanges(); + + //Grant all permissions to admin role + var permissions = PermissionFinder + .GetAllPermissions(new AppAuthorizationProvider()) + .Where(p => p.MultiTenancySides.HasFlag(MultiTenancySides.Tenant)) + .ToList(); + + foreach (var permission in permissions) + { + _context.Permissions.Add( + new RolePermissionSetting + { + TenantId = _tenantId, + Name = permission.Name, + IsGranted = true, + RoleId = adminRole.Id + }); + } + + _context.SaveChanges(); + } + + //User role + + var userRole = _context.Roles.FirstOrDefault(r => r.TenantId == _tenantId && r.Name == AppStaticRoleNames.Tenants.User); + if (userRole == null) + { + _context.Roles.Add(new Role(_tenantId, AppStaticRoleNames.Tenants.User, AppStaticRoleNames.Tenants.User) { IsStatic = true, IsDefault = true }); + _context.SaveChanges(); + } + + //admin user + + var adminUser = _context.Users.FirstOrDefault(u => u.TenantId == _tenantId && u.UserName == AbpUserBase.AdminUserName); + if (adminUser == null) + { + adminUser = User.CreateTenantAdminUser(_tenantId, "admin@defaulttenant.com"); + adminUser.Password = new PasswordHasher(new OptionsWrapper(new PasswordHasherOptions())).HashPassword(adminUser, "123qwe"); + adminUser.IsEmailConfirmed = true; + adminUser.IsActive = true; + + _context.Users.Add(adminUser); + _context.SaveChanges(); + + //Assign Admin role to admin user + _context.UserRoles.Add(new UserRole(_tenantId, adminUser.Id, adminRole.Id)); + _context.SaveChanges(); + + //User account of admin user + if (_tenantId == 1) + { + _context.UserAccounts.Add(new UserAccount + { + TenantId = _tenantId, + UserId = adminUser.Id, + UserName = AbpUserBase.AdminUserName, + EmailAddress = adminUser.EmailAddress + }); + _context.SaveChanges(); + } + } + } + } +} diff --git a/test/Abp.ZeroCore.Tests/Abp.ZeroCore.Tests.csproj b/test/Abp.ZeroCore.Tests/Abp.ZeroCore.Tests.csproj new file mode 100644 index 00000000..28c4f67d --- /dev/null +++ b/test/Abp.ZeroCore.Tests/Abp.ZeroCore.Tests.csproj @@ -0,0 +1,28 @@ + + + + netcoreapp1.0 + Abp + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Abp.ZeroCore.Tests/Zero/AbpZeroTestBase.cs b/test/Abp.ZeroCore.Tests/Zero/AbpZeroTestBase.cs new file mode 100644 index 00000000..263034a8 --- /dev/null +++ b/test/Abp.ZeroCore.Tests/Zero/AbpZeroTestBase.cs @@ -0,0 +1,306 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Abp.Authorization.Users; +using Abp.EntityFrameworkCore.Extensions; +using Abp.Events.Bus; +using Abp.Events.Bus.Entities; +using Abp.Runtime.Session; +using Abp.TestBase; +using Abp.Zero.TestData; +using Abp.ZeroCore.SampleApp.Core; +using Abp.ZeroCore.SampleApp.EntityFramework; +using Microsoft.EntityFrameworkCore; + +namespace Abp.Zero +{ + public abstract class AbpZeroTestBase : AbpIntegratedTestBase + { + protected AbpZeroTestBase() + { + SeedTestData(); + LoginAsDefaultTenantAdmin(); + } + + private void SeedTestData() + { + void NormalizeDbContext(SampleAppDbContext context) + { + context.EntityChangeEventHelper = NullEntityChangeEventHelper.Instance; + context.EventBus = NullEventBus.Instance; + context.SuppressAutoSetTenantId = true; + } + + //Seed initial data for default tenant + AbpSession.TenantId = 1; + UsingDbContext(context => + { + NormalizeDbContext(context); + new TestDataBuilder(context, 1).Create(); + }); + } + + protected IDisposable UsingTenantId(int? tenantId) + { + var previousTenantId = AbpSession.TenantId; + AbpSession.TenantId = tenantId; + return new DisposeAction(() => AbpSession.TenantId = previousTenantId); + } + + protected void UsingDbContext(Action action) + { + UsingDbContext(AbpSession.TenantId, action); + } + + protected Task UsingDbContextAsync(Func action) + { + return UsingDbContextAsync(AbpSession.TenantId, action); + } + + protected T UsingDbContext(Func func) + { + return UsingDbContext(AbpSession.TenantId, func); + } + + protected Task UsingDbContextAsync(Func> func) + { + return UsingDbContextAsync(AbpSession.TenantId, func); + } + + protected void UsingDbContext(int? tenantId, Action action) + { + using (UsingTenantId(tenantId)) + { + using (var context = LocalIocManager.Resolve()) + { + action(context); + context.SaveChanges(); + } + } + } + + protected async Task UsingDbContextAsync(int? tenantId, Func action) + { + using (UsingTenantId(tenantId)) + { + using (var context = LocalIocManager.Resolve()) + { + await action(context); + await context.SaveChangesAsync(); + } + } + } + + protected T UsingDbContext(int? tenantId, Func func) + { + T result; + + using (UsingTenantId(tenantId)) + { + using (var context = LocalIocManager.Resolve()) + { + result = func(context); + context.SaveChanges(); + } + } + + return result; + } + + protected async Task UsingDbContextAsync(int? tenantId, Func> func) + { + T result; + + using (UsingTenantId(tenantId)) + { + using (var context = LocalIocManager.Resolve()) + { + result = await func(context); + await context.SaveChangesAsync(); + } + } + + return result; + } + + #region Login + + protected void LoginAsHostAdmin() + { + LoginAsHost(AbpUserBase.AdminUserName); + } + + protected void LoginAsDefaultTenantAdmin() + { + LoginAsTenant(Tenant.DefaultTenantName, AbpUserBase.AdminUserName); + } + + protected void LoginAsHost(string userName) + { + AbpSession.TenantId = null; + + var user = UsingDbContext(context => context.Users.FirstOrDefault(u => u.TenantId == AbpSession.TenantId && u.UserName == userName)); + if (user == null) + { + throw new Exception("There is no user: " + userName + " for host."); + } + + AbpSession.UserId = user.Id; + } + + protected void LoginAsTenant(string tenancyName, string userName) + { + AbpSession.TenantId = null; + + var tenant = UsingDbContext(context => context.Tenants.FirstOrDefault(t => t.TenancyName == tenancyName)); + if (tenant == null) + { + throw new Exception("There is no tenant: " + tenancyName); + } + + AbpSession.TenantId = tenant.Id; + + var user = UsingDbContext(context => context.Users.FirstOrDefault(u => u.TenantId == AbpSession.TenantId && u.UserName == userName)); + if (user == null) + { + throw new Exception("There is no user: " + userName + " for tenant: " + tenancyName); + } + + AbpSession.UserId = user.Id; + } + + #endregion + + #region GetCurrentUser + + /// + /// Gets current user if is not null. + /// Throws exception if it's null. + /// + protected User GetCurrentUser() + { + var userId = AbpSession.GetUserId(); + return UsingDbContext(context => context.Users.Single(u => u.Id == userId)); + } + + /// + /// Gets current user if is not null. + /// Throws exception if it's null. + /// + protected async Task GetCurrentUserAsync() + { + var userId = AbpSession.GetUserId(); + return await UsingDbContext(context => context.Users.SingleAsync(u => u.Id == userId)); + } + + #endregion + + #region GetCurrentTenant + + /// + /// Gets current tenant if is not null. + /// Throws exception if there is no current tenant. + /// + protected Tenant GetCurrentTenant() + { + var tenantId = AbpSession.GetTenantId(); + return UsingDbContext(null, context => context.Tenants.Single(t => t.Id == tenantId)); + } + + /// + /// Gets current tenant if is not null. + /// Throws exception if there is no current tenant. + /// + protected async Task GetCurrentTenantAsync() + { + var tenantId = AbpSession.GetTenantId(); + return await UsingDbContext(null, context => context.Tenants.SingleAsync(t => t.Id == tenantId)); + } + + #endregion + + #region GetTenant / GetTenantOrNull + + protected Tenant GetTenant(string tenancyName) + { + return UsingDbContext(null, context => context.Tenants.Single(t => t.TenancyName == tenancyName)); + } + + protected async Task GetTenantAsync(string tenancyName) + { + return await UsingDbContext(null, async context => await context.Tenants.SingleAsync(t => t.TenancyName == tenancyName)); + } + + protected Tenant GetTenantOrNull(string tenancyName) + { + return UsingDbContext(null, context => context.Tenants.FirstOrDefault(t => t.TenancyName == tenancyName)); + } + + protected async Task GetTenantOrNullAsync(string tenancyName) + { + return await UsingDbContext(null, async context => await context.Tenants.FirstOrDefaultAsync(t => t.TenancyName == tenancyName)); + } + + #endregion + + #region GetRole + + protected Role GetRole(string roleName) + { + return UsingDbContext(context => context.Roles.Single(r => r.Name == roleName && r.TenantId == AbpSession.TenantId)); + } + + protected async Task GetRoleAsync(string roleName) + { + return await UsingDbContext(async context => await context.Roles.SingleAsync(r => r.Name == roleName && r.TenantId == AbpSession.TenantId)); + } + + #endregion + + #region GetUserByUserName + + protected User GetUserByUserName(string userName) + { + var user = GetUserByUserNameOrNull(userName); + if (user == null) + { + throw new Exception("Can not find a user with username: " + userName); + } + + return user; + } + + protected async Task GetUserByUserNameAsync(string userName) + { + var user = await GetUserByUserNameOrNullAsync(userName); + if (user == null) + { + throw new Exception("Can not find a user with username: " + userName); + } + + return user; + } + + protected User GetUserByUserNameOrNull(string userName) + { + return UsingDbContext(context => + context.Users.FirstOrDefault(u => + u.UserName == userName && + u.TenantId == AbpSession.TenantId + )); + } + + protected async Task GetUserByUserNameOrNullAsync(string userName, bool includeRoles = false) + { + return await UsingDbContextAsync(async context => + await context.Users + .IncludeIf(includeRoles, u => u.Roles) + .FirstOrDefaultAsync(u => + u.UserName == userName && + u.TenantId == AbpSession.TenantId + )); + } + + #endregion + } +} diff --git a/test/Abp.ZeroCore.Tests/Zero/AbpZeroTestModule.cs b/test/Abp.ZeroCore.Tests/Zero/AbpZeroTestModule.cs new file mode 100644 index 00000000..dba9bf5e --- /dev/null +++ b/test/Abp.ZeroCore.Tests/Zero/AbpZeroTestModule.cs @@ -0,0 +1,33 @@ +using System; +using Abp.AutoMapper; +using Abp.Modules; +using Abp.Reflection.Extensions; +using Abp.TestBase; +using Abp.Zero.Configuration; +using Abp.ZeroCore.SampleApp; + +namespace Abp.Zero +{ + [DependsOn(typeof(AbpZeroCoreSampleAppModule), typeof(AbpTestBaseModule))] + public class AbpZeroTestModule : AbpModule + { + public AbpZeroTestModule(AbpZeroCoreSampleAppModule sampleAppModule) + { + sampleAppModule.SkipDbContextRegistration = true; + } + + public override void PreInitialize() + { + Configuration.Modules.AbpAutoMapper().UseStaticMapper = false; + Configuration.BackgroundJobs.IsJobExecutionEnabled = false; + Configuration.Modules.Zero().LanguageManagement.EnableDbLocalization(); + Configuration.UnitOfWork.IsTransactional = false; + } + + public override void Initialize() + { + IocManager.RegisterAssemblyByConvention(typeof(AbpZeroTestModule).GetAssembly()); + TestServiceCollectionRegistrar.Register(IocManager); + } + } +} diff --git a/test/Abp.ZeroCore.Tests/Zero/DependencyInjection_Tests.cs b/test/Abp.ZeroCore.Tests/Zero/DependencyInjection_Tests.cs new file mode 100644 index 00000000..46361a59 --- /dev/null +++ b/test/Abp.ZeroCore.Tests/Zero/DependencyInjection_Tests.cs @@ -0,0 +1,111 @@ +using Abp.Application.Editions; +using Abp.Application.Features; +using Abp.Authorization; +using Abp.Authorization.Roles; +using Abp.Authorization.Users; +using Abp.MultiTenancy; +using Abp.ZeroCore.SampleApp.Core; +using Microsoft.AspNetCore.Identity; +using Xunit; + +using SecurityStampValidator = Abp.ZeroCore.SampleApp.Core.SecurityStampValidator; + +namespace Abp.Zero +{ + public class DependencyInjection_Tests : AbpZeroTestBase + { + [Fact] + public void Should_Resolve_UserManager() + { + LocalIocManager.Resolve>(); + LocalIocManager.Resolve>(); + LocalIocManager.Resolve(); + } + + [Fact] + public void Should_Resolve_RoleManager() + { + LocalIocManager.Resolve>(); + LocalIocManager.Resolve>(); + LocalIocManager.Resolve(); + } + + [Fact] + public void Should_Resolve_SignInManager() + { + LocalIocManager.Resolve>(); + LocalIocManager.Resolve>(); + LocalIocManager.Resolve(); + } + + [Fact] + public void Should_Resolve_LoginManager() + { + LocalIocManager.Resolve>(); + LocalIocManager.Resolve(); + } + + [Fact] + public void Should_Resolve_SecurityStampValidator() + { + LocalIocManager.Resolve>(); + LocalIocManager.Resolve>(); + LocalIocManager.Resolve(); + } + + [Fact] + public void Should_Resolve_UserClaimsPrincipalFactory() + { + LocalIocManager.Resolve>(); + LocalIocManager.Resolve>(); + LocalIocManager.Resolve>(); + LocalIocManager.Resolve(); + } + + [Fact] + public void Should_Resolve_TenantManager() + { + LocalIocManager.Resolve>(); + LocalIocManager.Resolve(); + } + + [Fact] + public void Should_Resolve_EditionManager() + { + LocalIocManager.Resolve(); + LocalIocManager.Resolve(); + } + + [Fact] + public void Should_Resolve_PermissionChecker() + { + LocalIocManager.Resolve(); + LocalIocManager.Resolve>(); + LocalIocManager.Resolve(); + } + + [Fact] + public void Should_Resolve_FeatureValueStore() + { + LocalIocManager.Resolve(); + LocalIocManager.Resolve>(); + LocalIocManager.Resolve(); + } + + [Fact] + public void Should_Resolve_UserStore() + { + LocalIocManager.Resolve>(); + LocalIocManager.Resolve>(); + LocalIocManager.Resolve(); + } + + [Fact] + public void Should_Resolve_RoleStore() + { + LocalIocManager.Resolve>(); + LocalIocManager.Resolve>(); + LocalIocManager.Resolve(); + } + } +} diff --git a/test/Abp.ZeroCore.Tests/Zero/Roles/RoleStore_Tests.cs b/test/Abp.ZeroCore.Tests/Zero/Roles/RoleStore_Tests.cs new file mode 100644 index 00000000..862d7b34 --- /dev/null +++ b/test/Abp.ZeroCore.Tests/Zero/Roles/RoleStore_Tests.cs @@ -0,0 +1,34 @@ +using System.Threading.Tasks; +using Abp.Domain.Uow; +using Abp.ZeroCore.SampleApp.Core; +using Shouldly; +using Xunit; + +namespace Abp.Zero.Roles +{ + public class RoleStore_Tests : AbpZeroTestBase + { + private readonly RoleStore _roleStore; + + public RoleStore_Tests() + { + _roleStore = Resolve(); + } + + [Fact] + public async Task Should_Get_Role_Claims() + { + using (var uow = Resolve().Begin()) + { + var role = await _roleStore.FindByNameAsync("ADMIN"); + role.ShouldNotBeNull(); + + var claims = await _roleStore.GetClaimsAsync(role); + + claims.ShouldNotBeNull(); + + await uow.CompleteAsync(); + } + } + } +} diff --git a/test/Abp.ZeroCore.Tests/Zero/TestData/TestDataBuilder.cs b/test/Abp.ZeroCore.Tests/Zero/TestData/TestDataBuilder.cs new file mode 100644 index 00000000..64c7516c --- /dev/null +++ b/test/Abp.ZeroCore.Tests/Zero/TestData/TestDataBuilder.cs @@ -0,0 +1,24 @@ +using Abp.ZeroCore.SampleApp.EntityFramework; + +namespace Abp.Zero.TestData +{ + public class TestDataBuilder + { + private readonly SampleAppDbContext _context; + private readonly int _tenantId; + + public TestDataBuilder(SampleAppDbContext context, int tenantId) + { + _context = context; + _tenantId = tenantId; + } + + public void Create() + { + new TestRolesBuilder(_context, _tenantId).Create(); + new TestOrganizationUnitsBuilder(_context, _tenantId).Create(); + + _context.SaveChanges(); + } + } +} diff --git a/test/Abp.ZeroCore.Tests/Zero/TestData/TestOrganizationUnitsBuilder.cs b/test/Abp.ZeroCore.Tests/Zero/TestData/TestOrganizationUnitsBuilder.cs new file mode 100644 index 00000000..ab565a1f --- /dev/null +++ b/test/Abp.ZeroCore.Tests/Zero/TestData/TestOrganizationUnitsBuilder.cs @@ -0,0 +1,50 @@ +using Abp.Organizations; +using Abp.ZeroCore.SampleApp.EntityFramework; + +namespace Abp.Zero.TestData +{ + /* Creates OU tree for default tenant as shown below: + * + * - OU1 + * - OU11 + * - OU111 + * - OU112 + * - OU12 + * - OU2 + * - OU21 + */ + public class TestOrganizationUnitsBuilder + { + private readonly SampleAppDbContext _context; + private readonly int _tenantId; + + public TestOrganizationUnitsBuilder(SampleAppDbContext context, int tenantId) + { + _context = context; + _tenantId = tenantId; + } + + public void Create() + { + CreateOUs(); + } + + private void CreateOUs() + { + var ou1 = CreateOU("OU1", OrganizationUnit.CreateCode(1)); + var ou11 = CreateOU("OU11", OrganizationUnit.CreateCode(1, 1), ou1.Id); + var ou111 = CreateOU("OU111", OrganizationUnit.CreateCode(1, 1, 1), ou11.Id); + var ou112 = CreateOU("OU112", OrganizationUnit.CreateCode(1, 1, 2), ou11.Id); + var ou12 = CreateOU("OU12", OrganizationUnit.CreateCode(1, 2), ou1.Id); + var ou2 = CreateOU("OU2", OrganizationUnit.CreateCode(2)); + var ou21 = CreateOU("OU21", OrganizationUnit.CreateCode(2, 1), ou2.Id); + } + + private OrganizationUnit CreateOU(string displayName, string code, long? parentId = null) + { + var ou = _context.OrganizationUnits.Add(new OrganizationUnit(_tenantId, displayName, parentId) { Code = code }).Entity; + _context.SaveChanges(); + return ou; + } + } +} diff --git a/test/Abp.ZeroCore.Tests/Zero/TestData/TestRolesBuilder.cs b/test/Abp.ZeroCore.Tests/Zero/TestData/TestRolesBuilder.cs new file mode 100644 index 00000000..37ea01e8 --- /dev/null +++ b/test/Abp.ZeroCore.Tests/Zero/TestData/TestRolesBuilder.cs @@ -0,0 +1,35 @@ +using System.Collections.Generic; +using System.Security.Claims; +using Abp.Authorization.Roles; +using Abp.ZeroCore.SampleApp.Core; +using Abp.ZeroCore.SampleApp.EntityFramework; + +namespace Abp.Zero.TestData +{ + public class TestRolesBuilder + { + private readonly SampleAppDbContext _context; + private readonly int _tenantId; + + public TestRolesBuilder(SampleAppDbContext context, int tenantId) + { + _context = context; + _tenantId = tenantId; + } + + public void Create() + { + var role = new Role(_tenantId, "ADMIN", "ADMIN") + { + Claims = new List() + }; + + role.Claims.Add(new RoleClaim(role, new Claim("MyClaim1", "MyClaim1Value"))); + role.Claims.Add(new RoleClaim(role, new Claim("MyClaim2", "MyClaim2Value"))); + + _context.Roles.Add(role); + + _context.SaveChanges(); + } + } +} \ No newline at end of file diff --git a/test/Abp.ZeroCore.Tests/Zero/TestServiceCollectionRegistrar.cs b/test/Abp.ZeroCore.Tests/Zero/TestServiceCollectionRegistrar.cs new file mode 100644 index 00000000..1c7bf18f --- /dev/null +++ b/test/Abp.ZeroCore.Tests/Zero/TestServiceCollectionRegistrar.cs @@ -0,0 +1,45 @@ +using Abp.Dependency; +using Abp.ZeroCore.SampleApp.Core; +using Abp.ZeroCore.SampleApp.EntityFramework; +using Castle.MicroKernel.Registration; +using Castle.Windsor.MsDependencyInjection; +using Microsoft.Data.Sqlite; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; + +namespace Abp.Zero +{ + public static class TestServiceCollectionRegistrar + { + public static void Register(IIocManager iocManager) + { + RegisterServiceCollectionServices(iocManager); + RegisterSqliteInMemoryDb(iocManager); + } + + private static void RegisterServiceCollectionServices(IIocManager iocManager) + { + var services = new ServiceCollection(); + ServicesCollectionDependencyRegistrar.Register(services); + WindsorRegistrationHelper.CreateServiceProvider(iocManager.IocContainer, services); + } + + private static void RegisterSqliteInMemoryDb(IIocManager iocManager) + { + var builder = new DbContextOptionsBuilder(); + + var inMemorySqlite = new SqliteConnection("Data Source=:memory:"); + builder.UseSqlite(inMemorySqlite); + + iocManager.IocContainer.Register( + Component + .For>() + .Instance(builder.Options) + .LifestyleSingleton() + ); + + inMemorySqlite.Open(); + new SampleAppDbContext(builder.Options).Database.EnsureCreated(); + } + } +} diff --git a/test/Abp.ZeroCore.Tests/Zero/Users/UserAppService_Tests.cs b/test/Abp.ZeroCore.Tests/Zero/Users/UserAppService_Tests.cs new file mode 100644 index 00000000..f24815d4 --- /dev/null +++ b/test/Abp.ZeroCore.Tests/Zero/Users/UserAppService_Tests.cs @@ -0,0 +1,26 @@ +using System.Threading.Tasks; +using Abp.Application.Services.Dto; +using Abp.ZeroCore.SampleApp.Application.Users; +using Shouldly; +using Xunit; + +namespace Abp.Zero.Users +{ + public class UserAppService_Tests: AbpZeroTestBase + { + private readonly IUserAppService _userAppService; + + public UserAppService_Tests() + { + _userAppService = Resolve(); + } + + [Fact] + public async Task Should_Get_All_Users() + { + var users = await _userAppService.GetAll(new PagedAndSortedResultRequestDto()); + users.TotalCount.ShouldBeGreaterThan(0); + users.Items.Count.ShouldBeGreaterThan(0); + } + } +}