1
0
mirror of https://github.com/bitwarden/server synced 2025-12-31 15:43:16 +00:00

Merge branch 'master' into flexible-collections/deprecate-custom-collection-perm

# Conflicts:
#	src/Api/AdminConsole/Controllers/OrganizationUsersController.cs
#	src/Api/Vault/AuthorizationHandlers/Collections/CollectionAuthorizationHandler.cs
#	test/Api.Test/Vault/AuthorizationHandlers/CollectionAuthorizationHandlerTests.cs
This commit is contained in:
Rui Tome
2023-11-23 16:36:52 +00:00
191 changed files with 3610 additions and 4080 deletions

View File

@@ -1,6 +1,6 @@
using Bit.Core.Auth.Models.Api.Request.Accounts;
using Bit.IntegrationTestCommon.Factories;
using IdentityServer4.AccessTokenValidation;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Data.Sqlite;
@@ -27,12 +27,12 @@ public class ApiApplicationFactory : WebApplicationFactoryBase<Startup>
builder.ConfigureTestServices(services =>
{
// Remove scheduled background jobs to prevent errors in parallel test execution
var jobService = services.First(sd => sd.ServiceType == typeof(Microsoft.Extensions.Hosting.IHostedService) && sd.ImplementationType == typeof(Bit.Api.Jobs.JobsHostedService));
var jobService = services.First(sd => sd.ServiceType == typeof(IHostedService) && sd.ImplementationType == typeof(Jobs.JobsHostedService));
services.Remove(jobService);
services.PostConfigure<IdentityServerAuthenticationOptions>(IdentityServerAuthenticationDefaults.AuthenticationScheme, options =>
services.Configure<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme, options =>
{
options.JwtBackChannelHandler = _identityApplicationFactory.Server.CreateHandler();
options.BackchannelHttpHandler = _identityApplicationFactory.Server.CreateHandler();
});
});
}

View File

