1
0
mirror of https://github.com/bitwarden/server synced 2025-12-27 05:33:17 +00:00

Merge branch 'feature/flexible-collections' into flexible-collections/add-feature-flags

This commit is contained in:
Thomas Rittson
2023-10-09 10:56:58 +10:00
18 changed files with 165 additions and 88 deletions

View File

@@ -161,7 +161,7 @@ public class CollectionsController : Controller
var groups = model.Groups?.Select(g => g.ToSelectionReadOnly());
var users = model.Users?.Select(g => g.ToSelectionReadOnly());
await _collectionService.SaveAsync(collection, groups, users, _currentContext.UserId);
await _collectionService.SaveAsync(collection, groups, users);
return new CollectionResponseModel(collection);
}

View File

@@ -118,6 +118,7 @@ public class OrganizationSubscriptionResponseModel : OrganizationResponseModel
{
Subscription = subscription.Subscription != null ? new BillingSubscription(subscription.Subscription) : null;
UpcomingInvoice = subscription.UpcomingInvoice != null ? new BillingSubscriptionUpcomingInvoice(subscription.UpcomingInvoice) : null;
Discount = subscription.Discount != null ? new BillingCustomerDiscount(subscription.Discount) : null;
Expiration = DateTime.UtcNow.AddYears(1); // Not used, so just give it a value.
if (hideSensitiveData)
@@ -148,6 +149,7 @@ public class OrganizationSubscriptionResponseModel : OrganizationResponseModel
public string StorageName { get; set; }
public double? StorageGb { get; set; }
public BillingCustomerDiscount Discount { get; set; }
public BillingSubscription Subscription { get; set; }
public BillingSubscriptionUpcomingInvoice UpcomingInvoice { get; set; }

View File

@@ -14,6 +14,7 @@ public class SubscriptionResponseModel : ResponseModel
Subscription = subscription.Subscription != null ? new BillingSubscription(subscription.Subscription) : null;
UpcomingInvoice = subscription.UpcomingInvoice != null ?
new BillingSubscriptionUpcomingInvoice(subscription.UpcomingInvoice) : null;
Discount = subscription.Discount != null ? new BillingCustomerDiscount(subscription.Discount) : null;
StorageName = user.Storage.HasValue ? CoreHelpers.ReadableBytesSize(user.Storage.Value) : null;
StorageGb = user.Storage.HasValue ? Math.Round(user.Storage.Value / 1073741824D, 2) : 0; // 1 GB
MaxStorageGb = user.MaxStorageGb;
@@ -41,11 +42,24 @@ public class SubscriptionResponseModel : ResponseModel
public short? MaxStorageGb { get; set; }
public BillingSubscriptionUpcomingInvoice UpcomingInvoice { get; set; }
public BillingSubscription Subscription { get; set; }
public BillingCustomerDiscount Discount { get; set; }
public UserLicense License { get; set; }
public DateTime? Expiration { get; set; }
public bool UsingInAppPurchase { get; set; }
}
public class BillingCustomerDiscount
{
public BillingCustomerDiscount(SubscriptionInfo.BillingCustomerDiscount discount)
{
Id = discount.Id;
Active = discount.Active;
}
public string Id { get; set; }
public bool Active { get; set; }
}
public class BillingSubscription
{
public BillingSubscription(SubscriptionInfo.BillingSubscription sub)

View File

@@ -20,7 +20,7 @@ public static class Constants
/// </summary>
public const int OrganizationSelfHostSubscriptionGracePeriodDays = 60;
public const string CipherKeyEncryptionMinimumVersion = "2023.9.1";
public const string CipherKeyEncryptionMinimumVersion = "2023.9.2";
}
public static class TokenPurposes

View File

