1
0
mirror of https://github.com/bitwarden/server.git synced 2024-11-21 12:05:42 +01:00

[BEEEP] Integration tests (#1945)

* Add api integration tests

* Add some stuff

* Make program mockable

* Work on IntegrationTests for Identity

* Formatting

* Update packages.lock.json

* Update more packages.lock.json

* Update all packages.lock.json

* Fix InMemory configuration

* Actually fix test configuration

* Fix tests for CI

* Fix event service

* Force EF EventRepository

* Add client_credentials test

* Remove Api.IntegrationTest

* Remove Api Program changes

* Cleanup

* Add more Auth-Email tests

* Run formatting

* Address some PR feedback

* Move integration stuff to it's own common project

* Ran linter

* Add shared project to test solution

* Remove sln changes

* Clean usings

* Add more coverage

* Address PR feedback
This commit is contained in:
Justin Baur 2022-05-20 15:24:59 -04:00 committed by GitHub
parent 98546a65ea
commit 719abc7e61
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 8706 additions and 161 deletions

View File

@ -86,6 +86,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing.Test", "test\Billin
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Identity.Test", "test\Identity.Test\Identity.Test.csproj", "{310A1D8E-2D3F-4FA0-84D4-FFE31FCE193E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Identity.IntegrationTest", "test\Identity.IntegrationTest\Identity.IntegrationTest.csproj", "{0D3B2BD2-53F3-421D-AD8F-C19B954C796B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IntegrationTestCommon", "test\IntegrationTestCommon\IntegrationTestCommon.csproj", "{0923DE59-5FB1-44F2-9302-A09D2236B470}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -162,14 +166,6 @@ Global
{C7BA2255-C1B1-4789-8BB9-C27540DA6FB8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C7BA2255-C1B1-4789-8BB9-C27540DA6FB8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C7BA2255-C1B1-4789-8BB9-C27540DA6FB8}.Release|Any CPU.Build.0 = Release|Any CPU
{BDC1D592-5947-47ED-9903-7CDBB12A50C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BDC1D592-5947-47ED-9903-7CDBB12A50C8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BDC1D592-5947-47ED-9903-7CDBB12A50C8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BDC1D592-5947-47ED-9903-7CDBB12A50C8}.Release|Any CPU.Build.0 = Release|Any CPU
{F72E0229-2EF7-49B3-9004-FF4C0043816E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F72E0229-2EF7-49B3-9004-FF4C0043816E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F72E0229-2EF7-49B3-9004-FF4C0043816E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F72E0229-2EF7-49B3-9004-FF4C0043816E}.Release|Any CPU.Build.0 = Release|Any CPU
{EDC0D688-D58C-4CE1-AA07-3606AC6874B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EDC0D688-D58C-4CE1-AA07-3606AC6874B8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EDC0D688-D58C-4CE1-AA07-3606AC6874B8}.Release|Any CPU.ActiveCfg = Release|Any CPU
@ -178,6 +174,14 @@ Global
{0E99A21B-684B-4C59-9831-90F775CAB6F7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0E99A21B-684B-4C59-9831-90F775CAB6F7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0E99A21B-684B-4C59-9831-90F775CAB6F7}.Release|Any CPU.Build.0 = Release|Any CPU
{BDC1D592-5947-47ED-9903-7CDBB12A50C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BDC1D592-5947-47ED-9903-7CDBB12A50C8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BDC1D592-5947-47ED-9903-7CDBB12A50C8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BDC1D592-5947-47ED-9903-7CDBB12A50C8}.Release|Any CPU.Build.0 = Release|Any CPU
{F72E0229-2EF7-49B3-9004-FF4C0043816E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F72E0229-2EF7-49B3-9004-FF4C0043816E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F72E0229-2EF7-49B3-9004-FF4C0043816E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F72E0229-2EF7-49B3-9004-FF4C0043816E}.Release|Any CPU.Build.0 = Release|Any CPU
{17DA09D7-0212-4009-879E-6B9CFDE5FA60}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{17DA09D7-0212-4009-879E-6B9CFDE5FA60}.Debug|Any CPU.Build.0 = Debug|Any CPU
{17DA09D7-0212-4009-879E-6B9CFDE5FA60}.Release|Any CPU.ActiveCfg = Release|Any CPU
@ -202,6 +206,14 @@ Global
{310A1D8E-2D3F-4FA0-84D4-FFE31FCE193E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{310A1D8E-2D3F-4FA0-84D4-FFE31FCE193E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{310A1D8E-2D3F-4FA0-84D4-FFE31FCE193E}.Release|Any CPU.Build.0 = Release|Any CPU
{0D3B2BD2-53F3-421D-AD8F-C19B954C796B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0D3B2BD2-53F3-421D-AD8F-C19B954C796B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0D3B2BD2-53F3-421D-AD8F-C19B954C796B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0D3B2BD2-53F3-421D-AD8F-C19B954C796B}.Release|Any CPU.Build.0 = Release|Any CPU
{0923DE59-5FB1-44F2-9302-A09D2236B470}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0923DE59-5FB1-44F2-9302-A09D2236B470}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0923DE59-5FB1-44F2-9302-A09D2236B470}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0923DE59-5FB1-44F2-9302-A09D2236B470}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -224,16 +236,18 @@ Global
{860DE301-0B3E-4717-9C21-A9B4C3C2B121} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84F}
{4866AF64-6640-4C65-A662-A31E02FF9064} = {4FDB6543-F68B-4202-9EA6-7FEA984D2D0A}
{C7BA2255-C1B1-4789-8BB9-C27540DA6FB8} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84F}
{BDC1D592-5947-47ED-9903-7CDBB12A50C8} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84E}
{F72E0229-2EF7-49B3-9004-FF4C0043816E} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84E}
{EDC0D688-D58C-4CE1-AA07-3606AC6874B8} = {4FDB6543-F68B-4202-9EA6-7FEA984D2D0A}
{0E99A21B-684B-4C59-9831-90F775CAB6F7} = {287CFF34-BBDB-4BC4-AF88-1E19A5A4679B}
{BDC1D592-5947-47ED-9903-7CDBB12A50C8} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84E}
{F72E0229-2EF7-49B3-9004-FF4C0043816E} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84E}
{17DA09D7-0212-4009-879E-6B9CFDE5FA60} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84F}
{AD933445-27CE-4D30-A6ED-9065309464AD} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84D}
{713D44C0-1BC1-4024-96A3-A98A49F33908} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84D}
{ED880735-0250-43C7-9662-FDC7C7416E7F} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84D}
{B8639B10-2157-44BC-8CE1-D9EB4B50971F} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84F}
{310A1D8E-2D3F-4FA0-84D4-FFE31FCE193E} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84F}
{0D3B2BD2-53F3-421D-AD8F-C19B954C796B} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84F}
{0923DE59-5FB1-44F2-9302-A09D2236B470} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84F}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {E01CBF68-2E20-425F-9EDB-E0A6510CA92F}

View File