@@ -364,6 +364,24 @@
"Microsoft.Win32.Registry": "5.0.0"
}
},
"Duende.IdentityServer": {
"type": "Transitive",
"resolved": "6.0.4",
"contentHash": "4HVjzx1F8v5J+U7oa8RGAQGj2QzmzNSu87r18Sh+dlh10uyZZL8teAaT/FaVLDObnfItGdPFvN8mwpF/HkI3Xw==",
"dependencies": {
"Duende.IdentityServer.Storage": "6.0.4",
"Microsoft.AspNetCore.Authentication.OpenIdConnect": "6.0.0"
}
},
"Duende.IdentityServer.Storage": {
"type": "Transitive",
"resolved": "6.0.4",
"contentHash": "s5gAjfbpr2IMgI+fU2Nx+2AZdzstmbt9gpo13iX7GwvqSeSaBVqj9ZskAN0R2KF1OemPdZuGnfaTcevdXMUrrw==",
"dependencies": {
"IdentityModel": "6.0.0",
"Microsoft.AspNetCore.DataProtection.Abstractions": "6.0.0"
}
},
"Fare": {
"type": "Transitive",
"resolved": "2.1.1",
@@ -408,49 +426,8 @@
},
"IdentityModel": {
"type": "Transitive",
"resolved": "4.4.0",
"contentHash": "b18wrIx5wnZlMxAX7oVsE+nDtAJ4hajYlH0xPlaRvo4r/fz08K6pPeZvbiqS9nfNbzfIgLFmNX+FL9qR9ZR5PA==",
"dependencies": {
"Newtonsoft.Json": "11.0.2",
"System.Text.Encodings.Web": "4.7.0"
}
},
"IdentityModel.AspNetCore.OAuth2Introspection": {
"type": "Transitive",
"resolved": "4.0.1",
"contentHash": "ZNdMZMaj9fqR3j50vYsu+1U3QGd6n8+fqwf+a8mCTcmXGor+HgFDfdq0mM34bsmD6uEgAQup7sv2ZW5kR36dbA==",
"dependencies": {
"IdentityModel": "4.0.0"
}
},
"IdentityServer4": {
"type": "Transitive",
"resolved": "4.1.2",
"contentHash": "blaxxGuOA7v/w1q+fxn97wZ+x2ecG1ZD4mc/N/ZOXMNeFZZhqv+4LF26Gecyik3nWrJPmbMEtQbLmRsKG8k61w==",
"dependencies": {
"IdentityModel": "4.4.0",
"IdentityServer4.Storage": "4.1.2",
"Microsoft.AspNetCore.Authentication.OpenIdConnect": "3.1.0",
"Microsoft.IdentityModel.Protocols.OpenIdConnect": "5.6.0",
"Newtonsoft.Json": "12.0.2"
}
},
"IdentityServer4.AccessTokenValidation": {
"type": "Transitive",
"resolved": "3.0.1",
"contentHash": "qu/M6UyN4o9NVep7q545Ms7hYAnsQqSdLbN1Fjjrn4m35lyBfeQPSSNzDryAKHbodyWOQfHaOqKEyMEJQ5Rpgw==",
"dependencies": {
"IdentityModel.AspNetCore.OAuth2Introspection": "4.0.1",
"Microsoft.AspNetCore.Authentication.JwtBearer": "3.0.0"
}
},
"IdentityServer4.Storage": {
"type": "Transitive",
"resolved": "4.1.2",
"contentHash": "KoSffyZyyeCNTIyJiZnCuPakJ1QbCHlpty6gbWUj/7yl+w0PXIchgmmJnJSvddzBb8iZ2xew/vGlxWUIP17P2g==",
"dependencies": {
"IdentityModel": "4.4.0"
}
"resolved": "6.0.0",
"contentHash": "eVHCR7a6m/dm5RFcBzE3qs/Jg5j9R5Rjpu8aTOv9e4AFvaQtBXb5ah7kmwU+YwA0ufRwz4wf1hnIvsD2hSnI4g=="
},
"Kralizek.AutoFixture.Extensions.MockHttp": {
"type": "Transitive",
@@ -551,10 +528,10 @@
},
"Microsoft.AspNetCore.Authentication.OpenIdConnect": {
"type": "Transitive",
"resolved": "3.1.0",
"contentHash": "O1cAQYUTU8EfRqwc5/rfTns4E4hKlFlg59fuKRrST+PzsxI6H07KqRN/JjdYhAuVYxF8jPnIGbj+zuc5paOWUw==",
"resolved": "6.0.0",
"contentHash": "cJxdro36spFzk/K2OFCddM6vZ+yoj6ug8mTFRH3Gdv1Pul/buSuCtfb/FSCp31UmS5S4C1315dU7wX3ErLFuDg==",
"dependencies": {
"Microsoft.IdentityModel.Protocols.OpenIdConnect": "5.5.0"
"Microsoft.IdentityModel.Protocols.OpenIdConnect": "6.10.0"
}
},
"Microsoft.AspNetCore.Cryptography.Internal": {
@@ -587,8 +564,8 @@
},
"Microsoft.AspNetCore.DataProtection.Abstractions": {
"type": "Transitive",
"resolved": "3.1.32",
"contentHash": "MPL4iVyiaRxnOUY5VATHjvhDWaAEFb77KFiUxVRklv3Z3v+STofUr1UG/aCt1O9cgN7FVTDaC5A7U+zsLub8Xg=="
"resolved": "6.0.0",
"contentHash": "Z/UU4NEBm5UgNufJmw+j5baW26ytCOIZ0G7sZocPaOzsUeBon1bkM3lSMNZQG2GmDjAIVP2XMSODf2jzSGbibw=="
},
"Microsoft.AspNetCore.Mvc.Testing": {
"type": "Transitive",
@@ -3154,25 +3131,25 @@
"AspNetCore.HealthChecks.SqlServer": "[6.0.2, )",
"AspNetCore.HealthChecks.Uris": "[6.0.3, )",
"Azure.Messaging.EventGrid": "[4.10.0, )",
"Commercial.Core": "[2023.10.2, )",
"Commercial.Infrastructure.EntityFramework": "[2023.10.2, )",
"Core": "[2023.10.2, )",
"SharedWeb": "[2023.10.2, )",
"Commercial.Core": "[2023.10.3, )",
"Commercial.Infrastructure.EntityFramework": "[2023.10.3, )",
"Core": "[2023.10.3, )",
"SharedWeb": "[2023.10.3, )",
"Swashbuckle.AspNetCore": "[6.5.0, )"
}
},
"commercial.core": {
"type": "Project",
"dependencies": {
"Core": "[2023.10.2, )"
"Core": "[2023.10.3, )"
}
},
"commercial.infrastructure.entityframework": {
"type": "Project",
"dependencies": {
"AutoMapper.Extensions.Microsoft.DependencyInjection": "[12.0.1, )",
"Core": "[2023.10.2, )",
"Infrastructure.EntityFramework": "[2023.10.2, )"
"Core": "[2023.10.3, )",
"Infrastructure.EntityFramework": "[2023.10.3, )"
}
},
"common": {
@@ -3180,7 +3157,7 @@
"dependencies": {
"AutoFixture.AutoNSubstitute": "[4.17.0, )",
"AutoFixture.Xunit2": "[4.17.0, )",
"Core": "[2023.10.2, )",
"Core": "[2023.10.3, )",
"Kralizek.AutoFixture.Extensions.MockHttp": "[1.2.0, )",
"Microsoft.NET.Test.Sdk": "[17.1.0, )",
"NSubstitute": "[4.3.0, )",
@@ -3202,10 +3179,9 @@
"BitPay.Light": "[1.0.1907, )",
"Braintree": "[5.19.0, )",
"DnsClient": "[1.7.0, )",
"Duende.IdentityServer": "[6.0.4, )",
"Fido2.AspNet": "[3.0.1, )",
"Handlebars.Net": "[2.1.2, )",
"IdentityServer4": "[4.1.2, )",
"IdentityServer4.AccessTokenValidation": "[3.0.1, )",
"LaunchDarkly.ServerSdk": "[8.0.0, )",
"MailKit": "[4.2.0, )",
"Microsoft.AspNetCore.Authentication.JwtBearer": "[6.0.4, )",
@@ -3233,15 +3209,15 @@
"identity": {
"type": "Project",
"dependencies": {
"Core": "[2023.10.2, )",
"SharedWeb": "[2023.10.2, )",
"Core": "[2023.10.3, )",
"SharedWeb": "[2023.10.3, )",
"Swashbuckle.AspNetCore.SwaggerGen": "[6.5.0, )"
}
},
"infrastructure.dapper": {
"type": "Project",
"dependencies": {
"Core": "[2023.10.2, )",
"Core": "[2023.10.3, )",
"Dapper": "[2.0.123, )"
}
},
@@ -3249,7 +3225,7 @@
"type": "Project",
"dependencies": {
"AutoMapper.Extensions.Microsoft.DependencyInjection": "[12.0.1, )",
"Core": "[2023.10.2, )",
"Core": "[2023.10.3, )",
"Microsoft.EntityFrameworkCore.Relational": "[7.0.5, )",
"Microsoft.EntityFrameworkCore.SqlServer": "[7.0.5, )",
"Microsoft.EntityFrameworkCore.Sqlite": "[7.0.5, )",
@@ -3261,8 +3237,8 @@
"integrationtestcommon": {
"type": "Project",
"dependencies": {
"Common": "[2023.10.2, )",
"Identity": "[2023.10.2, )",
"Common": "[2023.10.3, )",
"Identity": "[2023.10.3, )",
"Microsoft.AspNetCore.Mvc.Testing": "[6.0.5, )",
"Microsoft.Extensions.Configuration": "[6.0.1, )"
}
@@ -3270,9 +3246,9 @@
"sharedweb": {
"type": "Project",
"dependencies": {
"Core": "[2023.10.2, )",
"Infrastructure.Dapper": "[2023.10.2, )",
"Infrastructure.EntityFramework": "[2023.10.2, )"
"Core": "[2023.10.3, )",
"Infrastructure.Dapper": "[2023.10.3, )",
"Infrastructure.EntityFramework": "[2023.10.3, )"
}
}
}

View File

@@ -1,7 +1,10 @@
using Bit.Api.AdminConsole.Controllers;
using Bit.Api.AdminConsole.Models.Request.Organizations;
using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Enums;
using Bit.Core.AdminConsole.Models.Data.Organizations.Policies;
using Bit.Core.AdminConsole.Repositories;
using Bit.Core.Entities;
using Bit.Core.Models.Data.Organizations.Policies;
using Bit.Core.OrganizationFeatures.OrganizationUsers.Interfaces;
using Bit.Core.Repositories;
using Bit.Core.Services;
@@ -82,7 +85,7 @@ public class OrganizationUsersControllerTests
};
sutProvider.GetDependency<IUserService>().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(user);
sutProvider.GetDependency<IPolicyRepository>().GetByOrganizationIdTypeAsync(orgId,
Core.Enums.PolicyType.ResetPassword).Returns(policy);
PolicyType.ResetPassword).Returns(policy);
await sutProvider.Sut.Accept(orgId, orgUserId, model);

View File

@@ -1,9 +1,10 @@
using Bit.Api.Auth.Controllers;
using Bit.Api.Auth.Models.Request.Accounts;
using Bit.Api.Auth.Models.Request.Webauthn;
using Bit.Core.AdminConsole.Enums;
using Bit.Core.AdminConsole.Services;
using Bit.Core.Auth.Models.Business.Tokenables;
using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Exceptions;
using Bit.Core.Services;
using Bit.Core.Tokens;

View File

@@ -2,6 +2,7 @@
using Bit.Api.Auth.Models.Request.Accounts;
using Bit.Api.Controllers;
using Bit.Core.AdminConsole.Repositories;
using Bit.Core.AdminConsole.Services;
using Bit.Core.Auth.Models.Api.Request.Accounts;
using Bit.Core.Auth.Services;
using Bit.Core.Auth.UserFeatures.UserKey;

View File

@@ -1,10 +1,12 @@
using System.Security.Claims;
using System.Text.Json;
using Bit.Api.AdminConsole.Controllers;
using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Enums;
using Bit.Core.AdminConsole.Models.Data.Organizations.Policies;
using Bit.Core.AdminConsole.Repositories;
using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Exceptions;
using Bit.Core.Models.Data.Organizations.Policies;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Test.Common.AutoFixture;

View File

@@ -21,481 +21,6 @@ namespace Bit.Api.Test.Vault.AuthorizationHandlers;
[FeatureServiceCustomize(FeatureFlagKeys.FlexibleCollections)]
public class BulkCollectionAuthorizationHandlerTests
{
[Theory, CollectionCustomization]
[BitAutoData(OrganizationUserType.User, false, false)]
[BitAutoData(OrganizationUserType.Admin, false, true)]
[BitAutoData(OrganizationUserType.Owner, false, true)]
[BitAutoData(OrganizationUserType.Custom, true, true)]
public async Task CanCreateAsync_Success(
OrganizationUserType userType, bool createNewCollection, bool limitCollectionCreateDelete,
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<Collection> collections,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
organization.Type = userType;
organization.Permissions = new Permissions
{
CreateNewCollections = createNewCollection
};
organization.LimitCollectionCreationDeletion = limitCollectionCreateDelete;
var context = new AuthorizationHandlerContext(
new[] { CollectionOperations.Create },
new ClaimsPrincipal(),
collections);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
await sutProvider.Sut.HandleAsync(context);
Assert.True(context.HasSucceeded);
}
[Theory, BitAutoData, CollectionCustomization]
public async Task CanCreateAsync_WhenProviderUser_Success(
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<CollectionDetails> collections,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
organization.Type = OrganizationUserType.User;
organization.Permissions = new Permissions
{
CreateNewCollections = false
};
organization.LimitCollectionCreationDeletion = true;
var context = new AuthorizationHandlerContext(
new[] { CollectionOperations.Create },
new ClaimsPrincipal(),
collections);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICurrentContext>().ProviderUserForOrgAsync(organization.Id).Returns(true);
await sutProvider.Sut.HandleAsync(context);
Assert.True(context.HasSucceeded);
}
[Theory, CollectionCustomization]
[BitAutoData(OrganizationUserType.User)]
[BitAutoData(OrganizationUserType.Custom)]
public async Task CanCreateAsync_WhenMissingCreateCollectionPermission_Failure(
OrganizationUserType userType,
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<Collection> collections,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
organization.Type = userType;
organization.Permissions = new Permissions
{
CreateNewCollections = false
};
organization.LimitCollectionCreationDeletion = true;
var context = new AuthorizationHandlerContext(
new[] { CollectionOperations.Create },
new ClaimsPrincipal(),
collections);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
await sutProvider.Sut.HandleAsync(context);
Assert.False(context.HasSucceeded);
}
[Theory, CollectionCustomization]
[BitAutoData(OrganizationUserType.Admin)]
[BitAutoData(OrganizationUserType.Owner)]
public async Task CanReadAsync_WhenAdminOrOwner_Success(
OrganizationUserType userType,
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<CollectionDetails> collections,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
organization.Type = userType;
organization.Permissions = new Permissions();
var context = new AuthorizationHandlerContext(
new[] { CollectionOperations.Read },
new ClaimsPrincipal(),
collections);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
await sutProvider.Sut.HandleAsync(context);
Assert.True(context.HasSucceeded);
}
[Theory, BitAutoData, CollectionCustomization]
public async Task CanReadAsync_WhenProviderUser_Success(
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<CollectionDetails> collections,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
organization.Type = OrganizationUserType.User;
organization.Permissions = new Permissions();
var context = new AuthorizationHandlerContext(
new[] { CollectionOperations.Read },
new ClaimsPrincipal(),
collections);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICurrentContext>().ProviderUserForOrgAsync(organization.Id).Returns(true);
await sutProvider.Sut.HandleAsync(context);
Assert.True(context.HasSucceeded);
}
[Theory, CollectionCustomization]
[BitAutoData(true, false)]
[BitAutoData(false, true)]
public async Task CanReadAsync_WhenCustomUserWithRequiredPermissions_Success(
bool editAnyCollection, bool deleteAnyCollection,
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<CollectionDetails> collections,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
organization.Type = OrganizationUserType.Custom;
organization.Permissions = new Permissions
{
EditAnyCollection = editAnyCollection,
DeleteAnyCollection = deleteAnyCollection
};
var context = new AuthorizationHandlerContext(
new[] { CollectionOperations.Read },
new ClaimsPrincipal(),
collections);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
await sutProvider.Sut.HandleAsync(context);
Assert.True(context.HasSucceeded);
}
[Theory, BitAutoData, CollectionCustomization]
public async Task CanReadAsync_WhenUserAssigned_Success(
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<CollectionDetails> collections,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
organization.Type = OrganizationUserType.User;
organization.Permissions = new Permissions();
var context = new AuthorizationHandlerContext(
new[] { CollectionOperations.Read },
new ClaimsPrincipal(),
collections);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICollectionRepository>().GetManyByUserIdAsync(actingUserId).Returns(collections);
await sutProvider.Sut.HandleAsync(context);
Assert.True(context.HasSucceeded);
}
[Theory, CollectionCustomization]
[BitAutoData(OrganizationUserType.User)]
[BitAutoData(OrganizationUserType.Custom)]
public async Task CanReadAsync_WhenMissingAccess_Failure(
OrganizationUserType userType,
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<CollectionDetails> collections,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
organization.Type = userType;
organization.Permissions = new Permissions
{
EditAnyCollection = false,
DeleteAnyCollection = false
};
var context = new AuthorizationHandlerContext(
new[] { CollectionOperations.Read },
new ClaimsPrincipal(),
collections);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
await sutProvider.Sut.HandleAsync(context);
Assert.False(context.HasSucceeded);
}
[Theory, CollectionCustomization]
[BitAutoData(OrganizationUserType.Admin)]
[BitAutoData(OrganizationUserType.Owner)]
public async Task CanReadAccessAsync_WhenAdminOrOwner_Success(
OrganizationUserType userType,
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<CollectionDetails> collections,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
organization.Type = userType;
organization.Permissions = new Permissions();
var context = new AuthorizationHandlerContext(
new[] { CollectionOperations.ReadAccess },
new ClaimsPrincipal(),
collections);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
await sutProvider.Sut.HandleAsync(context);
Assert.True(context.HasSucceeded);
}
[Theory, BitAutoData, CollectionCustomization]
public async Task CanReadAccessAsync_WhenProviderUser_Success(
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<CollectionDetails> collections,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
organization.Type = OrganizationUserType.User;
organization.Permissions = new Permissions();
var context = new AuthorizationHandlerContext(
new[] { CollectionOperations.ReadAccess },
new ClaimsPrincipal(),
collections);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICurrentContext>().ProviderUserForOrgAsync(organization.Id).Returns(true);
await sutProvider.Sut.HandleAsync(context);
Assert.True(context.HasSucceeded);
}
[Theory, CollectionCustomization]
[BitAutoData(true, false)]
[BitAutoData(false, true)]
public async Task CanReadAccessAsync_WhenCustomUserWithRequiredPermissions_Success(
bool editAnyCollection, bool deleteAnyCollection,
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<CollectionDetails> collections,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
organization.Type = OrganizationUserType.Custom;
organization.Permissions = new Permissions
{
EditAnyCollection = editAnyCollection,
DeleteAnyCollection = deleteAnyCollection
};
var context = new AuthorizationHandlerContext(
new[] { CollectionOperations.ReadAccess },
new ClaimsPrincipal(),
collections);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
await sutProvider.Sut.HandleAsync(context);
Assert.True(context.HasSucceeded);
}
[Theory, BitAutoData, CollectionCustomization]
public async Task CanReadAccessAsync_WhenUserAssigned_Success(
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<CollectionDetails> collections,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
organization.Type = OrganizationUserType.User;
organization.Permissions = new Permissions();
var context = new AuthorizationHandlerContext(
new[] { CollectionOperations.ReadAccess },
new ClaimsPrincipal(),
collections);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICollectionRepository>().GetManyByUserIdAsync(actingUserId).Returns(collections);
await sutProvider.Sut.HandleAsync(context);
Assert.True(context.HasSucceeded);
}
[Theory, CollectionCustomization]
[BitAutoData(OrganizationUserType.User)]
[BitAutoData(OrganizationUserType.Custom)]
public async Task CanReadAccessAsync_WhenMissingAccess_Failure(
OrganizationUserType userType,
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<CollectionDetails> collections,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
organization.Type = userType;
organization.Permissions = new Permissions
{
EditAnyCollection = false,
DeleteAnyCollection = false
};
var context = new AuthorizationHandlerContext(
new[] { CollectionOperations.ReadAccess },
new ClaimsPrincipal(),
collections);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
await sutProvider.Sut.HandleAsync(context);
Assert.False(context.HasSucceeded);
}
[Theory, CollectionCustomization]
[BitAutoData(OrganizationUserType.User, false, true)]
[BitAutoData(OrganizationUserType.Admin, false, false)]
[BitAutoData(OrganizationUserType.Owner, false, false)]
[BitAutoData(OrganizationUserType.Custom, true, false)]
[BitAutoData(OrganizationUserType.Owner, true, true)]
public async Task CanUpdateAsync_Success(
OrganizationUserType userType, bool editAnyCollection, bool manageCollections,
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<Collection> collections,
ICollection<CollectionDetails> collectionDetails,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
foreach (var collectionDetail in collectionDetails)
{
collectionDetail.Manage = manageCollections;
}
organization.Type = userType;
organization.Permissions = new Permissions
{
EditAnyCollection = editAnyCollection
};
var context = new AuthorizationHandlerContext(
new[] { CollectionOperations.Update },
new ClaimsPrincipal(),
collections);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICollectionRepository>().GetManyByUserIdAsync(actingUserId).Returns(collectionDetails);
await sutProvider.Sut.HandleAsync(context);
Assert.True(context.HasSucceeded);
}
[Theory, BitAutoData, CollectionCustomization]
public async Task CanUpdateAsync_WhenProviderUser_Success(
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<Collection> collections,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
organization.Type = OrganizationUserType.User;
organization.Permissions = new Permissions();
var context = new AuthorizationHandlerContext(
new[] { CollectionOperations.Update },
new ClaimsPrincipal(),
collections);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICurrentContext>().ProviderUserForOrgAsync(organization.Id).Returns(true);
await sutProvider.Sut.HandleAsync(context);
Assert.True(context.HasSucceeded);
}
[Theory, BitAutoData, CollectionCustomization]
public async Task CanUpdateAsync_WhenMissingManageCollectionPermission_Failure(
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<Collection> collections,
ICollection<CollectionDetails> collectionDetails,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
foreach (var collectionDetail in collectionDetails)
{
collectionDetail.Manage = true;
}
// Simulate one collection missing the manage permission
collectionDetails.First().Manage = false;
// Ensure the user is not an owner/admin and does not have edit any collection permission
organization.Type = OrganizationUserType.User;
organization.Permissions = new Permissions
{
EditAnyCollection = false
};
var context = new AuthorizationHandlerContext(
new[] { CollectionOperations.Update },
new ClaimsPrincipal(),
collections
);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(Arg.Any<Guid>()).Returns(organization);
sutProvider.GetDependency<ICollectionRepository>().GetManyByUserIdAsync(actingUserId).Returns(collectionDetails);
await sutProvider.Sut.HandleAsync(context);
Assert.False(context.HasSucceeded);
sutProvider.GetDependency<ICurrentContext>().ReceivedWithAnyArgs().GetOrganization(default);
await sutProvider.GetDependency<ICollectionRepository>().ReceivedWithAnyArgs()
.GetManyByUserIdAsync(default);
}
[Theory, CollectionCustomization]
[BitAutoData(OrganizationUserType.User, false, true)]
[BitAutoData(OrganizationUserType.Admin, false, false)]
@@ -516,10 +41,7 @@ public class BulkCollectionAuthorizationHandlerTests
}
organization.Type = userType;
organization.Permissions = new Permissions
{
EditAnyCollection = editAnyCollection
};
organization.Permissions.EditAnyCollection = editAnyCollection;
var context = new AuthorizationHandlerContext(
new[] { CollectionOperations.ModifyAccess },
@@ -535,71 +57,36 @@ public class BulkCollectionAuthorizationHandlerTests
Assert.True(context.HasSucceeded);
}
[Theory, BitAutoData, CollectionCustomization]
public async Task CanManageCollectionAccessAsync_WhenProviderUser_Success(
[Theory, CollectionCustomization]
[BitAutoData(OrganizationUserType.User, false, false)]
[BitAutoData(OrganizationUserType.Admin, false, true)]
[BitAutoData(OrganizationUserType.Owner, false, true)]
[BitAutoData(OrganizationUserType.Custom, true, true)]
public async Task CanCreateAsync_Success(
OrganizationUserType userType, bool createNewCollection, bool limitCollectionCreateDelete,
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<Collection> collections,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
organization.Type = OrganizationUserType.User;
organization.Permissions = new Permissions();
organization.Type = userType;
organization.Permissions.CreateNewCollections = createNewCollection;
organization.LimitCollectionCreationDeletion = limitCollectionCreateDelete;
var context = new AuthorizationHandlerContext(
new[] { CollectionOperations.ModifyAccess },
new[] { CollectionOperations.Create },
new ClaimsPrincipal(),
collections);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICurrentContext>().ProviderUserForOrgAsync(organization.Id).Returns(true);
await sutProvider.Sut.HandleAsync(context);
Assert.True(context.HasSucceeded);
}
[Theory, BitAutoData, CollectionCustomization]
public async Task CanManageCollectionAccessAsync_WhenMissingManageCollectionPermission_Failure(
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<Collection> collections,
ICollection<CollectionDetails> collectionDetails,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
foreach (var collectionDetail in collectionDetails)
{
collectionDetail.Manage = true;
}
// Simulate one collection missing the manage permission
collectionDetails.First().Manage = false;
// Ensure the user is not an owner/admin and does not have edit any collection permission
organization.Type = OrganizationUserType.User;
organization.Permissions = new Permissions
{
EditAnyCollection = false
};
var context = new AuthorizationHandlerContext(
new[] { CollectionOperations.ModifyAccess },
new ClaimsPrincipal(),
collections
);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(Arg.Any<Guid>()).Returns(organization);
sutProvider.GetDependency<ICollectionRepository>().GetManyByUserIdAsync(actingUserId).Returns(collectionDetails);
await sutProvider.Sut.HandleAsync(context);
Assert.False(context.HasSucceeded);
sutProvider.GetDependency<ICurrentContext>().ReceivedWithAnyArgs().GetOrganization(default);
await sutProvider.GetDependency<ICollectionRepository>().ReceivedWithAnyArgs()
.GetManyByUserIdAsync(default);
}
[Theory, CollectionCustomization]
[BitAutoData(OrganizationUserType.User, false, false, true)]
[BitAutoData(OrganizationUserType.Admin, false, true, false)]
@@ -619,10 +106,7 @@ public class BulkCollectionAuthorizationHandlerTests
}
organization.Type = userType;
organization.Permissions = new Permissions
{
DeleteAnyCollection = deleteAnyCollection
};
organization.Permissions.DeleteAnyCollection = deleteAnyCollection;
organization.LimitCollectionCreationDeletion = limitCollectionCreateDelete;
var context = new AuthorizationHandlerContext(
@@ -639,125 +123,6 @@ public class BulkCollectionAuthorizationHandlerTests
Assert.True(context.HasSucceeded);
}
[Theory, BitAutoData, CollectionCustomization]
public async Task CanDeleteAsync_WithProviderUser_Success(
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<Collection> collections,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
organization.Type = OrganizationUserType.User;
organization.Permissions = new Permissions
{
DeleteAnyCollection = false
};
var context = new AuthorizationHandlerContext(
new[] { CollectionOperations.Delete },
new ClaimsPrincipal(),
collections);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
sutProvider.GetDependency<ICurrentContext>().ProviderUserForOrgAsync(organization.Id).Returns(true);
await sutProvider.Sut.HandleAsync(context);
Assert.True(context.HasSucceeded);
}
[Theory, BitAutoData, CollectionCustomization]
public async Task CanDeleteAsync_WhenCustomUserWithRequiredPermissions_Success(
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<CollectionDetails> collections,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
organization.Type = OrganizationUserType.Custom;
organization.Permissions = new Permissions
{
DeleteAnyCollection = true
};
var context = new AuthorizationHandlerContext(
new[] { CollectionOperations.Delete },
new ClaimsPrincipal(),
collections);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
await sutProvider.Sut.HandleAsync(context);
Assert.True(context.HasSucceeded);
}
[Theory, CollectionCustomization]
[BitAutoData(OrganizationUserType.User)]
[BitAutoData(OrganizationUserType.Custom)]
public async Task CanDeleteAsync_WhenMissingPermissions_Failure(
OrganizationUserType userType,
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<Collection> collections,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
organization.Type = userType;
organization.Permissions = new Permissions
{
DeleteAnyCollection = false
};
var context = new AuthorizationHandlerContext(
new[] { CollectionOperations.Delete },
new ClaimsPrincipal(),
collections);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
await sutProvider.Sut.HandleAsync(context);
Assert.False(context.HasSucceeded);
}
[Theory, BitAutoData, CollectionCustomization]
public async Task HandleRequirementAsync_NullResource_NoSuccessOrFailure(
SutProvider<BulkCollectionAuthorizationHandler> sutProvider)
{
var context = new AuthorizationHandlerContext(
new[] { CollectionOperations.Create },
new ClaimsPrincipal(),
null
);
await sutProvider.Sut.HandleAsync(context);
Assert.False(context.HasSucceeded);
Assert.False(context.HasFailed);
}
[Theory, BitAutoData, CollectionCustomization]
public async Task HandleRequirementAsync_EmptyResourceList_NoSuccessOrFailure(
SutProvider<BulkCollectionAuthorizationHandler> sutProvider)
{
var context = new AuthorizationHandlerContext(
new[] { CollectionOperations.Create },
new ClaimsPrincipal(),
new List<Collection>()
);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(new Guid());
await sutProvider.Sut.HandleAsync(context);
Assert.False(context.HasSucceeded);
Assert.False(context.HasFailed);
}
[Theory, BitAutoData, CollectionCustomization]
public async Task HandleRequirementAsync_MissingUserId_Failure(
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
@@ -778,7 +143,7 @@ public class BulkCollectionAuthorizationHandlerTests
}
[Theory, BitAutoData, CollectionCustomization]
public async Task HandleRequirementAsync_TargetCollectionsMultipleOrgs_Failure(
public async Task HandleRequirementAsync_TargetCollectionsMultipleOrgs_Exception(
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
IList<Collection> collections)
{
@@ -801,7 +166,7 @@ public class BulkCollectionAuthorizationHandlerTests
}
[Theory, BitAutoData, CollectionCustomization]
public async Task HandleRequirementAsync_MissingOrg_Failure(
public async Task HandleRequirementAsync_MissingOrg_NoSuccess(
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<Collection> collections)
{
@@ -817,6 +182,76 @@ public class BulkCollectionAuthorizationHandlerTests
sutProvider.GetDependency<ICurrentContext>().GetOrganization(Arg.Any<Guid>()).Returns((CurrentContextOrganization)null);
await sutProvider.Sut.HandleAsync(context);
Assert.True(context.HasFailed);
Assert.False(context.HasSucceeded);
}
[Theory, BitAutoData, CollectionCustomization]
public async Task HandleRequirementAsync_Provider_Success(
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<Collection> collections)
{
var operationsToTest = new[]
{
CollectionOperations.Create, CollectionOperations.Delete, CollectionOperations.ModifyAccess
};
foreach (var op in operationsToTest)
{
var actingUserId = Guid.NewGuid();
var context = new AuthorizationHandlerContext(
new[] { op },
new ClaimsPrincipal(),
collections
);
var orgId = collections.First().OrganizationId;
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(orgId).Returns((CurrentContextOrganization)null);
sutProvider.GetDependency<ICurrentContext>().ProviderUserForOrgAsync(Arg.Any<Guid>()).Returns(true);
await sutProvider.Sut.HandleAsync(context);
Assert.True(context.HasSucceeded);
await sutProvider.GetDependency<ICurrentContext>().Received().ProviderUserForOrgAsync(orgId);
// Recreate the SUT to reset the mocks/dependencies between tests
sutProvider.Recreate();
}
}
[Theory, BitAutoData, CollectionCustomization]
public async Task CanManageCollectionAccessAsync_MissingManageCollectionPermission_NoSuccess(
SutProvider<BulkCollectionAuthorizationHandler> sutProvider,
ICollection<Collection> collections,
ICollection<CollectionDetails> collectionDetails,
CurrentContextOrganization organization)
{
var actingUserId = Guid.NewGuid();
foreach (var collectionDetail in collectionDetails)
{
collectionDetail.Manage = true;
}
// Simulate one collection missing the manage permission
collectionDetails.First().Manage = false;
// Ensure the user is not an owner/admin and does not have edit any collection permission
organization.Type = OrganizationUserType.User;
organization.Permissions.EditAnyCollection = false;
var context = new AuthorizationHandlerContext(
new[] { CollectionOperations.ModifyAccess },
new ClaimsPrincipal(),
collections
);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().GetOrganization(Arg.Any<Guid>()).Returns(organization);
sutProvider.GetDependency<ICollectionRepository>().GetManyByUserIdAsync(actingUserId).Returns(collectionDetails);
await sutProvider.Sut.HandleAsync(context);
Assert.False(context.HasSucceeded);
sutProvider.GetDependency<ICurrentContext>().ReceivedWithAnyArgs().GetOrganization(default);
await sutProvider.GetDependency<ICollectionRepository>().ReceivedWithAnyArgs()
.GetManyByUserIdAsync(default);
}
}

View File

@@ -3,6 +3,7 @@ using System.Text.Json;
using AutoFixture;
using Bit.Api.Vault.Controllers;
using Bit.Api.Vault.Models.Response;
using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Enums.Provider;
using Bit.Core.AdminConsole.Models.Data.Provider;
using Bit.Core.AdminConsole.Repositories;

View File

@@ -374,6 +374,24 @@
"Microsoft.Win32.Registry": "5.0.0"
}
},
"Duende.IdentityServer": {
"type": "Transitive",
"resolved": "6.0.4",
"contentHash": "4HVjzx1F8v5J+U7oa8RGAQGj2QzmzNSu87r18Sh+dlh10uyZZL8teAaT/FaVLDObnfItGdPFvN8mwpF/HkI3Xw==",
"dependencies": {
"Duende.IdentityServer.Storage": "6.0.4",
"Microsoft.AspNetCore.Authentication.OpenIdConnect": "6.0.0"
}
},
"Duende.IdentityServer.Storage": {
"type": "Transitive",
"resolved": "6.0.4",
"contentHash": "s5gAjfbpr2IMgI+fU2Nx+2AZdzstmbt9gpo13iX7GwvqSeSaBVqj9ZskAN0R2KF1OemPdZuGnfaTcevdXMUrrw==",
"dependencies": {
"IdentityModel": "6.0.0",
"Microsoft.AspNetCore.DataProtection.Abstractions": "6.0.0"
}
},
"Fare": {
"type": "Transitive",
"resolved": "2.1.1",
@@ -418,49 +436,8 @@
},
"IdentityModel": {
"type": "Transitive",
"resolved": "4.4.0",
"contentHash": "b18wrIx5wnZlMxAX7oVsE+nDtAJ4hajYlH0xPlaRvo4r/fz08K6pPeZvbiqS9nfNbzfIgLFmNX+FL9qR9ZR5PA==",
"dependencies": {
"Newtonsoft.Json": "11.0.2",
"System.Text.Encodings.Web": "4.7.0"
}
},
"IdentityModel.AspNetCore.OAuth2Introspection": {
"type": "Transitive",
"resolved": "4.0.1",
"contentHash": "ZNdMZMaj9fqR3j50vYsu+1U3QGd6n8+fqwf+a8mCTcmXGor+HgFDfdq0mM34bsmD6uEgAQup7sv2ZW5kR36dbA==",
"dependencies": {
"IdentityModel": "4.0.0"
}
},
"IdentityServer4": {
"type": "Transitive",
"resolved": "4.1.2",
"contentHash": "blaxxGuOA7v/w1q+fxn97wZ+x2ecG1ZD4mc/N/ZOXMNeFZZhqv+4LF26Gecyik3nWrJPmbMEtQbLmRsKG8k61w==",
"dependencies": {
"IdentityModel": "4.4.0",
"IdentityServer4.Storage": "4.1.2",
"Microsoft.AspNetCore.Authentication.OpenIdConnect": "3.1.0",
"Microsoft.IdentityModel.Protocols.OpenIdConnect": "5.6.0",
"Newtonsoft.Json": "12.0.2"
}
},
"IdentityServer4.AccessTokenValidation": {
"type": "Transitive",
"resolved": "3.0.1",
"contentHash": "qu/M6UyN4o9NVep7q545Ms7hYAnsQqSdLbN1Fjjrn4m35lyBfeQPSSNzDryAKHbodyWOQfHaOqKEyMEJQ5Rpgw==",
"dependencies": {
"IdentityModel.AspNetCore.OAuth2Introspection": "4.0.1",
"Microsoft.AspNetCore.Authentication.JwtBearer": "3.0.0"
}
},
"IdentityServer4.Storage": {
"type": "Transitive",
"resolved": "4.1.2",
"contentHash": "KoSffyZyyeCNTIyJiZnCuPakJ1QbCHlpty6gbWUj/7yl+w0PXIchgmmJnJSvddzBb8iZ2xew/vGlxWUIP17P2g==",
"dependencies": {
"IdentityModel": "4.4.0"
}
"resolved": "6.0.0",
"contentHash": "eVHCR7a6m/dm5RFcBzE3qs/Jg5j9R5Rjpu8aTOv9e4AFvaQtBXb5ah7kmwU+YwA0ufRwz4wf1hnIvsD2hSnI4g=="
},
"Kralizek.AutoFixture.Extensions.MockHttp": {
"type": "Transitive",
@@ -561,10 +538,10 @@
},
"Microsoft.AspNetCore.Authentication.OpenIdConnect": {
"type": "Transitive",
"resolved": "3.1.0",
"contentHash": "O1cAQYUTU8EfRqwc5/rfTns4E4hKlFlg59fuKRrST+PzsxI6H07KqRN/JjdYhAuVYxF8jPnIGbj+zuc5paOWUw==",
"resolved": "6.0.0",
"contentHash": "cJxdro36spFzk/K2OFCddM6vZ+yoj6ug8mTFRH3Gdv1Pul/buSuCtfb/FSCp31UmS5S4C1315dU7wX3ErLFuDg==",
"dependencies": {
"Microsoft.IdentityModel.Protocols.OpenIdConnect": "5.5.0"
"Microsoft.IdentityModel.Protocols.OpenIdConnect": "6.10.0"
}
},
"Microsoft.AspNetCore.Cryptography.Internal": {
@@ -597,8 +574,8 @@
},
"Microsoft.AspNetCore.DataProtection.Abstractions": {
"type": "Transitive",
"resolved": "3.1.32",
"contentHash": "MPL4iVyiaRxnOUY5VATHjvhDWaAEFb77KFiUxVRklv3Z3v+STofUr1UG/aCt1O9cgN7FVTDaC5A7U+zsLub8Xg=="
"resolved": "6.0.0",
"contentHash": "Z/UU4NEBm5UgNufJmw+j5baW26ytCOIZ0G7sZocPaOzsUeBon1bkM3lSMNZQG2GmDjAIVP2XMSODf2jzSGbibw=="
},
"Microsoft.Azure.Amqp": {
"type": "Transitive",
@@ -3031,25 +3008,25 @@
"AspNetCore.HealthChecks.SqlServer": "[6.0.2, )",
"AspNetCore.HealthChecks.Uris": "[6.0.3, )",
"Azure.Messaging.EventGrid": "[4.10.0, )",
"Commercial.Core": "[2023.10.2, )",
"Commercial.Infrastructure.EntityFramework": "[2023.10.2, )",
"Core": "[2023.10.2, )",
"SharedWeb": "[2023.10.2, )",
"Commercial.Core": "[2023.10.3, )",
"Commercial.Infrastructure.EntityFramework": "[2023.10.3, )",
"Core": "[2023.10.3, )",
"SharedWeb": "[2023.10.3, )",
"Swashbuckle.AspNetCore": "[6.5.0, )"
}
},
"commercial.core": {
"type": "Project",
"dependencies": {
"Core": "[2023.10.2, )"
"Core": "[2023.10.3, )"
}
},
"commercial.infrastructure.entityframework": {
"type": "Project",
"dependencies": {
"AutoMapper.Extensions.Microsoft.DependencyInjection": "[12.0.1, )",
"Core": "[2023.10.2, )",
"Infrastructure.EntityFramework": "[2023.10.2, )"
"Core": "[2023.10.3, )",
"Infrastructure.EntityFramework": "[2023.10.3, )"
}
},
"common": {
@@ -3057,7 +3034,7 @@
"dependencies": {
"AutoFixture.AutoNSubstitute": "[4.17.0, )",
"AutoFixture.Xunit2": "[4.17.0, )",
"Core": "[2023.10.2, )",
"Core": "[2023.10.3, )",
"Kralizek.AutoFixture.Extensions.MockHttp": "[1.2.0, )",
"Microsoft.NET.Test.Sdk": "[17.1.0, )",
"NSubstitute": "[4.3.0, )",
@@ -3079,10 +3056,9 @@
"BitPay.Light": "[1.0.1907, )",
"Braintree": "[5.19.0, )",
"DnsClient": "[1.7.0, )",
"Duende.IdentityServer": "[6.0.4, )",
"Fido2.AspNet": "[3.0.1, )",
"Handlebars.Net": "[2.1.2, )",
"IdentityServer4": "[4.1.2, )",
"IdentityServer4.AccessTokenValidation": "[3.0.1, )",
"LaunchDarkly.ServerSdk": "[8.0.0, )",
"MailKit": "[4.2.0, )",
"Microsoft.AspNetCore.Authentication.JwtBearer": "[6.0.4, )",
@@ -3112,8 +3088,8 @@
"dependencies": {
"AutoFixture.AutoNSubstitute": "[4.17.0, )",
"AutoFixture.Xunit2": "[4.17.0, )",
"Common": "[2023.10.2, )",
"Core": "[2023.10.2, )",
"Common": "[2023.10.3, )",
"Core": "[2023.10.3, )",
"Kralizek.AutoFixture.Extensions.MockHttp": "[1.2.0, )",
"Microsoft.NET.Test.Sdk": "[17.1.0, )",
"NSubstitute": "[4.3.0, )",
@@ -3123,7 +3099,7 @@
"infrastructure.dapper": {
"type": "Project",
"dependencies": {
"Core": "[2023.10.2, )",
"Core": "[2023.10.3, )",
"Dapper": "[2.0.123, )"
}
},
@@ -3131,7 +3107,7 @@
"type": "Project",
"dependencies": {
"AutoMapper.Extensions.Microsoft.DependencyInjection": "[12.0.1, )",
"Core": "[2023.10.2, )",
"Core": "[2023.10.3, )",
"Microsoft.EntityFrameworkCore.Relational": "[7.0.5, )",
"Microsoft.EntityFrameworkCore.SqlServer": "[7.0.5, )",
"Microsoft.EntityFrameworkCore.Sqlite": "[7.0.5, )",
@@ -3143,9 +3119,9 @@
"sharedweb": {
"type": "Project",
"dependencies": {
"Core": "[2023.10.2, )",
"Infrastructure.Dapper": "[2023.10.2, )",
"Infrastructure.EntityFramework": "[2023.10.2, )"
"Core": "[2023.10.3, )",
"Infrastructure.Dapper": "[2023.10.3, )",
"Infrastructure.EntityFramework": "[2023.10.3, )"
}
}
}

View File

@@ -280,6 +280,24 @@
"Microsoft.Win32.Registry": "5.0.0"
}
},
"Duende.IdentityServer": {
"type": "Transitive",
"resolved": "6.0.4",
"contentHash": "4HVjzx1F8v5J+U7oa8RGAQGj2QzmzNSu87r18Sh+dlh10uyZZL8teAaT/FaVLDObnfItGdPFvN8mwpF/HkI3Xw==",
"dependencies": {
"Duende.IdentityServer.Storage": "6.0.4",
"Microsoft.AspNetCore.Authentication.OpenIdConnect": "6.0.0"
}
},
"Duende.IdentityServer.Storage": {
"type": "Transitive",
"resolved": "6.0.4",
"contentHash": "s5gAjfbpr2IMgI+fU2Nx+2AZdzstmbt9gpo13iX7GwvqSeSaBVqj9ZskAN0R2KF1OemPdZuGnfaTcevdXMUrrw==",
"dependencies": {
"IdentityModel": "6.0.0",
"Microsoft.AspNetCore.DataProtection.Abstractions": "6.0.0"
}
},
"Fare": {
"type": "Transitive",
"resolved": "2.1.1",
@@ -324,49 +342,8 @@
},
"IdentityModel": {
"type": "Transitive",
"resolved": "4.4.0",
"contentHash": "b18wrIx5wnZlMxAX7oVsE+nDtAJ4hajYlH0xPlaRvo4r/fz08K6pPeZvbiqS9nfNbzfIgLFmNX+FL9qR9ZR5PA==",
"dependencies": {
"Newtonsoft.Json": "11.0.2",
"System.Text.Encodings.Web": "4.7.0"
}
},
"IdentityModel.AspNetCore.OAuth2Introspection": {
"type": "Transitive",
"resolved": "4.0.1",
"contentHash": "ZNdMZMaj9fqR3j50vYsu+1U3QGd6n8+fqwf+a8mCTcmXGor+HgFDfdq0mM34bsmD6uEgAQup7sv2ZW5kR36dbA==",
"dependencies": {
"IdentityModel": "4.0.0"
}
},
"IdentityServer4": {
"type": "Transitive",
"resolved": "4.1.2",
"contentHash": "blaxxGuOA7v/w1q+fxn97wZ+x2ecG1ZD4mc/N/ZOXMNeFZZhqv+4LF26Gecyik3nWrJPmbMEtQbLmRsKG8k61w==",
"dependencies": {
"IdentityModel": "4.4.0",
"IdentityServer4.Storage": "4.1.2",
"Microsoft.AspNetCore.Authentication.OpenIdConnect": "3.1.0",
"Microsoft.IdentityModel.Protocols.OpenIdConnect": "5.6.0",
"Newtonsoft.Json": "12.0.2"
}
},
"IdentityServer4.AccessTokenValidation": {
"type": "Transitive",
"resolved": "3.0.1",
"contentHash": "qu/M6UyN4o9NVep7q545Ms7hYAnsQqSdLbN1Fjjrn4m35lyBfeQPSSNzDryAKHbodyWOQfHaOqKEyMEJQ5Rpgw==",
"dependencies": {
"IdentityModel.AspNetCore.OAuth2Introspection": "4.0.1",
"Microsoft.AspNetCore.Authentication.JwtBearer": "3.0.0"
}
},
"IdentityServer4.Storage": {
"type": "Transitive",
"resolved": "4.1.2",
"contentHash": "KoSffyZyyeCNTIyJiZnCuPakJ1QbCHlpty6gbWUj/7yl+w0PXIchgmmJnJSvddzBb8iZ2xew/vGlxWUIP17P2g==",
"dependencies": {
"IdentityModel": "4.4.0"
}
"resolved": "6.0.0",
"contentHash": "eVHCR7a6m/dm5RFcBzE3qs/Jg5j9R5Rjpu8aTOv9e4AFvaQtBXb5ah7kmwU+YwA0ufRwz4wf1hnIvsD2hSnI4g=="
},
"Kralizek.AutoFixture.Extensions.MockHttp": {
"type": "Transitive",
@@ -467,10 +444,10 @@
},
"Microsoft.AspNetCore.Authentication.OpenIdConnect": {
"type": "Transitive",
"resolved": "3.1.0",
"contentHash": "O1cAQYUTU8EfRqwc5/rfTns4E4hKlFlg59fuKRrST+PzsxI6H07KqRN/JjdYhAuVYxF8jPnIGbj+zuc5paOWUw==",
"resolved": "6.0.0",
"contentHash": "cJxdro36spFzk/K2OFCddM6vZ+yoj6ug8mTFRH3Gdv1Pul/buSuCtfb/FSCp31UmS5S4C1315dU7wX3ErLFuDg==",
"dependencies": {
"Microsoft.IdentityModel.Protocols.OpenIdConnect": "5.5.0"
"Microsoft.IdentityModel.Protocols.OpenIdConnect": "6.10.0"
}
},
"Microsoft.AspNetCore.Cryptography.Internal": {
@@ -503,8 +480,8 @@
},
"Microsoft.AspNetCore.DataProtection.Abstractions": {
"type": "Transitive",
"resolved": "3.1.32",
"contentHash": "MPL4iVyiaRxnOUY5VATHjvhDWaAEFb77KFiUxVRklv3Z3v+STofUr1UG/aCt1O9cgN7FVTDaC5A7U+zsLub8Xg=="
"resolved": "6.0.0",
"contentHash": "Z/UU4NEBm5UgNufJmw+j5baW26ytCOIZ0G7sZocPaOzsUeBon1bkM3lSMNZQG2GmDjAIVP2XMSODf2jzSGbibw=="
},
"Microsoft.Azure.Amqp": {
"type": "Transitive",
@@ -2858,8 +2835,8 @@
"billing": {
"type": "Project",
"dependencies": {
"Core": "[2023.10.2, )",
"SharedWeb": "[2023.10.2, )"
"Core": "[2023.10.3, )",
"SharedWeb": "[2023.10.3, )"
}
},
"common": {
@@ -2867,7 +2844,7 @@
"dependencies": {
"AutoFixture.AutoNSubstitute": "[4.17.0, )",
"AutoFixture.Xunit2": "[4.17.0, )",
"Core": "[2023.10.2, )",
"Core": "[2023.10.3, )",
"Kralizek.AutoFixture.Extensions.MockHttp": "[1.2.0, )",
"Microsoft.NET.Test.Sdk": "[17.1.0, )",
"NSubstitute": "[4.3.0, )",
@@ -2889,10 +2866,9 @@
"BitPay.Light": "[1.0.1907, )",
"Braintree": "[5.19.0, )",
"DnsClient": "[1.7.0, )",
"Duende.IdentityServer": "[6.0.4, )",
"Fido2.AspNet": "[3.0.1, )",
"Handlebars.Net": "[2.1.2, )",
"IdentityServer4": "[4.1.2, )",
"IdentityServer4.AccessTokenValidation": "[3.0.1, )",
"LaunchDarkly.ServerSdk": "[8.0.0, )",
"MailKit": "[4.2.0, )",
"Microsoft.AspNetCore.Authentication.JwtBearer": "[6.0.4, )",
@@ -2920,7 +2896,7 @@
"infrastructure.dapper": {
"type": "Project",
"dependencies": {
"Core": "[2023.10.2, )",
"Core": "[2023.10.3, )",
"Dapper": "[2.0.123, )"
}
},
@@ -2928,7 +2904,7 @@
"type": "Project",
"dependencies": {
"AutoMapper.Extensions.Microsoft.DependencyInjection": "[12.0.1, )",
"Core": "[2023.10.2, )",
"Core": "[2023.10.3, )",
"Microsoft.EntityFrameworkCore.Relational": "[7.0.5, )",
"Microsoft.EntityFrameworkCore.SqlServer": "[7.0.5, )",
"Microsoft.EntityFrameworkCore.Sqlite": "[7.0.5, )",
@@ -2940,9 +2916,9 @@
"sharedweb": {
"type": "Project",
"dependencies": {
"Core": "[2023.10.2, )",
"Infrastructure.Dapper": "[2023.10.2, )",
"Infrastructure.EntityFramework": "[2023.10.2, )"
"Core": "[2023.10.3, )",
"Infrastructure.Dapper": "[2023.10.3, )",
"Infrastructure.EntityFramework": "[2023.10.3, )"
}
}
}

View File

@@ -72,6 +72,12 @@ public class SutProvider<TSut> : ISutProvider
Sut = default;
}
public void Recreate()
{
_dependencies = new Dictionary<Type, Dictionary<string, object>>();
Sut = _fixture.Create<TSut>();
}
ISutProvider ISutProvider.Create() => Create();
public SutProvider<TSut> Create()
{

View File

@@ -254,6 +254,24 @@
"Microsoft.Win32.Registry": "5.0.0"
}
},
"Duende.IdentityServer": {
"type": "Transitive",
"resolved": "6.0.4",
"contentHash": "4HVjzx1F8v5J+U7oa8RGAQGj2QzmzNSu87r18Sh+dlh10uyZZL8teAaT/FaVLDObnfItGdPFvN8mwpF/HkI3Xw==",
"dependencies": {
"Duende.IdentityServer.Storage": "6.0.4",
"Microsoft.AspNetCore.Authentication.OpenIdConnect": "6.0.0"
}
},
"Duende.IdentityServer.Storage": {
"type": "Transitive",
"resolved": "6.0.4",
"contentHash": "s5gAjfbpr2IMgI+fU2Nx+2AZdzstmbt9gpo13iX7GwvqSeSaBVqj9ZskAN0R2KF1OemPdZuGnfaTcevdXMUrrw==",
"dependencies": {
"IdentityModel": "6.0.0",
"Microsoft.AspNetCore.DataProtection.Abstractions": "6.0.0"
}
},
"Fare": {
"type": "Transitive",
"resolved": "2.1.1",
@@ -298,49 +316,8 @@
},
"IdentityModel": {
"type": "Transitive",
"resolved": "4.4.0",
"contentHash": "b18wrIx5wnZlMxAX7oVsE+nDtAJ4hajYlH0xPlaRvo4r/fz08K6pPeZvbiqS9nfNbzfIgLFmNX+FL9qR9ZR5PA==",
"dependencies": {
"Newtonsoft.Json": "11.0.2",
"System.Text.Encodings.Web": "4.7.0"
}
},
"IdentityModel.AspNetCore.OAuth2Introspection": {
"type": "Transitive",
"resolved": "4.0.1",
"contentHash": "ZNdMZMaj9fqR3j50vYsu+1U3QGd6n8+fqwf+a8mCTcmXGor+HgFDfdq0mM34bsmD6uEgAQup7sv2ZW5kR36dbA==",
"dependencies": {
"IdentityModel": "4.0.0"
}
},
"IdentityServer4": {
"type": "Transitive",
"resolved": "4.1.2",
"contentHash": "blaxxGuOA7v/w1q+fxn97wZ+x2ecG1ZD4mc/N/ZOXMNeFZZhqv+4LF26Gecyik3nWrJPmbMEtQbLmRsKG8k61w==",
"dependencies": {
"IdentityModel": "4.4.0",
"IdentityServer4.Storage": "4.1.2",
"Microsoft.AspNetCore.Authentication.OpenIdConnect": "3.1.0",
"Microsoft.IdentityModel.Protocols.OpenIdConnect": "5.6.0",
"Newtonsoft.Json": "12.0.2"
}
},
"IdentityServer4.AccessTokenValidation": {
"type": "Transitive",
"resolved": "3.0.1",
"contentHash": "qu/M6UyN4o9NVep7q545Ms7hYAnsQqSdLbN1Fjjrn4m35lyBfeQPSSNzDryAKHbodyWOQfHaOqKEyMEJQ5Rpgw==",
"dependencies": {
"IdentityModel.AspNetCore.OAuth2Introspection": "4.0.1",
"Microsoft.AspNetCore.Authentication.JwtBearer": "3.0.0"
}
},
"IdentityServer4.Storage": {
"type": "Transitive",
"resolved": "4.1.2",
"contentHash": "KoSffyZyyeCNTIyJiZnCuPakJ1QbCHlpty6gbWUj/7yl+w0PXIchgmmJnJSvddzBb8iZ2xew/vGlxWUIP17P2g==",
"dependencies": {
"IdentityModel": "4.4.0"
}
"resolved": "6.0.0",
"contentHash": "eVHCR7a6m/dm5RFcBzE3qs/Jg5j9R5Rjpu8aTOv9e4AFvaQtBXb5ah7kmwU+YwA0ufRwz4wf1hnIvsD2hSnI4g=="
},
"LaunchDarkly.Cache": {
"type": "Transitive",
@@ -418,10 +395,10 @@
},
"Microsoft.AspNetCore.Authentication.OpenIdConnect": {
"type": "Transitive",
"resolved": "3.1.0",
"contentHash": "O1cAQYUTU8EfRqwc5/rfTns4E4hKlFlg59fuKRrST+PzsxI6H07KqRN/JjdYhAuVYxF8jPnIGbj+zuc5paOWUw==",
"resolved": "6.0.0",
"contentHash": "cJxdro36spFzk/K2OFCddM6vZ+yoj6ug8mTFRH3Gdv1Pul/buSuCtfb/FSCp31UmS5S4C1315dU7wX3ErLFuDg==",
"dependencies": {
"Microsoft.IdentityModel.Protocols.OpenIdConnect": "5.5.0"
"Microsoft.IdentityModel.Protocols.OpenIdConnect": "6.10.0"
}
},
"Microsoft.AspNetCore.Cryptography.Internal": {
@@ -454,8 +431,8 @@
},
"Microsoft.AspNetCore.DataProtection.Abstractions": {
"type": "Transitive",
"resolved": "3.1.32",
"contentHash": "MPL4iVyiaRxnOUY5VATHjvhDWaAEFb77KFiUxVRklv3Z3v+STofUr1UG/aCt1O9cgN7FVTDaC5A7U+zsLub8Xg=="
"resolved": "6.0.0",
"contentHash": "Z/UU4NEBm5UgNufJmw+j5baW26ytCOIZ0G7sZocPaOzsUeBon1bkM3lSMNZQG2GmDjAIVP2XMSODf2jzSGbibw=="
},
"Microsoft.Azure.Amqp": {
"type": "Transitive",
@@ -2690,10 +2667,9 @@
"BitPay.Light": "[1.0.1907, )",
"Braintree": "[5.19.0, )",
"DnsClient": "[1.7.0, )",
"Duende.IdentityServer": "[6.0.4, )",
"Fido2.AspNet": "[3.0.1, )",
"Handlebars.Net": "[2.1.2, )",
"IdentityServer4": "[4.1.2, )",
"IdentityServer4.AccessTokenValidation": "[3.0.1, )",
"LaunchDarkly.ServerSdk": "[8.0.0, )",
"MailKit": "[4.2.0, )",
"Microsoft.AspNetCore.Authentication.JwtBearer": "[6.0.4, )",

View File

@@ -1,10 +1,10 @@
using System.Reflection;
using AutoFixture;
using AutoFixture.Xunit2;
using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Enums;
namespace Bit.Core.Test.AutoFixture.PolicyFixtures;
namespace Bit.Core.Test.AdminConsole.AutoFixture;
internal class PolicyCustomization : ICustomization
{

View File

@@ -1,4 +1,9 @@
using Bit.Core.Auth.Entities;
using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Enums;
using Bit.Core.AdminConsole.Models.Data.Organizations.Policies;
using Bit.Core.AdminConsole.Repositories;
using Bit.Core.AdminConsole.Services.Implementations;
using Bit.Core.Auth.Entities;
using Bit.Core.Auth.Enums;
using Bit.Core.Auth.Models.Data;
using Bit.Core.Auth.Repositories;
@@ -6,24 +11,23 @@ using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Exceptions;
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
using Bit.Core.Models.Data.Organizations.Policies;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes;
using NSubstitute;
using Xunit;
using AdminConsoleFixtures = Bit.Core.Test.AdminConsole.AutoFixture;
using GlobalSettings = Bit.Core.Settings.GlobalSettings;
using PolicyFixtures = Bit.Core.Test.AutoFixture.PolicyFixtures;
namespace Bit.Core.Test.Services;
namespace Bit.Core.Test.AdminConsole.Services;
[SutProviderCustomize]
public class PolicyServiceTests
{
[Theory, BitAutoData]
public async Task SaveAsync_OrganizationDoesNotExist_ThrowsBadRequest(
[PolicyFixtures.Policy(PolicyType.DisableSend)] Policy policy, SutProvider<PolicyService> sutProvider)
[AdminConsoleFixtures.Policy(PolicyType.DisableSend)] Policy policy, SutProvider<PolicyService> sutProvider)
{
SetupOrg(sutProvider, policy.OrganizationId, null);
@@ -46,7 +50,7 @@ public class PolicyServiceTests
[Theory, BitAutoData]
public async Task SaveAsync_OrganizationCannotUsePolicies_ThrowsBadRequest(
[PolicyFixtures.Policy(PolicyType.DisableSend)] Policy policy, SutProvider<PolicyService> sutProvider)
[AdminConsoleFixtures.Policy(PolicyType.DisableSend)] Policy policy, SutProvider<PolicyService> sutProvider)
{
var orgId = Guid.NewGuid();
@@ -74,7 +78,7 @@ public class PolicyServiceTests
[Theory, BitAutoData]
public async Task SaveAsync_SingleOrg_RequireSsoEnabled_ThrowsBadRequest(
[PolicyFixtures.Policy(PolicyType.SingleOrg)] Policy policy, SutProvider<PolicyService> sutProvider)
[AdminConsoleFixtures.Policy(PolicyType.SingleOrg)] Policy policy, SutProvider<PolicyService> sutProvider)
{
policy.Enabled = false;
@@ -106,7 +110,7 @@ public class PolicyServiceTests
}
[Theory, BitAutoData]
public async Task SaveAsync_SingleOrg_VaultTimeoutEnabled_ThrowsBadRequest([PolicyFixtures.Policy(Enums.PolicyType.SingleOrg)] Policy policy, SutProvider<PolicyService> sutProvider)
public async Task SaveAsync_SingleOrg_VaultTimeoutEnabled_ThrowsBadRequest([AdminConsoleFixtures.Policy(PolicyType.SingleOrg)] Policy policy, SutProvider<PolicyService> sutProvider)
{
policy.Enabled = false;
@@ -117,7 +121,7 @@ public class PolicyServiceTests
});
sutProvider.GetDependency<IPolicyRepository>()
.GetByOrganizationIdTypeAsync(policy.OrganizationId, Enums.PolicyType.MaximumVaultTimeout)
.GetByOrganizationIdTypeAsync(policy.OrganizationId, PolicyType.MaximumVaultTimeout)
.Returns(new Policy { Enabled = true });
var badRequestException = await Assert.ThrowsAsync<BadRequestException>(
@@ -137,7 +141,7 @@ public class PolicyServiceTests
[BitAutoData(PolicyType.SingleOrg)]
[BitAutoData(PolicyType.RequireSso)]
public async Task SaveAsync_PolicyRequiredByKeyConnector_DisablePolicy_ThrowsBadRequest(
Enums.PolicyType policyType,
PolicyType policyType,
Policy policy,
SutProvider<PolicyService> sutProvider)
{
@@ -173,7 +177,7 @@ public class PolicyServiceTests
[Theory, BitAutoData]
public async Task SaveAsync_RequireSsoPolicy_NotEnabled_ThrowsBadRequestAsync(
[PolicyFixtures.Policy(Enums.PolicyType.RequireSso)] Policy policy, SutProvider<PolicyService> sutProvider)
[AdminConsoleFixtures.Policy(PolicyType.RequireSso)] Policy policy, SutProvider<PolicyService> sutProvider)
{
policy.Enabled = true;
@@ -206,7 +210,7 @@ public class PolicyServiceTests
[Theory, BitAutoData]
public async Task SaveAsync_NewPolicy_Created(
[PolicyFixtures.Policy(PolicyType.ResetPassword)] Policy policy, SutProvider<PolicyService> sutProvider)
[AdminConsoleFixtures.Policy(PolicyType.ResetPassword)] Policy policy, SutProvider<PolicyService> sutProvider)
{
policy.Id = default;
policy.Data = null;
@@ -218,7 +222,7 @@ public class PolicyServiceTests
});
sutProvider.GetDependency<IPolicyRepository>()
.GetByOrganizationIdTypeAsync(policy.OrganizationId, Enums.PolicyType.SingleOrg)
.GetByOrganizationIdTypeAsync(policy.OrganizationId, PolicyType.SingleOrg)
.Returns(Task.FromResult(new Policy { Enabled = true }));
var utcNow = DateTime.UtcNow;
@@ -237,7 +241,7 @@ public class PolicyServiceTests
[Theory, BitAutoData]
public async Task SaveAsync_VaultTimeoutPolicy_NotEnabled_ThrowsBadRequestAsync(
[PolicyFixtures.Policy(PolicyType.MaximumVaultTimeout)] Policy policy, SutProvider<PolicyService> sutProvider)
[AdminConsoleFixtures.Policy(PolicyType.MaximumVaultTimeout)] Policy policy, SutProvider<PolicyService> sutProvider)
{
policy.Enabled = true;
@@ -248,7 +252,7 @@ public class PolicyServiceTests
});
sutProvider.GetDependency<IPolicyRepository>()
.GetByOrganizationIdTypeAsync(policy.OrganizationId, Enums.PolicyType.SingleOrg)
.GetByOrganizationIdTypeAsync(policy.OrganizationId, PolicyType.SingleOrg)
.Returns(Task.FromResult(new Policy { Enabled = false }));
var badRequestException = await Assert.ThrowsAsync<BadRequestException>(
@@ -270,7 +274,7 @@ public class PolicyServiceTests
[Theory, BitAutoData]
public async Task SaveAsync_ExistingPolicy_UpdateTwoFactor(
[PolicyFixtures.Policy(PolicyType.TwoFactorAuthentication)] Policy policy, SutProvider<PolicyService> sutProvider)
[AdminConsoleFixtures.Policy(PolicyType.TwoFactorAuthentication)] Policy policy, SutProvider<PolicyService> sutProvider)
{
// If the policy that this is updating isn't enabled then do some work now that the current one is enabled
@@ -340,7 +344,7 @@ public class PolicyServiceTests
[Theory, BitAutoData]
public async Task SaveAsync_ExistingPolicy_UpdateSingleOrg(
[PolicyFixtures.Policy(PolicyType.TwoFactorAuthentication)] Policy policy, SutProvider<PolicyService> sutProvider)
[AdminConsoleFixtures.Policy(PolicyType.TwoFactorAuthentication)] Policy policy, SutProvider<PolicyService> sutProvider)
{
// If the policy that this is updating isn't enabled then do some work now that the current one is enabled
@@ -409,7 +413,7 @@ public class PolicyServiceTests
public async Task SaveAsync_ResetPasswordPolicyRequiredByTrustedDeviceEncryption_DisablePolicyOrDisableAutomaticEnrollment_ThrowsBadRequest(
bool policyEnabled,
bool autoEnrollEnabled,
[PolicyFixtures.Policy(PolicyType.ResetPassword)] Policy policy,
[AdminConsoleFixtures.Policy(PolicyType.ResetPassword)] Policy policy,
SutProvider<PolicyService> sutProvider)
{
policy.Enabled = policyEnabled;
@@ -450,7 +454,7 @@ public class PolicyServiceTests
[Theory, BitAutoData]
public async Task SaveAsync_RequireSsoPolicyRequiredByTrustedDeviceEncryption_DisablePolicy_ThrowsBadRequest(
[PolicyFixtures.Policy(PolicyType.RequireSso)] Policy policy,
[AdminConsoleFixtures.Policy(PolicyType.RequireSso)] Policy policy,
SutProvider<PolicyService> sutProvider)
{
policy.Enabled = false;
@@ -487,7 +491,7 @@ public class PolicyServiceTests
[Theory, BitAutoData]
public async Task SaveAsync_PolicyRequiredForAccountRecovery_NotEnabled_ThrowsBadRequestAsync(
[PolicyFixtures.Policy(Enums.PolicyType.ResetPassword)] Policy policy, SutProvider<PolicyService> sutProvider)
[AdminConsoleFixtures.Policy(PolicyType.ResetPassword)] Policy policy, SutProvider<PolicyService> sutProvider)
{
policy.Enabled = true;
policy.SetDataModel(new ResetPasswordDataModel());
@@ -522,7 +526,7 @@ public class PolicyServiceTests
[Theory, BitAutoData]
public async Task SaveAsync_SingleOrg_AccountRecoveryEnabled_ThrowsBadRequest(
[PolicyFixtures.Policy(Enums.PolicyType.SingleOrg)] Policy policy, SutProvider<PolicyService> sutProvider)
[AdminConsoleFixtures.Policy(PolicyType.SingleOrg)] Policy policy, SutProvider<PolicyService> sutProvider)
{
policy.Enabled = false;
@@ -533,7 +537,7 @@ public class PolicyServiceTests
});
sutProvider.GetDependency<IPolicyRepository>()
.GetByOrganizationIdTypeAsync(policy.OrganizationId, Enums.PolicyType.ResetPassword)
.GetByOrganizationIdTypeAsync(policy.OrganizationId, PolicyType.ResetPassword)
.Returns(new Policy { Enabled = true });
var badRequestException = await Assert.ThrowsAsync<BadRequestException>(

View File

@@ -0,0 +1,61 @@
using Bit.Core.Auth.Enums;
using Bit.Core.Auth.Models.Business.Tokenables;
using Bit.Test.Common.AutoFixture.Attributes;
using Fido2NetLib;
using Xunit;
namespace Bit.Core.Test.Auth.Models.Business.Tokenables;
public class WebAuthnLoginAssertionOptionsTokenableTests
{
[Theory, BitAutoData]
public void Valid_TokenWithoutOptions_ReturnsFalse(WebAuthnLoginAssertionOptionsScope scope)
{
var token = new WebAuthnLoginAssertionOptionsTokenable(scope, null);
var isValid = token.Valid;
Assert.False(isValid);
}
[Theory, BitAutoData]
public void Valid_NewlyCreatedToken_ReturnsTrue(WebAuthnLoginAssertionOptionsScope scope, AssertionOptions createOptions)
{
var token = new WebAuthnLoginAssertionOptionsTokenable(scope, createOptions);
var isValid = token.Valid;
Assert.True(isValid);
}
[Theory, BitAutoData]
public void ValidIsValid_TokenWithoutOptions_ReturnsFalse(WebAuthnLoginAssertionOptionsScope scope)
{
var token = new WebAuthnLoginAssertionOptionsTokenable(scope, null);
var isValid = token.TokenIsValid(scope);
Assert.False(isValid);
}
[Theory, BitAutoData]
public void ValidIsValid_NonMatchingScope_ReturnsFalse(WebAuthnLoginAssertionOptionsScope scope1, WebAuthnLoginAssertionOptionsScope scope2, AssertionOptions createOptions)
{
var token = new WebAuthnLoginAssertionOptionsTokenable(scope1, createOptions);
var isValid = token.TokenIsValid(scope2);
Assert.False(isValid);
}
[Theory, BitAutoData]
public void ValidIsValid_SameScope_ReturnsTrue(WebAuthnLoginAssertionOptionsScope scope, AssertionOptions createOptions)
{
var token = new WebAuthnLoginAssertionOptionsTokenable(scope, createOptions);
var isValid = token.TokenIsValid(scope);
Assert.True(isValid);
}
}

View File

@@ -1,4 +1,9 @@
using Bit.Core.Auth.Entities;
using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Enums;
using Bit.Core.AdminConsole.Models.Data.Organizations.Policies;
using Bit.Core.AdminConsole.Repositories;
using Bit.Core.AdminConsole.Services;
using Bit.Core.Auth.Entities;
using Bit.Core.Auth.Enums;
using Bit.Core.Auth.Models.Data;
using Bit.Core.Auth.Repositories;
@@ -6,7 +11,6 @@ using Bit.Core.Auth.Services;
using Bit.Core.Entities;
using Bit.Core.Exceptions;
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
using Bit.Core.Models.Data.Organizations.Policies;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Test.Common.AutoFixture;
@@ -205,7 +209,7 @@ public class SsoConfigServiceTests
};
sutProvider.GetDependency<IPolicyRepository>().GetByOrganizationIdTypeAsync(
Arg.Any<Guid>(), Enums.PolicyType.SingleOrg).Returns(new Policy
Arg.Any<Guid>(), PolicyType.SingleOrg).Returns(new Policy
{
Enabled = true
});
@@ -239,7 +243,7 @@ public class SsoConfigServiceTests
};
sutProvider.GetDependency<IPolicyRepository>().GetByOrganizationIdTypeAsync(
Arg.Any<Guid>(), Arg.Any<Enums.PolicyType>()).Returns(new Policy
Arg.Any<Guid>(), Arg.Any<PolicyType>()).Returns(new Policy
{
Enabled = true
});
@@ -274,7 +278,7 @@ public class SsoConfigServiceTests
};
sutProvider.GetDependency<IPolicyRepository>().GetByOrganizationIdTypeAsync(
Arg.Any<Guid>(), Arg.Any<Enums.PolicyType>()).Returns(new Policy
Arg.Any<Guid>(), Arg.Any<PolicyType>()).Returns(new Policy
{
Enabled = true,
});
@@ -309,7 +313,7 @@ public class SsoConfigServiceTests
};
sutProvider.GetDependency<IPolicyRepository>().GetByOrganizationIdTypeAsync(
Arg.Any<Guid>(), Arg.Any<Enums.PolicyType>()).Returns(new Policy
Arg.Any<Guid>(), Arg.Any<PolicyType>()).Returns(new Policy
{
Enabled = true,
});
@@ -338,7 +342,7 @@ public class SsoConfigServiceTests
await sutProvider.GetDependency<IPolicyService>().Received(1)
.SaveAsync(
Arg.Is<Policy>(t => t.Type == Enums.PolicyType.SingleOrg),
Arg.Is<Policy>(t => t.Type == PolicyType.SingleOrg),
Arg.Any<IUserService>(),
Arg.Any<IOrganizationService>(),
null
@@ -346,7 +350,7 @@ public class SsoConfigServiceTests
await sutProvider.GetDependency<IPolicyService>().Received(1)
.SaveAsync(
Arg.Is<Policy>(t => t.Type == Enums.PolicyType.ResetPassword && t.GetDataModel<ResetPasswordDataModel>().AutoEnrollEnabled),
Arg.Is<Policy>(t => t.Type == PolicyType.ResetPassword && t.GetDataModel<ResetPasswordDataModel>().AutoEnrollEnabled),
Arg.Any<IUserService>(),
Arg.Any<IOrganizationService>(),
null

View File

@@ -1,4 +1,6 @@
using Bit.Core.AdminConsole.Models.OrganizationConnectionConfigs;
using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Enums;
using Bit.Core.AdminConsole.Models.OrganizationConnectionConfigs;
using Bit.Core.Auth.Entities;
using Bit.Core.Auth.Enums;
using Bit.Core.Auth.Models.Data;

View File

@@ -1,4 +1,6 @@
using Bit.Core.Auth.Models.Business.Tokenables;
using Bit.Core.AdminConsole.Enums;
using Bit.Core.AdminConsole.Services;
using Bit.Core.Auth.Models.Business.Tokenables;
using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Exceptions;

View File

@@ -1,5 +1,6 @@
using System.Text.Json;
using Bit.Core.AdminConsole.Entities.Provider;
using Bit.Core.AdminConsole.Enums;
using Bit.Core.AdminConsole.Enums.Provider;
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
using Bit.Core.AdminConsole.Repositories;
@@ -21,9 +22,9 @@ using Bit.Core.OrganizationFeatures.OrganizationSubscriptions.Interface;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Core.Settings;
using Bit.Core.Test.AdminConsole.AutoFixture;
using Bit.Core.Test.AutoFixture.OrganizationFixtures;
using Bit.Core.Test.AutoFixture.OrganizationUserFixtures;
using Bit.Core.Test.AutoFixture.PolicyFixtures;
using Bit.Core.Tokens;
using Bit.Core.Tools.Enums;
using Bit.Core.Tools.Models.Business;
@@ -34,10 +35,11 @@ using Bit.Test.Common.AutoFixture.Attributes;
using Bit.Test.Common.Fakes;
using NSubstitute;
using NSubstitute.ExceptionExtensions;
using NSubstitute.ReturnsExtensions;
using Xunit;
using Organization = Bit.Core.Entities.Organization;
using OrganizationUser = Bit.Core.Entities.OrganizationUser;
using Policy = Bit.Core.Entities.Policy;
using Policy = Bit.Core.AdminConsole.Entities.Policy;
namespace Bit.Core.Test.Services;
@@ -1606,15 +1608,16 @@ public class OrganizationServiceTests
[BitAutoData(0, null, 100, true, "")]
[BitAutoData(1, 100, null, true, "")]
[BitAutoData(1, 100, 100, false, "Seat limit has been reached")]
public void CanScale(int seatsToAdd, int? currentSeats, int? maxAutoscaleSeats,
public async Task CanScaleAsync(int seatsToAdd, int? currentSeats, int? maxAutoscaleSeats,
bool expectedResult, string expectedFailureMessage, Organization organization,
SutProvider<OrganizationService> sutProvider)
{
organization.Seats = currentSeats;
organization.MaxAutoscaleSeats = maxAutoscaleSeats;
sutProvider.GetDependency<ICurrentContext>().ManageUsers(organization.Id).Returns(true);
sutProvider.GetDependency<IProviderRepository>().GetByOrganizationIdAsync(organization.Id).ReturnsNull();
var (result, failureMessage) = sutProvider.Sut.CanScale(organization, seatsToAdd);
var (result, failureMessage) = await sutProvider.Sut.CanScaleAsync(organization, seatsToAdd);
if (expectedFailureMessage == string.Empty)
{
@@ -1628,16 +1631,35 @@ public class OrganizationServiceTests
}
[Theory, PaidOrganizationCustomize, BitAutoData]
public void CanScale_FailsOnSelfHosted(Organization organization,
public async Task CanScaleAsync_FailsOnSelfHosted(Organization organization,
SutProvider<OrganizationService> sutProvider)
{
sutProvider.GetDependency<IGlobalSettings>().SelfHosted.Returns(true);
var (result, failureMessage) = sutProvider.Sut.CanScale(organization, 10);
var (result, failureMessage) = await sutProvider.Sut.CanScaleAsync(organization, 10);
Assert.False(result);
Assert.Contains("Cannot autoscale on self-hosted instance", failureMessage);
}
[Theory, PaidOrganizationCustomize, BitAutoData]
public async Task CanScaleAsync_FailsOnResellerManagedOrganization(
Organization organization,
SutProvider<OrganizationService> sutProvider)
{
var provider = new Provider
{
Enabled = true,
Type = ProviderType.Reseller
};
sutProvider.GetDependency<IProviderRepository>().GetByOrganizationIdAsync(organization.Id).Returns(provider);
var (result, failureMessage) = await sutProvider.Sut.CanScaleAsync(organization, 10);
Assert.False(result);
Assert.Contains("Seat limit has been reached. Contact your provider to purchase additional seats.", failureMessage);
}
[Theory, PaidOrganizationCustomize, BitAutoData]
public async Task Delete_Success(Organization organization, SutProvider<OrganizationService> sutProvider)
{

View File

@@ -739,300 +739,4 @@ public class StripePaymentServiceTests
Assert.Null(result);
}
[Theory, BitAutoData]
public async Task PreviewUpcomingInvoiceAndPayAsync_WithInAppPaymentMethod_ThrowsBadRequestException(SutProvider<StripePaymentService> sutProvider,
Organization subscriber, List<Stripe.InvoiceSubscriptionItemOptions> subItemOptions)
{
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
stripeAdapter.CustomerGetAsync(Arg.Any<string>(), Arg.Any<Stripe.CustomerGetOptions>())
.Returns(new Stripe.Customer { Metadata = new Dictionary<string, string> { { "appleReceipt", "dummyData" } } });
var ex = await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.PreviewUpcomingInvoiceAndPayAsync(subscriber, subItemOptions));
Assert.Equal("Cannot perform this action with in-app purchase payment method. Contact support.", ex.Message);
}
[Theory, BitAutoData]
public async void PreviewUpcomingInvoiceAndPayAsync_UpcomingInvoiceBelowThreshold_DoesNotInvoiceNow(SutProvider<StripePaymentService> sutProvider,
Organization subscriber, List<Stripe.InvoiceSubscriptionItemOptions> subItemOptions)
{
var prorateThreshold = 50000;
var invoiceAmountBelowThreshold = prorateThreshold - 100;
var customer = MockStripeCustomer(subscriber);
sutProvider.GetDependency<IStripeAdapter>().CustomerGetAsync(default, default).ReturnsForAnyArgs(customer);
var invoiceItem = MockInoviceItemList(subscriber, "planId", invoiceAmountBelowThreshold, customer);
sutProvider.GetDependency<IStripeAdapter>().InvoiceItemListAsync(new Stripe.InvoiceItemListOptions
{
Customer = subscriber.GatewayCustomerId
}).ReturnsForAnyArgs(invoiceItem);
var invoiceLineItem = CreateInvoiceLineTime(subscriber, "planId", invoiceAmountBelowThreshold);
sutProvider.GetDependency<IStripeAdapter>().InvoiceUpcomingAsync(new Stripe.UpcomingInvoiceOptions
{
Customer = subscriber.GatewayCustomerId,
Subscription = subscriber.GatewaySubscriptionId,
SubscriptionItems = subItemOptions
}).ReturnsForAnyArgs(invoiceLineItem);
sutProvider.GetDependency<IStripeAdapter>().InvoiceCreateAsync(Arg.Is<Stripe.InvoiceCreateOptions>(options =>
options.CollectionMethod == "send_invoice" &&
options.DaysUntilDue == 1 &&
options.Customer == subscriber.GatewayCustomerId &&
options.Subscription == subscriber.GatewaySubscriptionId &&
options.DefaultPaymentMethod == customer.InvoiceSettings.DefaultPaymentMethod.Id
)).ReturnsForAnyArgs(new Stripe.Invoice
{
Id = "mockInvoiceId",
CollectionMethod = "send_invoice",
DueDate = DateTime.Now.AddDays(1),
Customer = customer,
Subscription = new Stripe.Subscription
{
Id = "mockSubscriptionId",
Customer = customer,
Status = "active",
CurrentPeriodStart = DateTime.UtcNow,
CurrentPeriodEnd = DateTime.UtcNow.AddMonths(1),
CollectionMethod = "charge_automatically",
},
DefaultPaymentMethod = customer.InvoiceSettings.DefaultPaymentMethod,
AmountDue = invoiceAmountBelowThreshold,
Currency = "usd",
Status = "draft",
});
var result = await sutProvider.Sut.PreviewUpcomingInvoiceAndPayAsync(subscriber, new List<Stripe.InvoiceSubscriptionItemOptions>(), prorateThreshold);
Assert.False(result.IsInvoicedNow);
Assert.Null(result.PaymentIntentClientSecret);
}
[Theory, BitAutoData]
public async void PreviewUpcomingInvoiceAndPayAsync_NoPaymentMethod_ThrowsBadRequestException(SutProvider<StripePaymentService> sutProvider,
Organization subscriber, List<Stripe.InvoiceSubscriptionItemOptions> subItemOptions, string planId)
{
var prorateThreshold = 120000;
var invoiceAmountBelowThreshold = prorateThreshold;
var customer = new Stripe.Customer
{
Metadata = new Dictionary<string, string>(),
Id = subscriber.GatewayCustomerId,
DefaultSource = null,
InvoiceSettings = new Stripe.CustomerInvoiceSettings
{
DefaultPaymentMethod = null
}
};
sutProvider.GetDependency<IStripeAdapter>().CustomerGetAsync(default, default).ReturnsForAnyArgs(customer);
var invoiceItem = MockInoviceItemList(subscriber, planId, invoiceAmountBelowThreshold, customer);
sutProvider.GetDependency<IStripeAdapter>().InvoiceItemListAsync(new Stripe.InvoiceItemListOptions
{
Customer = subscriber.GatewayCustomerId
}).ReturnsForAnyArgs(invoiceItem);
var invoiceLineItem = CreateInvoiceLineTime(subscriber, planId, invoiceAmountBelowThreshold);
sutProvider.GetDependency<IStripeAdapter>().InvoiceUpcomingAsync(new Stripe.UpcomingInvoiceOptions
{
Customer = subscriber.GatewayCustomerId,
Subscription = subscriber.GatewaySubscriptionId,
SubscriptionItems = subItemOptions
}).ReturnsForAnyArgs(invoiceLineItem);
var ex = await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.PreviewUpcomingInvoiceAndPayAsync(subscriber, subItemOptions));
Assert.Equal("No payment method is available.", ex.Message);
}
[Theory, BitAutoData]
public async void PreviewUpcomingInvoiceAndPayAsync_UpcomingInvoiceAboveThreshold_DoesInvoiceNow(SutProvider<StripePaymentService> sutProvider,
Organization subscriber, List<Stripe.InvoiceSubscriptionItemOptions> subItemOptions, string planId)
{
var prorateThreshold = 50000;
var invoiceAmountBelowThreshold = 100000;
var customer = MockStripeCustomer(subscriber);
sutProvider.GetDependency<IStripeAdapter>().CustomerGetAsync(default, default).ReturnsForAnyArgs(customer);
var invoiceItem = MockInoviceItemList(subscriber, planId, invoiceAmountBelowThreshold, customer);
sutProvider.GetDependency<IStripeAdapter>().InvoiceItemListAsync(new Stripe.InvoiceItemListOptions
{
Customer = subscriber.GatewayCustomerId
}).ReturnsForAnyArgs(invoiceItem);
var invoiceLineItem = CreateInvoiceLineTime(subscriber, planId, invoiceAmountBelowThreshold);
sutProvider.GetDependency<IStripeAdapter>().InvoiceUpcomingAsync(new Stripe.UpcomingInvoiceOptions
{
Customer = subscriber.GatewayCustomerId,
Subscription = subscriber.GatewaySubscriptionId,
SubscriptionItems = subItemOptions
}).ReturnsForAnyArgs(invoiceLineItem);
var invoice = MockInVoice(customer, invoiceAmountBelowThreshold);
sutProvider.GetDependency<IStripeAdapter>().InvoiceCreateAsync(Arg.Is<Stripe.InvoiceCreateOptions>(options =>
options.CollectionMethod == "send_invoice" &&
options.DaysUntilDue == 1 &&
options.Customer == subscriber.GatewayCustomerId &&
options.Subscription == subscriber.GatewaySubscriptionId &&
options.DefaultPaymentMethod == customer.InvoiceSettings.DefaultPaymentMethod.Id
)).ReturnsForAnyArgs(invoice);
var result = await sutProvider.Sut.PreviewUpcomingInvoiceAndPayAsync(subscriber, new List<Stripe.InvoiceSubscriptionItemOptions>(), prorateThreshold);
await sutProvider.GetDependency<IStripeAdapter>().Received(1).InvoicePayAsync(invoice.Id,
Arg.Is<Stripe.InvoicePayOptions>((options =>
options.OffSession == true
)));
Assert.True(result.IsInvoicedNow);
Assert.Null(result.PaymentIntentClientSecret);
}
private static Stripe.Invoice MockInVoice(Stripe.Customer customer, int invoiceAmountBelowThreshold) =>
new()
{
Id = "mockInvoiceId",
CollectionMethod = "send_invoice",
DueDate = DateTime.Now.AddDays(1),
Customer = customer,
Subscription = new Stripe.Subscription
{
Id = "mockSubscriptionId",
Customer = customer,
Status = "active",
CurrentPeriodStart = DateTime.UtcNow,
CurrentPeriodEnd = DateTime.UtcNow.AddMonths(1),
CollectionMethod = "charge_automatically",
},
DefaultPaymentMethod = customer.InvoiceSettings.DefaultPaymentMethod,
AmountDue = invoiceAmountBelowThreshold,
Currency = "usd",
Status = "draft",
};
private static List<Stripe.InvoiceItem> MockInoviceItemList(Organization subscriber, string planId, int invoiceAmountBelowThreshold, Stripe.Customer customer) =>
new()
{
new Stripe.InvoiceItem
{
Id = "ii_1234567890",
Amount = invoiceAmountBelowThreshold,
Currency = "usd",
CustomerId = subscriber.GatewayCustomerId,
Description = "Sample invoice item 1",
Date = DateTime.UtcNow,
Discountable = true,
InvoiceId = "548458365"
},
new Stripe.InvoiceItem
{
Id = "ii_0987654321",
Amount = invoiceAmountBelowThreshold,
Currency = "usd",
CustomerId = customer.Id,
Description = "Sample invoice item 2",
Date = DateTime.UtcNow.AddDays(-5),
Discountable = false,
InvoiceId = null,
Proration = true,
Plan = new Stripe.Plan
{
Id = planId,
Amount = invoiceAmountBelowThreshold,
Currency = "usd",
Interval = "month",
IntervalCount = 1,
},
}
};
private static Stripe.Customer MockStripeCustomer(Organization subscriber)
{
var customer = new Stripe.Customer
{
Metadata = new Dictionary<string, string>(),
Id = subscriber.GatewayCustomerId,
DefaultSource = new Stripe.Card
{
Id = "card_12345",
Last4 = "1234",
Brand = "Visa",
ExpYear = 2025,
ExpMonth = 12
},
InvoiceSettings = new Stripe.CustomerInvoiceSettings
{
DefaultPaymentMethod = new Stripe.PaymentMethod
{
Id = "pm_12345",
Type = "card",
Card = new Stripe.PaymentMethodCard
{
Last4 = "1234",
Brand = "Visa",
ExpYear = 2025,
ExpMonth = 12
}
}
}
};
return customer;
}
private static Stripe.Invoice CreateInvoiceLineTime(Organization subscriber, string planId, int invoiceAmountBelowThreshold) =>
new()
{
AmountDue = invoiceAmountBelowThreshold,
AmountPaid = 0,
AmountRemaining = invoiceAmountBelowThreshold,
CustomerId = subscriber.GatewayCustomerId,
SubscriptionId = subscriber.GatewaySubscriptionId,
ApplicationFeeAmount = 0,
Currency = "usd",
Description = "Upcoming Invoice",
Discount = null,
DueDate = DateTime.UtcNow.AddDays(1),
EndingBalance = 0,
Number = "INV12345",
Paid = false,
PeriodStart = DateTime.UtcNow,
PeriodEnd = DateTime.UtcNow.AddMonths(1),
ReceiptNumber = null,
StartingBalance = 0,
Status = "draft",
Id = "ii_0987654321",
Total = invoiceAmountBelowThreshold,
Lines = new Stripe.StripeList<Stripe.InvoiceLineItem>
{
Data = new List<Stripe.InvoiceLineItem>
{
new Stripe.InvoiceLineItem
{
Amount = invoiceAmountBelowThreshold,
Currency = "usd",
Description = "Sample line item",
Id = "ii_0987654321",
Livemode = false,
Object = "line_item",
Discountable = false,
Period = new Stripe.InvoiceLineItemPeriod()
{
Start = DateTime.UtcNow,
End = DateTime.UtcNow.AddMonths(1)
},
Plan = new Stripe.Plan
{
Id = planId,
Amount = invoiceAmountBelowThreshold,
Currency = "usd",
Interval = "month",
IntervalCount = 1,
},
Proration = true,
Quantity = 1,
Subscription = subscriber.GatewaySubscriptionId,
SubscriptionItem = "si_12345",
Type = "subscription",
UnitAmountExcludingTax = invoiceAmountBelowThreshold,
}
}
}
};
}

View File

@@ -1,6 +1,8 @@
using System.Text.Json;
using System.Text;
using System.Text.Json;
using AutoFixture;
using Bit.Core.AdminConsole.Repositories;
using Bit.Core.AdminConsole.Services;
using Bit.Core.Auth.Entities;
using Bit.Core.Auth.Enums;
using Bit.Core.Auth.Models;
@@ -8,26 +10,29 @@ using Bit.Core.Auth.Models.Business.Tokenables;
using Bit.Core.Auth.Repositories;
using Bit.Core.Context;
using Bit.Core.Entities;
using Bit.Core.Exceptions;
using Bit.Core.Models.Business;
using Bit.Core.Models.Data.Organizations;
using Bit.Core.OrganizationFeatures.OrganizationUsers.Interfaces;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Core.Settings;
using Bit.Core.Tokens;
using Bit.Core.Tools.Services;
using Bit.Core.Utilities;
using Bit.Core.Vault.Repositories;
using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes;
using Bit.Test.Common.Fakes;
using Bit.Test.Common.Helpers;
using Fido2NetLib;
using Fido2NetLib.Objects;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using NSubstitute;
using NSubstitute.ReceivedExtensions;
using NSubstitute.ReturnsExtensions;
using Xunit;
namespace Bit.Core.Test.Services;
@@ -188,7 +193,7 @@ public class UserServiceTests
}
[Theory, BitAutoData]
public async void CompleteWebAuthLoginRegistrationAsync_ExceedsExistingCredentialsLimit_ReturnsFalse(SutProvider<UserService> sutProvider, User user, CredentialCreateOptions options, AuthenticatorAttestationRawResponse response, Generator<WebAuthnCredential> credentialGenerator)
public async Task CompleteWebAuthLoginRegistrationAsync_ExceedsExistingCredentialsLimit_ReturnsFalse(SutProvider<UserService> sutProvider, User user, CredentialCreateOptions options, AuthenticatorAttestationRawResponse response, Generator<WebAuthnCredential> credentialGenerator)
{
// Arrange
var existingCredentials = credentialGenerator.Take(5).ToList();
@@ -202,6 +207,92 @@ public class UserServiceTests
sutProvider.GetDependency<IWebAuthnCredentialRepository>().DidNotReceive();
}
[Theory, BitAutoData]
public async Task CompleteWebAuthLoginAssertionAsync_InvalidUserHandle_ThrowsBadRequestException(SutProvider<UserService> sutProvider, AssertionOptions options, AuthenticatorAssertionRawResponse response)
{
// Arrange
response.Response.UserHandle = Encoding.UTF8.GetBytes("invalid-user-handle");
// Act
var result = async () => await sutProvider.Sut.CompleteWebAuthLoginAssertionAsync(options, response);
// Assert
await Assert.ThrowsAsync<BadRequestException>(result);
}
[Theory, BitAutoData]
public async Task CompleteWebAuthLoginAssertionAsync_UserNotFound_ThrowsBadRequestException(SutProvider<UserService> sutProvider, User user, AssertionOptions options, AuthenticatorAssertionRawResponse response)
{
// Arrange
response.Response.UserHandle = user.Id.ToByteArray();
sutProvider.GetDependency<IUserRepository>().GetByIdAsync(user.Id).ReturnsNull();
// Act
var result = async () => await sutProvider.Sut.CompleteWebAuthLoginAssertionAsync(options, response);
// Assert
await Assert.ThrowsAsync<BadRequestException>(result);
}
[Theory, BitAutoData]
public async Task CompleteWebAuthLoginAssertionAsync_NoMatchingCredentialExists_ThrowsBadRequestException(SutProvider<UserService> sutProvider, User user, AssertionOptions options, AuthenticatorAssertionRawResponse response)
{
// Arrange
response.Response.UserHandle = user.Id.ToByteArray();
sutProvider.GetDependency<IUserRepository>().GetByIdAsync(user.Id).Returns(user);
sutProvider.GetDependency<IWebAuthnCredentialRepository>().GetManyByUserIdAsync(user.Id).Returns(new WebAuthnCredential[] { });
// Act
var result = async () => await sutProvider.Sut.CompleteWebAuthLoginAssertionAsync(options, response);
// Assert
await Assert.ThrowsAsync<BadRequestException>(result);
}
[Theory, BitAutoData]
public async Task CompleteWebAuthLoginAssertionAsync_AssertionFails_ThrowsBadRequestException(SutProvider<UserService> sutProvider, User user, AssertionOptions options, AuthenticatorAssertionRawResponse response, WebAuthnCredential credential, AssertionVerificationResult assertionResult)
{
// Arrange
var credentialId = Guid.NewGuid().ToByteArray();
credential.CredentialId = CoreHelpers.Base64UrlEncode(credentialId);
response.Id = credentialId;
response.Response.UserHandle = user.Id.ToByteArray();
assertionResult.Status = "Not ok";
sutProvider.GetDependency<IUserRepository>().GetByIdAsync(user.Id).Returns(user);
sutProvider.GetDependency<IWebAuthnCredentialRepository>().GetManyByUserIdAsync(user.Id).Returns(new WebAuthnCredential[] { credential });
sutProvider.GetDependency<IFido2>().MakeAssertionAsync(response, options, Arg.Any<byte[]>(), Arg.Any<uint>(), Arg.Any<IsUserHandleOwnerOfCredentialIdAsync>())
.Returns(assertionResult);
// Act
var result = async () => await sutProvider.Sut.CompleteWebAuthLoginAssertionAsync(options, response);
// Assert
await Assert.ThrowsAsync<BadRequestException>(result);
}
[Theory, BitAutoData]
public async Task CompleteWebAuthLoginAssertionAsync_AssertionSucceeds_ReturnsUserAndCredential(SutProvider<UserService> sutProvider, User user, AssertionOptions options, AuthenticatorAssertionRawResponse response, WebAuthnCredential credential, AssertionVerificationResult assertionResult)
{
// Arrange
var credentialId = Guid.NewGuid().ToByteArray();
credential.CredentialId = CoreHelpers.Base64UrlEncode(credentialId);
response.Id = credentialId;
response.Response.UserHandle = user.Id.ToByteArray();
assertionResult.Status = "ok";
sutProvider.GetDependency<IUserRepository>().GetByIdAsync(user.Id).Returns(user);
sutProvider.GetDependency<IWebAuthnCredentialRepository>().GetManyByUserIdAsync(user.Id).Returns(new WebAuthnCredential[] { credential });
sutProvider.GetDependency<IFido2>().MakeAssertionAsync(response, options, Arg.Any<byte[]>(), Arg.Any<uint>(), Arg.Any<IsUserHandleOwnerOfCredentialIdAsync>())
.Returns(assertionResult);
// Act
var result = await sutProvider.Sut.CompleteWebAuthLoginAssertionAsync(options, response);
// Assert
var (userResult, credentialResult) = result;
Assert.Equal(user, userResult);
Assert.Equal(credential, credentialResult);
}
[Flags]
public enum ShouldCheck
{
@@ -278,8 +369,7 @@ public class UserServiceTests
sutProvider.GetDependency<IProviderUserRepository>(),
sutProvider.GetDependency<IStripeSyncService>(),
new FakeDataProtectorTokenFactory<OrgUserInviteTokenable>(),
sutProvider.GetDependency<IWebAuthnCredentialRepository>(),
sutProvider.GetDependency<IDataProtectorTokenFactory<WebAuthnLoginTokenable>>()
sutProvider.GetDependency<IWebAuthnCredentialRepository>()
);
var actualIsVerified = await sut.VerifySecretAsync(user, secret);

View File

@@ -1,10 +1,12 @@
using System.Text;
using System.Text.Json;
using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Enums;
using Bit.Core.AdminConsole.Models.Data.Organizations.Policies;
using Bit.Core.AdminConsole.Services;
using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Exceptions;
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
using Bit.Core.Models.Data.Organizations.Policies;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Core.Test.AutoFixture.CurrentContextFixtures;

View File

@@ -4,6 +4,9 @@ using Bit.Core.Exceptions;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Core.Test.AutoFixture.CipherFixtures;
using Bit.Core.Tools.Enums;
using Bit.Core.Tools.Models.Business;
using Bit.Core.Tools.Services;
using Bit.Core.Utilities;
using Bit.Core.Vault.Entities;
using Bit.Core.Vault.Models.Data;
@@ -21,6 +24,64 @@ namespace Bit.Core.Test.Services;
[SutProviderCustomize]
public class CipherServiceTests
{
[Theory, BitAutoData]
public async Task ImportCiphersAsync_IntoOrganization_Success(
Organization organization,
Guid importingUserId,
OrganizationUser importingOrganizationUser,
List<Collection> collections,
List<CipherDetails> ciphers,
SutProvider<CipherService> sutProvider)
{
organization.MaxCollections = null;
importingOrganizationUser.OrganizationId = organization.Id;
foreach (var collection in collections)
{
collection.OrganizationId = organization.Id;
}
foreach (var cipher in ciphers)
{
cipher.OrganizationId = organization.Id;
}
KeyValuePair<int, int>[] collectionRelationships = {
new(0, 0),
new(1, 1),
new(2, 2)
};
sutProvider.GetDependency<IOrganizationRepository>()
.GetByIdAsync(organization.Id)
.Returns(organization);
sutProvider.GetDependency<IOrganizationUserRepository>()
.GetByOrganizationAsync(organization.Id, importingUserId)
.Returns(importingOrganizationUser);
// Set up a collection that already exists in the organization
sutProvider.GetDependency<ICollectionRepository>()
.GetManyByOrganizationIdAsync(organization.Id)
.Returns(new List<Collection> { collections[0] });
await sutProvider.Sut.ImportCiphersAsync(collections, ciphers, collectionRelationships, importingUserId);
await sutProvider.GetDependency<ICipherRepository>().Received(1).CreateAsync(
ciphers,
Arg.Is<IEnumerable<Collection>>(cols => cols.Count() == collections.Count - 1 &&
!cols.Any(c => c.Id == collections[0].Id) && // Check that the collection that already existed in the organization was not added
cols.All(c => collections.Any(x => c.Name == x.Name))),
Arg.Is<IEnumerable<CollectionCipher>>(c => c.Count() == ciphers.Count),
Arg.Is<IEnumerable<CollectionUser>>(cus =>
cus.Count() == collections.Count - 1 &&
!cus.Any(cu => cu.CollectionId == collections[0].Id) && // Check that access was not added for the collection that already existed in the organization
cus.All(cu => cu.OrganizationUserId == importingOrganizationUser.Id && cu.Manage == true)));
await sutProvider.GetDependency<IPushNotificationService>().Received(1).PushSyncVaultAsync(importingUserId);
await sutProvider.GetDependency<IReferenceEventService>().Received(1).RaiseEventAsync(
Arg.Is<ReferenceEvent>(e => e.Type == ReferenceEventType.VaultImported));
}
[Theory, BitAutoData]
public async Task SaveAsync_WrongRevisionDate_Throws(SutProvider<CipherService> sutProvider, Cipher cipher)
{

View File

@@ -260,6 +260,24 @@
"Microsoft.Win32.Registry": "5.0.0"
}
},
"Duende.IdentityServer": {
"type": "Transitive",
"resolved": "6.0.4",
"contentHash": "4HVjzx1F8v5J+U7oa8RGAQGj2QzmzNSu87r18Sh+dlh10uyZZL8teAaT/FaVLDObnfItGdPFvN8mwpF/HkI3Xw==",
"dependencies": {
"Duende.IdentityServer.Storage": "6.0.4",
"Microsoft.AspNetCore.Authentication.OpenIdConnect": "6.0.0"
}
},
"Duende.IdentityServer.Storage": {
"type": "Transitive",
"resolved": "6.0.4",
"contentHash": "s5gAjfbpr2IMgI+fU2Nx+2AZdzstmbt9gpo13iX7GwvqSeSaBVqj9ZskAN0R2KF1OemPdZuGnfaTcevdXMUrrw==",
"dependencies": {
"IdentityModel": "6.0.0",
"Microsoft.AspNetCore.DataProtection.Abstractions": "6.0.0"
}
},
"Fare": {
"type": "Transitive",
"resolved": "2.1.1",
@@ -304,49 +322,8 @@
},
"IdentityModel": {
"type": "Transitive",
"resolved": "4.4.0",
"contentHash": "b18wrIx5wnZlMxAX7oVsE+nDtAJ4hajYlH0xPlaRvo4r/fz08K6pPeZvbiqS9nfNbzfIgLFmNX+FL9qR9ZR5PA==",
"dependencies": {
"Newtonsoft.Json": "11.0.2",
"System.Text.Encodings.Web": "4.7.0"
}
},
"IdentityModel.AspNetCore.OAuth2Introspection": {
"type": "Transitive",
"resolved": "4.0.1",
"contentHash": "ZNdMZMaj9fqR3j50vYsu+1U3QGd6n8+fqwf+a8mCTcmXGor+HgFDfdq0mM34bsmD6uEgAQup7sv2ZW5kR36dbA==",
"dependencies": {
"IdentityModel": "4.0.0"
}
},
"IdentityServer4": {
"type": "Transitive",
"resolved": "4.1.2",
"contentHash": "blaxxGuOA7v/w1q+fxn97wZ+x2ecG1ZD4mc/N/ZOXMNeFZZhqv+4LF26Gecyik3nWrJPmbMEtQbLmRsKG8k61w==",
"dependencies": {
"IdentityModel": "4.4.0",
"IdentityServer4.Storage": "4.1.2",
"Microsoft.AspNetCore.Authentication.OpenIdConnect": "3.1.0",
"Microsoft.IdentityModel.Protocols.OpenIdConnect": "5.6.0",
"Newtonsoft.Json": "12.0.2"
}
},
"IdentityServer4.AccessTokenValidation": {
"type": "Transitive",
"resolved": "3.0.1",
"contentHash": "qu/M6UyN4o9NVep7q545Ms7hYAnsQqSdLbN1Fjjrn4m35lyBfeQPSSNzDryAKHbodyWOQfHaOqKEyMEJQ5Rpgw==",
"dependencies": {
"IdentityModel.AspNetCore.OAuth2Introspection": "4.0.1",
"Microsoft.AspNetCore.Authentication.JwtBearer": "3.0.0"
}
},
"IdentityServer4.Storage": {
"type": "Transitive",
"resolved": "4.1.2",
"contentHash": "KoSffyZyyeCNTIyJiZnCuPakJ1QbCHlpty6gbWUj/7yl+w0PXIchgmmJnJSvddzBb8iZ2xew/vGlxWUIP17P2g==",
"dependencies": {
"IdentityModel": "4.4.0"
}
"resolved": "6.0.0",
"contentHash": "eVHCR7a6m/dm5RFcBzE3qs/Jg5j9R5Rjpu8aTOv9e4AFvaQtBXb5ah7kmwU+YwA0ufRwz4wf1hnIvsD2hSnI4g=="
},
"LaunchDarkly.Cache": {
"type": "Transitive",
@@ -424,10 +401,10 @@
},
"Microsoft.AspNetCore.Authentication.OpenIdConnect": {
"type": "Transitive",
"resolved": "3.1.0",
"contentHash": "O1cAQYUTU8EfRqwc5/rfTns4E4hKlFlg59fuKRrST+PzsxI6H07KqRN/JjdYhAuVYxF8jPnIGbj+zuc5paOWUw==",
"resolved": "6.0.0",
"contentHash": "cJxdro36spFzk/K2OFCddM6vZ+yoj6ug8mTFRH3Gdv1Pul/buSuCtfb/FSCp31UmS5S4C1315dU7wX3ErLFuDg==",
"dependencies": {
"Microsoft.IdentityModel.Protocols.OpenIdConnect": "5.5.0"
"Microsoft.IdentityModel.Protocols.OpenIdConnect": "6.10.0"
}
},
"Microsoft.AspNetCore.Cryptography.Internal": {
@@ -460,8 +437,8 @@
},
"Microsoft.AspNetCore.DataProtection.Abstractions": {
"type": "Transitive",
"resolved": "3.1.32",
"contentHash": "MPL4iVyiaRxnOUY5VATHjvhDWaAEFb77KFiUxVRklv3Z3v+STofUr1UG/aCt1O9cgN7FVTDaC5A7U+zsLub8Xg=="
"resolved": "6.0.0",
"contentHash": "Z/UU4NEBm5UgNufJmw+j5baW26ytCOIZ0G7sZocPaOzsUeBon1bkM3lSMNZQG2GmDjAIVP2XMSODf2jzSGbibw=="
},
"Microsoft.Azure.Amqp": {
"type": "Transitive",
@@ -2686,7 +2663,7 @@
"dependencies": {
"AutoFixture.AutoNSubstitute": "[4.17.0, )",
"AutoFixture.Xunit2": "[4.17.0, )",
"Core": "[2023.10.2, )",
"Core": "[2023.10.3, )",
"Kralizek.AutoFixture.Extensions.MockHttp": "[1.2.0, )",
"Microsoft.NET.Test.Sdk": "[17.1.0, )",
"NSubstitute": "[4.3.0, )",
@@ -2708,10 +2685,9 @@
"BitPay.Light": "[1.0.1907, )",
"Braintree": "[5.19.0, )",
"DnsClient": "[1.7.0, )",
"Duende.IdentityServer": "[6.0.4, )",
"Fido2.AspNet": "[3.0.1, )",
"Handlebars.Net": "[2.1.2, )",
"IdentityServer4": "[4.1.2, )",
"IdentityServer4.AccessTokenValidation": "[3.0.1, )",
"LaunchDarkly.ServerSdk": "[8.0.0, )",
"MailKit": "[4.2.0, )",
"Microsoft.AspNetCore.Authentication.JwtBearer": "[6.0.4, )",

View File

@@ -278,6 +278,24 @@
"Microsoft.Win32.Registry": "5.0.0"
}
},
"Duende.IdentityServer": {
"type": "Transitive",
"resolved": "6.0.4",
"contentHash": "4HVjzx1F8v5J+U7oa8RGAQGj2QzmzNSu87r18Sh+dlh10uyZZL8teAaT/FaVLDObnfItGdPFvN8mwpF/HkI3Xw==",
"dependencies": {
"Duende.IdentityServer.Storage": "6.0.4",
"Microsoft.AspNetCore.Authentication.OpenIdConnect": "6.0.0"
}
},
"Duende.IdentityServer.Storage": {
"type": "Transitive",
"resolved": "6.0.4",
"contentHash": "s5gAjfbpr2IMgI+fU2Nx+2AZdzstmbt9gpo13iX7GwvqSeSaBVqj9ZskAN0R2KF1OemPdZuGnfaTcevdXMUrrw==",
"dependencies": {
"IdentityModel": "6.0.0",
"Microsoft.AspNetCore.DataProtection.Abstractions": "6.0.0"
}
},
"Fare": {
"type": "Transitive",
"resolved": "2.1.1",
@@ -322,49 +340,8 @@
},
"IdentityModel": {
"type": "Transitive",
"resolved": "4.4.0",
"contentHash": "b18wrIx5wnZlMxAX7oVsE+nDtAJ4hajYlH0xPlaRvo4r/fz08K6pPeZvbiqS9nfNbzfIgLFmNX+FL9qR9ZR5PA==",
"dependencies": {
"Newtonsoft.Json": "11.0.2",
"System.Text.Encodings.Web": "4.7.0"
}
},
"IdentityModel.AspNetCore.OAuth2Introspection": {
"type": "Transitive",
"resolved": "4.0.1",
"contentHash": "ZNdMZMaj9fqR3j50vYsu+1U3QGd6n8+fqwf+a8mCTcmXGor+HgFDfdq0mM34bsmD6uEgAQup7sv2ZW5kR36dbA==",
"dependencies": {
"IdentityModel": "4.0.0"
}
},
"IdentityServer4": {
"type": "Transitive",
"resolved": "4.1.2",
"contentHash": "blaxxGuOA7v/w1q+fxn97wZ+x2ecG1ZD4mc/N/ZOXMNeFZZhqv+4LF26Gecyik3nWrJPmbMEtQbLmRsKG8k61w==",
"dependencies": {
"IdentityModel": "4.4.0",
"IdentityServer4.Storage": "4.1.2",
"Microsoft.AspNetCore.Authentication.OpenIdConnect": "3.1.0",
"Microsoft.IdentityModel.Protocols.OpenIdConnect": "5.6.0",
"Newtonsoft.Json": "12.0.2"
}
},
"IdentityServer4.AccessTokenValidation": {
"type": "Transitive",
"resolved": "3.0.1",
"contentHash": "qu/M6UyN4o9NVep7q545Ms7hYAnsQqSdLbN1Fjjrn4m35lyBfeQPSSNzDryAKHbodyWOQfHaOqKEyMEJQ5Rpgw==",
"dependencies": {
"IdentityModel.AspNetCore.OAuth2Introspection": "4.0.1",
"Microsoft.AspNetCore.Authentication.JwtBearer": "3.0.0"
}
},
"IdentityServer4.Storage": {
"type": "Transitive",
"resolved": "4.1.2",
"contentHash": "KoSffyZyyeCNTIyJiZnCuPakJ1QbCHlpty6gbWUj/7yl+w0PXIchgmmJnJSvddzBb8iZ2xew/vGlxWUIP17P2g==",
"dependencies": {
"IdentityModel": "4.4.0"
}
"resolved": "6.0.0",
"contentHash": "eVHCR7a6m/dm5RFcBzE3qs/Jg5j9R5Rjpu8aTOv9e4AFvaQtBXb5ah7kmwU+YwA0ufRwz4wf1hnIvsD2hSnI4g=="
},
"Kralizek.AutoFixture.Extensions.MockHttp": {
"type": "Transitive",
@@ -465,10 +442,10 @@
},
"Microsoft.AspNetCore.Authentication.OpenIdConnect": {
"type": "Transitive",
"resolved": "3.1.0",
"contentHash": "O1cAQYUTU8EfRqwc5/rfTns4E4hKlFlg59fuKRrST+PzsxI6H07KqRN/JjdYhAuVYxF8jPnIGbj+zuc5paOWUw==",
"resolved": "6.0.0",
"contentHash": "cJxdro36spFzk/K2OFCddM6vZ+yoj6ug8mTFRH3Gdv1Pul/buSuCtfb/FSCp31UmS5S4C1315dU7wX3ErLFuDg==",
"dependencies": {
"Microsoft.IdentityModel.Protocols.OpenIdConnect": "5.5.0"
"Microsoft.IdentityModel.Protocols.OpenIdConnect": "6.10.0"
}
},
"Microsoft.AspNetCore.Cryptography.Internal": {
@@ -501,8 +478,8 @@
},
"Microsoft.AspNetCore.DataProtection.Abstractions": {
"type": "Transitive",
"resolved": "3.1.32",
"contentHash": "MPL4iVyiaRxnOUY5VATHjvhDWaAEFb77KFiUxVRklv3Z3v+STofUr1UG/aCt1O9cgN7FVTDaC5A7U+zsLub8Xg=="
"resolved": "6.0.0",
"contentHash": "Z/UU4NEBm5UgNufJmw+j5baW26ytCOIZ0G7sZocPaOzsUeBon1bkM3lSMNZQG2GmDjAIVP2XMSODf2jzSGbibw=="
},
"Microsoft.Azure.Amqp": {
"type": "Transitive",
@@ -2858,7 +2835,7 @@
"dependencies": {
"AutoFixture.AutoNSubstitute": "[4.17.0, )",
"AutoFixture.Xunit2": "[4.17.0, )",
"Core": "[2023.10.2, )",
"Core": "[2023.10.3, )",
"Kralizek.AutoFixture.Extensions.MockHttp": "[1.2.0, )",
"Microsoft.NET.Test.Sdk": "[17.1.0, )",
"NSubstitute": "[4.3.0, )",
@@ -2880,10 +2857,9 @@
"BitPay.Light": "[1.0.1907, )",
"Braintree": "[5.19.0, )",
"DnsClient": "[1.7.0, )",
"Duende.IdentityServer": "[6.0.4, )",
"Fido2.AspNet": "[3.0.1, )",
"Handlebars.Net": "[2.1.2, )",
"IdentityServer4": "[4.1.2, )",
"IdentityServer4.AccessTokenValidation": "[3.0.1, )",
"LaunchDarkly.ServerSdk": "[8.0.0, )",
"MailKit": "[4.2.0, )",
"Microsoft.AspNetCore.Authentication.JwtBearer": "[6.0.4, )",
@@ -2912,14 +2888,14 @@
"type": "Project",
"dependencies": {
"AngleSharp": "[1.0.4, )",
"Core": "[2023.10.2, )",
"SharedWeb": "[2023.10.2, )"
"Core": "[2023.10.3, )",
"SharedWeb": "[2023.10.3, )"
}
},
"infrastructure.dapper": {
"type": "Project",
"dependencies": {
"Core": "[2023.10.2, )",
"Core": "[2023.10.3, )",
"Dapper": "[2.0.123, )"
}
},
@@ -2927,7 +2903,7 @@
"type": "Project",
"dependencies": {
"AutoMapper.Extensions.Microsoft.DependencyInjection": "[12.0.1, )",
"Core": "[2023.10.2, )",
"Core": "[2023.10.3, )",
"Microsoft.EntityFrameworkCore.Relational": "[7.0.5, )",
"Microsoft.EntityFrameworkCore.SqlServer": "[7.0.5, )",
"Microsoft.EntityFrameworkCore.Sqlite": "[7.0.5, )",
@@ -2939,9 +2915,9 @@
"sharedweb": {
"type": "Project",
"dependencies": {
"Core": "[2023.10.2, )",
"Infrastructure.Dapper": "[2023.10.2, )",
"Infrastructure.EntityFramework": "[2023.10.2, )"
"Core": "[2023.10.3, )",
"Infrastructure.Dapper": "[2023.10.3, )",
"Infrastructure.EntityFramework": "[2023.10.3, )"
}
}
}

View File

@@ -15,9 +15,9 @@ using Bit.Core.Services;
using Bit.Core.Utilities;
using Bit.IntegrationTestCommon.Factories;
using Bit.Test.Common.Helpers;
using Duende.IdentityServer.Models;
using Duende.IdentityServer.Stores;
using IdentityModel;
using IdentityServer4.Models;
using IdentityServer4.Stores;
using Microsoft.EntityFrameworkCore;
using NSubstitute;
using Xunit;
@@ -564,7 +564,7 @@ public class IdentityServerSsoTests
new Claim(JwtClaimTypes.SessionId, "SOMETHING"),
new Claim(JwtClaimTypes.AuthenticationMethod, "external"),
new Claim(JwtClaimTypes.AuthenticationTime, DateTime.UtcNow.AddMinutes(-1).ToEpochTime().ToString())
}, "IdentityServer4", JwtClaimTypes.Name, JwtClaimTypes.Role));
}, "Duende.IdentityServer", JwtClaimTypes.Name, JwtClaimTypes.Role));
authorizationCode.Subject = subject;

