mirror of
https://github.com/bitwarden/server
synced 2026-01-09 03:53:42 +00:00
Merge branch 'feature/flexible-collections' into flexible-collections/deprecate-custom-collection-perm
This commit is contained in:
@@ -540,7 +540,7 @@ public class OrganizationsController : Controller
|
||||
if (model.Type == OrganizationApiKeyType.BillingSync || model.Type == OrganizationApiKeyType.Scim)
|
||||
{
|
||||
// Non-enterprise orgs should not be able to create or view an apikey of billing sync/scim key types
|
||||
var plan = StaticStore.GetPasswordManagerPlan(organization.PlanType);
|
||||
var plan = StaticStore.GetPlan(organization.PlanType);
|
||||
if (plan.Product != ProductType.Enterprise)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
|
||||
@@ -19,30 +19,12 @@ public class PlansController : Controller
|
||||
[HttpGet("")]
|
||||
[AllowAnonymous]
|
||||
public ListResponseModel<PlanResponseModel> Get()
|
||||
{
|
||||
var data = StaticStore.PasswordManagerPlans;
|
||||
var responses = data.Select(plan => new PlanResponseModel(plan));
|
||||
return new ListResponseModel<PlanResponseModel>(responses);
|
||||
}
|
||||
|
||||
[HttpGet("all")]
|
||||
[AllowAnonymous]
|
||||
public ListResponseModel<PlanResponseModel> GetAllPlans()
|
||||
{
|
||||
var data = StaticStore.Plans;
|
||||
var responses = data.Select(plan => new PlanResponseModel(plan));
|
||||
return new ListResponseModel<PlanResponseModel>(responses);
|
||||
}
|
||||
|
||||
[HttpGet("sm-plans")]
|
||||
[AllowAnonymous]
|
||||
public ListResponseModel<PlanResponseModel> GetSecretsManagerPlans()
|
||||
{
|
||||
var data = StaticStore.SecretManagerPlans;
|
||||
var responses = data.Select(plan => new PlanResponseModel(plan));
|
||||
return new ListResponseModel<PlanResponseModel>(responses);
|
||||
}
|
||||
|
||||
[HttpGet("sales-tax-rates")]
|
||||
public async Task<ListResponseModel<TaxRateResponseModel>> GetTaxRates()
|
||||
{
|
||||
|
||||
@@ -26,12 +26,7 @@ public class OrganizationResponseModel : ResponseModel
|
||||
BusinessCountry = organization.BusinessCountry;
|
||||
BusinessTaxNumber = organization.BusinessTaxNumber;
|
||||
BillingEmail = organization.BillingEmail;
|
||||
Plan = new PlanResponseModel(StaticStore.PasswordManagerPlans.FirstOrDefault(plan => plan.Type == organization.PlanType));
|
||||
var matchingPlan = StaticStore.GetSecretsManagerPlan(organization.PlanType);
|
||||
if (matchingPlan != null)
|
||||
{
|
||||
SecretsManagerPlan = new PlanResponseModel(matchingPlan);
|
||||
}
|
||||
Plan = new PlanResponseModel(StaticStore.GetPlan(organization.PlanType));
|
||||
PlanType = organization.PlanType;
|
||||
Seats = organization.Seats;
|
||||
MaxAutoscaleSeats = organization.MaxAutoscaleSeats;
|
||||
|
||||
@@ -21,15 +21,6 @@ public class PlanResponseModel : ResponseModel
|
||||
NameLocalizationKey = plan.NameLocalizationKey;
|
||||
DescriptionLocalizationKey = plan.DescriptionLocalizationKey;
|
||||
CanBeUsedByBusiness = plan.CanBeUsedByBusiness;
|
||||
BaseSeats = plan.BaseSeats;
|
||||
BaseStorageGb = plan.BaseStorageGb;
|
||||
MaxCollections = plan.MaxCollections;
|
||||
MaxUsers = plan.MaxUsers;
|
||||
HasAdditionalSeatsOption = plan.HasAdditionalSeatsOption;
|
||||
HasAdditionalStorageOption = plan.HasAdditionalStorageOption;
|
||||
MaxAdditionalSeats = plan.MaxAdditionalSeats;
|
||||
MaxAdditionalStorage = plan.MaxAdditionalStorage;
|
||||
HasPremiumAccessOption = plan.HasPremiumAccessOption;
|
||||
TrialPeriodDays = plan.TrialPeriodDays;
|
||||
HasSelfHost = plan.HasSelfHost;
|
||||
HasPolicies = plan.HasPolicies;
|
||||
@@ -45,22 +36,12 @@ public class PlanResponseModel : ResponseModel
|
||||
DisplaySortOrder = plan.DisplaySortOrder;
|
||||
LegacyYear = plan.LegacyYear;
|
||||
Disabled = plan.Disabled;
|
||||
StripePlanId = plan.StripePlanId;
|
||||
StripeSeatPlanId = plan.StripeSeatPlanId;
|
||||
StripeStoragePlanId = plan.StripeStoragePlanId;
|
||||
BasePrice = plan.BasePrice;
|
||||
SeatPrice = plan.SeatPrice;
|
||||
AdditionalStoragePricePerGb = plan.AdditionalStoragePricePerGb;
|
||||
PremiumAccessOptionPrice = plan.PremiumAccessOptionPrice;
|
||||
if (plan.SecretsManager != null)
|
||||
{
|
||||
SecretsManager = new SecretsManagerPlanFeaturesResponseModel(plan.SecretsManager);
|
||||
}
|
||||
|
||||
AdditionalPricePerServiceAccount = plan.AdditionalPricePerServiceAccount;
|
||||
BaseServiceAccount = plan.BaseServiceAccount;
|
||||
MaxServiceAccounts = plan.MaxServiceAccounts;
|
||||
MaxAdditionalServiceAccounts = plan.MaxAdditionalServiceAccount;
|
||||
HasAdditionalServiceAccountOption = plan.HasAdditionalServiceAccountOption;
|
||||
MaxProjects = plan.MaxProjects;
|
||||
BitwardenProduct = plan.BitwardenProduct;
|
||||
StripeServiceAccountPlanId = plan.StripeServiceAccountPlanId;
|
||||
PasswordManager = new PasswordManagerPlanFeaturesResponseModel(plan.PasswordManager);
|
||||
}
|
||||
|
||||
public PlanType Type { get; set; }
|
||||
@@ -70,16 +51,6 @@ public class PlanResponseModel : ResponseModel
|
||||
public string NameLocalizationKey { get; set; }
|
||||
public string DescriptionLocalizationKey { get; set; }
|
||||
public bool CanBeUsedByBusiness { get; set; }
|
||||
public int BaseSeats { get; set; }
|
||||
public short? BaseStorageGb { get; set; }
|
||||
public short? MaxCollections { get; set; }
|
||||
public short? MaxUsers { get; set; }
|
||||
|
||||
public bool HasAdditionalSeatsOption { get; set; }
|
||||
public int? MaxAdditionalSeats { get; set; }
|
||||
public bool HasAdditionalStorageOption { get; set; }
|
||||
public short? MaxAdditionalStorage { get; set; }
|
||||
public bool HasPremiumAccessOption { get; set; }
|
||||
public int? TrialPeriodDays { get; set; }
|
||||
|
||||
public bool HasSelfHost { get; set; }
|
||||
@@ -98,21 +69,95 @@ public class PlanResponseModel : ResponseModel
|
||||
public int DisplaySortOrder { get; set; }
|
||||
public int? LegacyYear { get; set; }
|
||||
public bool Disabled { get; set; }
|
||||
public SecretsManagerPlanFeaturesResponseModel SecretsManager { get; protected init; }
|
||||
public PasswordManagerPlanFeaturesResponseModel PasswordManager { get; protected init; }
|
||||
|
||||
public string StripePlanId { get; set; }
|
||||
public string StripeSeatPlanId { get; set; }
|
||||
public string StripeStoragePlanId { get; set; }
|
||||
public string StripePremiumAccessPlanId { get; set; }
|
||||
public decimal BasePrice { get; set; }
|
||||
public decimal SeatPrice { get; set; }
|
||||
public decimal AdditionalStoragePricePerGb { get; set; }
|
||||
public decimal PremiumAccessOptionPrice { get; set; }
|
||||
public string StripeServiceAccountPlanId { get; set; }
|
||||
public decimal? AdditionalPricePerServiceAccount { get; set; }
|
||||
public short? BaseServiceAccount { get; set; }
|
||||
public short? MaxServiceAccounts { get; set; }
|
||||
public short? MaxAdditionalServiceAccounts { get; set; }
|
||||
public bool HasAdditionalServiceAccountOption { get; set; }
|
||||
public short? MaxProjects { get; set; }
|
||||
public BitwardenProductType BitwardenProduct { get; set; }
|
||||
public class SecretsManagerPlanFeaturesResponseModel
|
||||
{
|
||||
public SecretsManagerPlanFeaturesResponseModel(Plan.SecretsManagerPlanFeatures plan)
|
||||
{
|
||||
MaxServiceAccounts = plan.MaxServiceAccounts;
|
||||
AllowServiceAccountsAutoscale = plan is { AllowServiceAccountsAutoscale: true };
|
||||
StripeServiceAccountPlanId = plan.StripeServiceAccountPlanId;
|
||||
AdditionalPricePerServiceAccount = plan.AdditionalPricePerServiceAccount;
|
||||
BaseServiceAccount = plan.BaseServiceAccount;
|
||||
MaxAdditionalServiceAccount = plan.MaxAdditionalServiceAccount;
|
||||
HasAdditionalServiceAccountOption = plan is { HasAdditionalServiceAccountOption: true };
|
||||
StripeSeatPlanId = plan.StripeSeatPlanId;
|
||||
HasAdditionalSeatsOption = plan is { HasAdditionalSeatsOption: true };
|
||||
BasePrice = plan.BasePrice;
|
||||
SeatPrice = plan.SeatPrice;
|
||||
BaseSeats = plan.BaseSeats;
|
||||
MaxSeats = plan.MaxSeats;
|
||||
MaxAdditionalSeats = plan.MaxAdditionalSeats;
|
||||
AllowSeatAutoscale = plan.AllowSeatAutoscale;
|
||||
MaxProjects = plan.MaxProjects;
|
||||
}
|
||||
// Service accounts
|
||||
public short? MaxServiceAccounts { get; init; }
|
||||
public bool AllowServiceAccountsAutoscale { get; init; }
|
||||
public string StripeServiceAccountPlanId { get; init; }
|
||||
public decimal? AdditionalPricePerServiceAccount { get; init; }
|
||||
public short? BaseServiceAccount { get; init; }
|
||||
public short? MaxAdditionalServiceAccount { get; init; }
|
||||
public bool HasAdditionalServiceAccountOption { get; init; }
|
||||
// Seats
|
||||
public string StripeSeatPlanId { get; init; }
|
||||
public bool HasAdditionalSeatsOption { get; init; }
|
||||
public decimal BasePrice { get; init; }
|
||||
public decimal SeatPrice { get; init; }
|
||||
public int BaseSeats { get; init; }
|
||||
public short? MaxSeats { get; init; }
|
||||
public int? MaxAdditionalSeats { get; init; }
|
||||
public bool AllowSeatAutoscale { get; init; }
|
||||
|
||||
// Features
|
||||
public int MaxProjects { get; init; }
|
||||
}
|
||||
|
||||
public record PasswordManagerPlanFeaturesResponseModel
|
||||
{
|
||||
public PasswordManagerPlanFeaturesResponseModel(Plan.PasswordManagerPlanFeatures plan)
|
||||
{
|
||||
StripePlanId = plan.StripePlanId;
|
||||
StripeSeatPlanId = plan.StripeSeatPlanId;
|
||||
BasePrice = plan.BasePrice;
|
||||
SeatPrice = plan.SeatPrice;
|
||||
AllowSeatAutoscale = plan.AllowSeatAutoscale;
|
||||
HasAdditionalSeatsOption = plan.HasAdditionalSeatsOption;
|
||||
MaxAdditionalSeats = plan.MaxAdditionalSeats;
|
||||
BaseSeats = plan.BaseSeats;
|
||||
HasPremiumAccessOption = plan.HasPremiumAccessOption;
|
||||
StripePremiumAccessPlanId = plan.StripePremiumAccessPlanId;
|
||||
PremiumAccessOptionPrice = plan.PremiumAccessOptionPrice;
|
||||
MaxSeats = plan.MaxSeats;
|
||||
BaseStorageGb = plan.BaseStorageGb;
|
||||
HasAdditionalStorageOption = plan.HasAdditionalStorageOption;
|
||||
AdditionalStoragePricePerGb = plan.AdditionalStoragePricePerGb;
|
||||
StripeStoragePlanId = plan.StripeStoragePlanId;
|
||||
MaxAdditionalStorage = plan.MaxAdditionalStorage;
|
||||
MaxCollections = plan.MaxCollections;
|
||||
}
|
||||
// Seats
|
||||
public string StripePlanId { get; init; }
|
||||
public string StripeSeatPlanId { get; init; }
|
||||
public decimal BasePrice { get; init; }
|
||||
public decimal SeatPrice { get; init; }
|
||||
public bool AllowSeatAutoscale { get; init; }
|
||||
public bool HasAdditionalSeatsOption { get; init; }
|
||||
public int? MaxAdditionalSeats { get; init; }
|
||||
public int BaseSeats { get; init; }
|
||||
public bool HasPremiumAccessOption { get; init; }
|
||||
public string StripePremiumAccessPlanId { get; init; }
|
||||
public decimal PremiumAccessOptionPrice { get; init; }
|
||||
public short? MaxSeats { get; init; }
|
||||
// Storage
|
||||
public short? BaseStorageGb { get; init; }
|
||||
public bool HasAdditionalStorageOption { get; init; }
|
||||
public decimal AdditionalStoragePricePerGb { get; init; }
|
||||
public string StripeStoragePlanId { get; init; }
|
||||
public short? MaxAdditionalStorage { get; init; }
|
||||
// Feature
|
||||
public short? MaxCollections { get; init; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ public class ProfileOrganizationResponseModel : ResponseModel
|
||||
FamilySponsorshipAvailable = FamilySponsorshipFriendlyName == null &&
|
||||
StaticStore.GetSponsoredPlan(PlanSponsorshipType.FamiliesForEnterprise)
|
||||
.UsersCanSponsor(organization);
|
||||
PlanProductType = StaticStore.GetPasswordManagerPlan(organization.PlanType).Product;
|
||||
PlanProductType = StaticStore.GetPlan(organization.PlanType).Product;
|
||||
FamilySponsorshipLastSyncDate = organization.FamilySponsorshipLastSyncDate;
|
||||
FamilySponsorshipToDelete = organization.FamilySponsorshipToDelete;
|
||||
FamilySponsorshipValidUntil = organization.FamilySponsorshipValidUntil;
|
||||
|
||||
@@ -42,6 +42,6 @@ public class ProfileProviderOrganizationResponseModel : ProfileOrganizationRespo
|
||||
UserId = organization.UserId;
|
||||
ProviderId = organization.ProviderId;
|
||||
ProviderName = organization.ProviderName;
|
||||
PlanProductType = StaticStore.GetPasswordManagerPlan(organization.PlanType).Product;
|
||||
PlanProductType = StaticStore.GetPlan(organization.PlanType).Product;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Api;
|
||||
using Bit.Core.Models.Business;
|
||||
using Bit.Core.Utilities;
|
||||
@@ -98,7 +97,6 @@ public class BillingSubscription
|
||||
Quantity = item.Quantity;
|
||||
SponsoredSubscriptionItem = item.SponsoredSubscriptionItem;
|
||||
AddonSubscriptionItem = item.AddonSubscriptionItem;
|
||||
BitwardenProduct = item.BitwardenProduct;
|
||||
}
|
||||
|
||||
public string Name { get; set; }
|
||||
@@ -107,7 +105,6 @@ public class BillingSubscription
|
||||
public string Interval { get; set; }
|
||||
public bool SponsoredSubscriptionItem { get; set; }
|
||||
public bool AddonSubscriptionItem { get; set; }
|
||||
public BitwardenProductType BitwardenProduct { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,14 +10,12 @@ using Bit.Core.SecretsManager.Commands.AccessPolicies.Interfaces;
|
||||
using Bit.Core.SecretsManager.Entities;
|
||||
using Bit.Core.SecretsManager.Repositories;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Utilities;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Bit.Api.SecretsManager.Controllers;
|
||||
|
||||
[Authorize("secrets")]
|
||||
[SelfHosted(NotSelfHostedOnly = true)]
|
||||
[Route("access-policies")]
|
||||
public class AccessPoliciesController : Controller
|
||||
{
|
||||
|
||||
@@ -10,14 +10,12 @@ using Bit.Core.SecretsManager.Entities;
|
||||
using Bit.Core.SecretsManager.Queries.Projects.Interfaces;
|
||||
using Bit.Core.SecretsManager.Repositories;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Utilities;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Bit.Api.SecretsManager.Controllers;
|
||||
|
||||
[Authorize("secrets")]
|
||||
[SelfHosted(NotSelfHostedOnly = true)]
|
||||
public class ProjectsController : Controller
|
||||
{
|
||||
private readonly ICurrentContext _currentContext;
|
||||
|
||||
@@ -14,14 +14,12 @@ using Bit.Core.Services;
|
||||
using Bit.Core.Tools.Enums;
|
||||
using Bit.Core.Tools.Models.Business;
|
||||
using Bit.Core.Tools.Services;
|
||||
using Bit.Core.Utilities;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Bit.Api.SecretsManager.Controllers;
|
||||
|
||||
[Authorize("secrets")]
|
||||
[SelfHosted(NotSelfHostedOnly = true)]
|
||||
public class SecretsController : Controller
|
||||
{
|
||||
private readonly ICurrentContext _currentContext;
|
||||
|
||||
@@ -7,14 +7,12 @@ using Bit.Core.SecretsManager.Commands.Porting.Interfaces;
|
||||
using Bit.Core.SecretsManager.Queries.Projects.Interfaces;
|
||||
using Bit.Core.SecretsManager.Repositories;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Utilities;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Bit.Api.SecretsManager.Controllers;
|
||||
|
||||
[Authorize("secrets")]
|
||||
[SelfHosted(NotSelfHostedOnly = true)]
|
||||
public class SecretsManagerPortingController : Controller
|
||||
{
|
||||
private readonly ISecretRepository _secretRepository;
|
||||
|
||||
@@ -3,14 +3,12 @@ using Bit.Core.Context;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.SecretsManager.Commands.Trash.Interfaces;
|
||||
using Bit.Core.SecretsManager.Repositories;
|
||||
using Bit.Core.Utilities;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Bit.Api.SecretsManager.Controllers;
|
||||
|
||||
[Authorize("secrets")]
|
||||
[SelfHosted(NotSelfHostedOnly = true)]
|
||||
public class TrashController : Controller
|
||||
{
|
||||
private readonly ICurrentContext _currentContext;
|
||||
|
||||
@@ -14,14 +14,12 @@ using Bit.Core.SecretsManager.Entities;
|
||||
using Bit.Core.SecretsManager.Queries.ServiceAccounts.Interfaces;
|
||||
using Bit.Core.SecretsManager.Repositories;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Utilities;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Bit.Api.SecretsManager.Controllers;
|
||||
|
||||
[Authorize("secrets")]
|
||||
[SelfHosted(NotSelfHostedOnly = true)]
|
||||
[Route("service-accounts")]
|
||||
public class ServiceAccountsController : Controller
|
||||
{
|
||||
|
||||
@@ -27,6 +27,8 @@ namespace Bit.Api.Vault.Controllers;
|
||||
[Authorize("Application")]
|
||||
public class CiphersController : Controller
|
||||
{
|
||||
private static readonly Version _fido2KeyCipherMinimumVersion = new Version(Constants.Fido2KeyCipherMinimumVersion);
|
||||
|
||||
private readonly ICipherRepository _cipherRepository;
|
||||
private readonly ICollectionCipherRepository _collectionCipherRepository;
|
||||
private readonly ICipherService _cipherService;
|
||||
@@ -178,7 +180,8 @@ public class CiphersController : Controller
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
ValidateItemLevelEncryptionIsAvailable(cipher);
|
||||
ValidateClientVersionForItemLevelEncryptionSupport(cipher);
|
||||
ValidateClientVersionForFido2CredentialSupport(cipher);
|
||||
|
||||
var collectionIds = (await _collectionCipherRepository.GetManyByUserIdCipherIdAsync(userId, id)).Select(c => c.CollectionId).ToList();
|
||||
var modelOrgId = string.IsNullOrWhiteSpace(model.OrganizationId) ?
|
||||
@@ -202,7 +205,8 @@ public class CiphersController : Controller
|
||||
var userId = _userService.GetProperUserId(User).Value;
|
||||
var cipher = await _cipherRepository.GetOrganizationDetailsByIdAsync(id);
|
||||
|
||||
ValidateItemLevelEncryptionIsAvailable(cipher);
|
||||
ValidateClientVersionForItemLevelEncryptionSupport(cipher);
|
||||
ValidateClientVersionForFido2CredentialSupport(cipher);
|
||||
|
||||
if (cipher == null || !cipher.OrganizationId.HasValue ||
|
||||
!await _currentContext.EditAnyCollection(cipher.OrganizationId.Value))
|
||||
@@ -267,6 +271,9 @@ public class CiphersController : Controller
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
ValidateClientVersionForItemLevelEncryptionSupport(cipher);
|
||||
ValidateClientVersionForFido2CredentialSupport(cipher);
|
||||
|
||||
var original = cipher.Clone();
|
||||
await _cipherService.ShareAsync(original, model.Cipher.ToCipher(cipher), new Guid(model.Cipher.OrganizationId),
|
||||
model.CollectionIds.Select(c => new Guid(c)), userId, model.Cipher.LastKnownRevisionDate);
|
||||
@@ -529,7 +536,12 @@ public class CiphersController : Controller
|
||||
throw new BadRequestException("Trying to move ciphers that you do not own.");
|
||||
}
|
||||
|
||||
shareCiphers.Add((cipher.ToCipher(ciphersDict[cipher.Id.Value]), cipher.LastKnownRevisionDate));
|
||||
var existingCipher = ciphersDict[cipher.Id.Value];
|
||||
|
||||
ValidateClientVersionForItemLevelEncryptionSupport(existingCipher);
|
||||
ValidateClientVersionForFido2CredentialSupport(existingCipher);
|
||||
|
||||
shareCiphers.Add((cipher.ToCipher(existingCipher), cipher.LastKnownRevisionDate));
|
||||
}
|
||||
|
||||
await _cipherService.ShareManyAsync(shareCiphers, organizationId,
|
||||
@@ -582,7 +594,7 @@ public class CiphersController : Controller
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
ValidateItemLevelEncryptionIsAvailable(cipher);
|
||||
ValidateClientVersionForItemLevelEncryptionSupport(cipher);
|
||||
|
||||
if (request.FileSize > CipherService.MAX_FILE_SIZE)
|
||||
{
|
||||
@@ -804,11 +816,23 @@ public class CiphersController : Controller
|
||||
}
|
||||
}
|
||||
|
||||
private void ValidateItemLevelEncryptionIsAvailable(Cipher cipher)
|
||||
private void ValidateClientVersionForItemLevelEncryptionSupport(Cipher cipher)
|
||||
{
|
||||
if (cipher.Key != null && _currentContext.ClientVersion < _cipherKeyEncryptionMinimumVersion)
|
||||
{
|
||||
throw new BadRequestException("Cannot edit item. Update to the latest version of Bitwarden and try again.");
|
||||
}
|
||||
}
|
||||
|
||||
private void ValidateClientVersionForFido2CredentialSupport(Cipher cipher)
|
||||
{
|
||||
if (cipher.Type == Core.Vault.Enums.CipherType.Login)
|
||||
{
|
||||
var loginData = JsonSerializer.Deserialize<CipherLoginData>(cipher.Data);
|
||||
if (loginData?.Fido2Credentials != null && _currentContext.ClientVersion < _fido2KeyCipherMinimumVersion)
|
||||
{
|
||||
throw new BadRequestException("Cannot edit item. Update to the latest version of Bitwarden and try again.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
89
src/Api/Vault/Models/CipherFido2CredentialModel.cs
Normal file
89
src/Api/Vault/Models/CipherFido2CredentialModel.cs
Normal file
@@ -0,0 +1,89 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Bit.Core.Utilities;
|
||||
using Bit.Core.Vault.Models.Data;
|
||||
|
||||
namespace Bit.Api.Vault.Models;
|
||||
|
||||
public class CipherFido2CredentialModel
|
||||
{
|
||||
public CipherFido2CredentialModel() { }
|
||||
|
||||
public CipherFido2CredentialModel(CipherLoginFido2CredentialData data)
|
||||
{
|
||||
CredentialId = data.CredentialId;
|
||||
KeyType = data.KeyType;
|
||||
KeyAlgorithm = data.KeyAlgorithm;
|
||||
KeyCurve = data.KeyCurve;
|
||||
KeyValue = data.KeyValue;
|
||||
RpId = data.RpId;
|
||||
RpName = data.RpName;
|
||||
UserHandle = data.UserHandle;
|
||||
UserDisplayName = data.UserDisplayName;
|
||||
Counter = data.Counter;
|
||||
Discoverable = data.Discoverable;
|
||||
CreationDate = data.CreationDate;
|
||||
}
|
||||
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(1000)]
|
||||
public string CredentialId { get; set; }
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(1000)]
|
||||
public string KeyType { get; set; }
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(1000)]
|
||||
public string KeyAlgorithm { get; set; }
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(1000)]
|
||||
public string KeyCurve { get; set; }
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(1000)]
|
||||
public string KeyValue { get; set; }
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(1000)]
|
||||
public string RpId { get; set; }
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(1000)]
|
||||
public string RpName { get; set; }
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(1000)]
|
||||
public string UserHandle { get; set; }
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(1000)]
|
||||
public string UserDisplayName { get; set; }
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(1000)]
|
||||
public string Counter { get; set; }
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(1000)]
|
||||
public string Discoverable { get; set; }
|
||||
[Required]
|
||||
public DateTime CreationDate { get; set; }
|
||||
|
||||
public CipherLoginFido2CredentialData ToCipherLoginFido2CredentialData()
|
||||
{
|
||||
return new CipherLoginFido2CredentialData
|
||||
{
|
||||
CredentialId = CredentialId,
|
||||
KeyType = KeyType,
|
||||
KeyAlgorithm = KeyAlgorithm,
|
||||
KeyCurve = KeyCurve,
|
||||
KeyValue = KeyValue,
|
||||
RpId = RpId,
|
||||
RpName = RpName,
|
||||
UserHandle = UserHandle,
|
||||
UserDisplayName = UserDisplayName,
|
||||
Counter = Counter,
|
||||
Discoverable = Discoverable,
|
||||
CreationDate = CreationDate
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
static class CipherFido2CredentialModelExtensions
|
||||
{
|
||||
public static CipherLoginFido2CredentialData[] ToCipherLoginFido2CredentialData(this CipherFido2CredentialModel[] models)
|
||||
{
|
||||
return models.Select(m => m.ToCipherLoginFido2CredentialData()).ToArray();
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,11 @@ public class CipherLoginModel
|
||||
Uri = data.Uri;
|
||||
}
|
||||
|
||||
if (data.Fido2Credentials != null)
|
||||
{
|
||||
Fido2Credentials = data.Fido2Credentials.Select(key => new CipherFido2CredentialModel(key)).ToArray();
|
||||
}
|
||||
|
||||
Username = data.Username;
|
||||
Password = data.Password;
|
||||
PasswordRevisionDate = data.PasswordRevisionDate;
|
||||
@@ -55,6 +60,7 @@ public class CipherLoginModel
|
||||
[EncryptedStringLength(1000)]
|
||||
public string Totp { get; set; }
|
||||
public bool? AutofillOnPageLoad { get; set; }
|
||||
public CipherFido2CredentialModel[] Fido2Credentials { get; set; }
|
||||
|
||||
public class CipherLoginUriModel
|
||||
{
|
||||
|
||||
@@ -166,6 +166,7 @@ public class CipherRequestModel
|
||||
PasswordRevisionDate = Login.PasswordRevisionDate,
|
||||
Totp = Login.Totp,
|
||||
AutofillOnPageLoad = Login.AutofillOnPageLoad,
|
||||
Fido2Credentials = Login.Fido2Credentials == null ? null : Login.Fido2Credentials.ToCipherLoginFido2CredentialData(),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user