@ -3392,7 +3392,7 @@
"infrastructure.dapper": {
"type": "Project",
"dependencies": {
"Core": "1.47.1",
"Core": "1.48.1",
"Dapper": "2.0.123",
"System.Data.SqlClient": "4.8.3"
}
@ -3401,7 +3401,7 @@
"type": "Project",
"dependencies": {
"AutoMapper.Extensions.Microsoft.DependencyInjection": "8.0.1",
"Core": "1.47.1",
"Core": "1.48.1",
"Microsoft.EntityFrameworkCore.Relational": "5.0.12",
"Npgsql.EntityFrameworkCore.PostgreSQL": "5.0.2",
"Pomelo.EntityFrameworkCore.MySql": "5.0.3",
@ -3411,9 +3411,9 @@
"sharedweb": {
"type": "Project",
"dependencies": {
"Core": "1.47.1",
"Infrastructure.Dapper": "1.47.1",
"Infrastructure.EntityFramework": "1.47.1"
"Core": "1.48.1",
"Infrastructure.Dapper": "1.48.1",
"Infrastructure.EntityFramework": "1.48.1"
}
}
}

View File

@ -3541,26 +3541,26 @@
"type": "Project",
"dependencies": {
"Azure.Messaging.EventGrid": "4.7.0",
"CommCore": "1.47.1",
"Core": "1.47.1",
"CommCore": "1.48.1",
"Core": "1.48.1",
"Microsoft.AspNetCore.Mvc.NewtonsoftJson": "5.0.9",
"SharedWeb": "1.47.1",
"SharedWeb": "1.48.1",
"Swashbuckle.AspNetCore": "6.2.3"
}
},
"commcore": {
"type": "Project",
"dependencies": {
"Core": "1.47.1"
"Core": "1.48.1"
}
},
"common": {
"type": "Project",
"dependencies": {
"Api": "1.47.1",
"Api": "1.48.1",
"AutoFixture.AutoNSubstitute": "4.14.0",
"AutoFixture.Xunit2": "4.14.0",
"Core": "1.47.1",
"Core": "1.48.1",
"Kralizek.AutoFixture.Extensions.MockHttp": "1.2.0",
"Microsoft.NET.Test.Sdk": "16.6.1",
"NSubstitute": "4.2.2",
@ -3609,11 +3609,11 @@
"core.test": {
"type": "Project",
"dependencies": {
"Api": "1.47.1",
"Api": "1.48.1",
"AutoFixture.AutoNSubstitute": "4.14.0",
"AutoFixture.Xunit2": "4.14.0",
"Common": "1.47.1",
"Core": "1.47.1",
"Common": "1.48.1",
"Core": "1.48.1",
"Kralizek.AutoFixture.Extensions.MockHttp": "1.2.0",
"Microsoft.NET.Test.Sdk": "16.6.1",
"Moq": "4.16.1",
@ -3624,7 +3624,7 @@
"infrastructure.dapper": {
"type": "Project",
"dependencies": {
"Core": "1.47.1",
"Core": "1.48.1",
"Dapper": "2.0.123",
"System.Data.SqlClient": "4.8.3"
}
@ -3633,7 +3633,7 @@
"type": "Project",
"dependencies": {
"AutoMapper.Extensions.Microsoft.DependencyInjection": "8.0.1",
"Core": "1.47.1",
"Core": "1.48.1",
"Microsoft.EntityFrameworkCore.Relational": "5.0.12",
"Npgsql.EntityFrameworkCore.PostgreSQL": "5.0.2",
"Pomelo.EntityFrameworkCore.MySql": "5.0.3",
@ -3643,9 +3643,9 @@
"sharedweb": {
"type": "Project",
"dependencies": {
"Core": "1.47.1",
"Infrastructure.Dapper": "1.47.1",
"Infrastructure.EntityFramework": "1.47.1"
"Core": "1.48.1",
"Infrastructure.Dapper": "1.48.1",
"Infrastructure.EntityFramework": "1.48.1"
}
}
}

View File

@ -3416,7 +3416,7 @@
"commcore": {
"type": "Project",
"dependencies": {
"Core": "1.47.1"
"Core": "1.48.1"
}
},
"core": {
@ -3461,7 +3461,7 @@
"infrastructure.dapper": {
"type": "Project",
"dependencies": {
"Core": "1.47.1",
"Core": "1.48.1",
"Dapper": "2.0.123",
"System.Data.SqlClient": "4.8.3"
}
@ -3470,7 +3470,7 @@
"type": "Project",
"dependencies": {
"AutoMapper.Extensions.Microsoft.DependencyInjection": "8.0.1",
"Core": "1.47.1",
"Core": "1.48.1",
"Microsoft.EntityFrameworkCore.Relational": "5.0.12",
"Npgsql.EntityFrameworkCore.PostgreSQL": "5.0.2",
"Pomelo.EntityFrameworkCore.MySql": "5.0.3",
@ -3480,7 +3480,7 @@
"migrator": {
"type": "Project",
"dependencies": {
"Core": "1.47.1",
"Core": "1.48.1",
"Microsoft.Extensions.Logging": "5.0.0",
"dbup-sqlserver": "4.4.0"
}
@ -3488,9 +3488,9 @@
"sharedweb": {
"type": "Project",
"dependencies": {
"Core": "1.47.1",
"Infrastructure.Dapper": "1.47.1",
"Infrastructure.EntityFramework": "1.47.1"
"Core": "1.48.1",
"Infrastructure.Dapper": "1.48.1",
"Infrastructure.EntityFramework": "1.48.1"
}
}
}

View File

@ -3355,7 +3355,7 @@
"commcore": {
"type": "Project",
"dependencies": {
"Core": "1.47.1"
"Core": "1.48.1"
}
},
"core": {
@ -3400,7 +3400,7 @@
"infrastructure.dapper": {
"type": "Project",
"dependencies": {
"Core": "1.47.1",
"Core": "1.48.1",
"Dapper": "2.0.123",
"System.Data.SqlClient": "4.8.3"
}
@ -3409,7 +3409,7 @@
"type": "Project",
"dependencies": {
"AutoMapper.Extensions.Microsoft.DependencyInjection": "8.0.1",
"Core": "1.47.1",
"Core": "1.48.1",
"Microsoft.EntityFrameworkCore.Relational": "5.0.12",
"Npgsql.EntityFrameworkCore.PostgreSQL": "5.0.2",
"Pomelo.EntityFrameworkCore.MySql": "5.0.3",
@ -3419,9 +3419,9 @@
"sharedweb": {
"type": "Project",
"dependencies": {
"Core": "1.47.1",
"Infrastructure.Dapper": "1.47.1",
"Infrastructure.EntityFramework": "1.47.1"
"Core": "1.48.1",
"Infrastructure.Dapper": "1.48.1",
"Infrastructure.EntityFramework": "1.48.1"
}
}
}

View File

@ -3424,7 +3424,7 @@
"infrastructure.dapper": {
"type": "Project",
"dependencies": {
"Core": "1.47.1",
"Core": "1.48.1",
"Dapper": "2.0.123",
"System.Data.SqlClient": "4.8.3"
}
@ -3433,7 +3433,7 @@
"type": "Project",
"dependencies": {
"AutoMapper.Extensions.Microsoft.DependencyInjection": "8.0.1",
"Core": "1.47.1",
"Core": "1.48.1",
"Microsoft.EntityFrameworkCore.Relational": "5.0.12",
"Npgsql.EntityFrameworkCore.PostgreSQL": "5.0.2",
"Pomelo.EntityFrameworkCore.MySql": "5.0.3",
@ -3443,9 +3443,9 @@
"sharedweb": {
"type": "Project",
"dependencies": {
"Core": "1.47.1",
"Infrastructure.Dapper": "1.47.1",
"Infrastructure.EntityFramework": "1.47.1"
"Core": "1.48.1",
"Infrastructure.Dapper": "1.48.1",
"Infrastructure.EntityFramework": "1.48.1"
}
}
}

View File

@ -3333,7 +3333,7 @@
"infrastructure.dapper": {
"type": "Project",
"dependencies": {
"Core": "1.47.1",
"Core": "1.48.1",
"Dapper": "2.0.123",
"System.Data.SqlClient": "4.8.3"
}
@ -3342,7 +3342,7 @@
"type": "Project",
"dependencies": {
"AutoMapper.Extensions.Microsoft.DependencyInjection": "8.0.1",
"Core": "1.47.1",
"Core": "1.48.1",
"Microsoft.EntityFrameworkCore.Relational": "5.0.12",
"Npgsql.EntityFrameworkCore.PostgreSQL": "5.0.2",
"Pomelo.EntityFrameworkCore.MySql": "5.0.3",
@ -3352,9 +3352,9 @@
"sharedweb": {
"type": "Project",
"dependencies": {
"Core": "1.47.1",
"Infrastructure.Dapper": "1.47.1",
"Infrastructure.EntityFramework": "1.47.1"
"Core": "1.48.1",
"Infrastructure.Dapper": "1.48.1",
"Infrastructure.EntityFramework": "1.48.1"
}
}
}

View File

@ -3333,7 +3333,7 @@
"infrastructure.dapper": {
"type": "Project",
"dependencies": {
"Core": "1.47.1",
"Core": "1.48.1",
"Dapper": "2.0.123",
"System.Data.SqlClient": "4.8.3"
}
@ -3342,7 +3342,7 @@
"type": "Project",
"dependencies": {
"AutoMapper.Extensions.Microsoft.DependencyInjection": "8.0.1",
"Core": "1.47.1",
"Core": "1.48.1",
"Microsoft.EntityFrameworkCore.Relational": "5.0.12",
"Npgsql.EntityFrameworkCore.PostgreSQL": "5.0.2",
"Pomelo.EntityFrameworkCore.MySql": "5.0.3",
@ -3352,9 +3352,9 @@
"sharedweb": {
"type": "Project",
"dependencies": {
"Core": "1.47.1",
"Infrastructure.Dapper": "1.47.1",
"Infrastructure.EntityFramework": "1.47.1"
"Core": "1.48.1",
"Infrastructure.Dapper": "1.48.1",
"Infrastructure.EntityFramework": "1.48.1"
}
}
}

View File

@ -3342,7 +3342,7 @@
"infrastructure.dapper": {
"type": "Project",
"dependencies": {
"Core": "1.46.2",
"Core": "1.48.1",
"Dapper": "2.0.123",
"System.Data.SqlClient": "4.8.3"
}
@ -3351,7 +3351,7 @@
"type": "Project",
"dependencies": {
"AutoMapper.Extensions.Microsoft.DependencyInjection": "8.0.1",
"Core": "1.46.2",
"Core": "1.48.1",
"Microsoft.EntityFrameworkCore.Relational": "5.0.12",
"Npgsql.EntityFrameworkCore.PostgreSQL": "5.0.2",
"Pomelo.EntityFrameworkCore.MySql": "5.0.3",
@ -3361,9 +3361,9 @@
"sharedweb": {
"type": "Project",
"dependencies": {
"Core": "1.46.2",
"Infrastructure.Dapper": "1.46.2",
"Infrastructure.EntityFramework": "1.46.2"
"Core": "1.48.1",
"Infrastructure.Dapper": "1.48.1",
"Infrastructure.EntityFramework": "1.48.1"
}
}
}

View File

@ -10,7 +10,14 @@ namespace Bit.Identity
{
public static void Main(string[] args)
{
Host
CreateHostBuilder(args)
.Build()
.Run();
}
public static IHostBuilder CreateHostBuilder(string[] args)
{
return Host
.CreateDefaultBuilder(args)
.ConfigureCustomAppConfiguration(args)
.ConfigureWebHostDefaults(webBuilder =>
@ -34,9 +41,7 @@ namespace Bit.Identity
return e.Level >= LogEventLevel.Error;
}));
})
.Build()
.Run();
});
}
}
}

View File

@ -3333,7 +3333,7 @@
"infrastructure.dapper": {
"type": "Project",
"dependencies": {
"Core": "1.47.1",
"Core": "1.48.1",
"Dapper": "2.0.123",
"System.Data.SqlClient": "4.8.3"
}
@ -3342,7 +3342,7 @@
"type": "Project",
"dependencies": {
"AutoMapper.Extensions.Microsoft.DependencyInjection": "8.0.1",
"Core": "1.47.1",
"Core": "1.48.1",
"Microsoft.EntityFrameworkCore.Relational": "5.0.12",
"Npgsql.EntityFrameworkCore.PostgreSQL": "5.0.2",
"Pomelo.EntityFrameworkCore.MySql": "5.0.3",
@ -3352,9 +3352,9 @@
"sharedweb": {
"type": "Project",
"dependencies": {
"Core": "1.47.1",
"Infrastructure.Dapper": "1.47.1",
"Infrastructure.EntityFramework": "1.47.1"
"Core": "1.48.1",
"Infrastructure.Dapper": "1.48.1",
"Infrastructure.EntityFramework": "1.48.1"
}
}
}

View File

@ -3427,7 +3427,7 @@
"infrastructure.dapper": {
"type": "Project",
"dependencies": {
"Core": "1.47.1",
"Core": "1.48.1",
"Dapper": "2.0.123",
"System.Data.SqlClient": "4.8.3"
}
@ -3436,7 +3436,7 @@
"type": "Project",
"dependencies": {
"AutoMapper.Extensions.Microsoft.DependencyInjection": "8.0.1",
"Core": "1.47.1",
"Core": "1.48.1",
"Microsoft.EntityFrameworkCore.Relational": "5.0.12",
"Npgsql.EntityFrameworkCore.PostgreSQL": "5.0.2",
"Pomelo.EntityFrameworkCore.MySql": "5.0.3",
@ -3446,9 +3446,9 @@
"sharedweb": {
"type": "Project",
"dependencies": {
"Core": "1.47.1",
"Infrastructure.Dapper": "1.47.1",
"Infrastructure.EntityFramework": "1.47.1"
"Core": "1.48.1",
"Infrastructure.Dapper": "1.48.1",
"Infrastructure.EntityFramework": "1.48.1"
}
}
}

View File

@ -3333,7 +3333,7 @@
"infrastructure.dapper": {
"type": "Project",
"dependencies": {
"Core": "1.46.2",
"Core": "1.48.1",
"Dapper": "2.0.123",
"System.Data.SqlClient": "4.8.3"
}
@ -3342,7 +3342,7 @@
"type": "Project",
"dependencies": {
"AutoMapper.Extensions.Microsoft.DependencyInjection": "8.0.1",
"Core": "1.46.2",
"Core": "1.48.1",
"Microsoft.EntityFrameworkCore.Relational": "5.0.12",
"Npgsql.EntityFrameworkCore.PostgreSQL": "5.0.2",
"Pomelo.EntityFrameworkCore.MySql": "5.0.3",

View File

@ -3534,26 +3534,26 @@
"type": "Project",
"dependencies": {
"Azure.Messaging.EventGrid": "4.7.0",
"CommCore": "1.47.1",
"Core": "1.47.1",
"CommCore": "1.48.1",
"Core": "1.48.1",
"Microsoft.AspNetCore.Mvc.NewtonsoftJson": "5.0.9",
"SharedWeb": "1.47.1",
"SharedWeb": "1.48.1",
"Swashbuckle.AspNetCore": "6.2.3"
}
},
"commcore": {
"type": "Project",
"dependencies": {
"Core": "1.47.1"
"Core": "1.48.1"
}
},
"common": {
"type": "Project",
"dependencies": {
"Api": "1.47.1",
"Api": "1.48.1",
"AutoFixture.AutoNSubstitute": "4.14.0",
"AutoFixture.Xunit2": "4.14.0",
"Core": "1.47.1",
"Core": "1.48.1",
"Kralizek.AutoFixture.Extensions.MockHttp": "1.2.0",
"Microsoft.NET.Test.Sdk": "16.6.1",
"NSubstitute": "4.2.2",
@ -3602,7 +3602,7 @@
"infrastructure.dapper": {
"type": "Project",
"dependencies": {
"Core": "1.47.1",
"Core": "1.48.1",
"Dapper": "2.0.123",
"System.Data.SqlClient": "4.8.3"
}
@ -3611,7 +3611,7 @@
"type": "Project",
"dependencies": {
"AutoMapper.Extensions.Microsoft.DependencyInjection": "8.0.1",
"Core": "1.47.1",
"Core": "1.48.1",
"Microsoft.EntityFrameworkCore.Relational": "5.0.12",
"Npgsql.EntityFrameworkCore.PostgreSQL": "5.0.2",
"Pomelo.EntityFrameworkCore.MySql": "5.0.3",
@ -3621,9 +3621,9 @@
"sharedweb": {
"type": "Project",
"dependencies": {
"Core": "1.47.1",
"Infrastructure.Dapper": "1.47.1",
"Infrastructure.EntityFramework": "1.47.1"
"Core": "1.48.1",
"Infrastructure.Dapper": "1.48.1",
"Infrastructure.EntityFramework": "1.48.1"
}
}
}

View File

@ -3625,34 +3625,34 @@
"type": "Project",
"dependencies": {
"Azure.Messaging.EventGrid": "4.7.0",
"CommCore": "1.47.1",
"Core": "1.47.1",
"CommCore": "1.48.1",
"Core": "1.48.1",
"Microsoft.AspNetCore.Mvc.NewtonsoftJson": "5.0.9",
"SharedWeb": "1.47.1",
"SharedWeb": "1.48.1",
"Swashbuckle.AspNetCore": "6.2.3"
}
},
"billing": {
"type": "Project",
"dependencies": {
"Core": "1.47.1",
"Core": "1.48.1",
"Microsoft.VisualStudio.Web.CodeGeneration.Design": "5.0.2",
"SharedWeb": "1.47.1"
"SharedWeb": "1.48.1"
}
},
"commcore": {
"type": "Project",
"dependencies": {
"Core": "1.47.1"
"Core": "1.48.1"
}
},
"common": {
"type": "Project",
"dependencies": {
"Api": "1.47.1",
"Api": "1.48.1",
"AutoFixture.AutoNSubstitute": "4.14.0",
"AutoFixture.Xunit2": "4.14.0",
"Core": "1.47.1",
"Core": "1.48.1",
"Kralizek.AutoFixture.Extensions.MockHttp": "1.2.0",
"Microsoft.NET.Test.Sdk": "16.6.1",
"NSubstitute": "4.2.2",
@ -3701,7 +3701,7 @@
"infrastructure.dapper": {
"type": "Project",
"dependencies": {
"Core": "1.47.1",
"Core": "1.48.1",
"Dapper": "2.0.123",
"System.Data.SqlClient": "4.8.3"
}
@ -3710,7 +3710,7 @@
"type": "Project",
"dependencies": {
"AutoMapper.Extensions.Microsoft.DependencyInjection": "8.0.1",
"Core": "1.47.1",
"Core": "1.48.1",
"Microsoft.EntityFrameworkCore.Relational": "5.0.12",
"Npgsql.EntityFrameworkCore.PostgreSQL": "5.0.2",
"Pomelo.EntityFrameworkCore.MySql": "5.0.3",
@ -3720,9 +3720,9 @@
"sharedweb": {
"type": "Project",
"dependencies": {
"Core": "1.47.1",
"Infrastructure.Dapper": "1.47.1",
"Infrastructure.EntityFramework": "1.47.1"
"Core": "1.48.1",
"Infrastructure.Dapper": "1.48.1",
"Infrastructure.EntityFramework": "1.48.1"
}
}
}

View File

@ -1,9 +1,12 @@
using System;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text.Json;
using System.Threading.Tasks;
using Bit.Core.Utilities;
using Microsoft.AspNetCore.Http;
using Xunit;
using Xunit.Sdk;
@ -84,6 +87,116 @@ namespace Bit.Test.Common.Helpers
return subElement;
}
public static void AssertEqualJson(JsonElement a, JsonElement b)
{
switch (a.ValueKind)
{
case JsonValueKind.Array:
Assert.Equal(JsonValueKind.Array, b.ValueKind);
AssertEqualJsonArray(a, b);
break;
case JsonValueKind.Object:
Assert.Equal(JsonValueKind.Object, b.ValueKind);
AssertEqualJsonObject(a, b);
break;
case JsonValueKind.False:
Assert.Equal(JsonValueKind.False, b.ValueKind);
break;
case JsonValueKind.True:
Assert.Equal(JsonValueKind.True, b.ValueKind);
break;
case JsonValueKind.Number:
Assert.Equal(JsonValueKind.Number, b.ValueKind);
Assert.Equal(a.GetDouble(), b.GetDouble());
break;
case JsonValueKind.String:
Assert.Equal(JsonValueKind.String, b.ValueKind);
Assert.Equal(a.GetString(), b.GetString());
break;
case JsonValueKind.Null:
Assert.Equal(JsonValueKind.Null, b.ValueKind);
break;
default:
throw new XunitException($"Bad JsonValueKind '{a.ValueKind}'");
}
}
private static void AssertEqualJsonObject(JsonElement a, JsonElement b)
{
Debug.Assert(a.ValueKind == JsonValueKind.Object && b.ValueKind == JsonValueKind.Object);
var aObjectEnumerator = a.EnumerateObject();
var bObjectEnumerator = b.EnumerateObject();
while (true)
{
var aCanMove = aObjectEnumerator.MoveNext();
var bCanMove = bObjectEnumerator.MoveNext();
if (aCanMove)
{
Assert.True(bCanMove, $"a was able to enumerate over object '{a}' but b was NOT able to '{b}'");
}
else
{
Assert.False(bCanMove, $"a was NOT able to enumerate over object '{a}' but b was able to '{b}'");
}
if (aCanMove == false && bCanMove == false)
{
// They both can't continue to enumerate at the same time, that is valid
break;
}
var aProp = aObjectEnumerator.Current;
var bProp = bObjectEnumerator.Current;
Assert.Equal(aProp.Name, bProp.Name);
// Recursion!
AssertEqualJson(aProp.Value, bProp.Value);
}
}
private static void AssertEqualJsonArray(JsonElement a, JsonElement b)
{
Debug.Assert(a.ValueKind == JsonValueKind.Array && b.ValueKind == JsonValueKind.Array);
var aArrayEnumerator = a.EnumerateArray();
var bArrayEnumerator = b.EnumerateArray();
while (true)
{
var aCanMove = aArrayEnumerator.MoveNext();
var bCanMove = bArrayEnumerator.MoveNext();
if (aCanMove)
{
Assert.True(bCanMove, $"a was able to enumerate over array '{a}' but b was NOT able to '{b}'");
}
else
{
Assert.False(bCanMove, $"a was NOT able to enumerate over array '{a}' but b was able to '{b}'");
}
if (aCanMove == false && bCanMove == false)
{
// They both can't continue to enumerate at the same time, that is valid
break;
}
var aElement = aArrayEnumerator.Current;
var bElement = bArrayEnumerator.Current;
// Recursion!
AssertEqualJson(aElement, bElement);
}
}
public async static Task<T> AssertResponseTypeIs<T>(HttpContext context)
{
return await JsonSerializer.DeserializeAsync<T>(context.Response.Body);
}
public static TimeSpan AssertRecent(DateTime dateTime, int skewSeconds = 2)
=> AssertRecent(dateTime, TimeSpan.FromSeconds(skewSeconds));

View File

@ -3530,17 +3530,17 @@
"type": "Project",
"dependencies": {
"Azure.Messaging.EventGrid": "4.7.0",
"CommCore": "1.47.1",
"Core": "1.47.1",
"CommCore": "1.48.1",
"Core": "1.48.1",
"Microsoft.AspNetCore.Mvc.NewtonsoftJson": "5.0.9",
"SharedWeb": "1.47.1",
"SharedWeb": "1.48.1",
"Swashbuckle.AspNetCore": "6.2.3"
}
},
"commcore": {
"type": "Project",
"dependencies": {
"Core": "1.47.1"
"Core": "1.48.1"
}
},
"core": {
@ -3585,7 +3585,7 @@
"infrastructure.dapper": {
"type": "Project",
"dependencies": {
"Core": "1.47.1",
"Core": "1.48.1",
"Dapper": "2.0.123",
"System.Data.SqlClient": "4.8.3"
}
@ -3594,7 +3594,7 @@
"type": "Project",
"dependencies": {
"AutoMapper.Extensions.Microsoft.DependencyInjection": "8.0.1",
"Core": "1.47.1",
"Core": "1.48.1",
"Microsoft.EntityFrameworkCore.Relational": "5.0.12",
"Npgsql.EntityFrameworkCore.PostgreSQL": "5.0.2",
"Pomelo.EntityFrameworkCore.MySql": "5.0.3",
@ -3604,9 +3604,9 @@
"sharedweb": {
"type": "Project",
"dependencies": {
"Core": "1.47.1",
"Infrastructure.Dapper": "1.47.1",
"Infrastructure.EntityFramework": "1.47.1"
"Core": "1.48.1",
"Infrastructure.Dapper": "1.48.1",
"Infrastructure.EntityFramework": "1.48.1"
}
}
}

View File

@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using AutoFixture;
using Bit.Core.Context;
using Bit.Core.Entities;
@ -13,6 +14,9 @@ using Bit.Infrastructure.Dapper;
using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes;
using IdentityModel;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.WebUtilities;
using NSubstitute;
using Xunit;
namespace Bit.Core.Test.Utilities
@ -390,6 +394,47 @@ namespace Bit.Core.Test.Utilities
}
}
public static IEnumerable<object[]> TokenIsValidData()
{
return new[]
{
new object[]
{
"first_part 476669d4-9642-4af8-9b29-9366efad4ed3 test@email.com {0}", // unprotectedTokenTemplate
"first_part", // firstPart
"test@email.com", // email
Guid.Parse("476669d4-9642-4af8-9b29-9366efad4ed3"), // id
DateTime.UtcNow.AddHours(-1), // creationTime
12, // expirationInHours
true, // isValid
}
};
}
[Theory]
[MemberData(nameof(TokenIsValidData))]
public void TokenIsValid_Success(string unprotectedTokenTemplate, string firstPart, string userEmail, Guid id, DateTime creationTime, double expirationInHours, bool isValid)
{
var protector = new TestDataProtector(string.Format(unprotectedTokenTemplate, CoreHelpers.ToEpocMilliseconds(creationTime)));
Assert.Equal(isValid, CoreHelpers.TokenIsValid(firstPart, protector, "protected_token", userEmail, id, expirationInHours));
}
private class TestDataProtector : IDataProtector
{
private readonly string _token;
public TestDataProtector(string token)
{
_token = token;
}
public IDataProtector CreateProtector(string purpose) => throw new NotImplementedException();
public byte[] Protect(byte[] plaintext) => throw new NotImplementedException();
public byte[] Unprotect(byte[] protectedData)
{
return Encoding.UTF8.GetBytes(_token);
}
}
[Theory]
[InlineData("hi@email.com", "hi@email.com")] // Short email with no room to obfuscate
[InlineData("name@email.com", "na**@email.com")] // Can obfuscate

View File

@ -3546,26 +3546,26 @@
"type": "Project",
"dependencies": {
"Azure.Messaging.EventGrid": "4.7.0",
"CommCore": "1.47.1",
"Core": "1.47.1",
"CommCore": "1.48.1",
"Core": "1.48.1",
"Microsoft.AspNetCore.Mvc.NewtonsoftJson": "5.0.9",
"SharedWeb": "1.47.1",
"SharedWeb": "1.48.1",
"Swashbuckle.AspNetCore": "6.2.3"
}
},
"commcore": {
"type": "Project",
"dependencies": {
"Core": "1.47.1"
"Core": "1.48.1"
}
},
"common": {
"type": "Project",
"dependencies": {
"Api": "1.47.1",
"Api": "1.48.1",
"AutoFixture.AutoNSubstitute": "4.14.0",
"AutoFixture.Xunit2": "4.14.0",
"Core": "1.47.1",
"Core": "1.48.1",
"Kralizek.AutoFixture.Extensions.MockHttp": "1.2.0",
"Microsoft.NET.Test.Sdk": "16.6.1",
"NSubstitute": "4.2.2",
@ -3614,7 +3614,7 @@
"infrastructure.dapper": {
"type": "Project",
"dependencies": {
"Core": "1.47.1",
"Core": "1.48.1",
"Dapper": "2.0.123",
"System.Data.SqlClient": "4.8.3"
}
@ -3623,7 +3623,7 @@
"type": "Project",
"dependencies": {
"AutoMapper.Extensions.Microsoft.DependencyInjection": "8.0.1",
"Core": "1.47.1",
"Core": "1.48.1",
"Microsoft.EntityFrameworkCore.Relational": "5.0.12",
"Npgsql.EntityFrameworkCore.PostgreSQL": "5.0.2",
"Pomelo.EntityFrameworkCore.MySql": "5.0.3",
@ -3633,9 +3633,9 @@
"sharedweb": {
"type": "Project",
"dependencies": {
"Core": "1.47.1",
"Infrastructure.Dapper": "1.47.1",
"Infrastructure.EntityFramework": "1.47.1"
"Core": "1.48.1",
"Infrastructure.Dapper": "1.48.1",
"Infrastructure.EntityFramework": "1.48.1"
}
}
}

View File

@ -3473,14 +3473,14 @@
"type": "Project",
"dependencies": {
"AngleSharp": "0.14.0",
"Core": "1.47.1",
"SharedWeb": "1.47.1"
"Core": "1.48.1",
"SharedWeb": "1.48.1"
}
},
"infrastructure.dapper": {
"type": "Project",
"dependencies": {
"Core": "1.47.1",
"Core": "1.48.1",
"Dapper": "2.0.123",
"System.Data.SqlClient": "4.8.3"
}
@ -3489,7 +3489,7 @@
"type": "Project",
"dependencies": {
"AutoMapper.Extensions.Microsoft.DependencyInjection": "8.0.1",
"Core": "1.47.1",
"Core": "1.48.1",
"Microsoft.EntityFrameworkCore.Relational": "5.0.12",
"Npgsql.EntityFrameworkCore.PostgreSQL": "5.0.2",
"Pomelo.EntityFrameworkCore.MySql": "5.0.3",
@ -3499,9 +3499,9 @@
"sharedweb": {
"type": "Project",
"dependencies": {
"Core": "1.47.1",
"Infrastructure.Dapper": "1.47.1",
"Infrastructure.EntityFramework": "1.47.1"
"Core": "1.48.1",
"Infrastructure.Dapper": "1.48.1",
"Infrastructure.EntityFramework": "1.48.1"
}
}
}

View File

@ -0,0 +1,37 @@
using System.Threading.Tasks;
using Bit.Core.Models.Api.Request.Accounts;
using Bit.IntegrationTestCommon.Factories;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Xunit;
namespace Bit.Identity.IntegrationTest.Controllers
{
public class AccountsControllerTests : IClassFixture<IdentityApplicationFactory>
{
private readonly IdentityApplicationFactory _factory;
public AccountsControllerTests(IdentityApplicationFactory factory)
{
_factory = factory;
}
[Fact]
public async Task PostRegister_Success()
{
var context = await _factory.RegisterAsync(new RegisterRequestModel
{
Email = "test+register@email.com",
MasterPasswordHash = "master_password_hash"
});
Assert.Equal(StatusCodes.Status200OK, context.Response.StatusCode);
var database = _factory.GetDatabaseContext();
var user = await database.Users
.SingleAsync(u => u.Email == "test+register@email.com");
Assert.NotNull(user);
}
}
}

View File

@ -0,0 +1,454 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;
using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Models.Api.Request.Accounts;
using Bit.Core.Repositories;
using Bit.Core.Utilities;
using Bit.IntegrationTestCommon.Factories;
using Bit.Test.Common.AutoFixture.Attributes;
using Bit.Test.Common.Helpers;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Xunit;
namespace Bit.Identity.IntegrationTest.Endpoints
{
public class IdentityServerTests : IClassFixture<IdentityApplicationFactory>
{
private const int SecondsInMinute = 60;
private const int MinutesInHour = 60;
private const int SecondsInHour = SecondsInMinute * MinutesInHour;
private readonly IdentityApplicationFactory _factory;
public IdentityServerTests(IdentityApplicationFactory factory)
{
_factory = factory;
}
[Fact]
public async Task WellKnownEndpoint_Success()
{
var context = await _factory.Server.GetAsync("/.well-known/openid-configuration");
using var body = await AssertHelper.AssertResponseTypeIs<JsonDocument>(context);
var endpointRoot = body.RootElement;
// WARNING: Edits to this file should NOT just be made to "get the test to work" they should be made when intentional
// changes were made to this endpoint and proper testing will take place to ensure clients are backwards compatible
// or loss of functionality is properly noted.
await using var fs = File.OpenRead("openid-configuration.json");
using var knownConfiguration = await JsonSerializer.DeserializeAsync<JsonDocument>(fs);
var knownConfigurationRoot = knownConfiguration.RootElement;
AssertHelper.AssertEqualJson(endpointRoot, knownConfigurationRoot);
}
[Fact]
public async Task TokenEndpoint_GrantTypePassword_Success()
{
var deviceId = "92b9d953-b9b6-4eaf-9d3e-11d57144dfeb";
var username = "test+tokenpassword@email.com";
await _factory.RegisterAsync(new RegisterRequestModel
{
Email = username,
MasterPasswordHash = "master_password_hash"
});
var context = await _factory.Server.PostAsync("/connect/token", new FormUrlEncodedContent(new Dictionary<string, string>
{
{ "scope", "api offline_access" },
{ "client_id", "web" },
{ "deviceType", DeviceTypeAsString(DeviceType.FirefoxBrowser) },
{ "deviceIdentifier", deviceId },
{ "deviceName", "firefox" },
{ "grant_type", "password" },
{ "username", username },
{ "password", "master_password_hash" },
}), context => context.Request.Headers.Add("Auth-Email", CoreHelpers.Base64UrlEncodeString(username)));
using var body = await AssertDefaultTokenBodyAsync(context);
var root = body.RootElement;
AssertRefreshTokenExists(root);
AssertHelper.AssertJsonProperty(root, "ForcePasswordReset", JsonValueKind.False);
AssertHelper.AssertJsonProperty(root, "ResetMasterPassword", JsonValueKind.False);
var kdf = AssertHelper.AssertJsonProperty(root, "Kdf", JsonValueKind.Number).GetInt32();
Assert.Equal(0, kdf);
var kdfIterations = AssertHelper.AssertJsonProperty(root, "KdfIterations", JsonValueKind.Number).GetInt32();
Assert.Equal(5000, kdfIterations);
}
[Fact]
public async Task TokenEndpoint_GrantTypePassword_NoAuthEmailHeader_Fails()
{
var deviceId = "92b9d953-b9b6-4eaf-9d3e-11d57144dfeb";
var username = "test+noauthemailheader@email.com";
await _factory.RegisterAsync(new RegisterRequestModel
{
Email = username,
MasterPasswordHash = "master_password_hash",
});
var context = await _factory.Server.PostAsync("/connect/token", new FormUrlEncodedContent(new Dictionary<string, string>
{
{ "scope", "api offline_access" },
{ "client_id", "web" },
{ "deviceType", DeviceTypeAsString(DeviceType.FirefoxBrowser) },
{ "deviceIdentifier", deviceId },
{ "deviceName", "firefox" },
{ "grant_type", "password" },
{ "username", username },
{ "password", "master_password_hash" },
}));
Assert.Equal(StatusCodes.Status400BadRequest, context.Response.StatusCode);
var body = await AssertHelper.AssertResponseTypeIs<JsonDocument>(context);
var root = body.RootElement;
var error = AssertHelper.AssertJsonProperty(root, "error", JsonValueKind.String).GetString();
Assert.Equal("invalid_grant", error);
AssertHelper.AssertJsonProperty(root, "error_description", JsonValueKind.String);
}
[Fact]
public async Task TokenEndpoint_GrantTypePassword_InvalidBase64AuthEmailHeader_Fails()
{
var deviceId = "92b9d953-b9b6-4eaf-9d3e-11d57144dfeb";
var username = "test+badauthheader@email.com";
await _factory.RegisterAsync(new RegisterRequestModel
{
Email = username,
MasterPasswordHash = "master_password_hash",
});
var context = await _factory.Server.PostAsync("/connect/token", new FormUrlEncodedContent(new Dictionary<string, string>
{
{ "scope", "api offline_access" },
{ "client_id", "web" },
{ "deviceType", DeviceTypeAsString(DeviceType.FirefoxBrowser) },
{ "deviceIdentifier", deviceId },
{ "deviceName", "firefox" },
{ "grant_type", "password" },
{ "username", username },
{ "password", "master_password_hash" },
}), context => context.Request.Headers.Add("Auth-Email", "bad_value"));
Assert.Equal(StatusCodes.Status400BadRequest, context.Response.StatusCode);
var body = await AssertHelper.AssertResponseTypeIs<JsonDocument>(context);
var root = body.RootElement;
var error = AssertHelper.AssertJsonProperty(root, "error", JsonValueKind.String).GetString();
Assert.Equal("invalid_grant", error);
AssertHelper.AssertJsonProperty(root, "error_description", JsonValueKind.String);
}
[Fact]
public async Task TokenEndpoint_GrantTypePassword_WrongAuthEmailHeader_Fails()
{
var deviceId = "92b9d953-b9b6-4eaf-9d3e-11d57144dfeb";
var username = "test+badauthheader@email.com";
await _factory.RegisterAsync(new RegisterRequestModel
{
Email = username,
MasterPasswordHash = "master_password_hash",
});
var context = await _factory.Server.PostAsync("/connect/token", new FormUrlEncodedContent(new Dictionary<string, string>
{
{ "scope", "api offline_access" },
{ "client_id", "web" },
{ "deviceType", DeviceTypeAsString(DeviceType.FirefoxBrowser) },
{ "deviceIdentifier", deviceId },
{ "deviceName", "firefox" },
{ "grant_type", "password" },
{ "username", username },
{ "password", "master_password_hash" },
}), context => context.Request.Headers.Add("Auth-Email", CoreHelpers.Base64UrlEncodeString("bad_value")));
Assert.Equal(StatusCodes.Status400BadRequest, context.Response.StatusCode);
var body = await AssertHelper.AssertResponseTypeIs<JsonDocument>(context);
var root = body.RootElement;
var error = AssertHelper.AssertJsonProperty(root, "error", JsonValueKind.String).GetString();
Assert.Equal("invalid_grant", error);
AssertHelper.AssertJsonProperty(root, "error_description", JsonValueKind.String);
}
[Fact]
public async Task TokenEndpoint_GrantTypeRefreshToken_Success()
{
var deviceId = "5a7b19df-0c9d-46bf-a104-8034b5a17182";
var username = "test+tokenrefresh@email.com";
await _factory.RegisterAsync(new RegisterRequestModel
{
Email = username,
MasterPasswordHash = "master_password_hash",
});
var (_, refreshToken) = await _factory.TokenFromPasswordAsync(username, "master_password_hash", deviceId);
var context = await _factory.Server.PostAsync("/connect/token", new FormUrlEncodedContent(new Dictionary<string, string>
{
{ "grant_type", "refresh_token" },
{ "client_id", "web" },
{ "refresh_token", refreshToken },
}));
using var body = await AssertDefaultTokenBodyAsync(context);
AssertRefreshTokenExists(body.RootElement);
}
[Fact]
public async Task TokenEndpoint_GrantTypeClientCredentials_Success()
{
var username = "test+tokenclientcredentials@email.com";
var deviceId = "8f14a393-edfe-40ba-8c67-a856cb89c509";
await _factory.RegisterAsync(new RegisterRequestModel
{
Email = username,
MasterPasswordHash = "master_password_hash",
});
var database = _factory.GetDatabaseContext();
var user = await database.Users
.FirstAsync(u => u.Email == username);
var context = await _factory.Server.PostAsync("/connect/token", new FormUrlEncodedContent(new Dictionary<string, string>
{
{ "grant_type", "client_credentials" },
{ "client_id", $"user.{user.Id}" },
{ "client_secret", user.ApiKey },
{ "scope", "api" },
{ "DeviceIdentifier", deviceId },
{ "DeviceType", DeviceTypeAsString(DeviceType.FirefoxBrowser) },
{ "DeviceName", "firefox" },
}));
await AssertDefaultTokenBodyAsync(context, "api");
}
[Theory, BitAutoData]
public async Task TokenEndpoint_GrantTypeClientCredentials_AsOrganization_Success(Organization organization, OrganizationApiKey organizationApiKey)
{
var orgRepo = _factory.Services.GetRequiredService<IOrganizationRepository>();
organization = await orgRepo.CreateAsync(organization);
organizationApiKey.OrganizationId = organization.Id;
organizationApiKey.Type = OrganizationApiKeyType.Default;
var orgApiKeyRepo = _factory.Services.GetRequiredService<IOrganizationApiKeyRepository>();
await orgApiKeyRepo.CreateAsync(organizationApiKey);
var context = await _factory.Server.PostAsync("/connect/token", new FormUrlEncodedContent(new Dictionary<string, string>
{
{ "grant_type", "client_credentials" },
{ "client_id", $"organization.{organization.Id}" },
{ "client_secret", organizationApiKey.ApiKey },
{ "scope", "api.organization" },
}));
Assert.Equal(StatusCodes.Status200OK, context.Response.StatusCode);
await AssertDefaultTokenBodyAsync(context, "api.organization");
}
[Fact]
public async Task TokenEndpoint_GrantTypeClientCredentials_AsOrganization_BadOrgId_Fails()
{
var context = await _factory.Server.PostAsync("/connect/token", new FormUrlEncodedContent(new Dictionary<string, string>
{
{ "grant_type", "client_credentials" },
{ "client_id", "organization.bad_guid_zz&" },
{ "client_secret", "something" },
{ "scope", "api.organization" },
}));
Assert.Equal(StatusCodes.Status400BadRequest, context.Response.StatusCode);
var errorBody = await AssertHelper.AssertResponseTypeIs<JsonDocument>(context);
var error = AssertHelper.AssertJsonProperty(errorBody.RootElement, "error", JsonValueKind.String).GetString();
Assert.Equal("invalid_client", error);
}
/// <summary>
/// This test currently does not test any code that is not covered by other tests but
/// it shows that we probably have some dead code in <see cref="Core.IdentityServer.ClientStore"/>
/// for installation, organization, and user they split on a <c>'.'</c> but have already checked that at least one
/// <c>'.'</c> exists in the <c>client_id</c> by checking it with <see cref="string.StartsWith(string)"/>
/// I believe that idParts.Length > 1 will ALWAYS return true
/// </summary>
[Fact]
public async Task TokenEndpoint_GrantTypeClientCredentials_AsOrganization_NoIdPart_Fails()
{
var context = await _factory.Server.PostAsync("/connect/token", new FormUrlEncodedContent(new Dictionary<string, string>
{
{ "grant_type", "client_credentials" },
{ "client_id", "organization." },
{ "client_secret", "something" },
{ "scope", "api.organization" },
}));
Assert.Equal(StatusCodes.Status400BadRequest, context.Response.StatusCode);
var errorBody = await AssertHelper.AssertResponseTypeIs<JsonDocument>(context);
var error = AssertHelper.AssertJsonProperty(errorBody.RootElement, "error", JsonValueKind.String).GetString();
Assert.Equal("invalid_client", error);
}
[Fact]
public async Task TokenEndpoint_GrantTypeClientCredentials_AsOrganization_OrgDoesNotExist_Fails()
{
var context = await _factory.Server.PostAsync("/connect/token", new FormUrlEncodedContent(new Dictionary<string, string>
{
{ "grant_type", "client_credentials" },
{ "client_id", $"organization.{Guid.NewGuid()}" },
{ "client_secret", "something" },
{ "scope", "api.organization" },
}));
Assert.Equal(StatusCodes.Status400BadRequest, context.Response.StatusCode);
var errorBody = await AssertHelper.AssertResponseTypeIs<JsonDocument>(context);
var error = AssertHelper.AssertJsonProperty(errorBody.RootElement, "error", JsonValueKind.String).GetString();
Assert.Equal("invalid_client", error);
}
[Theory, BitAutoData]
public async Task TokenEndpoint_GrantTypeClientCredentials_AsInstallation_InstallationExists_Succeeds(Installation installation)
{
var installationRepo = _factory.Services.GetRequiredService<IInstallationRepository>();
installation = await installationRepo.CreateAsync(installation);
var context = await _factory.Server.PostAsync("/connect/token", new FormUrlEncodedContent(new Dictionary<string, string>
{
{ "grant_type", "client_credentials" },
{ "client_id", $"installation.{installation.Id}" },
{ "client_secret", installation.Key },
{ "scope", "api.push" },
}));
Assert.Equal(StatusCodes.Status200OK, context.Response.StatusCode);
await AssertDefaultTokenBodyAsync(context, "api.push", 24 * SecondsInHour);
}
[Fact]
public async Task TokenEndpoint_GrantTypeClientCredentials_AsInstallation_InstallationDoesNotExist_Fails()
{
var context = await _factory.Server.PostAsync("/connect/token", new FormUrlEncodedContent(new Dictionary<string, string>
{
{ "grant_type", "client_credentials" },
{ "client_id", $"installation.{Guid.NewGuid()}" },
{ "client_secret", "something" },
{ "scope", "api.push" },
}));
Assert.Equal(StatusCodes.Status400BadRequest, context.Response.StatusCode);
var errorBody = await AssertHelper.AssertResponseTypeIs<JsonDocument>(context);
var error = AssertHelper.AssertJsonProperty(errorBody.RootElement, "error", JsonValueKind.String).GetString();
Assert.Equal("invalid_client", error);
}
[Fact]
public async Task TokenEndpoint_GrantTypeClientCredentials_AsInstallation_BadInsallationId_Fails()
{
var context = await _factory.Server.PostAsync("/connect/token", new FormUrlEncodedContent(new Dictionary<string, string>
{
{ "grant_type", "client_credentials" },
{ "client_id", "organization.bad_guid_zz&" },
{ "client_secret", "something" },
{ "scope", "api.organization" },
}));
Assert.Equal(StatusCodes.Status400BadRequest, context.Response.StatusCode);
var errorBody = await AssertHelper.AssertResponseTypeIs<JsonDocument>(context);
var error = AssertHelper.AssertJsonProperty(errorBody.RootElement, "error", JsonValueKind.String).GetString();
Assert.Equal("invalid_client", error);
}
/// <inheritdoc cref="TokenEndpoint_GrantTypeClientCredentials_AsOrganization_NoIdPart_Fails"/>
[Fact]
public async Task TokenEndpoint_GrantTypeClientCredentials_AsInstallation_NoIdPart_Fails()
{
var context = await _factory.Server.PostAsync("/connect/token", new FormUrlEncodedContent(new Dictionary<string, string>
{
{ "grant_type", "client_credentials" },
{ "client_id", "installation." },
{ "client_secret", "something" },
{ "scope", "api.push" },
}));
Assert.Equal(StatusCodes.Status400BadRequest, context.Response.StatusCode);
var errorBody = await AssertHelper.AssertResponseTypeIs<JsonDocument>(context);
var error = AssertHelper.AssertJsonProperty(errorBody.RootElement, "error", JsonValueKind.String).GetString();
Assert.Equal("invalid_client", error);
}
private static string DeviceTypeAsString(DeviceType deviceType)
{
return ((int)deviceType).ToString();
}
private static async Task<JsonDocument> AssertDefaultTokenBodyAsync(HttpContext httpContext, string expectedScope = "api offline_access", int expectedExpiresIn = SecondsInHour * 1)
{
var body = await AssertHelper.AssertResponseTypeIs<JsonDocument>(httpContext);
var root = body.RootElement;
Assert.Equal(JsonValueKind.Object, root.ValueKind);
AssertAccessTokenExists(root);
AssertExpiresIn(root, expectedExpiresIn);
AssertTokenType(root);
AssertScope(root, expectedScope);
return body;
}
private static void AssertTokenType(JsonElement tokenResponse)
{
var tokenTypeProperty = AssertHelper.AssertJsonProperty(tokenResponse, "token_type", JsonValueKind.String).GetString();
Assert.Equal("Bearer", tokenTypeProperty);
}
private static int AssertExpiresIn(JsonElement tokenResponse, int expectedExpiresIn = 3600)
{
var expiresIn = AssertHelper.AssertJsonProperty(tokenResponse, "expires_in", JsonValueKind.Number).GetInt32();
Assert.Equal(expectedExpiresIn, expiresIn);
return expiresIn;
}
private static string AssertAccessTokenExists(JsonElement tokenResponse)
{
return AssertHelper.AssertJsonProperty(tokenResponse, "access_token", JsonValueKind.String).GetString();
}
private static string AssertRefreshTokenExists(JsonElement tokenResponse)
{
return AssertHelper.AssertJsonProperty(tokenResponse, "refresh_token", JsonValueKind.String).GetString();
}
private static string AssertScopeExists(JsonElement tokenResponse)
{
return AssertHelper.AssertJsonProperty(tokenResponse, "scope", JsonValueKind.String).GetString();
}
private static void AssertScope(JsonElement tokenResponse, string expectedScope)
{
var actualScope = AssertScopeExists(tokenResponse);
Assert.Equal(expectedScope, actualScope);
}
}
}

View File

@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.collector" Version="3.0.3">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="5.0.15" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
<PackageReference Include="NSubstitute" Version="4.2.2" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="AutoFixture.Xunit2" Version="4.14.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Identity\Identity.csproj" />
<ProjectReference Include="..\Common\Common.csproj" />
<ProjectReference Include="..\IntegrationTestCommon\IntegrationTestCommon.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,12 @@
{
"profiles": {
"Identity.IntegrationTest": {
"commandName": "Project",
"launchBrowser": false,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "https://localhost:55088;http://localhost:55089"
}
}
}

View File

@ -0,0 +1,69 @@
{
"issuer": "http://localhost",
"jwks_uri": "http://localhost:33656/.well-known/openid-configuration/jwks",
"authorization_endpoint": "http://localhost:33656/connect/authorize",
"token_endpoint": "http://localhost:33656/connect/token",
"device_authorization_endpoint": "http://localhost:33656/connect/deviceauthorization",
"scopes_supported": [
"api",
"api.push",
"api.licensing",
"api.organization",
"api.installation",
"internal",
"offline_access"
],
"claims_supported": [
"name",
"email",
"email_verified",
"sstamp",
"premium",
"device",
"orgowner",
"orgadmin",
"orgmanager",
"orguser",
"orgcustom",
"providerprovideradmin",
"providerserviceuser",
"sub"
],
"grant_types_supported": [
"authorization_code",
"client_credentials",
"refresh_token",
"implicit",
"password",
"urn:ietf:params:oauth:grant-type:device_code"
],
"response_types_supported": [
"code",
"token",
"id_token",
"id_token token",
"code id_token",
"code token",
"code id_token token"
],
"response_modes_supported": [
"form_post",
"query",
"fragment"
],
"token_endpoint_auth_methods_supported": [
"client_secret_basic",
"client_secret_post"
],
"id_token_signing_alg_values_supported": [
"RS256"
],
"subject_types_supported": [
"public"
],
"code_challenge_methods_supported": [
"plain",
"S256"
],
"request_parameter_supported": true
}

File diff suppressed because it is too large Load Diff

View File

@ -3534,26 +3534,26 @@
"type": "Project",
"dependencies": {
"Azure.Messaging.EventGrid": "4.7.0",
"CommCore": "1.47.1",
"Core": "1.47.1",
"CommCore": "1.48.1",
"Core": "1.48.1",
"Microsoft.AspNetCore.Mvc.NewtonsoftJson": "5.0.9",
"SharedWeb": "1.47.1",
"SharedWeb": "1.48.1",
"Swashbuckle.AspNetCore": "6.2.3"
}
},
"commcore": {
"type": "Project",
"dependencies": {
"Core": "1.47.1"
"Core": "1.48.1"
}
},
"common": {
"type": "Project",
"dependencies": {
"Api": "1.47.1",
"Api": "1.48.1",
"AutoFixture.AutoNSubstitute": "4.14.0",
"AutoFixture.Xunit2": "4.14.0",
"Core": "1.47.1",
"Core": "1.48.1",
"Kralizek.AutoFixture.Extensions.MockHttp": "1.2.0",
"Microsoft.NET.Test.Sdk": "16.6.1",
"NSubstitute": "4.2.2",
@ -3602,14 +3602,14 @@
"identity": {
"type": "Project",
"dependencies": {
"Core": "1.47.1",
"SharedWeb": "1.47.1"
"Core": "1.48.1",
"SharedWeb": "1.48.1"
}
},
"infrastructure.dapper": {
"type": "Project",
"dependencies": {
"Core": "1.47.1",
"Core": "1.48.1",
"Dapper": "2.0.123",
"System.Data.SqlClient": "4.8.3"
}
@ -3618,7 +3618,7 @@
"type": "Project",
"dependencies": {
"AutoMapper.Extensions.Microsoft.DependencyInjection": "8.0.1",
"Core": "1.47.1",
"Core": "1.48.1",
"Microsoft.EntityFrameworkCore.Relational": "5.0.12",
"Npgsql.EntityFrameworkCore.PostgreSQL": "5.0.2",
"Pomelo.EntityFrameworkCore.MySql": "5.0.3",
@ -3628,9 +3628,9 @@
"sharedweb": {
"type": "Project",
"dependencies": {
"Core": "1.47.1",
"Infrastructure.Dapper": "1.47.1",
"Infrastructure.EntityFramework": "1.47.1"
"Core": "1.48.1",
"Infrastructure.Dapper": "1.48.1",
"Infrastructure.EntityFramework": "1.48.1"
}
}
}

View File

@ -0,0 +1,49 @@
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Json;
using System.Text.Json;
using System.Threading.Tasks;
using Bit.Core.Enums;
using Bit.Core.Models.Api.Request.Accounts;
using Bit.Core.Utilities;
using Bit.Identity;
using Bit.Test.Common.Helpers;
using Microsoft.AspNetCore.Http;
namespace Bit.IntegrationTestCommon.Factories
{
public class IdentityApplicationFactory : WebApplicationFactoryBase<Startup>
{
public const string DefaultDeviceIdentifier = "92b9d953-b9b6-4eaf-9d3e-11d57144dfeb";
public async Task<HttpContext> RegisterAsync(RegisterRequestModel model)
{
return await Server.PostAsync("/accounts/register", JsonContent.Create(model));
}
public async Task<(string Token, string RefreshToken)> TokenFromPasswordAsync(string username,
string password,
string deviceIdentifier = DefaultDeviceIdentifier,
string clientId = "web",
DeviceType deviceType = DeviceType.FirefoxBrowser,
string deviceName = "firefox")
{
var context = await Server.PostAsync("/connect/token", new FormUrlEncodedContent(new Dictionary<string, string>
{
{ "scope", "api offline_access" },
{ "client_id", clientId },
{ "deviceType", ((int)deviceType).ToString() },
{ "deviceIdentifier", deviceIdentifier },
{ "deviceName", deviceName },
{ "grant_type", "password" },
{ "username", username },
{ "password", password },
}), context => context.Request.Headers.Add("Auth-Email", CoreHelpers.Base64UrlEncodeString(username)));
using var body = await AssertHelper.AssertResponseTypeIs<JsonDocument>(context);
var root = body.RootElement;
return (root.GetProperty("access_token").GetString(), root.GetProperty("refresh_token").GetString());
}
}
}

View File

@ -0,0 +1,102 @@
using System.Collections.Generic;
using System.Linq;
using AspNetCoreRateLimit;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Infrastructure.EntityFramework.Repositories;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.AspNetCore.TestHost;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace Bit.IntegrationTestCommon.Factories
{
public static class FactoryConstants
{
public const string DefaultDatabaseName = "test_database";
public const string WhitelistedIp = "1.1.1.1";
}
public abstract class WebApplicationFactoryBase<T> : WebApplicationFactory<T>
where T : class
{
/// <summary>
/// The database name to use for this instance of the factory. By default it will use a shared database name so all instances will connect to the same database during it's lifetime.
/// </summary>
/// <remarks>
/// This will need to be set BEFORE using the <c>Server</c> property
/// </remarks>
public string DatabaseName { get; set; } = FactoryConstants.DefaultDatabaseName;
/// <summary>
/// Configure the web host to use an EF in memory database
/// </summary>
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureAppConfiguration(c =>
{
c.AddInMemoryCollection(new Dictionary<string, string>
{
// Manually insert a EF provider so that ConfigureServices will add EF repositories but we will override
// DbContextOptions to use an in memory database
{ "globalSettings:databaseProvider", "postgres" },
{ "globalSettings:postgreSql:connectionString", "Host=localhost;Username=test;Password=test;Database=test" },
});
});
builder.ConfigureTestServices(services =>
{
var dbContextOptions = services.First(sd => sd.ServiceType == typeof(DbContextOptions<DatabaseContext>));
services.Remove(dbContextOptions);
services.AddScoped(_ =>
{
return new DbContextOptionsBuilder<DatabaseContext>()
.UseInMemoryDatabase(DatabaseName)
.Options;
});
// QUESTION: The normal licensing service should run fine on developer machines but not in CI
// should we have a fork here to leave the normal service for developers?
// TODO: Eventually add the license file to CI
var licensingService = services.First(sd => sd.ServiceType == typeof(ILicensingService));
services.Remove(licensingService);
services.AddSingleton<ILicensingService, NoopLicensingService>();
// FUTURE CONSIDERATION: Add way to run this self hosted/cloud, for now it is cloud only
var pushRegistrationService = services.First(sd => sd.ServiceType == typeof(IPushRegistrationService));
services.Remove(pushRegistrationService);
services.AddSingleton<IPushRegistrationService, NoopPushRegistrationService>();
// Even though we are cloud we currently set this up as cloud, we can use the EF/selfhosted service
// instead of using Noop for this service
// TODO: Install and use azurite in CI pipeline
var eventWriteService = services.First(sd => sd.ServiceType == typeof(IEventWriteService));
services.Remove(eventWriteService);
services.AddSingleton<IEventWriteService, RepositoryEventWriteService>();
var eventRepositoryService = services.First(sd => sd.ServiceType == typeof(IEventRepository));
services.Remove(eventRepositoryService);
services.AddSingleton<IEventRepository, EventRepository>();
// Our Rate limiter works so well that it begins to fail tests unless we carve out
// one whitelisted ip. We should still test the rate limiter though and they should change the Ip
// to something that is NOT whitelisted
services.Configure<IpRateLimitOptions>(options =>
{
options.IpWhitelist = new List<string>
{
FactoryConstants.WhitelistedIp,
};
});
});
}
public DatabaseContext GetDatabaseContext()
{
var scope = Services.CreateScope();
return scope.ServiceProvider.GetRequiredService<DatabaseContext>();
}
}
}

View File

@ -0,0 +1,59 @@
using System;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.Primitives;
namespace Bit.IntegrationTestCommon.Factories
{
public static class WebApplicationFactoryExtensions
{
private static async Task<HttpContext> SendAsync(this TestServer server,
HttpMethod method,
string requestUri,
HttpContent content = null,
Action<HttpContext> extraConfiguration = null)
{
return await server.SendAsync(httpContext =>
{
// Automatically set the whitelisted IP so normal tests do not run into rate limit issues
// to test rate limiter, use the extraConfiguration parameter to set Connection.RemoteIpAddress
// it runs after this so it will take precedence.
httpContext.Connection.RemoteIpAddress = IPAddress.Parse(FactoryConstants.WhitelistedIp);
httpContext.Request.Path = new PathString(requestUri);
httpContext.Request.Method = method.Method;
if (content != null)
{
foreach (var header in content.Headers)
{
httpContext.Request.Headers.Add(header.Key, new StringValues(header.Value.ToArray()));
}
httpContext.Request.Body = content.ReadAsStream();
}
extraConfiguration?.Invoke(httpContext);
});
}
public static Task<HttpContext> PostAsync(this TestServer server,
string requestUri,
HttpContent content,
Action<HttpContext> extraConfiguration = null)
=> SendAsync(server, HttpMethod.Post, requestUri, content, extraConfiguration);
public static Task<HttpContext> GetAsync(this TestServer server,
string requestUri,
Action<HttpContext> extraConfiguration = null)
=> SendAsync(server, HttpMethod.Get, requestUri, content: null, extraConfiguration);
public static async Task<string> ReadBodyAsStringAsync(this HttpContext context)
{
using var sr = new StreamReader(context.Response.Body);
return await sr.ReadToEndAsync();
}
}
}

View File

@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="5.0.15" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="5.0.15" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="5.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Identity\Identity.csproj" />
<ProjectReference Include="..\Common\Common.csproj" />
</ItemGroup>
</Project>

File diff suppressed because it is too large Load Diff

View File

@ -13,6 +13,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Common", "Common\Common.csp
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing.Test", "Billing.Test\Billing.Test.csproj", "{8CD044FE-3FED-4F29-858C-B06BCE70EAA6}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Identity.IntegrationTest", "Identity.IntegrationTest\Identity.IntegrationTest.csproj", "{E2BB0D89-4570-43AB-A2E7-C8069AD90E6A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IntegrationTestCommon", "IntegrationTestCommon\IntegrationTestCommon.csproj", "{41188BB8-1FAF-45F6-8DC8-F316B8A6C56B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -86,5 +90,29 @@ Global
{8CD044FE-3FED-4F29-858C-B06BCE70EAA6}.Release|x64.Build.0 = Release|Any CPU
{8CD044FE-3FED-4F29-858C-B06BCE70EAA6}.Release|x86.ActiveCfg = Release|Any CPU
{8CD044FE-3FED-4F29-858C-B06BCE70EAA6}.Release|x86.Build.0 = Release|Any CPU
{E2BB0D89-4570-43AB-A2E7-C8069AD90E6A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E2BB0D89-4570-43AB-A2E7-C8069AD90E6A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E2BB0D89-4570-43AB-A2E7-C8069AD90E6A}.Debug|x64.ActiveCfg = Debug|Any CPU
{E2BB0D89-4570-43AB-A2E7-C8069AD90E6A}.Debug|x64.Build.0 = Debug|Any CPU
{E2BB0D89-4570-43AB-A2E7-C8069AD90E6A}.Debug|x86.ActiveCfg = Debug|Any CPU
{E2BB0D89-4570-43AB-A2E7-C8069AD90E6A}.Debug|x86.Build.0 = Debug|Any CPU
{E2BB0D89-4570-43AB-A2E7-C8069AD90E6A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E2BB0D89-4570-43AB-A2E7-C8069AD90E6A}.Release|Any CPU.Build.0 = Release|Any CPU
{E2BB0D89-4570-43AB-A2E7-C8069AD90E6A}.Release|x64.ActiveCfg = Release|Any CPU
{E2BB0D89-4570-43AB-A2E7-C8069AD90E6A}.Release|x64.Build.0 = Release|Any CPU
{E2BB0D89-4570-43AB-A2E7-C8069AD90E6A}.Release|x86.ActiveCfg = Release|Any CPU
{E2BB0D89-4570-43AB-A2E7-C8069AD90E6A}.Release|x86.Build.0 = Release|Any CPU
{41188BB8-1FAF-45F6-8DC8-F316B8A6C56B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{41188BB8-1FAF-45F6-8DC8-F316B8A6C56B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{41188BB8-1FAF-45F6-8DC8-F316B8A6C56B}.Debug|x64.ActiveCfg = Debug|Any CPU
{41188BB8-1FAF-45F6-8DC8-F316B8A6C56B}.Debug|x64.Build.0 = Debug|Any CPU
{41188BB8-1FAF-45F6-8DC8-F316B8A6C56B}.Debug|x86.ActiveCfg = Debug|Any CPU
{41188BB8-1FAF-45F6-8DC8-F316B8A6C56B}.Debug|x86.Build.0 = Debug|Any CPU
{41188BB8-1FAF-45F6-8DC8-F316B8A6C56B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{41188BB8-1FAF-45F6-8DC8-F316B8A6C56B}.Release|Any CPU.Build.0 = Release|Any CPU
{41188BB8-1FAF-45F6-8DC8-F316B8A6C56B}.Release|x64.ActiveCfg = Release|Any CPU
{41188BB8-1FAF-45F6-8DC8-F316B8A6C56B}.Release|x64.Build.0 = Release|Any CPU
{41188BB8-1FAF-45F6-8DC8-F316B8A6C56B}.Release|x86.ActiveCfg = Release|Any CPU
{41188BB8-1FAF-45F6-8DC8-F316B8A6C56B}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

View File

@ -3369,17 +3369,17 @@
"type": "Project",
"dependencies": {
"Azure.Messaging.EventGrid": "4.7.0",
"CommCore": "1.47.1",
"Core": "1.47.1",
"CommCore": "1.48.1",
"Core": "1.48.1",
"Microsoft.AspNetCore.Mvc.NewtonsoftJson": "5.0.9",
"SharedWeb": "1.47.1",
"SharedWeb": "1.48.1",
"Swashbuckle.AspNetCore": "6.2.3"
}
},
"commcore": {
"type": "Project",
"dependencies": {
"Core": "1.47.1"
"Core": "1.48.1"
}
},
"core": {
@ -3424,7 +3424,7 @@
"infrastructure.dapper": {
"type": "Project",
"dependencies": {
"Core": "1.47.1",
"Core": "1.48.1",
"Dapper": "2.0.123",
"System.Data.SqlClient": "4.8.3"
}
@ -3433,7 +3433,7 @@
"type": "Project",
"dependencies": {
"AutoMapper.Extensions.Microsoft.DependencyInjection": "8.0.1",
"Core": "1.47.1",
"Core": "1.48.1",
"Microsoft.EntityFrameworkCore.Relational": "5.0.12",
"Npgsql.EntityFrameworkCore.PostgreSQL": "5.0.2",
"Pomelo.EntityFrameworkCore.MySql": "5.0.3",
@ -3443,9 +3443,9 @@
"sharedweb": {
"type": "Project",
"dependencies": {
"Core": "1.47.1",
"Infrastructure.Dapper": "1.47.1",
"Infrastructure.EntityFramework": "1.47.1"
"Core": "1.48.1",
"Infrastructure.Dapper": "1.48.1",
"Infrastructure.EntityFramework": "1.48.1"
}
}
}

View File

@ -3369,17 +3369,17 @@
"type": "Project",
"dependencies": {
"Azure.Messaging.EventGrid": "4.7.0",
"CommCore": "1.47.1",
"Core": "1.47.1",
"CommCore": "1.48.1",
"Core": "1.48.1",
"Microsoft.AspNetCore.Mvc.NewtonsoftJson": "5.0.9",
"SharedWeb": "1.47.1",
"SharedWeb": "1.48.1",
"Swashbuckle.AspNetCore": "6.2.3"
}
},
"commcore": {
"type": "Project",
"dependencies": {
"Core": "1.47.1"
"Core": "1.48.1"
}
},
"core": {
@ -3424,7 +3424,7 @@
"infrastructure.dapper": {
"type": "Project",
"dependencies": {
"Core": "1.47.1",
"Core": "1.48.1",
"Dapper": "2.0.123",
"System.Data.SqlClient": "4.8.3"
}
@ -3433,7 +3433,7 @@
"type": "Project",
"dependencies": {
"AutoMapper.Extensions.Microsoft.DependencyInjection": "8.0.1",
"Core": "1.47.1",
"Core": "1.48.1",
"Microsoft.EntityFrameworkCore.Relational": "5.0.12",
"Npgsql.EntityFrameworkCore.PostgreSQL": "5.0.2",
"Pomelo.EntityFrameworkCore.MySql": "5.0.3",
@ -3443,9 +3443,9 @@
"sharedweb": {
"type": "Project",
"dependencies": {
"Core": "1.47.1",
"Infrastructure.Dapper": "1.47.1",
"Infrastructure.EntityFramework": "1.47.1"
"Core": "1.48.1",
"Infrastructure.Dapper": "1.48.1",
"Infrastructure.EntityFramework": "1.48.1"
}
}
}

View File

@ -3282,7 +3282,7 @@
"migrator": {
"type": "Project",
"dependencies": {
"Core": "1.46.2",
"Core": "1.48.1",
"Microsoft.Extensions.Logging": "5.0.0",
"dbup-sqlserver": "4.4.0"
}