mirror of
https://github.com/bitwarden/server
synced 2026-01-04 01:23:25 +00:00
Merge branch 'main' into km/account-keys-command
This commit is contained in:
@@ -1790,118 +1790,6 @@ public class CiphersControllerTests
|
||||
);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task PutShareMany_ArchivedCipher_ThrowsBadRequestException(
|
||||
Guid organizationId,
|
||||
Guid userId,
|
||||
CipherWithIdRequestModel request,
|
||||
SutProvider<CiphersController> sutProvider)
|
||||
{
|
||||
request.EncryptedFor = userId;
|
||||
request.OrganizationId = organizationId.ToString();
|
||||
request.ArchivedDate = DateTime.UtcNow;
|
||||
var model = new CipherBulkShareRequestModel
|
||||
{
|
||||
Ciphers = [request],
|
||||
CollectionIds = [Guid.NewGuid().ToString()]
|
||||
};
|
||||
|
||||
sutProvider.GetDependency<ICurrentContext>()
|
||||
.OrganizationUser(organizationId)
|
||||
.Returns(Task.FromResult(true));
|
||||
sutProvider.GetDependency<IUserService>()
|
||||
.GetProperUserId(default)
|
||||
.ReturnsForAnyArgs(userId);
|
||||
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
||||
() => sutProvider.Sut.PutShareMany(model)
|
||||
);
|
||||
|
||||
Assert.Equal("Cannot move archived items to an organization.", exception.Message);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task PutShareMany_ExistingCipherArchived_ThrowsBadRequestException(
|
||||
Guid organizationId,
|
||||
Guid userId,
|
||||
CipherWithIdRequestModel request,
|
||||
SutProvider<CiphersController> sutProvider)
|
||||
{
|
||||
// Request model does not have ArchivedDate (only the existing cipher does)
|
||||
request.EncryptedFor = userId;
|
||||
request.OrganizationId = organizationId.ToString();
|
||||
request.ArchivedDate = null;
|
||||
|
||||
var model = new CipherBulkShareRequestModel
|
||||
{
|
||||
Ciphers = [request],
|
||||
CollectionIds = [Guid.NewGuid().ToString()]
|
||||
};
|
||||
|
||||
// The existing cipher from the repository IS archived
|
||||
var existingCipher = new CipherDetails
|
||||
{
|
||||
Id = request.Id!.Value,
|
||||
UserId = userId,
|
||||
Type = CipherType.Login,
|
||||
Data = JsonSerializer.Serialize(new CipherLoginData()),
|
||||
ArchivedDate = DateTime.UtcNow
|
||||
};
|
||||
|
||||
sutProvider.GetDependency<ICurrentContext>()
|
||||
.OrganizationUser(organizationId)
|
||||
.Returns(Task.FromResult(true));
|
||||
sutProvider.GetDependency<IUserService>()
|
||||
.GetProperUserId(default)
|
||||
.ReturnsForAnyArgs(userId);
|
||||
sutProvider.GetDependency<ICipherRepository>()
|
||||
.GetManyByUserIdAsync(userId, withOrganizations: false)
|
||||
.Returns(Task.FromResult((ICollection<CipherDetails>)[existingCipher]));
|
||||
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
||||
() => sutProvider.Sut.PutShareMany(model)
|
||||
);
|
||||
|
||||
Assert.Equal("Cannot move archived items to an organization.", exception.Message);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task PutShare_ArchivedCipher_ThrowsBadRequestException(
|
||||
Guid cipherId,
|
||||
Guid organizationId,
|
||||
User user,
|
||||
CipherShareRequestModel model,
|
||||
SutProvider<CiphersController> sutProvider)
|
||||
{
|
||||
model.Cipher.OrganizationId = organizationId.ToString();
|
||||
model.Cipher.EncryptedFor = user.Id;
|
||||
|
||||
var cipher = new Cipher
|
||||
{
|
||||
Id = cipherId,
|
||||
UserId = user.Id,
|
||||
ArchivedDate = DateTime.UtcNow.AddDays(-1),
|
||||
Type = CipherType.Login,
|
||||
Data = JsonSerializer.Serialize(new CipherLoginData())
|
||||
};
|
||||
|
||||
sutProvider.GetDependency<IUserService>()
|
||||
.GetUserByPrincipalAsync(Arg.Any<ClaimsPrincipal>())
|
||||
.Returns(user);
|
||||
sutProvider.GetDependency<ICipherRepository>()
|
||||
.GetByIdAsync(cipherId)
|
||||
.Returns(cipher);
|
||||
sutProvider.GetDependency<ICurrentContext>()
|
||||
.OrganizationUser(organizationId)
|
||||
.Returns(Task.FromResult(true));
|
||||
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
||||
() => sutProvider.Sut.PutShare(cipherId, model)
|
||||
);
|
||||
|
||||
Assert.Equal("Cannot move an archived item to an organization.", exception.Message);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task PostPurge_WhenUserNotFound_ThrowsUnauthorizedAccessException(
|
||||
SecretVerificationRequestModel model,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using Bit.Billing.Constants;
|
||||
using Bit.Billing.Services;
|
||||
using Bit.Billing.Services.Implementations;
|
||||
using Bit.Core;
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.AdminConsole.Entities.Provider;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.Organizations.Interfaces;
|
||||
@@ -126,79 +125,6 @@ public class SubscriptionUpdatedHandlerTests
|
||||
Arg.Is<ITrigger>(t => t.Key.Name == $"cancel-trigger-{subscriptionId}"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task
|
||||
HandleAsync_UnpaidProviderSubscription_WithManualSuspensionViaMetadata_DisablesProviderAndSchedulesCancellation()
|
||||
{
|
||||
// Arrange
|
||||
var providerId = Guid.NewGuid();
|
||||
var subscriptionId = "sub_test123";
|
||||
|
||||
var previousSubscription = new Subscription
|
||||
{
|
||||
Id = subscriptionId,
|
||||
Status = StripeSubscriptionStatus.Active,
|
||||
Metadata = new Dictionary<string, string>
|
||||
{
|
||||
["suspend_provider"] = null // This is the key part - metadata exists, but value is null
|
||||
}
|
||||
};
|
||||
|
||||
var currentSubscription = new Subscription
|
||||
{
|
||||
Id = subscriptionId,
|
||||
Status = StripeSubscriptionStatus.Unpaid,
|
||||
Items = new StripeList<SubscriptionItem>
|
||||
{
|
||||
Data =
|
||||
[
|
||||
new SubscriptionItem { CurrentPeriodEnd = DateTime.UtcNow.AddDays(30) }
|
||||
]
|
||||
},
|
||||
Metadata = new Dictionary<string, string>
|
||||
{
|
||||
["providerId"] = providerId.ToString(),
|
||||
["suspend_provider"] = "true" // Now has a value, indicating manual suspension
|
||||
},
|
||||
TestClock = null
|
||||
};
|
||||
|
||||
var parsedEvent = new Event
|
||||
{
|
||||
Id = "evt_test123",
|
||||
Type = HandledStripeWebhook.SubscriptionUpdated,
|
||||
Data = new EventData
|
||||
{
|
||||
Object = currentSubscription,
|
||||
PreviousAttributes = JObject.FromObject(previousSubscription)
|
||||
}
|
||||
};
|
||||
|
||||
var provider = new Provider { Id = providerId, Enabled = true };
|
||||
|
||||
_featureService.IsEnabled(FeatureFlagKeys.PM21821_ProviderPortalTakeover).Returns(true);
|
||||
_stripeEventService.GetSubscription(parsedEvent, true, Arg.Any<List<string>>()).Returns(currentSubscription);
|
||||
_stripeEventUtilityService.GetIdsFromMetadata(currentSubscription.Metadata)
|
||||
.Returns(Tuple.Create<Guid?, Guid?, Guid?>(null, null, providerId));
|
||||
_providerRepository.GetByIdAsync(providerId).Returns(provider);
|
||||
|
||||
// Act
|
||||
await _sut.HandleAsync(parsedEvent);
|
||||
|
||||
// Assert
|
||||
Assert.False(provider.Enabled);
|
||||
await _providerService.Received(1).UpdateAsync(provider);
|
||||
|
||||
// Verify that UpdateSubscription was called with both CancelAt and the new metadata
|
||||
await _stripeFacade.Received(1).UpdateSubscription(
|
||||
subscriptionId,
|
||||
Arg.Is<SubscriptionUpdateOptions>(options =>
|
||||
options.CancelAt.HasValue &&
|
||||
options.CancelAt.Value <= DateTime.UtcNow.AddDays(7).AddMinutes(1) &&
|
||||
options.Metadata != null &&
|
||||
options.Metadata.ContainsKey("suspended_provider_via_webhook_at")));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task
|
||||
HandleAsync_UnpaidProviderSubscription_WithValidTransition_DisablesProviderAndSchedulesCancellation()
|
||||
@@ -243,7 +169,6 @@ public class SubscriptionUpdatedHandlerTests
|
||||
|
||||
var provider = new Provider { Id = providerId, Enabled = true };
|
||||
|
||||
_featureService.IsEnabled(FeatureFlagKeys.PM21821_ProviderPortalTakeover).Returns(true);
|
||||
_stripeEventService.GetSubscription(parsedEvent, true, Arg.Any<List<string>>()).Returns(currentSubscription);
|
||||
_stripeEventUtilityService.GetIdsFromMetadata(currentSubscription.Metadata)
|
||||
.Returns(Tuple.Create<Guid?, Guid?, Guid?>(null, null, providerId));
|
||||
@@ -256,13 +181,12 @@ public class SubscriptionUpdatedHandlerTests
|
||||
Assert.False(provider.Enabled);
|
||||
await _providerService.Received(1).UpdateAsync(provider);
|
||||
|
||||
// Verify that UpdateSubscription was called with CancelAt but WITHOUT suspension metadata
|
||||
// Verify that UpdateSubscription was called with CancelAt
|
||||
await _stripeFacade.Received(1).UpdateSubscription(
|
||||
subscriptionId,
|
||||
Arg.Is<SubscriptionUpdateOptions>(options =>
|
||||
options.CancelAt.HasValue &&
|
||||
options.CancelAt.Value <= DateTime.UtcNow.AddDays(7).AddMinutes(1) &&
|
||||
(options.Metadata == null || !options.Metadata.ContainsKey("suspended_provider_via_webhook_at"))));
|
||||
options.CancelAt.Value <= DateTime.UtcNow.AddDays(7).AddMinutes(1)));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -306,9 +230,6 @@ public class SubscriptionUpdatedHandlerTests
|
||||
_stripeEventUtilityService.GetIdsFromMetadata(Arg.Any<Dictionary<string, string>>())
|
||||
.Returns(Tuple.Create<Guid?, Guid?, Guid?>(null, null, providerId));
|
||||
|
||||
_featureService.IsEnabled(FeatureFlagKeys.PM21821_ProviderPortalTakeover)
|
||||
.Returns(true);
|
||||
|
||||
_providerRepository.GetByIdAsync(providerId)
|
||||
.Returns(provider);
|
||||
|
||||
@@ -353,9 +274,6 @@ public class SubscriptionUpdatedHandlerTests
|
||||
_stripeEventUtilityService.GetIdsFromMetadata(Arg.Any<Dictionary<string, string>>())
|
||||
.Returns(Tuple.Create<Guid?, Guid?, Guid?>(null, null, providerId));
|
||||
|
||||
_featureService.IsEnabled(FeatureFlagKeys.PM21821_ProviderPortalTakeover)
|
||||
.Returns(true);
|
||||
|
||||
_providerRepository.GetByIdAsync(providerId)
|
||||
.Returns(provider);
|
||||
|
||||
@@ -401,9 +319,6 @@ public class SubscriptionUpdatedHandlerTests
|
||||
_stripeEventUtilityService.GetIdsFromMetadata(Arg.Any<Dictionary<string, string>>())
|
||||
.Returns(Tuple.Create<Guid?, Guid?, Guid?>(null, null, providerId));
|
||||
|
||||
_featureService.IsEnabled(FeatureFlagKeys.PM21821_ProviderPortalTakeover)
|
||||
.Returns(true);
|
||||
|
||||
_providerRepository.GetByIdAsync(providerId)
|
||||
.Returns(provider);
|
||||
|
||||
@@ -416,48 +331,6 @@ public class SubscriptionUpdatedHandlerTests
|
||||
await _stripeFacade.DidNotReceive().UpdateSubscription(Arg.Any<string>(), Arg.Any<SubscriptionUpdateOptions>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task HandleAsync_UnpaidProviderSubscription_WhenFeatureFlagDisabled_DoesNothing()
|
||||
{
|
||||
// Arrange
|
||||
var providerId = Guid.NewGuid();
|
||||
var subscriptionId = "sub_123";
|
||||
var currentPeriodEnd = DateTime.UtcNow.AddDays(30);
|
||||
|
||||
var subscription = new Subscription
|
||||
{
|
||||
Id = subscriptionId,
|
||||
Status = StripeSubscriptionStatus.Unpaid,
|
||||
Items = new StripeList<SubscriptionItem>
|
||||
{
|
||||
Data =
|
||||
[
|
||||
new SubscriptionItem { CurrentPeriodEnd = currentPeriodEnd }
|
||||
]
|
||||
},
|
||||
Metadata = new Dictionary<string, string> { { "providerId", providerId.ToString() } },
|
||||
LatestInvoice = new Invoice { BillingReason = "subscription_cycle" }
|
||||
};
|
||||
|
||||
var parsedEvent = new Event { Data = new EventData() };
|
||||
|
||||
_stripeEventService.GetSubscription(Arg.Any<Event>(), Arg.Any<bool>(), Arg.Any<List<string>>())
|
||||
.Returns(subscription);
|
||||
|
||||
_stripeEventUtilityService.GetIdsFromMetadata(Arg.Any<Dictionary<string, string>>())
|
||||
.Returns(Tuple.Create<Guid?, Guid?, Guid?>(null, null, providerId));
|
||||
|
||||
_featureService.IsEnabled(FeatureFlagKeys.PM21821_ProviderPortalTakeover)
|
||||
.Returns(false);
|
||||
|
||||
// Act
|
||||
await _sut.HandleAsync(parsedEvent);
|
||||
|
||||
// Assert
|
||||
await _providerRepository.DidNotReceive().GetByIdAsync(Arg.Any<Guid>());
|
||||
await _providerService.DidNotReceive().UpdateAsync(Arg.Any<Provider>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task HandleAsync_UnpaidProviderSubscription_WhenProviderNotFound_DoesNothing()
|
||||
{
|
||||
@@ -489,9 +362,6 @@ public class SubscriptionUpdatedHandlerTests
|
||||
_stripeEventUtilityService.GetIdsFromMetadata(Arg.Any<Dictionary<string, string>>())
|
||||
.Returns(Tuple.Create<Guid?, Guid?, Guid?>(null, null, providerId));
|
||||
|
||||
_featureService.IsEnabled(FeatureFlagKeys.PM21821_ProviderPortalTakeover)
|
||||
.Returns(true);
|
||||
|
||||
_providerRepository.GetByIdAsync(providerId)
|
||||
.Returns((Provider)null);
|
||||
|
||||
@@ -777,8 +647,6 @@ public class SubscriptionUpdatedHandlerTests
|
||||
_stripeFacade
|
||||
.UpdateSubscription(Arg.Any<string>(), Arg.Any<SubscriptionUpdateOptions>())
|
||||
.Returns(newSubscription);
|
||||
_featureService.IsEnabled(FeatureFlagKeys.PM21821_ProviderPortalTakeover)
|
||||
.Returns(true);
|
||||
|
||||
// Act
|
||||
await _sut.HandleAsync(parsedEvent);
|
||||
@@ -800,9 +668,6 @@ public class SubscriptionUpdatedHandlerTests
|
||||
.Received(1)
|
||||
.UpdateSubscription(newSubscription.Id,
|
||||
Arg.Is<SubscriptionUpdateOptions>(options => options.CancelAtPeriodEnd == false));
|
||||
_featureService
|
||||
.Received(1)
|
||||
.IsEnabled(FeatureFlagKeys.PM21821_ProviderPortalTakeover);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -823,8 +688,6 @@ public class SubscriptionUpdatedHandlerTests
|
||||
_providerRepository
|
||||
.GetByIdAsync(Arg.Any<Guid>())
|
||||
.Returns(provider);
|
||||
_featureService.IsEnabled(FeatureFlagKeys.PM21821_ProviderPortalTakeover)
|
||||
.Returns(true);
|
||||
|
||||
// Act
|
||||
await _sut.HandleAsync(parsedEvent);
|
||||
@@ -843,9 +706,6 @@ public class SubscriptionUpdatedHandlerTests
|
||||
await _stripeFacade
|
||||
.DidNotReceiveWithAnyArgs()
|
||||
.UpdateSubscription(Arg.Any<string>());
|
||||
_featureService
|
||||
.Received(1)
|
||||
.IsEnabled(FeatureFlagKeys.PM21821_ProviderPortalTakeover);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -866,8 +726,6 @@ public class SubscriptionUpdatedHandlerTests
|
||||
_providerRepository
|
||||
.GetByIdAsync(Arg.Any<Guid>())
|
||||
.Returns(provider);
|
||||
_featureService.IsEnabled(FeatureFlagKeys.PM21821_ProviderPortalTakeover)
|
||||
.Returns(true);
|
||||
|
||||
// Act
|
||||
await _sut.HandleAsync(parsedEvent);
|
||||
@@ -886,9 +744,6 @@ public class SubscriptionUpdatedHandlerTests
|
||||
await _stripeFacade
|
||||
.DidNotReceiveWithAnyArgs()
|
||||
.UpdateSubscription(Arg.Any<string>());
|
||||
_featureService
|
||||
.Received(1)
|
||||
.IsEnabled(FeatureFlagKeys.PM21821_ProviderPortalTakeover);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -909,8 +764,6 @@ public class SubscriptionUpdatedHandlerTests
|
||||
_providerRepository
|
||||
.GetByIdAsync(Arg.Any<Guid>())
|
||||
.Returns(provider);
|
||||
_featureService.IsEnabled(FeatureFlagKeys.PM21821_ProviderPortalTakeover)
|
||||
.Returns(true);
|
||||
|
||||
// Act
|
||||
await _sut.HandleAsync(parsedEvent);
|
||||
@@ -929,9 +782,6 @@ public class SubscriptionUpdatedHandlerTests
|
||||
await _stripeFacade
|
||||
.DidNotReceiveWithAnyArgs()
|
||||
.UpdateSubscription(Arg.Any<string>());
|
||||
_featureService
|
||||
.Received(1)
|
||||
.IsEnabled(FeatureFlagKeys.PM21821_ProviderPortalTakeover);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -953,8 +803,6 @@ public class SubscriptionUpdatedHandlerTests
|
||||
_providerRepository
|
||||
.GetByIdAsync(Arg.Any<Guid>())
|
||||
.Returns(provider);
|
||||
_featureService.IsEnabled(FeatureFlagKeys.PM21821_ProviderPortalTakeover)
|
||||
.Returns(true);
|
||||
|
||||
// Act
|
||||
await _sut.HandleAsync(parsedEvent);
|
||||
@@ -975,9 +823,6 @@ public class SubscriptionUpdatedHandlerTests
|
||||
await _stripeFacade
|
||||
.DidNotReceiveWithAnyArgs()
|
||||
.UpdateSubscription(Arg.Any<string>());
|
||||
_featureService
|
||||
.Received(1)
|
||||
.IsEnabled(FeatureFlagKeys.PM21821_ProviderPortalTakeover);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -997,8 +842,6 @@ public class SubscriptionUpdatedHandlerTests
|
||||
_providerRepository
|
||||
.GetByIdAsync(Arg.Any<Guid>())
|
||||
.ReturnsNull();
|
||||
_featureService.IsEnabled(FeatureFlagKeys.PM21821_ProviderPortalTakeover)
|
||||
.Returns(true);
|
||||
|
||||
// Act
|
||||
await _sut.HandleAsync(parsedEvent);
|
||||
@@ -1019,9 +862,6 @@ public class SubscriptionUpdatedHandlerTests
|
||||
await _stripeFacade
|
||||
.DidNotReceive()
|
||||
.UpdateSubscription(Arg.Any<string>());
|
||||
_featureService
|
||||
.Received(1)
|
||||
.IsEnabled(FeatureFlagKeys.PM21821_ProviderPortalTakeover);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -1040,8 +880,6 @@ public class SubscriptionUpdatedHandlerTests
|
||||
_providerRepository
|
||||
.GetByIdAsync(Arg.Any<Guid>())
|
||||
.Returns(provider);
|
||||
_featureService.IsEnabled(FeatureFlagKeys.PM21821_ProviderPortalTakeover)
|
||||
.Returns(true);
|
||||
|
||||
// Act
|
||||
await _sut.HandleAsync(parsedEvent);
|
||||
@@ -1062,9 +900,6 @@ public class SubscriptionUpdatedHandlerTests
|
||||
await _stripeFacade
|
||||
.DidNotReceive()
|
||||
.UpdateSubscription(Arg.Any<string>());
|
||||
_featureService
|
||||
.Received(1)
|
||||
.IsEnabled(FeatureFlagKeys.PM21821_ProviderPortalTakeover);
|
||||
}
|
||||
|
||||
private static (Guid providerId, Subscription newSubscription, Provider provider, Event parsedEvent)
|
||||
|
||||
Reference in New Issue
Block a user