View File

@@ -1,4 +1,7 @@
using System.Text.Json;
using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Enums;
using Bit.Core.AdminConsole.Repositories;
using Bit.Core.Auth.Models.Api.Request.Accounts;
using Bit.Core.Enums;
using Bit.Core.Repositories;
@@ -569,7 +572,7 @@ public class IdentityServerTests : IClassFixture<IdentityApplicationFactory>
};
await organizationUserRepository.CreateAsync(organizationUser);
var ssoPolicy = new Bit.Core.Entities.Policy { OrganizationId = organization.Id, Type = PolicyType.RequireSso, Enabled = ssoPolicyEnabled };
var ssoPolicy = new Policy { OrganizationId = organization.Id, Type = PolicyType.RequireSso, Enabled = ssoPolicyEnabled };
await policyRepository.CreateAsync(ssoPolicy);
}

View File

@@ -4,6 +4,7 @@
"authorization_endpoint": "http://localhost:33656/connect/authorize",
"token_endpoint": "http://localhost:33656/connect/token",
"device_authorization_endpoint": "http://localhost:33656/connect/deviceauthorization",
"backchannel_authentication_endpoint": "http://localhost:33656/connect/ciba",
"scopes_supported": [
"api",
"api.push",
@@ -38,7 +39,9 @@
"refresh_token",
"implicit",
"password",
"urn:ietf:params:oauth:grant-type:device_code"
"urn:ietf:params:oauth:grant-type:device_code",
"urn:openid:params:grant-type:ciba",
"webauthn"
],
"response_types_supported": [
"code",
@@ -49,24 +52,30 @@
"code token",
"code id_token token"
],
"response_modes_supported": [
"form_post",
"query",
"fragment"
],
"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"
"id_token_signing_alg_values_supported": ["RS256"],
"subject_types_supported": ["public"],
"code_challenge_methods_supported": ["plain", "S256"],
"request_parameter_supported": true,
"request_object_signing_alg_values_supported": [
"RS256",
"RS384",
"RS512",
"PS256",
"PS384",
"PS512",
"ES256",
"ES384",
"ES512",
"HS256",
"HS384",
"HS512"
],
"subject_types_supported": [
"public"
],
"code_challenge_methods_supported": [
"plain",
"S256"
],
"request_parameter_supported": true
"authorization_response_iss_parameter_supported": true,
"backchannel_token_delivery_modes_supported": ["poll"],
"backchannel_user_code_parameter_supported": true
}

