mirror of
https://github.com/bitwarden/server
synced 2026-01-04 09:33:40 +00:00
Merge branch 'main' into jmccannon/ac/pm-27131-auto-confirm-req
This commit is contained in:
@@ -6,8 +6,11 @@ using Bit.Core.AdminConsole.OrganizationFeatures.Policies;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.Implementations;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.Models;
|
||||
using Bit.Core.AdminConsole.Repositories;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Models;
|
||||
using Bit.Core.Models.Data.Organizations;
|
||||
using Bit.Core.Platform.Push;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Test.AdminConsole.AutoFixture;
|
||||
using Bit.Test.Common.AutoFixture;
|
||||
@@ -95,7 +98,8 @@ public class SavePolicyCommandTests
|
||||
Substitute.For<IPolicyRepository>(),
|
||||
[new FakeSingleOrgPolicyValidator(), new FakeSingleOrgPolicyValidator()],
|
||||
Substitute.For<TimeProvider>(),
|
||||
Substitute.For<IPostSavePolicySideEffect>()));
|
||||
Substitute.For<IPostSavePolicySideEffect>(),
|
||||
Substitute.For<IPushNotificationService>()));
|
||||
Assert.Contains("Duplicate PolicyValidator for SingleOrg policy", exception.Message);
|
||||
}
|
||||
|
||||
@@ -360,6 +364,103 @@ public class SavePolicyCommandTests
|
||||
.ExecuteSideEffectsAsync(default!, default!, default!);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task VNextSaveAsync_SendsPushNotification(
|
||||
[PolicyUpdate(PolicyType.SingleOrg)] PolicyUpdate policyUpdate,
|
||||
[Policy(PolicyType.SingleOrg, false)] Policy currentPolicy)
|
||||
{
|
||||
// Arrange
|
||||
var fakePolicyValidator = new FakeSingleOrgPolicyValidator();
|
||||
fakePolicyValidator.ValidateAsyncMock(policyUpdate, null).Returns("");
|
||||
var sutProvider = SutProviderFactory([fakePolicyValidator]);
|
||||
var savePolicyModel = new SavePolicyModel(policyUpdate);
|
||||
|
||||
currentPolicy.OrganizationId = policyUpdate.OrganizationId;
|
||||
sutProvider.GetDependency<IPolicyRepository>()
|
||||
.GetByOrganizationIdTypeAsync(policyUpdate.OrganizationId, policyUpdate.Type)
|
||||
.Returns(currentPolicy);
|
||||
|
||||
ArrangeOrganization(sutProvider, policyUpdate);
|
||||
sutProvider.GetDependency<IPolicyRepository>()
|
||||
.GetManyByOrganizationIdAsync(policyUpdate.OrganizationId)
|
||||
.Returns([currentPolicy]);
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.VNextSaveAsync(savePolicyModel);
|
||||
|
||||
// Assert
|
||||
await sutProvider.GetDependency<IPushNotificationService>().Received(1)
|
||||
.PushAsync(Arg.Is<PushNotification<SyncPolicyPushNotification>>(p =>
|
||||
p.Type == PushType.PolicyChanged &&
|
||||
p.Target == NotificationTarget.Organization &&
|
||||
p.TargetId == policyUpdate.OrganizationId &&
|
||||
p.ExcludeCurrentContext == false &&
|
||||
p.Payload.OrganizationId == policyUpdate.OrganizationId &&
|
||||
p.Payload.Policy.Id == result.Id &&
|
||||
p.Payload.Policy.Type == policyUpdate.Type &&
|
||||
p.Payload.Policy.Enabled == policyUpdate.Enabled &&
|
||||
p.Payload.Policy.Data == policyUpdate.Data));
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task SaveAsync_SendsPushNotification([PolicyUpdate(PolicyType.SingleOrg)] PolicyUpdate policyUpdate)
|
||||
{
|
||||
var fakePolicyValidator = new FakeSingleOrgPolicyValidator();
|
||||
fakePolicyValidator.ValidateAsyncMock(policyUpdate, null).Returns("");
|
||||
var sutProvider = SutProviderFactory([fakePolicyValidator]);
|
||||
|
||||
ArrangeOrganization(sutProvider, policyUpdate);
|
||||
sutProvider.GetDependency<IPolicyRepository>().GetManyByOrganizationIdAsync(policyUpdate.OrganizationId).Returns([]);
|
||||
|
||||
var result = await sutProvider.Sut.SaveAsync(policyUpdate);
|
||||
|
||||
await sutProvider.GetDependency<IPushNotificationService>().Received(1)
|
||||
.PushAsync(Arg.Is<PushNotification<SyncPolicyPushNotification>>(p =>
|
||||
p.Type == PushType.PolicyChanged &&
|
||||
p.Target == NotificationTarget.Organization &&
|
||||
p.TargetId == policyUpdate.OrganizationId &&
|
||||
p.ExcludeCurrentContext == false &&
|
||||
p.Payload.OrganizationId == policyUpdate.OrganizationId &&
|
||||
p.Payload.Policy.Id == result.Id &&
|
||||
p.Payload.Policy.Type == policyUpdate.Type &&
|
||||
p.Payload.Policy.Enabled == policyUpdate.Enabled &&
|
||||
p.Payload.Policy.Data == policyUpdate.Data));
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task SaveAsync_ExistingPolicy_SendsPushNotificationWithUpdatedPolicy(
|
||||
[PolicyUpdate(PolicyType.SingleOrg)] PolicyUpdate policyUpdate,
|
||||
[Policy(PolicyType.SingleOrg, false)] Policy currentPolicy)
|
||||
{
|
||||
var fakePolicyValidator = new FakeSingleOrgPolicyValidator();
|
||||
fakePolicyValidator.ValidateAsyncMock(policyUpdate, null).Returns("");
|
||||
var sutProvider = SutProviderFactory([fakePolicyValidator]);
|
||||
|
||||
currentPolicy.OrganizationId = policyUpdate.OrganizationId;
|
||||
sutProvider.GetDependency<IPolicyRepository>()
|
||||
.GetByOrganizationIdTypeAsync(policyUpdate.OrganizationId, policyUpdate.Type)
|
||||
.Returns(currentPolicy);
|
||||
|
||||
ArrangeOrganization(sutProvider, policyUpdate);
|
||||
sutProvider.GetDependency<IPolicyRepository>()
|
||||
.GetManyByOrganizationIdAsync(policyUpdate.OrganizationId)
|
||||
.Returns([currentPolicy]);
|
||||
|
||||
var result = await sutProvider.Sut.SaveAsync(policyUpdate);
|
||||
|
||||
await sutProvider.GetDependency<IPushNotificationService>().Received(1)
|
||||
.PushAsync(Arg.Is<PushNotification<SyncPolicyPushNotification>>(p =>
|
||||
p.Type == PushType.PolicyChanged &&
|
||||
p.Target == NotificationTarget.Organization &&
|
||||
p.TargetId == policyUpdate.OrganizationId &&
|
||||
p.ExcludeCurrentContext == false &&
|
||||
p.Payload.OrganizationId == policyUpdate.OrganizationId &&
|
||||
p.Payload.Policy.Id == result.Id &&
|
||||
p.Payload.Policy.Type == policyUpdate.Type &&
|
||||
p.Payload.Policy.Enabled == policyUpdate.Enabled &&
|
||||
p.Payload.Policy.Data == policyUpdate.Data));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new SutProvider with the PolicyValidators registered in the Sut.
|
||||
/// </summary>
|
||||
|
||||
@@ -0,0 +1,275 @@
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.Auth.Sso;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Test.Common.AutoFixture;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Auth.UserFeatures.Sso;
|
||||
|
||||
[SutProviderCustomize]
|
||||
public class UserSsoOrganizationIdentifierQueryTests
|
||||
{
|
||||
[Theory, BitAutoData]
|
||||
public async Task GetSsoOrganizationIdentifierAsync_UserHasSingleConfirmedOrganization_ReturnsIdentifier(
|
||||
SutProvider<UserSsoOrganizationIdentifierQuery> sutProvider,
|
||||
Guid userId,
|
||||
Organization organization,
|
||||
OrganizationUser organizationUser)
|
||||
{
|
||||
// Arrange
|
||||
organizationUser.UserId = userId;
|
||||
organizationUser.OrganizationId = organization.Id;
|
||||
organizationUser.Status = OrganizationUserStatusType.Confirmed;
|
||||
organization.Identifier = "test-org-identifier";
|
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetManyByUserAsync(userId)
|
||||
.Returns([organizationUser]);
|
||||
|
||||
sutProvider.GetDependency<IOrganizationRepository>()
|
||||
.GetByIdAsync(organization.Id)
|
||||
.Returns(organization);
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.GetSsoOrganizationIdentifierAsync(userId);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("test-org-identifier", result);
|
||||
await sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.Received(1)
|
||||
.GetManyByUserAsync(userId);
|
||||
await sutProvider.GetDependency<IOrganizationRepository>()
|
||||
.Received(1)
|
||||
.GetByIdAsync(organization.Id);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task GetSsoOrganizationIdentifierAsync_UserHasNoOrganizations_ReturnsNull(
|
||||
SutProvider<UserSsoOrganizationIdentifierQuery> sutProvider,
|
||||
Guid userId)
|
||||
{
|
||||
// Arrange
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetManyByUserAsync(userId)
|
||||
.Returns(Array.Empty<OrganizationUser>());
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.GetSsoOrganizationIdentifierAsync(userId);
|
||||
|
||||
// Assert
|
||||
Assert.Null(result);
|
||||
await sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.Received(1)
|
||||
.GetManyByUserAsync(userId);
|
||||
await sutProvider.GetDependency<IOrganizationRepository>()
|
||||
.DidNotReceive()
|
||||
.GetByIdAsync(Arg.Any<Guid>());
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task GetSsoOrganizationIdentifierAsync_UserHasMultipleConfirmedOrganizations_ReturnsNull(
|
||||
SutProvider<UserSsoOrganizationIdentifierQuery> sutProvider,
|
||||
Guid userId,
|
||||
OrganizationUser organizationUser1,
|
||||
OrganizationUser organizationUser2)
|
||||
{
|
||||
// Arrange
|
||||
organizationUser1.UserId = userId;
|
||||
organizationUser1.Status = OrganizationUserStatusType.Confirmed;
|
||||
organizationUser2.UserId = userId;
|
||||
organizationUser2.Status = OrganizationUserStatusType.Confirmed;
|
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetManyByUserAsync(userId)
|
||||
.Returns([organizationUser1, organizationUser2]);
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.GetSsoOrganizationIdentifierAsync(userId);
|
||||
|
||||
// Assert
|
||||
Assert.Null(result);
|
||||
await sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.Received(1)
|
||||
.GetManyByUserAsync(userId);
|
||||
await sutProvider.GetDependency<IOrganizationRepository>()
|
||||
.DidNotReceive()
|
||||
.GetByIdAsync(Arg.Any<Guid>());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData(OrganizationUserStatusType.Invited)]
|
||||
[BitAutoData(OrganizationUserStatusType.Accepted)]
|
||||
[BitAutoData(OrganizationUserStatusType.Revoked)]
|
||||
public async Task GetSsoOrganizationIdentifierAsync_UserHasOnlyInvitedOrganization_ReturnsNull(
|
||||
OrganizationUserStatusType status,
|
||||
SutProvider<UserSsoOrganizationIdentifierQuery> sutProvider,
|
||||
Guid userId,
|
||||
OrganizationUser organizationUser)
|
||||
{
|
||||
// Arrange
|
||||
organizationUser.UserId = userId;
|
||||
organizationUser.Status = status;
|
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetManyByUserAsync(userId)
|
||||
.Returns([organizationUser]);
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.GetSsoOrganizationIdentifierAsync(userId);
|
||||
|
||||
// Assert
|
||||
Assert.Null(result);
|
||||
await sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.Received(1)
|
||||
.GetManyByUserAsync(userId);
|
||||
await sutProvider.GetDependency<IOrganizationRepository>()
|
||||
.DidNotReceive()
|
||||
.GetByIdAsync(Arg.Any<Guid>());
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task GetSsoOrganizationIdentifierAsync_UserHasMixedStatusOrganizations_OnlyOneConfirmed_ReturnsIdentifier(
|
||||
SutProvider<UserSsoOrganizationIdentifierQuery> sutProvider,
|
||||
Guid userId,
|
||||
Organization organization,
|
||||
OrganizationUser confirmedOrgUser,
|
||||
OrganizationUser invitedOrgUser,
|
||||
OrganizationUser revokedOrgUser)
|
||||
{
|
||||
// Arrange
|
||||
confirmedOrgUser.UserId = userId;
|
||||
confirmedOrgUser.OrganizationId = organization.Id;
|
||||
confirmedOrgUser.Status = OrganizationUserStatusType.Confirmed;
|
||||
|
||||
invitedOrgUser.UserId = userId;
|
||||
invitedOrgUser.Status = OrganizationUserStatusType.Invited;
|
||||
|
||||
revokedOrgUser.UserId = userId;
|
||||
revokedOrgUser.Status = OrganizationUserStatusType.Revoked;
|
||||
|
||||
organization.Identifier = "mixed-status-org";
|
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetManyByUserAsync(userId)
|
||||
.Returns(new[] { confirmedOrgUser, invitedOrgUser, revokedOrgUser });
|
||||
|
||||
sutProvider.GetDependency<IOrganizationRepository>()
|
||||
.GetByIdAsync(organization.Id)
|
||||
.Returns(organization);
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.GetSsoOrganizationIdentifierAsync(userId);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("mixed-status-org", result);
|
||||
await sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.Received(1)
|
||||
.GetManyByUserAsync(userId);
|
||||
await sutProvider.GetDependency<IOrganizationRepository>()
|
||||
.Received(1)
|
||||
.GetByIdAsync(organization.Id);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task GetSsoOrganizationIdentifierAsync_OrganizationNotFound_ReturnsNull(
|
||||
SutProvider<UserSsoOrganizationIdentifierQuery> sutProvider,
|
||||
Guid userId,
|
||||
OrganizationUser organizationUser)
|
||||
{
|
||||
// Arrange
|
||||
organizationUser.UserId = userId;
|
||||
organizationUser.Status = OrganizationUserStatusType.Confirmed;
|
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetManyByUserAsync(userId)
|
||||
.Returns([organizationUser]);
|
||||
|
||||
sutProvider.GetDependency<IOrganizationRepository>()
|
||||
.GetByIdAsync(organizationUser.OrganizationId)
|
||||
.Returns((Organization)null);
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.GetSsoOrganizationIdentifierAsync(userId);
|
||||
|
||||
// Assert
|
||||
Assert.Null(result);
|
||||
await sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.Received(1)
|
||||
.GetManyByUserAsync(userId);
|
||||
await sutProvider.GetDependency<IOrganizationRepository>()
|
||||
.Received(1)
|
||||
.GetByIdAsync(organizationUser.OrganizationId);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task GetSsoOrganizationIdentifierAsync_OrganizationIdentifierIsNull_ReturnsNull(
|
||||
SutProvider<UserSsoOrganizationIdentifierQuery> sutProvider,
|
||||
Guid userId,
|
||||
Organization organization,
|
||||
OrganizationUser organizationUser)
|
||||
{
|
||||
// Arrange
|
||||
organizationUser.UserId = userId;
|
||||
organizationUser.OrganizationId = organization.Id;
|
||||
organizationUser.Status = OrganizationUserStatusType.Confirmed;
|
||||
organization.Identifier = null;
|
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetManyByUserAsync(userId)
|
||||
.Returns(new[] { organizationUser });
|
||||
|
||||
sutProvider.GetDependency<IOrganizationRepository>()
|
||||
.GetByIdAsync(organization.Id)
|
||||
.Returns(organization);
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.GetSsoOrganizationIdentifierAsync(userId);
|
||||
|
||||
// Assert
|
||||
Assert.Null(result);
|
||||
await sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.Received(1)
|
||||
.GetManyByUserAsync(userId);
|
||||
await sutProvider.GetDependency<IOrganizationRepository>()
|
||||
.Received(1)
|
||||
.GetByIdAsync(organization.Id);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task GetSsoOrganizationIdentifierAsync_OrganizationIdentifierIsEmpty_ReturnsEmpty(
|
||||
SutProvider<UserSsoOrganizationIdentifierQuery> sutProvider,
|
||||
Guid userId,
|
||||
Organization organization,
|
||||
OrganizationUser organizationUser)
|
||||
{
|
||||
// Arrange
|
||||
organizationUser.UserId = userId;
|
||||
organizationUser.OrganizationId = organization.Id;
|
||||
organizationUser.Status = OrganizationUserStatusType.Confirmed;
|
||||
organization.Identifier = string.Empty;
|
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetManyByUserAsync(userId)
|
||||
.Returns(new[] { organizationUser });
|
||||
|
||||
sutProvider.GetDependency<IOrganizationRepository>()
|
||||
.GetByIdAsync(organization.Id)
|
||||
.Returns(organization);
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.GetSsoOrganizationIdentifierAsync(userId);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(string.Empty, result);
|
||||
await sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.Received(1)
|
||||
.GetManyByUserAsync(userId);
|
||||
await sutProvider.GetDependency<IOrganizationRepository>()
|
||||
.Received(1)
|
||||
.GetByIdAsync(organization.Id);
|
||||
}
|
||||
}
|
||||
@@ -107,30 +107,6 @@ public class CurrentContextTests
|
||||
Assert.Equal(deviceType, sutProvider.Sut.DeviceType);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task BuildAsync_HttpContext_SetsCloudflareFlags(
|
||||
SutProvider<CurrentContext> sutProvider)
|
||||
{
|
||||
var httpContext = new DefaultHttpContext();
|
||||
var globalSettings = new Core.Settings.GlobalSettings();
|
||||
sutProvider.Sut.BotScore = null;
|
||||
// Arrange
|
||||
var botScore = 85;
|
||||
httpContext.Request.Headers["X-Cf-Bot-Score"] = botScore.ToString();
|
||||
httpContext.Request.Headers["X-Cf-Worked-Proxied"] = "1";
|
||||
httpContext.Request.Headers["X-Cf-Is-Bot"] = "1";
|
||||
httpContext.Request.Headers["X-Cf-Maybe-Bot"] = "1";
|
||||
|
||||
// Act
|
||||
await sutProvider.Sut.BuildAsync(httpContext, globalSettings);
|
||||
|
||||
// Assert
|
||||
Assert.True(sutProvider.Sut.CloudflareWorkerProxied);
|
||||
Assert.True(sutProvider.Sut.IsBot);
|
||||
Assert.True(sutProvider.Sut.MaybeBot);
|
||||
Assert.Equal(botScore, sutProvider.Sut.BotScore);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task BuildAsync_HttpContext_SetsClientVersion(
|
||||
SutProvider<CurrentContext> sutProvider)
|
||||
|
||||
@@ -74,7 +74,7 @@ public class SendGridMailDeliveryServiceTests : IDisposable
|
||||
Assert.Equal(mailMessage.HtmlContent, msg.HtmlContent);
|
||||
Assert.Equal(mailMessage.TextContent, msg.PlainTextContent);
|
||||
|
||||
Assert.Contains("type:Cateogry", msg.Categories);
|
||||
Assert.Contains("type:Category", msg.Categories);
|
||||
Assert.Contains(msg.Categories, x => x.StartsWith("env:"));
|
||||
Assert.Contains(msg.Categories, x => x.StartsWith("sender:"));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user