@@ -5,10 +5,25 @@ namespace Bit.Core.Models.Business;
public class SubscriptionInfo
{
public BillingCustomerDiscount Discount { get; set; }
public BillingSubscription Subscription { get; set; }
public BillingUpcomingInvoice UpcomingInvoice { get; set; }
public bool UsingInAppPurchase { get; set; }
public class BillingCustomerDiscount
{
public BillingCustomerDiscount() { }
public BillingCustomerDiscount(Discount discount)
{
Id = discount.Id;
Active = discount.Start != null && discount.End == null;
}
public string Id { get; }
public bool Active { get; }
}
public class BillingSubscription
{
public BillingSubscription(Subscription sub)

View File

@@ -5,7 +5,7 @@ namespace Bit.Core.Services;
public interface ICollectionService
{
Task SaveAsync(Collection collection, IEnumerable<CollectionAccessSelection> groups = null, IEnumerable<CollectionAccessSelection> users = null, Guid? assignUserId = null);
Task SaveAsync(Collection collection, IEnumerable<CollectionAccessSelection> groups = null, IEnumerable<CollectionAccessSelection> users = null);
Task DeleteUserAsync(Collection collection, Guid organizationUserId);
Task<IEnumerable<Collection>> GetOrganizationCollectionsAsync(Guid organizationId);
}

View File

@@ -41,7 +41,7 @@ public class CollectionService : ICollectionService
}
public async Task SaveAsync(Collection collection, IEnumerable<CollectionAccessSelection> groups = null,
IEnumerable<CollectionAccessSelection> users = null, Guid? assignUserId = null)
IEnumerable<CollectionAccessSelection> users = null)
{
var org = await _organizationRepository.GetByIdAsync(collection.OrganizationId);
if (org == null)
@@ -49,6 +49,16 @@ public class CollectionService : ICollectionService
throw new BadRequestException("Organization not found");
}
var groupsList = groups?.ToList();
var usersList = users?.ToList();
var groupHasManageAccess = groupsList?.Any(g => g.Manage) ?? false;
var userHasManageAccess = usersList?.Any(u => u.Manage) ?? false;
if (!groupHasManageAccess && !userHasManageAccess)
{
throw new BadRequestException(
"At least one member or group must have can manage permission.");
}
if (collection.Id == default(Guid))
{
if (org.MaxCollections.HasValue)
@@ -61,26 +71,13 @@ public class CollectionService : ICollectionService
}
}
await _collectionRepository.CreateAsync(collection, org.UseGroups ? groups : null, users);
// Assign a user to the newly created collection.
if (assignUserId.HasValue)
{
var orgUser = await _organizationUserRepository.GetByOrganizationAsync(org.Id, assignUserId.Value);
if (orgUser != null && orgUser.Status == Enums.OrganizationUserStatusType.Confirmed)
{
await _collectionRepository.UpdateUsersAsync(collection.Id,
new List<CollectionAccessSelection> {
new CollectionAccessSelection { Id = orgUser.Id, Manage = true} });
}
}
await _collectionRepository.CreateAsync(collection, org.UseGroups ? groupsList : null, usersList);
await _eventService.LogCollectionEventAsync(collection, Enums.EventType.Collection_Created);
await _referenceEventService.RaiseEventAsync(new ReferenceEvent(ReferenceEventType.CollectionCreated, org, _currentContext));
}
else
{
await _collectionRepository.ReplaceAsync(collection, org.UseGroups ? groups : null, users);
await _collectionRepository.ReplaceAsync(collection, org.UseGroups ? groupsList : null, usersList);
await _eventService.LogCollectionEventAsync(collection, Enums.EventType.Collection_Updated);
}
}

View File

@@ -1557,10 +1557,19 @@ public class StripePaymentService : IPaymentService
{
var subscriptionInfo = new SubscriptionInfo();
if (subscriber.IsUser() && !string.IsNullOrWhiteSpace(subscriber.GatewayCustomerId))
if (!string.IsNullOrWhiteSpace(subscriber.GatewayCustomerId))
{
var customer = await _stripeAdapter.CustomerGetAsync(subscriber.GatewayCustomerId);
subscriptionInfo.UsingInAppPurchase = customer.Metadata.ContainsKey("appleReceipt");
if (customer.Discount != null)
{
subscriptionInfo.Discount = new SubscriptionInfo.BillingCustomerDiscount(customer.Discount);
}
if (subscriber.IsUser())
{
subscriptionInfo.UsingInAppPurchase = customer.Metadata.ContainsKey("appleReceipt");
}
}
if (!string.IsNullOrWhiteSpace(subscriber.GatewaySubscriptionId))