View File

@@ -282,6 +282,24 @@
"Microsoft.Win32.Registry": "5.0.0"
}
},
"Duende.IdentityServer": {
"type": "Transitive",
"resolved": "6.0.4",
"contentHash": "4HVjzx1F8v5J+U7oa8RGAQGj2QzmzNSu87r18Sh+dlh10uyZZL8teAaT/FaVLDObnfItGdPFvN8mwpF/HkI3Xw==",
"dependencies": {
"Duende.IdentityServer.Storage": "6.0.4",
"Microsoft.AspNetCore.Authentication.OpenIdConnect": "6.0.0"
}
},
"Duende.IdentityServer.Storage": {
"type": "Transitive",
"resolved": "6.0.4",
"contentHash": "s5gAjfbpr2IMgI+fU2Nx+2AZdzstmbt9gpo13iX7GwvqSeSaBVqj9ZskAN0R2KF1OemPdZuGnfaTcevdXMUrrw==",
"dependencies": {
"IdentityModel": "6.0.0",
"Microsoft.AspNetCore.DataProtection.Abstractions": "6.0.0"
}
},
"Fare": {
"type": "Transitive",
"resolved": "2.1.1",
@@ -326,49 +344,8 @@
},
"IdentityModel": {
"type": "Transitive",
"resolved": "4.4.0",
"contentHash": "b18wrIx5wnZlMxAX7oVsE+nDtAJ4hajYlH0xPlaRvo4r/fz08K6pPeZvbiqS9nfNbzfIgLFmNX+FL9qR9ZR5PA==",
"dependencies": {
"Newtonsoft.Json": "11.0.2",
"System.Text.Encodings.Web": "4.7.0"
}
},
"IdentityModel.AspNetCore.OAuth2Introspection": {
"type": "Transitive",
"resolved": "4.0.1",
"contentHash": "ZNdMZMaj9fqR3j50vYsu+1U3QGd6n8+fqwf+a8mCTcmXGor+HgFDfdq0mM34bsmD6uEgAQup7sv2ZW5kR36dbA==",
"dependencies": {
"IdentityModel": "4.0.0"
}
},
"IdentityServer4": {
"type": "Transitive",
"resolved": "4.1.2",
"contentHash": "blaxxGuOA7v/w1q+fxn97wZ+x2ecG1ZD4mc/N/ZOXMNeFZZhqv+4LF26Gecyik3nWrJPmbMEtQbLmRsKG8k61w==",
"dependencies": {
"IdentityModel": "4.4.0",
"IdentityServer4.Storage": "4.1.2",
"Microsoft.AspNetCore.Authentication.OpenIdConnect": "3.1.0",
"Microsoft.IdentityModel.Protocols.OpenIdConnect": "5.6.0",
"Newtonsoft.Json": "12.0.2"
}
},
"IdentityServer4.AccessTokenValidation": {
"type": "Transitive",
"resolved": "3.0.1",
"contentHash": "qu/M6UyN4o9NVep7q545Ms7hYAnsQqSdLbN1Fjjrn4m35lyBfeQPSSNzDryAKHbodyWOQfHaOqKEyMEJQ5Rpgw==",
"dependencies": {
"IdentityModel.AspNetCore.OAuth2Introspection": "4.0.1",
"Microsoft.AspNetCore.Authentication.JwtBearer": "3.0.0"
}
},
"IdentityServer4.Storage": {
"type": "Transitive",
"resolved": "4.1.2",
"contentHash": "KoSffyZyyeCNTIyJiZnCuPakJ1QbCHlpty6gbWUj/7yl+w0PXIchgmmJnJSvddzBb8iZ2xew/vGlxWUIP17P2g==",
"dependencies": {
"IdentityModel": "4.4.0"
}
"resolved": "6.0.0",
"contentHash": "eVHCR7a6m/dm5RFcBzE3qs/Jg5j9R5Rjpu8aTOv9e4AFvaQtBXb5ah7kmwU+YwA0ufRwz4wf1hnIvsD2hSnI4g=="
},
"Kralizek.AutoFixture.Extensions.MockHttp": {
"type": "Transitive",
@@ -469,10 +446,10 @@
},
"Microsoft.AspNetCore.Authentication.OpenIdConnect": {
"type": "Transitive",
"resolved": "3.1.0",
"contentHash": "O1cAQYUTU8EfRqwc5/rfTns4E4hKlFlg59fuKRrST+PzsxI6H07KqRN/JjdYhAuVYxF8jPnIGbj+zuc5paOWUw==",
"resolved": "6.0.0",
"contentHash": "cJxdro36spFzk/K2OFCddM6vZ+yoj6ug8mTFRH3Gdv1Pul/buSuCtfb/FSCp31UmS5S4C1315dU7wX3ErLFuDg==",
"dependencies": {
"Microsoft.IdentityModel.Protocols.OpenIdConnect": "5.5.0"
"Microsoft.IdentityModel.Protocols.OpenIdConnect": "6.10.0"
}
},
"Microsoft.AspNetCore.Cryptography.Internal": {
@@ -505,8 +482,8 @@
},
"Microsoft.AspNetCore.DataProtection.Abstractions": {
"type": "Transitive",
"resolved": "3.1.32",
"contentHash": "MPL4iVyiaRxnOUY5VATHjvhDWaAEFb77KFiUxVRklv3Z3v+STofUr1UG/aCt1O9cgN7FVTDaC5A7U+zsLub8Xg=="
"resolved": "6.0.0",
"contentHash": "Z/UU4NEBm5UgNufJmw+j5baW26ytCOIZ0G7sZocPaOzsUeBon1bkM3lSMNZQG2GmDjAIVP2XMSODf2jzSGbibw=="
},
"Microsoft.AspNetCore.TestHost": {
"type": "Transitive",
@@ -2997,7 +2974,7 @@
"dependencies": {
"AutoFixture.AutoNSubstitute": "[4.17.0, )",
"AutoFixture.Xunit2": "[4.17.0, )",
"Core": "[2023.10.2, )",
"Core": "[2023.10.3, )",
"Kralizek.AutoFixture.Extensions.MockHttp": "[1.2.0, )",
"Microsoft.NET.Test.Sdk": "[17.1.0, )",
"NSubstitute": "[4.3.0, )",
@@ -3019,10 +2996,9 @@
"BitPay.Light": "[1.0.1907, )",
"Braintree": "[5.19.0, )",
"DnsClient": "[1.7.0, )",
"Duende.IdentityServer": "[6.0.4, )",
"Fido2.AspNet": "[3.0.1, )",
"Handlebars.Net": "[2.1.2, )",
"IdentityServer4": "[4.1.2, )",
"IdentityServer4.AccessTokenValidation": "[3.0.1, )",
"LaunchDarkly.ServerSdk": "[8.0.0, )",
"MailKit": "[4.2.0, )",
"Microsoft.AspNetCore.Authentication.JwtBearer": "[6.0.4, )",
@@ -3050,15 +3026,15 @@
"identity": {
"type": "Project",
"dependencies": {
"Core": "[2023.10.2, )",
"SharedWeb": "[2023.10.2, )",
"Core": "[2023.10.3, )",
"SharedWeb": "[2023.10.3, )",
"Swashbuckle.AspNetCore.SwaggerGen": "[6.5.0, )"
}
},
"infrastructure.dapper": {
"type": "Project",
"dependencies": {
"Core": "[2023.10.2, )",
"Core": "[2023.10.3, )",
"Dapper": "[2.0.123, )"
}
},
@@ -3066,7 +3042,7 @@
"type": "Project",
"dependencies": {
"AutoMapper.Extensions.Microsoft.DependencyInjection": "[12.0.1, )",
"Core": "[2023.10.2, )",
"Core": "[2023.10.3, )",
"Microsoft.EntityFrameworkCore.Relational": "[7.0.5, )",
"Microsoft.EntityFrameworkCore.SqlServer": "[7.0.5, )",
"Microsoft.EntityFrameworkCore.Sqlite": "[7.0.5, )",
@@ -3078,8 +3054,8 @@
"integrationtestcommon": {
"type": "Project",
"dependencies": {
"Common": "[2023.10.2, )",
"Identity": "[2023.10.2, )",
"Common": "[2023.10.3, )",
"Identity": "[2023.10.3, )",
"Microsoft.AspNetCore.Mvc.Testing": "[6.0.5, )",
"Microsoft.Extensions.Configuration": "[6.0.1, )"
}
@@ -3087,9 +3063,9 @@
"sharedweb": {
"type": "Project",
"dependencies": {
"Core": "[2023.10.2, )",
"Infrastructure.Dapper": "[2023.10.2, )",
"Infrastructure.EntityFramework": "[2023.10.2, )"
"Core": "[2023.10.3, )",
"Infrastructure.Dapper": "[2023.10.3, )",
"Infrastructure.EntityFramework": "[2023.10.3, )"
}
}
}

View File

@@ -1,4 +1,5 @@
using Bit.Core.Auth.Models.Api.Request.Accounts;
using Bit.Core.Auth.Models.Business.Tokenables;
using Bit.Core.Auth.Services;
using Bit.Core.Entities;
using Bit.Core.Enums;
@@ -6,6 +7,7 @@ using Bit.Core.Exceptions;
using Bit.Core.Models.Data;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Core.Tokens;
using Bit.Identity.Controllers;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Logging;
@@ -22,6 +24,7 @@ public class AccountsControllerTests : IDisposable
private readonly IUserRepository _userRepository;
private readonly IUserService _userService;
private readonly ICaptchaValidationService _captchaValidationService;
private readonly IDataProtectorTokenFactory<WebAuthnLoginAssertionOptionsTokenable> _assertionOptionsDataProtector;
public AccountsControllerTests()
{
@@ -29,11 +32,13 @@ public class AccountsControllerTests : IDisposable
_userRepository = Substitute.For<IUserRepository>();
_userService = Substitute.For<IUserService>();
_captchaValidationService = Substitute.For<ICaptchaValidationService>();
_assertionOptionsDataProtector = Substitute.For<IDataProtectorTokenFactory<WebAuthnLoginAssertionOptionsTokenable>>();
_sut = new AccountsController(
_logger,
_userRepository,
_userService,
_captchaValidationService
_captchaValidationService,
_assertionOptionsDataProtector
);
}

View File

@@ -0,0 +1,172 @@
using Bit.Core;
using Bit.Core.Auth.Entities;
using Bit.Core.Auth.Enums;
using Bit.Core.Auth.Models.Data;
using Bit.Core.Context;
using Bit.Core.Entities;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Identity.IdentityServer;
using Bit.Identity.Utilities;
using Bit.Test.Common.AutoFixture.Attributes;
using NSubstitute;
using Xunit;
namespace Bit.Identity.Test.IdentityServer;
public class UserDecryptionOptionsBuilderTests
{
private readonly ICurrentContext _currentContext;
private readonly IFeatureService _featureService;
private readonly IDeviceRepository _deviceRepository;
private readonly IOrganizationUserRepository _organizationUserRepository;
private readonly UserDecryptionOptionsBuilder _builder;
public UserDecryptionOptionsBuilderTests()
{
_currentContext = Substitute.For<ICurrentContext>();
_featureService = Substitute.For<IFeatureService>();
_deviceRepository = Substitute.For<IDeviceRepository>();
_organizationUserRepository = Substitute.For<IOrganizationUserRepository>();
_builder = new UserDecryptionOptionsBuilder(_currentContext, _featureService, _deviceRepository, _organizationUserRepository);
}
[Theory]
[BitAutoData(true, true, true)] // All keys are non-null
[BitAutoData(false, false, false)] // All keys are null
[BitAutoData(false, false, true)] // EncryptedUserKey is non-null, others are null
[BitAutoData(false, true, false)] // EncryptedPublicKey is non-null, others are null
[BitAutoData(true, false, false)] // EncryptedPrivateKey is non-null, others are null
[BitAutoData(true, false, true)] // EncryptedPrivateKey and EncryptedUserKey are non-null, EncryptedPublicKey is null
[BitAutoData(true, true, false)] // EncryptedPrivateKey and EncryptedPublicKey are non-null, EncryptedUserKey is null
[BitAutoData(false, true, true)] // EncryptedPublicKey and EncryptedUserKey are non-null, EncryptedPrivateKey is null
public async Task WithWebAuthnLoginCredential_VariousKeyCombinations_ShouldReturnCorrectPrfOption(
bool hasEncryptedPrivateKey,
bool hasEncryptedPublicKey,
bool hasEncryptedUserKey,
WebAuthnCredential credential)
{
credential.EncryptedPrivateKey = hasEncryptedPrivateKey ? "encryptedPrivateKey" : null;
credential.EncryptedPublicKey = hasEncryptedPublicKey ? "encryptedPublicKey" : null;
credential.EncryptedUserKey = hasEncryptedUserKey ? "encryptedUserKey" : null;
var result = await _builder.WithWebAuthnLoginCredential(credential).BuildAsync();
if (credential.GetPrfStatus() == WebAuthnPrfStatus.Enabled)
{
Assert.NotNull(result.WebAuthnPrfOption);
Assert.Equal(credential.EncryptedPrivateKey, result.WebAuthnPrfOption!.EncryptedPrivateKey);
Assert.Equal(credential.EncryptedUserKey, result.WebAuthnPrfOption!.EncryptedUserKey);
}
else
{
Assert.Null(result.WebAuthnPrfOption);
}
}
[Theory, BitAutoData]
public async Task Build_WhenKeyConnectorIsEnabled_ShouldReturnKeyConnectorOptions(SsoConfig ssoConfig, SsoConfigurationData configurationData)
{
configurationData.MemberDecryptionType = MemberDecryptionType.KeyConnector;
ssoConfig.Data = configurationData.Serialize();
var result = await _builder.WithSso(ssoConfig).BuildAsync();
Assert.NotNull(result.KeyConnectorOption);
Assert.Equal(configurationData.KeyConnectorUrl, result.KeyConnectorOption!.KeyConnectorUrl);
}
[Theory, BitAutoData]
public async Task Build_WhenTrustedDeviceIsEnabled_ShouldReturnTrustedDeviceOptions(SsoConfig ssoConfig, SsoConfigurationData configurationData, Device device)
{
_featureService.IsEnabled(FeatureFlagKeys.TrustedDeviceEncryption, _currentContext).Returns(true);
configurationData.MemberDecryptionType = MemberDecryptionType.TrustedDeviceEncryption;
ssoConfig.Data = configurationData.Serialize();
var result = await _builder.WithSso(ssoConfig).WithDevice(device).BuildAsync();
Assert.NotNull(result.TrustedDeviceOption);
Assert.False(result.TrustedDeviceOption!.HasAdminApproval);
Assert.False(result.TrustedDeviceOption!.HasLoginApprovingDevice);
Assert.False(result.TrustedDeviceOption!.HasManageResetPasswordPermission);
}
// TODO: Remove when FeatureFlagKeys.TrustedDeviceEncryption is removed
[Theory, BitAutoData]
public async Task Build_WhenTrustedDeviceIsEnabledButFeatureFlagIsDisabled_ShouldNotReturnTrustedDeviceOptions(SsoConfig ssoConfig, SsoConfigurationData configurationData, Device device)
{
_featureService.IsEnabled(FeatureFlagKeys.TrustedDeviceEncryption, _currentContext).Returns(false);
configurationData.MemberDecryptionType = MemberDecryptionType.TrustedDeviceEncryption;
ssoConfig.Data = configurationData.Serialize();
var result = await _builder.WithSso(ssoConfig).WithDevice(device).BuildAsync();
Assert.Null(result.TrustedDeviceOption);
}
[Theory, BitAutoData]
public async Task Build_WhenDeviceIsTrusted_ShouldReturnKeys(SsoConfig ssoConfig, SsoConfigurationData configurationData, Device device)
{
_featureService.IsEnabled(FeatureFlagKeys.TrustedDeviceEncryption, _currentContext).Returns(true);
configurationData.MemberDecryptionType = MemberDecryptionType.TrustedDeviceEncryption;
ssoConfig.Data = configurationData.Serialize();
device.EncryptedPrivateKey = "encryptedPrivateKey";
device.EncryptedPublicKey = "encryptedPublicKey";
device.EncryptedUserKey = "encryptedUserKey";
var result = await _builder.WithSso(ssoConfig).WithDevice(device).BuildAsync();
Assert.Equal(device.EncryptedPrivateKey, result.TrustedDeviceOption?.EncryptedPrivateKey);
Assert.Equal(device.EncryptedUserKey, result.TrustedDeviceOption?.EncryptedUserKey);
}
[Theory, BitAutoData]
public async Task Build_WhenHasLoginApprovingDevice_ShouldApprovingDeviceTrue(SsoConfig ssoConfig, SsoConfigurationData configurationData, User user, Device device, Device approvingDevice)
{
_featureService.IsEnabled(FeatureFlagKeys.TrustedDeviceEncryption, _currentContext).Returns(true);
configurationData.MemberDecryptionType = MemberDecryptionType.TrustedDeviceEncryption;
ssoConfig.Data = configurationData.Serialize();
approvingDevice.Type = LoginApprovingDeviceTypes.Types.First();
_deviceRepository.GetManyByUserIdAsync(user.Id).Returns(new Device[] { approvingDevice });
var result = await _builder.ForUser(user).WithSso(ssoConfig).WithDevice(device).BuildAsync();
Assert.True(result.TrustedDeviceOption?.HasLoginApprovingDevice);
}
[Theory, BitAutoData]
public async Task Build_WhenManageResetPasswordPermissions_ShouldReturnHasManageResetPasswordPermissionTrue(
SsoConfig ssoConfig,
SsoConfigurationData configurationData,
CurrentContextOrganization organization)
{
_featureService.IsEnabled(FeatureFlagKeys.TrustedDeviceEncryption, _currentContext).Returns(true);
configurationData.MemberDecryptionType = MemberDecryptionType.TrustedDeviceEncryption;
ssoConfig.Data = configurationData.Serialize();
ssoConfig.OrganizationId = organization.Id;
_currentContext.Organizations.Returns(new List<CurrentContextOrganization>(new CurrentContextOrganization[] { organization }));
_currentContext.ManageResetPassword(organization.Id).Returns(true);
var result = await _builder.WithSso(ssoConfig).BuildAsync();
Assert.True(result.TrustedDeviceOption?.HasManageResetPasswordPermission);
}
[Theory, BitAutoData]
public async Task Build_WhenUserHasEnrolledIntoPasswordReset_ShouldReturnHasAdminApprovalTrue(
SsoConfig ssoConfig,
SsoConfigurationData configurationData,
OrganizationUser organizationUser,
User user)
{
_featureService.IsEnabled(FeatureFlagKeys.TrustedDeviceEncryption, _currentContext).Returns(true);
configurationData.MemberDecryptionType = MemberDecryptionType.TrustedDeviceEncryption;
ssoConfig.Data = configurationData.Serialize();
organizationUser.ResetPasswordKey = "resetPasswordKey";
_organizationUserRepository.GetByOrganizationAsync(ssoConfig.OrganizationId, user.Id).Returns(organizationUser);
var result = await _builder.ForUser(user).WithSso(ssoConfig).BuildAsync();
Assert.True(result.TrustedDeviceOption?.HasAdminApproval);
}
}

View File

@@ -271,6 +271,24 @@
"Microsoft.Win32.Registry": "5.0.0"
}
},
"Duende.IdentityServer": {
"type": "Transitive",
"resolved": "6.0.4",
"contentHash": "4HVjzx1F8v5J+U7oa8RGAQGj2QzmzNSu87r18Sh+dlh10uyZZL8teAaT/FaVLDObnfItGdPFvN8mwpF/HkI3Xw==",
"dependencies": {
"Duende.IdentityServer.Storage": "6.0.4",
"Microsoft.AspNetCore.Authentication.OpenIdConnect": "6.0.0"
}
},
"Duende.IdentityServer.Storage": {
"type": "Transitive",
"resolved": "6.0.4",
"contentHash": "s5gAjfbpr2IMgI+fU2Nx+2AZdzstmbt9gpo13iX7GwvqSeSaBVqj9ZskAN0R2KF1OemPdZuGnfaTcevdXMUrrw==",
"dependencies": {
"IdentityModel": "6.0.0",
"Microsoft.AspNetCore.DataProtection.Abstractions": "6.0.0"
}
},
"Fare": {
"type": "Transitive",
"resolved": "2.1.1",
@@ -315,49 +333,8 @@
},
"IdentityModel": {
"type": "Transitive",
"resolved": "4.4.0",
"contentHash": "b18wrIx5wnZlMxAX7oVsE+nDtAJ4hajYlH0xPlaRvo4r/fz08K6pPeZvbiqS9nfNbzfIgLFmNX+FL9qR9ZR5PA==",
"dependencies": {
"Newtonsoft.Json": "11.0.2",
"System.Text.Encodings.Web": "4.7.0"
}
},
"IdentityModel.AspNetCore.OAuth2Introspection": {
"type": "Transitive",
"resolved": "4.0.1",
"contentHash": "ZNdMZMaj9fqR3j50vYsu+1U3QGd6n8+fqwf+a8mCTcmXGor+HgFDfdq0mM34bsmD6uEgAQup7sv2ZW5kR36dbA==",
"dependencies": {
"IdentityModel": "4.0.0"
}
},
"IdentityServer4": {
"type": "Transitive",
"resolved": "4.1.2",
"contentHash": "blaxxGuOA7v/w1q+fxn97wZ+x2ecG1ZD4mc/N/ZOXMNeFZZhqv+4LF26Gecyik3nWrJPmbMEtQbLmRsKG8k61w==",
"dependencies": {
"IdentityModel": "4.4.0",
"IdentityServer4.Storage": "4.1.2",
"Microsoft.AspNetCore.Authentication.OpenIdConnect": "3.1.0",
"Microsoft.IdentityModel.Protocols.OpenIdConnect": "5.6.0",
"Newtonsoft.Json": "12.0.2"
}
},
"IdentityServer4.AccessTokenValidation": {
"type": "Transitive",
"resolved": "3.0.1",
"contentHash": "qu/M6UyN4o9NVep7q545Ms7hYAnsQqSdLbN1Fjjrn4m35lyBfeQPSSNzDryAKHbodyWOQfHaOqKEyMEJQ5Rpgw==",
"dependencies": {
"IdentityModel.AspNetCore.OAuth2Introspection": "4.0.1",
"Microsoft.AspNetCore.Authentication.JwtBearer": "3.0.0"
}
},
"IdentityServer4.Storage": {
"type": "Transitive",
"resolved": "4.1.2",
"contentHash": "KoSffyZyyeCNTIyJiZnCuPakJ1QbCHlpty6gbWUj/7yl+w0PXIchgmmJnJSvddzBb8iZ2xew/vGlxWUIP17P2g==",
"dependencies": {
"IdentityModel": "4.4.0"
}
"resolved": "6.0.0",
"contentHash": "eVHCR7a6m/dm5RFcBzE3qs/Jg5j9R5Rjpu8aTOv9e4AFvaQtBXb5ah7kmwU+YwA0ufRwz4wf1hnIvsD2hSnI4g=="
},
"Kralizek.AutoFixture.Extensions.MockHttp": {
"type": "Transitive",
@@ -458,10 +435,10 @@
},
"Microsoft.AspNetCore.Authentication.OpenIdConnect": {
"type": "Transitive",
"resolved": "3.1.0",
"contentHash": "O1cAQYUTU8EfRqwc5/rfTns4E4hKlFlg59fuKRrST+PzsxI6H07KqRN/JjdYhAuVYxF8jPnIGbj+zuc5paOWUw==",
"resolved": "6.0.0",
"contentHash": "cJxdro36spFzk/K2OFCddM6vZ+yoj6ug8mTFRH3Gdv1Pul/buSuCtfb/FSCp31UmS5S4C1315dU7wX3ErLFuDg==",
"dependencies": {
"Microsoft.IdentityModel.Protocols.OpenIdConnect": "5.5.0"
"Microsoft.IdentityModel.Protocols.OpenIdConnect": "6.10.0"
}
},
"Microsoft.AspNetCore.Cryptography.Internal": {
@@ -494,8 +471,8 @@
},
"Microsoft.AspNetCore.DataProtection.Abstractions": {
"type": "Transitive",
"resolved": "3.1.32",
"contentHash": "MPL4iVyiaRxnOUY5VATHjvhDWaAEFb77KFiUxVRklv3Z3v+STofUr1UG/aCt1O9cgN7FVTDaC5A7U+zsLub8Xg=="
"resolved": "6.0.0",
"contentHash": "Z/UU4NEBm5UgNufJmw+j5baW26ytCOIZ0G7sZocPaOzsUeBon1bkM3lSMNZQG2GmDjAIVP2XMSODf2jzSGbibw=="
},
"Microsoft.Azure.Amqp": {
"type": "Transitive",
@@ -2872,7 +2849,7 @@
"dependencies": {
"AutoFixture.AutoNSubstitute": "[4.17.0, )",
"AutoFixture.Xunit2": "[4.17.0, )",
"Core": "[2023.10.2, )",
"Core": "[2023.10.3, )",
"Kralizek.AutoFixture.Extensions.MockHttp": "[1.2.0, )",
"Microsoft.NET.Test.Sdk": "[17.1.0, )",
"NSubstitute": "[4.3.0, )",
@@ -2894,10 +2871,9 @@
"BitPay.Light": "[1.0.1907, )",
"Braintree": "[5.19.0, )",
"DnsClient": "[1.7.0, )",
"Duende.IdentityServer": "[6.0.4, )",
"Fido2.AspNet": "[3.0.1, )",
"Handlebars.Net": "[2.1.2, )",
"IdentityServer4": "[4.1.2, )",
"IdentityServer4.AccessTokenValidation": "[3.0.1, )",
"LaunchDarkly.ServerSdk": "[8.0.0, )",
"MailKit": "[4.2.0, )",
"Microsoft.AspNetCore.Authentication.JwtBearer": "[6.0.4, )",
@@ -2925,15 +2901,15 @@
"identity": {
"type": "Project",
"dependencies": {
"Core": "[2023.10.2, )",
"SharedWeb": "[2023.10.2, )",
"Core": "[2023.10.3, )",
"SharedWeb": "[2023.10.3, )",
"Swashbuckle.AspNetCore.SwaggerGen": "[6.5.0, )"
}
},
"infrastructure.dapper": {
"type": "Project",
"dependencies": {
"Core": "[2023.10.2, )",
"Core": "[2023.10.3, )",
"Dapper": "[2.0.123, )"
}
},
@@ -2941,7 +2917,7 @@
"type": "Project",
"dependencies": {
"AutoMapper.Extensions.Microsoft.DependencyInjection": "[12.0.1, )",
"Core": "[2023.10.2, )",
"Core": "[2023.10.3, )",
"Microsoft.EntityFrameworkCore.Relational": "[7.0.5, )",
"Microsoft.EntityFrameworkCore.SqlServer": "[7.0.5, )",
"Microsoft.EntityFrameworkCore.Sqlite": "[7.0.5, )",
@@ -2953,9 +2929,9 @@
"sharedweb": {
"type": "Project",
"dependencies": {
"Core": "[2023.10.2, )",
"Infrastructure.Dapper": "[2023.10.2, )",
"Infrastructure.EntityFramework": "[2023.10.2, )"
"Core": "[2023.10.3, )",
"Infrastructure.Dapper": "[2023.10.3, )",
"Infrastructure.EntityFramework": "[2023.10.3, )"
}
}
}

View File

@@ -3,11 +3,13 @@ using Bit.Core.Test.AutoFixture.Attributes;
using Bit.Infrastructure.EFIntegration.Test.AutoFixture;
using Bit.Infrastructure.EFIntegration.Test.Repositories.EqualityComparers;
using Xunit;
using EfAdminConsoleRepo = Bit.Infrastructure.EntityFramework.AdminConsole.Repositories;
using EfRepo = Bit.Infrastructure.EntityFramework.Repositories;
using Policy = Bit.Core.Entities.Policy;
using Policy = Bit.Core.AdminConsole.Entities.Policy;
using SqlAdminConsoleRepo = Bit.Infrastructure.Dapper.AdminConsole.Repositories;
using SqlRepo = Bit.Infrastructure.Dapper.Repositories;
namespace Bit.Infrastructure.EFIntegration.Test.Repositories;
namespace Bit.Infrastructure.EFIntegration.Test.AdminConsole.Repositories;
public class PolicyRepositoryTests
{
@@ -16,9 +18,9 @@ public class PolicyRepositoryTests
Policy policy,
Organization organization,
PolicyCompare equalityComparer,
List<EfRepo.PolicyRepository> suts,
List<EfAdminConsoleRepo.PolicyRepository> suts,
List<EfRepo.OrganizationRepository> efOrganizationRepos,
SqlRepo.PolicyRepository sqlPolicyRepo,
SqlAdminConsoleRepo.PolicyRepository sqlPolicyRepo,
SqlRepo.OrganizationRepository sqlOrganizationRepo
)
{

View File

@@ -4,6 +4,7 @@ using AutoFixture.Kernel;
using AutoMapper;
using Bit.Core.Settings;
using Bit.Infrastructure.EFIntegration.Test.Helpers;
using Bit.Infrastructure.EntityFramework.AdminConsole.Models;
using Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider;
using Bit.Infrastructure.EntityFramework.Auth.Models;
using Bit.Infrastructure.EntityFramework.Models;

View File

@@ -1,6 +1,6 @@
using AutoFixture;
using AutoFixture.Kernel;
using Bit.Core.Entities;
using Bit.Core.AdminConsole.Entities;
using Bit.Infrastructure.EntityFramework.AdminConsole.Repositories;
using Bit.Infrastructure.EntityFramework.Repositories;
using Bit.Test.Common.AutoFixture;

View File

@@ -1,5 +1,5 @@
using System.Diagnostics.CodeAnalysis;
using Bit.Core.Entities;
using Bit.Core.AdminConsole.Entities;
namespace Bit.Infrastructure.EFIntegration.Test.Repositories.EqualityComparers;

View File

@@ -1,5 +1,7 @@
using System.Text.Json;
using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Entities.Provider;
using Bit.Core.AdminConsole.Enums;
using Bit.Core.AdminConsole.Repositories;
using Bit.Core.Entities;
using Bit.Core.Enums;
@@ -13,6 +15,7 @@ using Xunit;
using EfAdminConsoleRepo = Bit.Infrastructure.EntityFramework.AdminConsole.Repositories;
using EfRepo = Bit.Infrastructure.EntityFramework.Repositories;
using OrganizationUser = Bit.Core.Entities.OrganizationUser;
using SqlAdminConsoleRepo = Bit.Infrastructure.Dapper.AdminConsole.Repositories;
using SqlRepo = Bit.Infrastructure.Dapper.Repositories;
namespace Bit.Infrastructure.EFIntegration.Test.Repositories;
@@ -182,7 +185,7 @@ public class OrganizationUserRepositoryTests
OrganizationUserPolicyDetailsCompare equalityComparer,
// Auto data - EF repos
List<EfRepo.PolicyRepository> efPolicyRepository,
List<EfAdminConsoleRepo.PolicyRepository> efPolicyRepository,
List<EfRepo.UserRepository> efUserRepository,
List<EfRepo.OrganizationRepository> efOrganizationRepository,
List<EfRepo.OrganizationUserRepository> suts,
@@ -191,7 +194,7 @@ public class OrganizationUserRepositoryTests
List<EfAdminConsoleRepo.ProviderUserRepository> efProviderUserRepository,
// Auto data - SQL repos
SqlRepo.PolicyRepository sqlPolicyRepo,
SqlAdminConsoleRepo.PolicyRepository sqlPolicyRepo,
SqlRepo.UserRepository sqlUserRepo,
SqlRepo.OrganizationRepository sqlOrganizationRepo,
EfAdminConsoleRepo.ProviderRepository sqlProviderRepo,

View File

@@ -272,6 +272,24 @@
"Microsoft.Win32.Registry": "5.0.0"
}
},
"Duende.IdentityServer": {
"type": "Transitive",
"resolved": "6.0.4",
"contentHash": "4HVjzx1F8v5J+U7oa8RGAQGj2QzmzNSu87r18Sh+dlh10uyZZL8teAaT/FaVLDObnfItGdPFvN8mwpF/HkI3Xw==",
"dependencies": {
"Duende.IdentityServer.Storage": "6.0.4",
"Microsoft.AspNetCore.Authentication.OpenIdConnect": "6.0.0"
}
},
"Duende.IdentityServer.Storage": {
"type": "Transitive",
"resolved": "6.0.4",
"contentHash": "s5gAjfbpr2IMgI+fU2Nx+2AZdzstmbt9gpo13iX7GwvqSeSaBVqj9ZskAN0R2KF1OemPdZuGnfaTcevdXMUrrw==",
"dependencies": {
"IdentityModel": "6.0.0",
"Microsoft.AspNetCore.DataProtection.Abstractions": "6.0.0"
}
},
"Fare": {
"type": "Transitive",
"resolved": "2.1.1",
@@ -316,49 +334,8 @@
},
"IdentityModel": {
"type": "Transitive",
"resolved": "4.4.0",
"contentHash": "b18wrIx5wnZlMxAX7oVsE+nDtAJ4hajYlH0xPlaRvo4r/fz08K6pPeZvbiqS9nfNbzfIgLFmNX+FL9qR9ZR5PA==",
"dependencies": {
"Newtonsoft.Json": "11.0.2",
"System.Text.Encodings.Web": "4.7.0"
}
},
"IdentityModel.AspNetCore.OAuth2Introspection": {
"type": "Transitive",
"resolved": "4.0.1",
"contentHash": "ZNdMZMaj9fqR3j50vYsu+1U3QGd6n8+fqwf+a8mCTcmXGor+HgFDfdq0mM34bsmD6uEgAQup7sv2ZW5kR36dbA==",
"dependencies": {
"IdentityModel": "4.0.0"
}
},
"IdentityServer4": {
"type": "Transitive",
"resolved": "4.1.2",
"contentHash": "blaxxGuOA7v/w1q+fxn97wZ+x2ecG1ZD4mc/N/ZOXMNeFZZhqv+4LF26Gecyik3nWrJPmbMEtQbLmRsKG8k61w==",
"dependencies": {
"IdentityModel": "4.4.0",
"IdentityServer4.Storage": "4.1.2",
"Microsoft.AspNetCore.Authentication.OpenIdConnect": "3.1.0",
"Microsoft.IdentityModel.Protocols.OpenIdConnect": "5.6.0",
"Newtonsoft.Json": "12.0.2"
}
},
"IdentityServer4.AccessTokenValidation": {
"type": "Transitive",
"resolved": "3.0.1",
"contentHash": "qu/M6UyN4o9NVep7q545Ms7hYAnsQqSdLbN1Fjjrn4m35lyBfeQPSSNzDryAKHbodyWOQfHaOqKEyMEJQ5Rpgw==",
"dependencies": {
"IdentityModel.AspNetCore.OAuth2Introspection": "4.0.1",
"Microsoft.AspNetCore.Authentication.JwtBearer": "3.0.0"
}
},
"IdentityServer4.Storage": {
"type": "Transitive",
"resolved": "4.1.2",
"contentHash": "KoSffyZyyeCNTIyJiZnCuPakJ1QbCHlpty6gbWUj/7yl+w0PXIchgmmJnJSvddzBb8iZ2xew/vGlxWUIP17P2g==",
"dependencies": {
"IdentityModel": "4.4.0"
}
"resolved": "6.0.0",
"contentHash": "eVHCR7a6m/dm5RFcBzE3qs/Jg5j9R5Rjpu8aTOv9e4AFvaQtBXb5ah7kmwU+YwA0ufRwz4wf1hnIvsD2hSnI4g=="
},
"Kralizek.AutoFixture.Extensions.MockHttp": {
"type": "Transitive",
@@ -459,10 +436,10 @@
},
"Microsoft.AspNetCore.Authentication.OpenIdConnect": {
"type": "Transitive",
"resolved": "3.1.0",
"contentHash": "O1cAQYUTU8EfRqwc5/rfTns4E4hKlFlg59fuKRrST+PzsxI6H07KqRN/JjdYhAuVYxF8jPnIGbj+zuc5paOWUw==",
"resolved": "6.0.0",
"contentHash": "cJxdro36spFzk/K2OFCddM6vZ+yoj6ug8mTFRH3Gdv1Pul/buSuCtfb/FSCp31UmS5S4C1315dU7wX3ErLFuDg==",
"dependencies": {
"Microsoft.IdentityModel.Protocols.OpenIdConnect": "5.5.0"
"Microsoft.IdentityModel.Protocols.OpenIdConnect": "6.10.0"
}
},
"Microsoft.AspNetCore.Cryptography.Internal": {
@@ -495,8 +472,8 @@
},
"Microsoft.AspNetCore.DataProtection.Abstractions": {
"type": "Transitive",
"resolved": "3.1.32",
"contentHash": "MPL4iVyiaRxnOUY5VATHjvhDWaAEFb77KFiUxVRklv3Z3v+STofUr1UG/aCt1O9cgN7FVTDaC5A7U+zsLub8Xg=="
"resolved": "6.0.0",
"contentHash": "Z/UU4NEBm5UgNufJmw+j5baW26ytCOIZ0G7sZocPaOzsUeBon1bkM3lSMNZQG2GmDjAIVP2XMSODf2jzSGbibw=="
},
"Microsoft.Azure.Amqp": {
"type": "Transitive",
@@ -2852,7 +2829,7 @@
"dependencies": {
"AutoFixture.AutoNSubstitute": "[4.17.0, )",
"AutoFixture.Xunit2": "[4.17.0, )",
"Core": "[2023.10.2, )",
"Core": "[2023.10.3, )",
"Kralizek.AutoFixture.Extensions.MockHttp": "[1.2.0, )",
"Microsoft.NET.Test.Sdk": "[17.1.0, )",
"NSubstitute": "[4.3.0, )",
@@ -2874,10 +2851,9 @@
"BitPay.Light": "[1.0.1907, )",
"Braintree": "[5.19.0, )",
"DnsClient": "[1.7.0, )",
"Duende.IdentityServer": "[6.0.4, )",
"Fido2.AspNet": "[3.0.1, )",
"Handlebars.Net": "[2.1.2, )",
"IdentityServer4": "[4.1.2, )",
"IdentityServer4.AccessTokenValidation": "[3.0.1, )",
"LaunchDarkly.ServerSdk": "[8.0.0, )",
"MailKit": "[4.2.0, )",
"Microsoft.AspNetCore.Authentication.JwtBearer": "[6.0.4, )",
@@ -2907,8 +2883,8 @@
"dependencies": {
"AutoFixture.AutoNSubstitute": "[4.17.0, )",
"AutoFixture.Xunit2": "[4.17.0, )",
"Common": "[2023.10.2, )",
"Core": "[2023.10.2, )",
"Common": "[2023.10.3, )",
"Core": "[2023.10.3, )",
"Kralizek.AutoFixture.Extensions.MockHttp": "[1.2.0, )",
"Microsoft.NET.Test.Sdk": "[17.1.0, )",
"NSubstitute": "[4.3.0, )",
@@ -2918,7 +2894,7 @@
"infrastructure.dapper": {
"type": "Project",
"dependencies": {
"Core": "[2023.10.2, )",
"Core": "[2023.10.3, )",
"Dapper": "[2.0.123, )"
}
},
@@ -2926,7 +2902,7 @@
"type": "Project",
"dependencies": {
"AutoMapper.Extensions.Microsoft.DependencyInjection": "[12.0.1, )",
"Core": "[2023.10.2, )",
"Core": "[2023.10.3, )",
"Microsoft.EntityFrameworkCore.Relational": "[7.0.5, )",
"Microsoft.EntityFrameworkCore.SqlServer": "[7.0.5, )",
"Microsoft.EntityFrameworkCore.Sqlite": "[7.0.5, )",

View File

@@ -254,6 +254,24 @@
"Microsoft.Win32.Registry": "5.0.0"
}
},
"Duende.IdentityServer": {
"type": "Transitive",
"resolved": "6.0.4",
"contentHash": "4HVjzx1F8v5J+U7oa8RGAQGj2QzmzNSu87r18Sh+dlh10uyZZL8teAaT/FaVLDObnfItGdPFvN8mwpF/HkI3Xw==",
"dependencies": {
"Duende.IdentityServer.Storage": "6.0.4",
"Microsoft.AspNetCore.Authentication.OpenIdConnect": "6.0.0"
}
},
"Duende.IdentityServer.Storage": {
"type": "Transitive",
"resolved": "6.0.4",
"contentHash": "s5gAjfbpr2IMgI+fU2Nx+2AZdzstmbt9gpo13iX7GwvqSeSaBVqj9ZskAN0R2KF1OemPdZuGnfaTcevdXMUrrw==",
"dependencies": {
"IdentityModel": "6.0.0",
"Microsoft.AspNetCore.DataProtection.Abstractions": "6.0.0"
}
},
"Fido2": {
"type": "Transitive",
"resolved": "3.0.1",
@@ -290,49 +308,8 @@
},
"IdentityModel": {
"type": "Transitive",
"resolved": "4.4.0",
"contentHash": "b18wrIx5wnZlMxAX7oVsE+nDtAJ4hajYlH0xPlaRvo4r/fz08K6pPeZvbiqS9nfNbzfIgLFmNX+FL9qR9ZR5PA==",
"dependencies": {
"Newtonsoft.Json": "11.0.2",
"System.Text.Encodings.Web": "4.7.0"
}
},
"IdentityModel.AspNetCore.OAuth2Introspection": {
"type": "Transitive",
"resolved": "4.0.1",
"contentHash": "ZNdMZMaj9fqR3j50vYsu+1U3QGd6n8+fqwf+a8mCTcmXGor+HgFDfdq0mM34bsmD6uEgAQup7sv2ZW5kR36dbA==",
"dependencies": {
"IdentityModel": "4.0.0"
}
},
"IdentityServer4": {
"type": "Transitive",
"resolved": "4.1.2",
"contentHash": "blaxxGuOA7v/w1q+fxn97wZ+x2ecG1ZD4mc/N/ZOXMNeFZZhqv+4LF26Gecyik3nWrJPmbMEtQbLmRsKG8k61w==",
"dependencies": {
"IdentityModel": "4.4.0",
"IdentityServer4.Storage": "4.1.2",
"Microsoft.AspNetCore.Authentication.OpenIdConnect": "3.1.0",
"Microsoft.IdentityModel.Protocols.OpenIdConnect": "5.6.0",
"Newtonsoft.Json": "12.0.2"
}
},
"IdentityServer4.AccessTokenValidation": {
"type": "Transitive",
"resolved": "3.0.1",
"contentHash": "qu/M6UyN4o9NVep7q545Ms7hYAnsQqSdLbN1Fjjrn4m35lyBfeQPSSNzDryAKHbodyWOQfHaOqKEyMEJQ5Rpgw==",
"dependencies": {
"IdentityModel.AspNetCore.OAuth2Introspection": "4.0.1",
"Microsoft.AspNetCore.Authentication.JwtBearer": "3.0.0"
}
},
"IdentityServer4.Storage": {
"type": "Transitive",
"resolved": "4.1.2",
"contentHash": "KoSffyZyyeCNTIyJiZnCuPakJ1QbCHlpty6gbWUj/7yl+w0PXIchgmmJnJSvddzBb8iZ2xew/vGlxWUIP17P2g==",
"dependencies": {
"IdentityModel": "4.4.0"
}
"resolved": "6.0.0",
"contentHash": "eVHCR7a6m/dm5RFcBzE3qs/Jg5j9R5Rjpu8aTOv9e4AFvaQtBXb5ah7kmwU+YwA0ufRwz4wf1hnIvsD2hSnI4g=="
},
"LaunchDarkly.Cache": {
"type": "Transitive",
@@ -424,10 +401,10 @@
},
"Microsoft.AspNetCore.Authentication.OpenIdConnect": {
"type": "Transitive",
"resolved": "3.1.0",
"contentHash": "O1cAQYUTU8EfRqwc5/rfTns4E4hKlFlg59fuKRrST+PzsxI6H07KqRN/JjdYhAuVYxF8jPnIGbj+zuc5paOWUw==",
"resolved": "6.0.0",
"contentHash": "cJxdro36spFzk/K2OFCddM6vZ+yoj6ug8mTFRH3Gdv1Pul/buSuCtfb/FSCp31UmS5S4C1315dU7wX3ErLFuDg==",
"dependencies": {
"Microsoft.IdentityModel.Protocols.OpenIdConnect": "5.5.0"
"Microsoft.IdentityModel.Protocols.OpenIdConnect": "6.10.0"
}
},
"Microsoft.AspNetCore.Cryptography.Internal": {
@@ -460,8 +437,8 @@
},
"Microsoft.AspNetCore.DataProtection.Abstractions": {
"type": "Transitive",
"resolved": "3.1.32",
"contentHash": "MPL4iVyiaRxnOUY5VATHjvhDWaAEFb77KFiUxVRklv3Z3v+STofUr1UG/aCt1O9cgN7FVTDaC5A7U+zsLub8Xg=="
"resolved": "6.0.0",
"contentHash": "Z/UU4NEBm5UgNufJmw+j5baW26ytCOIZ0G7sZocPaOzsUeBon1bkM3lSMNZQG2GmDjAIVP2XMSODf2jzSGbibw=="
},
"Microsoft.Azure.Amqp": {
"type": "Transitive",
@@ -2732,10 +2709,9 @@
"BitPay.Light": "[1.0.1907, )",
"Braintree": "[5.19.0, )",
"DnsClient": "[1.7.0, )",
"Duende.IdentityServer": "[6.0.4, )",
"Fido2.AspNet": "[3.0.1, )",
"Handlebars.Net": "[2.1.2, )",
"IdentityServer4": "[4.1.2, )",
"IdentityServer4.AccessTokenValidation": "[3.0.1, )",
"LaunchDarkly.ServerSdk": "[8.0.0, )",
"MailKit": "[4.2.0, )",
"Microsoft.AspNetCore.Authentication.JwtBearer": "[6.0.4, )",
@@ -2763,7 +2739,7 @@
"infrastructure.dapper": {
"type": "Project",
"dependencies": {
"Core": "[2023.10.2, )",
"Core": "[2023.10.3, )",
"Dapper": "[2.0.123, )"
}
},
@@ -2771,7 +2747,7 @@
"type": "Project",
"dependencies": {
"AutoMapper.Extensions.Microsoft.DependencyInjection": "[12.0.1, )",
"Core": "[2023.10.2, )",
"Core": "[2023.10.3, )",
"Microsoft.EntityFrameworkCore.Relational": "[7.0.5, )",
"Microsoft.EntityFrameworkCore.SqlServer": "[7.0.5, )",
"Microsoft.EntityFrameworkCore.Sqlite": "[7.0.5, )",

View File

@@ -249,6 +249,24 @@
"Microsoft.Win32.Registry": "5.0.0"
}
},
"Duende.IdentityServer": {
"type": "Transitive",
"resolved": "6.0.4",
"contentHash": "4HVjzx1F8v5J+U7oa8RGAQGj2QzmzNSu87r18Sh+dlh10uyZZL8teAaT/FaVLDObnfItGdPFvN8mwpF/HkI3Xw==",
"dependencies": {
"Duende.IdentityServer.Storage": "6.0.4",
"Microsoft.AspNetCore.Authentication.OpenIdConnect": "6.0.0"
}
},
"Duende.IdentityServer.Storage": {
"type": "Transitive",
"resolved": "6.0.4",
"contentHash": "s5gAjfbpr2IMgI+fU2Nx+2AZdzstmbt9gpo13iX7GwvqSeSaBVqj9ZskAN0R2KF1OemPdZuGnfaTcevdXMUrrw==",
"dependencies": {
"IdentityModel": "6.0.0",
"Microsoft.AspNetCore.DataProtection.Abstractions": "6.0.0"
}
},
"Fare": {
"type": "Transitive",
"resolved": "2.1.1",
@@ -293,49 +311,8 @@
},
"IdentityModel": {
"type": "Transitive",
"resolved": "4.4.0",
"contentHash": "b18wrIx5wnZlMxAX7oVsE+nDtAJ4hajYlH0xPlaRvo4r/fz08K6pPeZvbiqS9nfNbzfIgLFmNX+FL9qR9ZR5PA==",
"dependencies": {
"Newtonsoft.Json": "11.0.2",
"System.Text.Encodings.Web": "4.7.0"
}
},
"IdentityModel.AspNetCore.OAuth2Introspection": {
"type": "Transitive",
"resolved": "4.0.1",
"contentHash": "ZNdMZMaj9fqR3j50vYsu+1U3QGd6n8+fqwf+a8mCTcmXGor+HgFDfdq0mM34bsmD6uEgAQup7sv2ZW5kR36dbA==",
"dependencies": {
"IdentityModel": "4.0.0"
}
},
"IdentityServer4": {
"type": "Transitive",
"resolved": "4.1.2",
"contentHash": "blaxxGuOA7v/w1q+fxn97wZ+x2ecG1ZD4mc/N/ZOXMNeFZZhqv+4LF26Gecyik3nWrJPmbMEtQbLmRsKG8k61w==",
"dependencies": {
"IdentityModel": "4.4.0",
"IdentityServer4.Storage": "4.1.2",
"Microsoft.AspNetCore.Authentication.OpenIdConnect": "3.1.0",
"Microsoft.IdentityModel.Protocols.OpenIdConnect": "5.6.0",
"Newtonsoft.Json": "12.0.2"
}
},
"IdentityServer4.AccessTokenValidation": {
"type": "Transitive",
"resolved": "3.0.1",
"contentHash": "qu/M6UyN4o9NVep7q545Ms7hYAnsQqSdLbN1Fjjrn4m35lyBfeQPSSNzDryAKHbodyWOQfHaOqKEyMEJQ5Rpgw==",
"dependencies": {
"IdentityModel.AspNetCore.OAuth2Introspection": "4.0.1",
"Microsoft.AspNetCore.Authentication.JwtBearer": "3.0.0"
}
},
"IdentityServer4.Storage": {
"type": "Transitive",
"resolved": "4.1.2",
"contentHash": "KoSffyZyyeCNTIyJiZnCuPakJ1QbCHlpty6gbWUj/7yl+w0PXIchgmmJnJSvddzBb8iZ2xew/vGlxWUIP17P2g==",
"dependencies": {
"IdentityModel": "4.4.0"
}
"resolved": "6.0.0",
"contentHash": "eVHCR7a6m/dm5RFcBzE3qs/Jg5j9R5Rjpu8aTOv9e4AFvaQtBXb5ah7kmwU+YwA0ufRwz4wf1hnIvsD2hSnI4g=="
},
"Kralizek.AutoFixture.Extensions.MockHttp": {
"type": "Transitive",
@@ -436,10 +413,10 @@
},
"Microsoft.AspNetCore.Authentication.OpenIdConnect": {
"type": "Transitive",
"resolved": "3.1.0",
"contentHash": "O1cAQYUTU8EfRqwc5/rfTns4E4hKlFlg59fuKRrST+PzsxI6H07KqRN/JjdYhAuVYxF8jPnIGbj+zuc5paOWUw==",
"resolved": "6.0.0",
"contentHash": "cJxdro36spFzk/K2OFCddM6vZ+yoj6ug8mTFRH3Gdv1Pul/buSuCtfb/FSCp31UmS5S4C1315dU7wX3ErLFuDg==",
"dependencies": {
"Microsoft.IdentityModel.Protocols.OpenIdConnect": "5.5.0"
"Microsoft.IdentityModel.Protocols.OpenIdConnect": "6.10.0"
}
},
"Microsoft.AspNetCore.Cryptography.Internal": {
@@ -472,8 +449,8 @@
},
"Microsoft.AspNetCore.DataProtection.Abstractions": {
"type": "Transitive",
"resolved": "3.1.32",
"contentHash": "MPL4iVyiaRxnOUY5VATHjvhDWaAEFb77KFiUxVRklv3Z3v+STofUr1UG/aCt1O9cgN7FVTDaC5A7U+zsLub8Xg=="
"resolved": "6.0.0",
"contentHash": "Z/UU4NEBm5UgNufJmw+j5baW26ytCOIZ0G7sZocPaOzsUeBon1bkM3lSMNZQG2GmDjAIVP2XMSODf2jzSGbibw=="
},
"Microsoft.AspNetCore.TestHost": {
"type": "Transitive",
@@ -2982,7 +2959,7 @@
"dependencies": {
"AutoFixture.AutoNSubstitute": "[4.17.0, )",
"AutoFixture.Xunit2": "[4.17.0, )",
"Core": "[2023.10.2, )",
"Core": "[2023.10.3, )",
"Kralizek.AutoFixture.Extensions.MockHttp": "[1.2.0, )",
"Microsoft.NET.Test.Sdk": "[17.1.0, )",
"NSubstitute": "[4.3.0, )",
@@ -3004,10 +2981,9 @@
"BitPay.Light": "[1.0.1907, )",
"Braintree": "[5.19.0, )",
"DnsClient": "[1.7.0, )",
"Duende.IdentityServer": "[6.0.4, )",
"Fido2.AspNet": "[3.0.1, )",
"Handlebars.Net": "[2.1.2, )",
"IdentityServer4": "[4.1.2, )",
"IdentityServer4.AccessTokenValidation": "[3.0.1, )",
"LaunchDarkly.ServerSdk": "[8.0.0, )",
"MailKit": "[4.2.0, )",
"Microsoft.AspNetCore.Authentication.JwtBearer": "[6.0.4, )",
@@ -3035,15 +3011,15 @@
"identity": {
"type": "Project",
"dependencies": {
"Core": "[2023.10.2, )",
"SharedWeb": "[2023.10.2, )",
"Core": "[2023.10.3, )",
"SharedWeb": "[2023.10.3, )",
"Swashbuckle.AspNetCore.SwaggerGen": "[6.5.0, )"
}
},
"infrastructure.dapper": {
"type": "Project",
"dependencies": {
"Core": "[2023.10.2, )",
"Core": "[2023.10.3, )",
"Dapper": "[2.0.123, )"
}
},
@@ -3051,7 +3027,7 @@
"type": "Project",
"dependencies": {
"AutoMapper.Extensions.Microsoft.DependencyInjection": "[12.0.1, )",
"Core": "[2023.10.2, )",
"Core": "[2023.10.3, )",
"Microsoft.EntityFrameworkCore.Relational": "[7.0.5, )",
"Microsoft.EntityFrameworkCore.SqlServer": "[7.0.5, )",
"Microsoft.EntityFrameworkCore.Sqlite": "[7.0.5, )",
@@ -3063,9 +3039,9 @@
"sharedweb": {
"type": "Project",
"dependencies": {
"Core": "[2023.10.2, )",
"Infrastructure.Dapper": "[2023.10.2, )",
"Infrastructure.EntityFramework": "[2023.10.2, )"
"Core": "[2023.10.3, )",
"Infrastructure.Dapper": "[2023.10.3, )",
"Infrastructure.EntityFramework": "[2023.10.3, )"
}
}
}