mirror of
https://github.com/bitwarden/server.git
synced 2025-06-30 23:52:50 -05:00
Merge remote-tracking branch 'origin/master' into ac/ac-1638/disallow-secrets-manager-for-msp-managed-organizations
This commit is contained in:
20
.github/workflows/build.yml
vendored
20
.github/workflows/build.yml
vendored
@ -19,7 +19,7 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
|
||||
|
||||
- name: Install cloc
|
||||
run: |
|
||||
@ -34,7 +34,7 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
|
||||
|
||||
- name: Set up dotnet
|
||||
uses: actions/setup-dotnet@3447fd6a9f9e57506b15f895c5b76d3b197dc7c2 # v3.2.0
|
||||
@ -49,7 +49,7 @@ jobs:
|
||||
NUGET_PACKAGES: ${{ github.workspace }}/.nuget/packages
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
|
||||
|
||||
- name: Set up dotnet
|
||||
uses: actions/setup-dotnet@3447fd6a9f9e57506b15f895c5b76d3b197dc7c2 # v3.2.0
|
||||
@ -135,7 +135,7 @@ jobs:
|
||||
node: true
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
|
||||
|
||||
- name: Set up dotnet
|
||||
uses: actions/setup-dotnet@3447fd6a9f9e57506b15f895c5b76d3b197dc7c2 # v3.2.0
|
||||
@ -246,7 +246,7 @@ jobs:
|
||||
dotnet: true
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
|
||||
|
||||
- name: Check Branch to Publish
|
||||
env:
|
||||
@ -277,7 +277,7 @@ jobs:
|
||||
|
||||
- name: Retrieve github PAT secrets
|
||||
id: retrieve-secret-pat
|
||||
uses: bitwarden/gh-actions/get-keyvault-secrets@f096207b7a2f31723165aee6ad03e91716686e78
|
||||
uses: bitwarden/gh-actions/get-keyvault-secrets@fdcf1fcec3b04762ce48216cbf3af32498bed74c
|
||||
with:
|
||||
keyvault: "bitwarden-ci"
|
||||
secrets: "github-pat-bitwarden-devops-bot-repo-scope"
|
||||
@ -337,7 +337,7 @@ jobs:
|
||||
needs: build-docker
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
|
||||
|
||||
- name: Set up dotnet
|
||||
uses: actions/setup-dotnet@3447fd6a9f9e57506b15f895c5b76d3b197dc7c2 # v3.2.0
|
||||
@ -476,7 +476,7 @@ jobs:
|
||||
- win-x64
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
|
||||
|
||||
- name: Set up dotnet
|
||||
uses: actions/setup-dotnet@3447fd6a9f9e57506b15f895c5b76d3b197dc7c2 # v3.2.0
|
||||
@ -528,7 +528,7 @@ jobs:
|
||||
|
||||
- name: Retrieve github PAT secrets
|
||||
id: retrieve-secret-pat
|
||||
uses: bitwarden/gh-actions/get-keyvault-secrets@f096207b7a2f31723165aee6ad03e91716686e78
|
||||
uses: bitwarden/gh-actions/get-keyvault-secrets@fdcf1fcec3b04762ce48216cbf3af32498bed74c
|
||||
with:
|
||||
keyvault: "bitwarden-ci"
|
||||
secrets: "github-pat-bitwarden-devops-bot-repo-scope"
|
||||
@ -603,7 +603,7 @@ jobs:
|
||||
|
||||
- name: Retrieve secrets
|
||||
id: retrieve-secrets
|
||||
uses: bitwarden/gh-actions/get-keyvault-secrets@f096207b7a2f31723165aee6ad03e91716686e78
|
||||
uses: bitwarden/gh-actions/get-keyvault-secrets@fdcf1fcec3b04762ce48216cbf3af32498bed74c
|
||||
if: failure()
|
||||
with:
|
||||
keyvault: "bitwarden-ci"
|
||||
|
2
.github/workflows/cleanup-after-pr.yml
vendored
2
.github/workflows/cleanup-after-pr.yml
vendored
@ -11,7 +11,7 @@ jobs:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
|
||||
|
||||
########## ACR ##########
|
||||
- name: Login to Azure - QA Subscription
|
||||
|
@ -92,7 +92,7 @@ jobs:
|
||||
|
||||
- name: Retrieve secrets
|
||||
id: retrieve-secrets
|
||||
uses: bitwarden/gh-actions/get-keyvault-secrets@f096207b7a2f31723165aee6ad03e91716686e78
|
||||
uses: bitwarden/gh-actions/get-keyvault-secrets@fdcf1fcec3b04762ce48216cbf3af32498bed74c
|
||||
if: failure()
|
||||
with:
|
||||
keyvault: "bitwarden-ci"
|
||||
|
2
.github/workflows/database.yml
vendored
2
.github/workflows/database.yml
vendored
@ -25,7 +25,7 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
|
||||
|
||||
- name: Set up dotnet
|
||||
uses: actions/setup-dotnet@3447fd6a9f9e57506b15f895c5b76d3b197dc7c2 # v3.2.0
|
||||
|
2
.github/workflows/infrastructure-tests.yml
vendored
2
.github/workflows/infrastructure-tests.yml
vendored
@ -38,7 +38,7 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
|
||||
|
||||
- name: Set up dotnet
|
||||
uses: actions/setup-dotnet@3447fd6a9f9e57506b15f895c5b76d3b197dc7c2 # v3.2.0
|
||||
|
2
.github/workflows/protect-files.yml
vendored
2
.github/workflows/protect-files.yml
vendored
@ -30,7 +30,7 @@ jobs:
|
||||
label: "DB-migrations-changed"
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
|
14
.github/workflows/release.yml
vendored
14
.github/workflows/release.yml
vendored
@ -37,11 +37,11 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
|
||||
|
||||
- name: Check Release Version
|
||||
id: version
|
||||
uses: bitwarden/gh-actions/release-version-check@f096207b7a2f31723165aee6ad03e91716686e78
|
||||
uses: bitwarden/gh-actions/release-version-check@fdcf1fcec3b04762ce48216cbf3af32498bed74c
|
||||
with:
|
||||
release-type: ${{ github.event.inputs.release_type }}
|
||||
project-type: dotnet
|
||||
@ -89,7 +89,7 @@ jobs:
|
||||
|
||||
- name: Download latest Release ${{ matrix.name }} asset
|
||||
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
|
||||
uses: bitwarden/gh-actions/download-artifacts@bc3bf31f1d9cac9c9d02cae01fc615fa25d38929
|
||||
uses: bitwarden/gh-actions/download-artifacts@fdcf1fcec3b04762ce48216cbf3af32498bed74c
|
||||
with:
|
||||
workflow: build.yml
|
||||
workflow_conclusion: success
|
||||
@ -98,7 +98,7 @@ jobs:
|
||||
|
||||
- name: Dry Run - Download latest Release ${{ matrix.name }} asset
|
||||
if: ${{ github.event.inputs.release_type == 'Dry Run' }}
|
||||
uses: bitwarden/gh-actions/download-artifacts@bc3bf31f1d9cac9c9d02cae01fc615fa25d38929
|
||||
uses: bitwarden/gh-actions/download-artifacts@fdcf1fcec3b04762ce48216cbf3af32498bed74c
|
||||
with:
|
||||
workflow: build.yml
|
||||
workflow_conclusion: success
|
||||
@ -211,7 +211,7 @@ jobs:
|
||||
echo "Github Release Option: $RELEASE_OPTION"
|
||||
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
|
||||
|
||||
- name: Setup project name
|
||||
id: setup
|
||||
@ -274,7 +274,7 @@ jobs:
|
||||
steps:
|
||||
- name: Download latest Release Docker Stubs
|
||||
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
|
||||
uses: bitwarden/gh-actions/download-artifacts@bc3bf31f1d9cac9c9d02cae01fc615fa25d38929
|
||||
uses: bitwarden/gh-actions/download-artifacts@fdcf1fcec3b04762ce48216cbf3af32498bed74c
|
||||
with:
|
||||
workflow: build.yml
|
||||
workflow_conclusion: success
|
||||
@ -287,7 +287,7 @@ jobs:
|
||||
|
||||
- name: Dry Run - Download latest Release Docker Stubs
|
||||
if: ${{ github.event.inputs.release_type == 'Dry Run' }}
|
||||
uses: bitwarden/gh-actions/download-artifacts@bc3bf31f1d9cac9c9d02cae01fc615fa25d38929
|
||||
uses: bitwarden/gh-actions/download-artifacts@fdcf1fcec3b04762ce48216cbf3af32498bed74c
|
||||
with:
|
||||
workflow: build.yml
|
||||
workflow_conclusion: success
|
||||
|
6
.github/workflows/version-bump.yml
vendored
6
.github/workflows/version-bump.yml
vendored
@ -14,7 +14,7 @@ jobs:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Checkout Branch
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
|
||||
|
||||
- name: Login to Azure - CI Subscription
|
||||
uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7
|
||||
@ -23,7 +23,7 @@ jobs:
|
||||
|
||||
- name: Retrieve secrets
|
||||
id: retrieve-secrets
|
||||
uses: bitwarden/gh-actions/get-keyvault-secrets@f096207b7a2f31723165aee6ad03e91716686e78
|
||||
uses: bitwarden/gh-actions/get-keyvault-secrets@fdcf1fcec3b04762ce48216cbf3af32498bed74c
|
||||
with:
|
||||
keyvault: "bitwarden-ci"
|
||||
secrets: "github-gpg-private-key, github-gpg-private-key-passphrase"
|
||||
@ -40,7 +40,7 @@ jobs:
|
||||
run: git switch -c version_bump_${{ github.event.inputs.version_number }}
|
||||
|
||||
- name: Bump Version - Props
|
||||
uses: bitwarden/gh-actions/version-bump@f096207b7a2f31723165aee6ad03e91716686e78
|
||||
uses: bitwarden/gh-actions/version-bump@fdcf1fcec3b04762ce48216cbf3af32498bed74c
|
||||
with:
|
||||
version: ${{ github.event.inputs.version_number }}
|
||||
file_path: "Directory.Build.props"
|
||||
|
2
.github/workflows/workflow-linter.yml
vendored
2
.github/workflows/workflow-linter.yml
vendored
@ -8,4 +8,4 @@ on:
|
||||
|
||||
jobs:
|
||||
call-workflow:
|
||||
uses: bitwarden/gh-actions/.github/workflows/workflow-linter.yml@f096207b7a2f31723165aee6ad03e91716686e78
|
||||
uses: bitwarden/gh-actions/.github/workflows/workflow-linter.yml@fdcf1fcec3b04762ce48216cbf3af32498bed74c
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Version>2023.9.0</Version>
|
||||
<Version>2023.9.1</Version>
|
||||
<RootNamespace>Bit.$(MSBuildProjectName)</RootNamespace>
|
||||
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
|
@ -19,6 +19,7 @@ using Bit.Core.Utilities;
|
||||
using Bit.Core.Vault.Repositories;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Stripe;
|
||||
|
||||
namespace Bit.Admin.Controllers;
|
||||
|
||||
@ -47,6 +48,7 @@ public class OrganizationsController : Controller
|
||||
private readonly ISecretRepository _secretRepository;
|
||||
private readonly IProjectRepository _projectRepository;
|
||||
private readonly IServiceAccountRepository _serviceAccountRepository;
|
||||
private readonly IStripeSyncService _stripeSyncService;
|
||||
|
||||
public OrganizationsController(
|
||||
IOrganizationService organizationService,
|
||||
@ -70,7 +72,8 @@ public class OrganizationsController : Controller
|
||||
ICurrentContext currentContext,
|
||||
ISecretRepository secretRepository,
|
||||
IProjectRepository projectRepository,
|
||||
IServiceAccountRepository serviceAccountRepository)
|
||||
IServiceAccountRepository serviceAccountRepository,
|
||||
IStripeSyncService stripeSyncService)
|
||||
{
|
||||
_organizationService = organizationService;
|
||||
_organizationRepository = organizationRepository;
|
||||
@ -94,6 +97,7 @@ public class OrganizationsController : Controller
|
||||
_secretRepository = secretRepository;
|
||||
_projectRepository = projectRepository;
|
||||
_serviceAccountRepository = serviceAccountRepository;
|
||||
_stripeSyncService = stripeSyncService;
|
||||
}
|
||||
|
||||
[RequirePermission(Permission.Org_List_View)]
|
||||
@ -208,6 +212,16 @@ public class OrganizationsController : Controller
|
||||
throw new BadRequestException("Plan does not support Secrets Manager");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await _stripeSyncService.UpdateCustomerEmailAddress(organization.GatewayCustomerId, organization.BillingEmail);
|
||||
}
|
||||
catch (StripeException stripeException)
|
||||
{
|
||||
_logger.LogError(stripeException, "Failed to update billing email address in Stripe for Organization with ID '{organizationId}'", organization.Id);
|
||||
throw;
|
||||
}
|
||||
|
||||
await _organizationRepository.ReplaceAsync(organization);
|
||||
await _applicationCacheService.UpsertOrganizationAbilityAsync(organization);
|
||||
await _referenceEventService.RaiseEventAsync(new ReferenceEvent(ReferenceEventType.OrganizationEditedByAdmin, organization, _currentContext)
|
||||
@ -215,6 +229,7 @@ public class OrganizationsController : Controller
|
||||
EventRaisedByUser = _userService.GetUserName(User),
|
||||
SalesAssistedTrialStarted = model.SalesAssistedTrialStarted,
|
||||
});
|
||||
|
||||
return RedirectToAction("Edit", new { id });
|
||||
}
|
||||
|
||||
|
@ -277,7 +277,13 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
<input type="email" class="form-control" asp-for="BillingEmail" readonly='@(!canEditBilling)'>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
asp-for="BillingEmail"
|
||||
readonly='@(!canEditBilling)'
|
||||
pattern="@(@"[^@\s]+@[^@\s]+\.[^@\s]+")"
|
||||
title="Email address must be in the format 'address@domain.com'.">
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -116,6 +116,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)
|
||||
@ -146,6 +147,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; }
|
||||
|
||||
|
@ -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)
|
||||
|
@ -36,6 +36,7 @@ public class CiphersController : Controller
|
||||
private readonly ICurrentContext _currentContext;
|
||||
private readonly ILogger<CiphersController> _logger;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly Version _cipherKeyEncryptionMinimumVersion = new Version(Constants.CipherKeyEncryptionMinimumVersion);
|
||||
|
||||
public CiphersController(
|
||||
ICipherRepository cipherRepository,
|
||||
@ -177,6 +178,8 @@ public class CiphersController : Controller
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
ValidateItemLevelEncryptionIsAvailable(cipher);
|
||||
|
||||
var collectionIds = (await _collectionCipherRepository.GetManyByUserIdCipherIdAsync(userId, id)).Select(c => c.CollectionId).ToList();
|
||||
var modelOrgId = string.IsNullOrWhiteSpace(model.OrganizationId) ?
|
||||
(Guid?)null : new Guid(model.OrganizationId);
|
||||
@ -198,6 +201,9 @@ public class CiphersController : Controller
|
||||
{
|
||||
var userId = _userService.GetProperUserId(User).Value;
|
||||
var cipher = await _cipherRepository.GetOrganizationDetailsByIdAsync(id);
|
||||
|
||||
ValidateItemLevelEncryptionIsAvailable(cipher);
|
||||
|
||||
if (cipher == null || !cipher.OrganizationId.HasValue ||
|
||||
!await _currentContext.EditAnyCollection(cipher.OrganizationId.Value))
|
||||
{
|
||||
@ -576,6 +582,8 @@ public class CiphersController : Controller
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
ValidateItemLevelEncryptionIsAvailable(cipher);
|
||||
|
||||
if (request.FileSize > CipherService.MAX_FILE_SIZE)
|
||||
{
|
||||
throw new BadRequestException($"Max file size is {CipherService.MAX_FILE_SIZE_READABLE}.");
|
||||
@ -795,4 +803,12 @@ public class CiphersController : Controller
|
||||
throw new BadRequestException("Invalid content.");
|
||||
}
|
||||
}
|
||||
|
||||
private void ValidateItemLevelEncryptionIsAvailable(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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ public class CipherRequestModel
|
||||
public string FolderId { get; set; }
|
||||
public bool Favorite { get; set; }
|
||||
public CipherRepromptType Reprompt { get; set; }
|
||||
public string Key { get; set; }
|
||||
[Required]
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(1000)]
|
||||
@ -86,6 +87,7 @@ public class CipherRequestModel
|
||||
}
|
||||
|
||||
existingCipher.Reprompt = Reprompt;
|
||||
existingCipher.Key = Key;
|
||||
|
||||
var hasAttachments2 = (Attachments2?.Count ?? 0) > 0;
|
||||
var hasAttachments = (Attachments?.Count ?? 0) > 0;
|
||||
|
@ -63,6 +63,7 @@ public class CipherMiniResponseModel : ResponseModel
|
||||
CreationDate = cipher.CreationDate;
|
||||
DeletedDate = cipher.DeletedDate;
|
||||
Reprompt = cipher.Reprompt.GetValueOrDefault(CipherRepromptType.None);
|
||||
Key = cipher.Key;
|
||||
}
|
||||
|
||||
public Guid Id { get; set; }
|
||||
@ -83,6 +84,7 @@ public class CipherMiniResponseModel : ResponseModel
|
||||
public DateTime CreationDate { get; set; }
|
||||
public DateTime? DeletedDate { get; set; }
|
||||
public CipherRepromptType Reprompt { get; set; }
|
||||
public string Key { get; set; }
|
||||
}
|
||||
|
||||
public class CipherResponseModel : CipherMiniResponseModel
|
||||
|
@ -19,6 +19,8 @@ public static class Constants
|
||||
/// their subscription has expired.
|
||||
/// </summary>
|
||||
public const int OrganizationSelfHostSubscriptionGracePeriodDays = 60;
|
||||
|
||||
public const string CipherKeyEncryptionMinimumVersion = "2023.9.2";
|
||||
}
|
||||
|
||||
public static class TokenPurposes
|
||||
@ -37,6 +39,7 @@ public static class FeatureFlagKeys
|
||||
public const string DisplayLowKdfIterationWarning = "display-kdf-iteration-warning";
|
||||
public const string TrustedDeviceEncryption = "trusted-device-encryption";
|
||||
public const string AutofillV2 = "autofill-v2";
|
||||
public const string BrowserFilelessImport = "browser-fileless-import";
|
||||
|
||||
public static List<string> GetAllKeys()
|
||||
{
|
||||
|
@ -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)
|
||||
|
@ -1,5 +1,6 @@
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Settings;
|
||||
using LaunchDarkly.Logging;
|
||||
using LaunchDarkly.Sdk.Server;
|
||||
using LaunchDarkly.Sdk.Server.Integrations;
|
||||
|
||||
@ -14,6 +15,7 @@ public class LaunchDarklyFeatureService : IFeatureService, IDisposable
|
||||
IGlobalSettings globalSettings)
|
||||
{
|
||||
var ldConfig = Configuration.Builder(globalSettings.LaunchDarkly?.SdkKey);
|
||||
ldConfig.Logging(Components.Logging().Level(LogLevel.Error));
|
||||
|
||||
if (string.IsNullOrEmpty(globalSettings.LaunchDarkly?.SdkKey))
|
||||
{
|
||||
|
@ -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))
|
||||
|
@ -21,6 +21,7 @@ public class Cipher : ITableObject<Guid>, ICloneable
|
||||
public DateTime RevisionDate { get; set; } = DateTime.UtcNow;
|
||||
public DateTime? DeletedDate { get; set; }
|
||||
public Enums.CipherRepromptType? Reprompt { get; set; }
|
||||
public string Key { get; set; }
|
||||
|
||||
public void SetNewId()
|
||||
{
|
||||
|
@ -392,7 +392,8 @@ public class CipherRepository : Repository<Cipher, Guid>, ICipherRepository
|
||||
SET
|
||||
[Data] = TC.[Data],
|
||||
[Attachments] = TC.[Attachments],
|
||||
[RevisionDate] = TC.[RevisionDate]
|
||||
[RevisionDate] = TC.[RevisionDate],
|
||||
[Key] = TC.[Key]
|
||||
FROM
|
||||
[dbo].[Cipher] C
|
||||
INNER JOIN
|
||||
@ -506,7 +507,8 @@ public class CipherRepository : Repository<Cipher, Guid>, ICipherRepository
|
||||
[Data] = TC.[Data],
|
||||
[Attachments] = TC.[Attachments],
|
||||
[RevisionDate] = TC.[RevisionDate],
|
||||
[DeletedDate] = TC.[DeletedDate]
|
||||
[DeletedDate] = TC.[DeletedDate],
|
||||
[Key] = TC.[Key]
|
||||
FROM
|
||||
[dbo].[Cipher] C
|
||||
INNER JOIN
|
||||
@ -728,6 +730,8 @@ public class CipherRepository : Repository<Cipher, Guid>, ICipherRepository
|
||||
ciphersTable.Columns.Add(deletedDateColumn);
|
||||
var repromptColumn = new DataColumn(nameof(c.Reprompt), typeof(short));
|
||||
ciphersTable.Columns.Add(repromptColumn);
|
||||
var keyColummn = new DataColumn(nameof(c.Key), typeof(string));
|
||||
ciphersTable.Columns.Add(keyColummn);
|
||||
|
||||
foreach (DataColumn col in ciphersTable.Columns)
|
||||
{
|
||||
@ -754,6 +758,7 @@ public class CipherRepository : Repository<Cipher, Guid>, ICipherRepository
|
||||
row[revisionDateColumn] = cipher.RevisionDate;
|
||||
row[deletedDateColumn] = cipher.DeletedDate.HasValue ? (object)cipher.DeletedDate : DBNull.Value;
|
||||
row[repromptColumn] = cipher.Reprompt;
|
||||
row[keyColummn] = cipher.Key;
|
||||
|
||||
ciphersTable.Rows.Add(row);
|
||||
}
|
||||
|
@ -73,6 +73,7 @@ public class UserCipherDetailsQuery : IQuery<CipherDetails>
|
||||
Reprompt = c.Reprompt,
|
||||
ViewPassword = true,
|
||||
OrganizationUseTotp = false,
|
||||
Key = c.Key
|
||||
});
|
||||
return union;
|
||||
}
|
||||
|
@ -366,6 +366,7 @@ public class CipherRepository : Repository<Core.Vault.Entities.Cipher, Cipher, G
|
||||
Reprompt = c.Reprompt,
|
||||
ViewPassword = true,
|
||||
OrganizationUseTotp = false,
|
||||
Key = c.Key
|
||||
};
|
||||
}
|
||||
var ciphers = await cipherDetailsView.ToListAsync();
|
||||
@ -591,6 +592,7 @@ public class CipherRepository : Repository<Core.Vault.Entities.Cipher, Cipher, G
|
||||
trackedCipher.Attachments = cipher.Attachments;
|
||||
trackedCipher.RevisionDate = cipher.RevisionDate;
|
||||
trackedCipher.DeletedDate = cipher.DeletedDate;
|
||||
trackedCipher.Key = cipher.Key;
|
||||
|
||||
await transaction.CommitAsync();
|
||||
|
||||
|
@ -29,6 +29,7 @@ public class CipherDetailsQuery : IQuery<CipherDetails>
|
||||
RevisionDate = c.RevisionDate,
|
||||
DeletedDate = c.DeletedDate,
|
||||
Reprompt = c.Reprompt,
|
||||
Key = c.Key,
|
||||
Favorite = _userId.HasValue && c.Favorites != null && c.Favorites.ToLowerInvariant().Contains($"\"{_userId}\":true"),
|
||||
FolderId = (_ignoreFolders || !_userId.HasValue || c.Folders == null || !c.Folders.ToLowerInvariant().Contains(_userId.Value.ToString())) ?
|
||||
null :
|
||||
|
@ -26,6 +26,7 @@ SELECT
|
||||
ELSE TRY_CONVERT(UNIQUEIDENTIFIER, JSON_VALUE(C.[Folders], CONCAT('$."', @UserId, '"')))
|
||||
END [FolderId],
|
||||
C.[DeletedDate],
|
||||
C.[Reprompt]
|
||||
C.[Reprompt],
|
||||
C.[Key]
|
||||
FROM
|
||||
[dbo].[Cipher] C
|
@ -15,7 +15,8 @@
|
||||
@ViewPassword BIT, -- not used
|
||||
@OrganizationUseTotp BIT, -- not used
|
||||
@DeletedDate DATETIME2(7),
|
||||
@Reprompt TINYINT
|
||||
@Reprompt TINYINT,
|
||||
@Key VARCHAR(MAX) = NULL
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
@ -35,7 +36,8 @@ BEGIN
|
||||
[CreationDate],
|
||||
[RevisionDate],
|
||||
[DeletedDate],
|
||||
[Reprompt]
|
||||
[Reprompt],
|
||||
[Key]
|
||||
)
|
||||
VALUES
|
||||
(
|
||||
@ -49,7 +51,8 @@ BEGIN
|
||||
@CreationDate,
|
||||
@RevisionDate,
|
||||
@DeletedDate,
|
||||
@Reprompt
|
||||
@Reprompt,
|
||||
@Key
|
||||
)
|
||||
|
||||
IF @OrganizationId IS NOT NULL
|
||||
|
@ -16,6 +16,7 @@
|
||||
@OrganizationUseTotp BIT, -- not used
|
||||
@DeletedDate DATETIME2(7),
|
||||
@Reprompt TINYINT,
|
||||
@Key VARCHAR(MAX) = NULL,
|
||||
@CollectionIds AS [dbo].[GuidIdArray] READONLY
|
||||
AS
|
||||
BEGIN
|
||||
@ -23,7 +24,7 @@ BEGIN
|
||||
|
||||
EXEC [dbo].[CipherDetails_Create] @Id, @UserId, @OrganizationId, @Type, @Data, @Favorites, @Folders,
|
||||
@Attachments, @CreationDate, @RevisionDate, @FolderId, @Favorite, @Edit, @ViewPassword,
|
||||
@OrganizationUseTotp, @DeletedDate, @Reprompt
|
||||
@OrganizationUseTotp, @DeletedDate, @Reprompt, @Key
|
||||
|
||||
DECLARE @UpdateCollectionsSuccess INT
|
||||
EXEC @UpdateCollectionsSuccess = [dbo].[Cipher_UpdateCollections] @Id, @UserId, @OrganizationId, @CollectionIds
|
||||
|
@ -6,7 +6,7 @@
|
||||
@Data NVARCHAR(MAX),
|
||||
@Favorites NVARCHAR(MAX), -- not used
|
||||
@Folders NVARCHAR(MAX), -- not used
|
||||
@Attachments NVARCHAR(MAX), -- not used
|
||||
@Attachments NVARCHAR(MAX),
|
||||
@CreationDate DATETIME2(7),
|
||||
@RevisionDate DATETIME2(7),
|
||||
@FolderId UNIQUEIDENTIFIER,
|
||||
@ -15,7 +15,8 @@
|
||||
@ViewPassword BIT, -- not used
|
||||
@OrganizationUseTotp BIT, -- not used
|
||||
@DeletedDate DATETIME2(2),
|
||||
@Reprompt TINYINT
|
||||
@Reprompt TINYINT,
|
||||
@Key VARCHAR(MAX) = NULL
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
@ -48,10 +49,12 @@ BEGIN
|
||||
ELSE
|
||||
JSON_MODIFY([Favorites], @UserIdPath, NULL)
|
||||
END,
|
||||
[Attachments] = @Attachments,
|
||||
[Reprompt] = @Reprompt,
|
||||
[CreationDate] = @CreationDate,
|
||||
[RevisionDate] = @RevisionDate,
|
||||
[DeletedDate] = @DeletedDate
|
||||
[DeletedDate] = @DeletedDate,
|
||||
[Key] = @Key
|
||||
WHERE
|
||||
[Id] = @Id
|
||||
|
||||
|
@ -10,7 +10,8 @@
|
||||
@CreationDate DATETIME2(7),
|
||||
@RevisionDate DATETIME2(7),
|
||||
@DeletedDate DATETIME2(7),
|
||||
@Reprompt TINYINT
|
||||
@Reprompt TINYINT,
|
||||
@Key VARCHAR(MAX) = NULL
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
@ -28,7 +29,8 @@ BEGIN
|
||||
[CreationDate],
|
||||
[RevisionDate],
|
||||
[DeletedDate],
|
||||
[Reprompt]
|
||||
[Reprompt],
|
||||
[Key]
|
||||
)
|
||||
VALUES
|
||||
(
|
||||
@ -43,7 +45,8 @@ BEGIN
|
||||
@CreationDate,
|
||||
@RevisionDate,
|
||||
@DeletedDate,
|
||||
@Reprompt
|
||||
@Reprompt,
|
||||
@Key
|
||||
)
|
||||
|
||||
IF @OrganizationId IS NOT NULL
|
||||
|
@ -11,13 +11,14 @@
|
||||
@RevisionDate DATETIME2(7),
|
||||
@DeletedDate DATETIME2(7),
|
||||
@Reprompt TINYINT,
|
||||
@Key VARCHAR(MAX) = NULL,
|
||||
@CollectionIds AS [dbo].[GuidIdArray] READONLY
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
|
||||
EXEC [dbo].[Cipher_Create] @Id, @UserId, @OrganizationId, @Type, @Data, @Favorites, @Folders,
|
||||
@Attachments, @CreationDate, @RevisionDate, @DeletedDate, @Reprompt
|
||||
@Attachments, @CreationDate, @RevisionDate, @DeletedDate, @Reprompt, @Key
|
||||
|
||||
DECLARE @UpdateCollectionsSuccess INT
|
||||
EXEC @UpdateCollectionsSuccess = [dbo].[Cipher_UpdateCollections] @Id, @UserId, @OrganizationId, @CollectionIds
|
||||
|
@ -10,7 +10,8 @@
|
||||
@CreationDate DATETIME2(7),
|
||||
@RevisionDate DATETIME2(7),
|
||||
@DeletedDate DATETIME2(7),
|
||||
@Reprompt TINYINT
|
||||
@Reprompt TINYINT,
|
||||
@Key VARCHAR(MAX) = NULL
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
@ -28,7 +29,8 @@ BEGIN
|
||||
[CreationDate] = @CreationDate,
|
||||
[RevisionDate] = @RevisionDate,
|
||||
[DeletedDate] = @DeletedDate,
|
||||
[Reprompt] = @Reprompt
|
||||
[Reprompt] = @Reprompt,
|
||||
[Key] = @Key
|
||||
WHERE
|
||||
[Id] = @Id
|
||||
|
||||
|
@ -11,6 +11,7 @@
|
||||
@RevisionDate DATETIME2(7),
|
||||
@DeletedDate DATETIME2(7),
|
||||
@Reprompt TINYINT,
|
||||
@Key VARCHAR(MAX) = NULL,
|
||||
@CollectionIds AS [dbo].[GuidIdArray] READONLY
|
||||
AS
|
||||
BEGIN
|
||||
@ -36,7 +37,8 @@ BEGIN
|
||||
[Data] = @Data,
|
||||
[Attachments] = @Attachments,
|
||||
[RevisionDate] = @RevisionDate,
|
||||
[DeletedDate] = @DeletedDate
|
||||
[DeletedDate] = @DeletedDate,
|
||||
[Key] = @Key
|
||||
-- No need to update CreationDate, Favorites, Folders, or Type since that data will not change
|
||||
WHERE
|
||||
[Id] = @Id
|
||||
|
@ -12,6 +12,7 @@ CREATE TABLE [dbo].[Cipher] (
|
||||
[RevisionDate] DATETIME2 (7) NOT NULL,
|
||||
[DeletedDate] DATETIME2 (7) NULL,
|
||||
[Reprompt] TINYINT NULL,
|
||||
[Key] VARCHAR(MAX) NULL,
|
||||
CONSTRAINT [PK_Cipher] PRIMARY KEY CLUSTERED ([Id] ASC),
|
||||
CONSTRAINT [FK_Cipher_Organization] FOREIGN KEY ([OrganizationId]) REFERENCES [dbo].[Organization] ([Id]),
|
||||
CONSTRAINT [FK_Cipher_User] FOREIGN KEY ([UserId]) REFERENCES [dbo].[User] ([Id])
|
||||
|
@ -2,6 +2,7 @@
|
||||
using System.Reflection;
|
||||
using Bit.Core;
|
||||
using DbUp;
|
||||
using DbUp.Helpers;
|
||||
using Microsoft.Data.SqlClient;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
@ -24,6 +25,8 @@ public class DbMigrator
|
||||
}
|
||||
|
||||
public bool MigrateMsSqlDatabaseWithRetries(bool enableLogging = true,
|
||||
bool repeatable = false,
|
||||
string folderName = MigratorConstants.DefaultMigrationsFolderName,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
var attempt = 1;
|
||||
@ -32,7 +35,7 @@ public class DbMigrator
|
||||
{
|
||||
try
|
||||
{
|
||||
var success = MigrateDatabase(enableLogging, cancellationToken);
|
||||
var success = MigrateDatabase(enableLogging, repeatable, folderName, cancellationToken);
|
||||
return success;
|
||||
}
|
||||
catch (SqlException ex)
|
||||
@ -54,6 +57,8 @@ public class DbMigrator
|
||||
}
|
||||
|
||||
public bool MigrateDatabase(bool enableLogging = true,
|
||||
bool repeatable = false,
|
||||
string folderName = MigratorConstants.DefaultMigrationsFolderName,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
if (_logger != null)
|
||||
@ -98,12 +103,20 @@ public class DbMigrator
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
var builder = DeployChanges.To
|
||||
.SqlDatabase(_connectionString)
|
||||
.JournalToSqlTable("dbo", "Migration")
|
||||
.WithScriptsAndCodeEmbeddedInAssembly(Assembly.GetExecutingAssembly(),
|
||||
s => s.Contains($".DbScripts.") && !s.Contains(".Archive."))
|
||||
s => s.Contains($".{folderName}.") && !s.Contains(".Archive."))
|
||||
.WithTransaction()
|
||||
.WithExecutionTimeout(new TimeSpan(0, 5, 0));
|
||||
|
||||
if (repeatable)
|
||||
{
|
||||
builder.JournalTo(new NullJournal());
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.JournalToSqlTable("dbo", MigratorConstants.SqlTableJournalName);
|
||||
}
|
||||
|
||||
if (enableLogging)
|
||||
{
|
||||
if (_logger != null)
|
||||
|
428
util/Migrator/DbScripts/2023-09-27_00_CipherKeyUpdate.sql
Normal file
428
util/Migrator/DbScripts/2023-09-27_00_CipherKeyUpdate.sql
Normal file
@ -0,0 +1,428 @@
|
||||
-- Add Key Column
|
||||
IF COL_LENGTH('[dbo].[Cipher]', 'Key') IS NULL
|
||||
BEGIN
|
||||
ALTER TABLE [dbo].[Cipher] ADD [Key] VARCHAR (MAX) NULL;
|
||||
END
|
||||
GO
|
||||
|
||||
-- Remove ForceKeyRotation Column from the Cipher table
|
||||
-- (this is to help with the dev work as the column was no longer needed)
|
||||
IF COL_LENGTH('[dbo].[Cipher]', 'ForceKeyRotation') IS NOT NULL
|
||||
BEGIN
|
||||
ALTER TABLE [dbo].[Cipher] DROP CONSTRAINT [D_Cipher_ForceKeyRotation];
|
||||
ALTER TABLE [dbo].[Cipher] DROP COLUMN [ForceKeyRotation];
|
||||
END
|
||||
GO
|
||||
|
||||
CREATE OR ALTER FUNCTION [dbo].[CipherDetails](@UserId UNIQUEIDENTIFIER)
|
||||
RETURNS TABLE
|
||||
AS RETURN
|
||||
SELECT
|
||||
C.[Id],
|
||||
C.[UserId],
|
||||
C.[OrganizationId],
|
||||
C.[Type],
|
||||
C.[Data],
|
||||
C.[Attachments],
|
||||
C.[CreationDate],
|
||||
C.[RevisionDate],
|
||||
CASE
|
||||
WHEN
|
||||
@UserId IS NULL
|
||||
OR C.[Favorites] IS NULL
|
||||
OR JSON_VALUE(C.[Favorites], CONCAT('$."', @UserId, '"')) IS NULL
|
||||
THEN 0
|
||||
ELSE 1
|
||||
END [Favorite],
|
||||
CASE
|
||||
WHEN
|
||||
@UserId IS NULL
|
||||
OR C.[Folders] IS NULL
|
||||
THEN NULL
|
||||
ELSE TRY_CONVERT(UNIQUEIDENTIFIER, JSON_VALUE(C.[Folders], CONCAT('$."', @UserId, '"')))
|
||||
END [FolderId],
|
||||
C.[DeletedDate],
|
||||
C.[Reprompt],
|
||||
C.[Key]
|
||||
FROM
|
||||
[dbo].[Cipher] C
|
||||
GO
|
||||
|
||||
IF OBJECT_ID('[dbo].[UserCipherDetails]') IS NOT NULL
|
||||
BEGIN
|
||||
EXECUTE sp_refreshsqlmodule N'[dbo].[UserCipherDetails]';
|
||||
END
|
||||
GO
|
||||
|
||||
IF OBJECT_ID('[dbo].[CipherView]') IS NOT NULL
|
||||
BEGIN
|
||||
EXECUTE sp_refreshsqlmodule N'[dbo].[CipherView]';
|
||||
END
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE [dbo].[CipherDetails_Create]
|
||||
@Id UNIQUEIDENTIFIER,
|
||||
@UserId UNIQUEIDENTIFIER,
|
||||
@OrganizationId UNIQUEIDENTIFIER,
|
||||
@Type TINYINT,
|
||||
@Data NVARCHAR(MAX),
|
||||
@Favorites NVARCHAR(MAX), -- not used
|
||||
@Folders NVARCHAR(MAX), -- not used
|
||||
@Attachments NVARCHAR(MAX), -- not used
|
||||
@CreationDate DATETIME2(7),
|
||||
@RevisionDate DATETIME2(7),
|
||||
@FolderId UNIQUEIDENTIFIER,
|
||||
@Favorite BIT,
|
||||
@Edit BIT, -- not used
|
||||
@ViewPassword BIT, -- not used
|
||||
@OrganizationUseTotp BIT, -- not used
|
||||
@DeletedDate DATETIME2(7),
|
||||
@Reprompt TINYINT,
|
||||
@Key VARCHAR(MAX) = NULL
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
|
||||
DECLARE @UserIdKey VARCHAR(50) = CONCAT('"', @UserId, '"')
|
||||
DECLARE @UserIdPath VARCHAR(50) = CONCAT('$.', @UserIdKey)
|
||||
|
||||
INSERT INTO [dbo].[Cipher]
|
||||
(
|
||||
[Id],
|
||||
[UserId],
|
||||
[OrganizationId],
|
||||
[Type],
|
||||
[Data],
|
||||
[Favorites],
|
||||
[Folders],
|
||||
[CreationDate],
|
||||
[RevisionDate],
|
||||
[DeletedDate],
|
||||
[Reprompt],
|
||||
[Key]
|
||||
)
|
||||
VALUES
|
||||
(
|
||||
@Id,
|
||||
CASE WHEN @OrganizationId IS NULL THEN @UserId ELSE NULL END,
|
||||
@OrganizationId,
|
||||
@Type,
|
||||
@Data,
|
||||
CASE WHEN @Favorite = 1 THEN CONCAT('{', @UserIdKey, ':true}') ELSE NULL END,
|
||||
CASE WHEN @FolderId IS NOT NULL THEN CONCAT('{', @UserIdKey, ':"', @FolderId, '"', '}') ELSE NULL END,
|
||||
@CreationDate,
|
||||
@RevisionDate,
|
||||
@DeletedDate,
|
||||
@Reprompt,
|
||||
@Key
|
||||
)
|
||||
|
||||
IF @OrganizationId IS NOT NULL
|
||||
BEGIN
|
||||
EXEC [dbo].[User_BumpAccountRevisionDateByCipherId] @Id, @OrganizationId
|
||||
END
|
||||
ELSE IF @UserId IS NOT NULL
|
||||
BEGIN
|
||||
EXEC [dbo].[User_BumpAccountRevisionDate] @UserId
|
||||
END
|
||||
END
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE [dbo].[CipherDetails_Update]
|
||||
@Id UNIQUEIDENTIFIER,
|
||||
@UserId UNIQUEIDENTIFIER,
|
||||
@OrganizationId UNIQUEIDENTIFIER,
|
||||
@Type TINYINT,
|
||||
@Data NVARCHAR(MAX),
|
||||
@Favorites NVARCHAR(MAX), -- not used
|
||||
@Folders NVARCHAR(MAX), -- not used
|
||||
@Attachments NVARCHAR(MAX),
|
||||
@CreationDate DATETIME2(7),
|
||||
@RevisionDate DATETIME2(7),
|
||||
@FolderId UNIQUEIDENTIFIER,
|
||||
@Favorite BIT,
|
||||
@Edit BIT, -- not used
|
||||
@ViewPassword BIT, -- not used
|
||||
@OrganizationUseTotp BIT, -- not used
|
||||
@DeletedDate DATETIME2(2),
|
||||
@Reprompt TINYINT,
|
||||
@Key VARCHAR(MAX) = NULL
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
|
||||
DECLARE @UserIdKey VARCHAR(50) = CONCAT('"', @UserId, '"')
|
||||
DECLARE @UserIdPath VARCHAR(50) = CONCAT('$.', @UserIdKey)
|
||||
|
||||
UPDATE
|
||||
[dbo].[Cipher]
|
||||
SET
|
||||
[UserId] = CASE WHEN @OrganizationId IS NULL THEN @UserId ELSE NULL END,
|
||||
[OrganizationId] = @OrganizationId,
|
||||
[Type] = @Type,
|
||||
[Data] = @Data,
|
||||
[Folders] =
|
||||
CASE
|
||||
WHEN @FolderId IS NOT NULL AND [Folders] IS NULL THEN
|
||||
CONCAT('{', @UserIdKey, ':"', @FolderId, '"', '}')
|
||||
WHEN @FolderId IS NOT NULL THEN
|
||||
JSON_MODIFY([Folders], @UserIdPath, CAST(@FolderId AS VARCHAR(50)))
|
||||
ELSE
|
||||
JSON_MODIFY([Folders], @UserIdPath, NULL)
|
||||
END,
|
||||
[Favorites] =
|
||||
CASE
|
||||
WHEN @Favorite = 1 AND [Favorites] IS NULL THEN
|
||||
CONCAT('{', @UserIdKey, ':true}')
|
||||
WHEN @Favorite = 1 THEN
|
||||
JSON_MODIFY([Favorites], @UserIdPath, CAST(1 AS BIT))
|
||||
ELSE
|
||||
JSON_MODIFY([Favorites], @UserIdPath, NULL)
|
||||
END,
|
||||
[Attachments] = @Attachments,
|
||||
[Reprompt] = @Reprompt,
|
||||
[CreationDate] = @CreationDate,
|
||||
[RevisionDate] = @RevisionDate,
|
||||
[DeletedDate] = @DeletedDate,
|
||||
[Key] = @Key
|
||||
WHERE
|
||||
[Id] = @Id
|
||||
|
||||
IF @OrganizationId IS NOT NULL
|
||||
BEGIN
|
||||
EXEC [dbo].[User_BumpAccountRevisionDateByCipherId] @Id, @OrganizationId
|
||||
END
|
||||
ELSE IF @UserId IS NOT NULL
|
||||
BEGIN
|
||||
EXEC [dbo].[User_BumpAccountRevisionDate] @UserId
|
||||
END
|
||||
END
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE [dbo].[Cipher_Update]
|
||||
@Id UNIQUEIDENTIFIER,
|
||||
@UserId UNIQUEIDENTIFIER,
|
||||
@OrganizationId UNIQUEIDENTIFIER,
|
||||
@Type TINYINT,
|
||||
@Data NVARCHAR(MAX),
|
||||
@Favorites NVARCHAR(MAX),
|
||||
@Folders NVARCHAR(MAX),
|
||||
@Attachments NVARCHAR(MAX),
|
||||
@CreationDate DATETIME2(7),
|
||||
@RevisionDate DATETIME2(7),
|
||||
@DeletedDate DATETIME2(7),
|
||||
@Reprompt TINYINT,
|
||||
@Key VARCHAR(MAX) = NULL
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
|
||||
UPDATE
|
||||
[dbo].[Cipher]
|
||||
SET
|
||||
[UserId] = CASE WHEN @OrganizationId IS NULL THEN @UserId ELSE NULL END,
|
||||
[OrganizationId] = @OrganizationId,
|
||||
[Type] = @Type,
|
||||
[Data] = @Data,
|
||||
[Favorites] = @Favorites,
|
||||
[Folders] = @Folders,
|
||||
[Attachments] = @Attachments,
|
||||
[CreationDate] = @CreationDate,
|
||||
[RevisionDate] = @RevisionDate,
|
||||
[DeletedDate] = @DeletedDate,
|
||||
[Reprompt] = @Reprompt,
|
||||
[Key] = @Key
|
||||
WHERE
|
||||
[Id] = @Id
|
||||
|
||||
IF @OrganizationId IS NOT NULL
|
||||
BEGIN
|
||||
EXEC [dbo].[User_BumpAccountRevisionDateByCipherId] @Id, @OrganizationId
|
||||
END
|
||||
ELSE IF @UserId IS NOT NULL
|
||||
BEGIN
|
||||
EXEC [dbo].[User_BumpAccountRevisionDate] @UserId
|
||||
END
|
||||
END
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE [dbo].[Cipher_Create]
|
||||
@Id UNIQUEIDENTIFIER OUTPUT,
|
||||
@UserId UNIQUEIDENTIFIER,
|
||||
@OrganizationId UNIQUEIDENTIFIER,
|
||||
@Type TINYINT,
|
||||
@Data NVARCHAR(MAX),
|
||||
@Favorites NVARCHAR(MAX),
|
||||
@Folders NVARCHAR(MAX),
|
||||
@Attachments NVARCHAR(MAX),
|
||||
@CreationDate DATETIME2(7),
|
||||
@RevisionDate DATETIME2(7),
|
||||
@DeletedDate DATETIME2(7),
|
||||
@Reprompt TINYINT,
|
||||
@Key VARCHAR(MAX) = NULL
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
|
||||
INSERT INTO [dbo].[Cipher]
|
||||
(
|
||||
[Id],
|
||||
[UserId],
|
||||
[OrganizationId],
|
||||
[Type],
|
||||
[Data],
|
||||
[Favorites],
|
||||
[Folders],
|
||||
[Attachments],
|
||||
[CreationDate],
|
||||
[RevisionDate],
|
||||
[DeletedDate],
|
||||
[Reprompt],
|
||||
[Key]
|
||||
)
|
||||
VALUES
|
||||
(
|
||||
@Id,
|
||||
CASE WHEN @OrganizationId IS NULL THEN @UserId ELSE NULL END,
|
||||
@OrganizationId,
|
||||
@Type,
|
||||
@Data,
|
||||
@Favorites,
|
||||
@Folders,
|
||||
@Attachments,
|
||||
@CreationDate,
|
||||
@RevisionDate,
|
||||
@DeletedDate,
|
||||
@Reprompt,
|
||||
@Key
|
||||
)
|
||||
|
||||
IF @OrganizationId IS NOT NULL
|
||||
BEGIN
|
||||
EXEC [dbo].[User_BumpAccountRevisionDateByCipherId] @Id, @OrganizationId
|
||||
END
|
||||
ELSE IF @UserId IS NOT NULL
|
||||
BEGIN
|
||||
EXEC [dbo].[User_BumpAccountRevisionDate] @UserId
|
||||
END
|
||||
END
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE [dbo].[Cipher_CreateWithCollections]
|
||||
@Id UNIQUEIDENTIFIER,
|
||||
@UserId UNIQUEIDENTIFIER,
|
||||
@OrganizationId UNIQUEIDENTIFIER,
|
||||
@Type TINYINT,
|
||||
@Data NVARCHAR(MAX),
|
||||
@Favorites NVARCHAR(MAX),
|
||||
@Folders NVARCHAR(MAX),
|
||||
@Attachments NVARCHAR(MAX),
|
||||
@CreationDate DATETIME2(7),
|
||||
@RevisionDate DATETIME2(7),
|
||||
@DeletedDate DATETIME2(7),
|
||||
@Reprompt TINYINT,
|
||||
@Key VARCHAR(MAX) = NULL,
|
||||
@CollectionIds AS [dbo].[GuidIdArray] READONLY
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
|
||||
EXEC [dbo].[Cipher_Create] @Id, @UserId, @OrganizationId, @Type, @Data, @Favorites, @Folders,
|
||||
@Attachments, @CreationDate, @RevisionDate, @DeletedDate, @Reprompt, @Key
|
||||
|
||||
DECLARE @UpdateCollectionsSuccess INT
|
||||
EXEC @UpdateCollectionsSuccess = [dbo].[Cipher_UpdateCollections] @Id, @UserId, @OrganizationId, @CollectionIds
|
||||
END
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE [dbo].[Cipher_UpdateWithCollections]
|
||||
@Id UNIQUEIDENTIFIER,
|
||||
@UserId UNIQUEIDENTIFIER,
|
||||
@OrganizationId UNIQUEIDENTIFIER,
|
||||
@Type TINYINT,
|
||||
@Data NVARCHAR(MAX),
|
||||
@Favorites NVARCHAR(MAX),
|
||||
@Folders NVARCHAR(MAX),
|
||||
@Attachments NVARCHAR(MAX),
|
||||
@CreationDate DATETIME2(7),
|
||||
@RevisionDate DATETIME2(7),
|
||||
@DeletedDate DATETIME2(7),
|
||||
@Reprompt TINYINT,
|
||||
@Key VARCHAR(MAX) = NULL,
|
||||
@CollectionIds AS [dbo].[GuidIdArray] READONLY
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
|
||||
BEGIN TRANSACTION Cipher_UpdateWithCollections
|
||||
|
||||
DECLARE @UpdateCollectionsSuccess INT
|
||||
EXEC @UpdateCollectionsSuccess = [dbo].[Cipher_UpdateCollections] @Id, @UserId, @OrganizationId, @CollectionIds
|
||||
|
||||
IF @UpdateCollectionsSuccess < 0
|
||||
BEGIN
|
||||
COMMIT TRANSACTION Cipher_UpdateWithCollections
|
||||
SELECT -1 -- -1 = Failure
|
||||
RETURN
|
||||
END
|
||||
|
||||
UPDATE
|
||||
[dbo].[Cipher]
|
||||
SET
|
||||
[UserId] = NULL,
|
||||
[OrganizationId] = @OrganizationId,
|
||||
[Data] = @Data,
|
||||
[Attachments] = @Attachments,
|
||||
[RevisionDate] = @RevisionDate,
|
||||
[DeletedDate] = @DeletedDate,
|
||||
[Key] = @Key
|
||||
-- No need to update CreationDate, Favorites, Folders, or Type since that data will not change
|
||||
WHERE
|
||||
[Id] = @Id
|
||||
|
||||
COMMIT TRANSACTION Cipher_UpdateWithCollections
|
||||
|
||||
IF @Attachments IS NOT NULL
|
||||
BEGIN
|
||||
EXEC [dbo].[Organization_UpdateStorage] @OrganizationId
|
||||
EXEC [dbo].[User_UpdateStorage] @UserId
|
||||
END
|
||||
|
||||
EXEC [dbo].[User_BumpAccountRevisionDateByCipherId] @Id, @OrganizationId
|
||||
|
||||
SELECT 0 -- 0 = Success
|
||||
END
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE [dbo].[CipherDetails_CreateWithCollections]
|
||||
@Id UNIQUEIDENTIFIER,
|
||||
@UserId UNIQUEIDENTIFIER,
|
||||
@OrganizationId UNIQUEIDENTIFIER,
|
||||
@Type TINYINT,
|
||||
@Data NVARCHAR(MAX),
|
||||
@Favorites NVARCHAR(MAX), -- not used
|
||||
@Folders NVARCHAR(MAX), -- not used
|
||||
@Attachments NVARCHAR(MAX), -- not used
|
||||
@CreationDate DATETIME2(7),
|
||||
@RevisionDate DATETIME2(7),
|
||||
@FolderId UNIQUEIDENTIFIER,
|
||||
@Favorite BIT,
|
||||
@Edit BIT, -- not used
|
||||
@ViewPassword BIT, -- not used
|
||||
@OrganizationUseTotp BIT, -- not used
|
||||
@DeletedDate DATETIME2(7),
|
||||
@Reprompt TINYINT,
|
||||
@Key VARCHAR(MAX) = NULL,
|
||||
@CollectionIds AS [dbo].[GuidIdArray] READONLY
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
|
||||
EXEC [dbo].[CipherDetails_Create] @Id, @UserId, @OrganizationId, @Type, @Data, @Favorites, @Folders,
|
||||
@Attachments, @CreationDate, @RevisionDate, @FolderId, @Favorite, @Edit, @ViewPassword,
|
||||
@OrganizationUseTotp, @DeletedDate, @Reprompt, @Key
|
||||
|
||||
DECLARE @UpdateCollectionsSuccess INT
|
||||
EXEC @UpdateCollectionsSuccess = [dbo].[Cipher_UpdateCollections] @Id, @UserId, @OrganizationId, @CollectionIds
|
||||
END
|
||||
GO
|
@ -2,6 +2,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="DbScripts\**\*.sql" />
|
||||
<EmbeddedResource Include="DbScripts_data_migration\**\*.sql" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
8
util/Migrator/MigratorConstants.cs
Normal file
8
util/Migrator/MigratorConstants.cs
Normal file
@ -0,0 +1,8 @@
|
||||
namespace Bit.Migrator;
|
||||
|
||||
public static class MigratorConstants
|
||||
{
|
||||
public const string SqlTableJournalName = "Migration";
|
||||
public const string DefaultMigrationsFolderName = "DbScripts";
|
||||
public const string TransitionMigrationsFolderName = "DbScripts_data_migration";
|
||||
}
|
@ -70,7 +70,7 @@ public class SqlServerDbMigrator : IDbMigrator
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
var builder = DeployChanges.To
|
||||
.SqlDatabase(_connectionString)
|
||||
.JournalToSqlTable("dbo", "Migration")
|
||||
.JournalToSqlTable("dbo", MigratorConstants.SqlTableJournalName)
|
||||
.WithScriptsAndCodeEmbeddedInAssembly(Assembly.GetExecutingAssembly(),
|
||||
s => s.Contains($".DbScripts.") && !s.Contains(".Archive."))
|
||||
.WithTransaction()
|
||||
|
@ -10,6 +10,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CommandDotNet" Version="7.0.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="6.0.0" />
|
||||
</ItemGroup>
|
||||
|
@ -1,55 +1,51 @@
|
||||
using Bit.Migrator;
|
||||
using CommandDotNet;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
internal class Program
|
||||
{
|
||||
private static IDictionary<string, string> Parameters { get; set; }
|
||||
|
||||
private static int Main(string[] args)
|
||||
{
|
||||
if (args.Length == 0)
|
||||
{
|
||||
Console.WriteLine("Please enter a database connection string argument.");
|
||||
WriteUsageToConsole();
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (args.Length == 1 && (args[0] == "--verbose" || args[0] == "-v"))
|
||||
{
|
||||
Console.WriteLine($"Please enter a database connection string argument before {args[0]} option.");
|
||||
WriteUsageToConsole();
|
||||
return 1;
|
||||
}
|
||||
|
||||
var databaseConnectionString = args[0];
|
||||
|
||||
var verbose = false;
|
||||
|
||||
if (args.Length == 2 && (args[1] == "--verbose" || args[1] == "-v"))
|
||||
{
|
||||
verbose = true;
|
||||
}
|
||||
|
||||
var success = MigrateDatabase(databaseConnectionString, verbose);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return new AppRunner<Program>().Run(args);
|
||||
}
|
||||
|
||||
[DefaultCommand]
|
||||
public void Execute(
|
||||
[Operand(Description = "Database connection string")]
|
||||
string databaseConnectionString,
|
||||
[Option('v', "verbose", Description = "Enable verbose output of migrator logs")]
|
||||
bool verbose = false,
|
||||
[Option('r', "repeatable", Description = "Mark scripts as repeatable")]
|
||||
bool repeatable = false,
|
||||
[Option('f', "folder", Description = "Folder name of database scripts")]
|
||||
string folderName = MigratorConstants.DefaultMigrationsFolderName) => MigrateDatabase(databaseConnectionString, verbose, repeatable, folderName);
|
||||
|
||||
private static void WriteUsageToConsole()
|
||||
{
|
||||
Console.WriteLine("Usage: MsSqlMigratorUtility <database-connection-string>");
|
||||
Console.WriteLine("Usage: MsSqlMigratorUtility <database-connection-string> -v|--verbose (for verbose output of migrator logs)");
|
||||
Console.WriteLine("Usage: MsSqlMigratorUtility <database-connection-string> -r|--repeatable (for marking scripts as repeatable) -f|--folder <folder-name-in-migrator-project> (for specifying folder name of scripts)");
|
||||
Console.WriteLine("Usage: MsSqlMigratorUtility <database-connection-string> -v|--verbose (for verbose output of migrator logs) -r|--repeatable (for marking scripts as repeatable) -f|--folder <folder-name-in-migrator-project> (for specifying folder name of scripts)");
|
||||
}
|
||||
|
||||
private static bool MigrateDatabase(string databaseConnectionString, bool verbose = false, int attempt = 1)
|
||||
private static bool MigrateDatabase(string databaseConnectionString, bool verbose = false, bool repeatable = false, string folderName = "")
|
||||
{
|
||||
var logger = CreateLogger(verbose);
|
||||
|
||||
logger.LogInformation($"Migrating database with repeatable: {repeatable} and folderName: {folderName}.");
|
||||
|
||||
var migrator = new DbMigrator(databaseConnectionString, logger);
|
||||
var success = migrator.MigrateMsSqlDatabaseWithRetries(verbose);
|
||||
bool success = false;
|
||||
if (!string.IsNullOrWhiteSpace(folderName))
|
||||
{
|
||||
success = migrator.MigrateMsSqlDatabaseWithRetries(verbose, repeatable, folderName);
|
||||
}
|
||||
else
|
||||
{
|
||||
success = migrator.MigrateMsSqlDatabaseWithRetries(verbose, repeatable);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
@ -2,6 +2,15 @@
|
||||
"version": 1,
|
||||
"dependencies": {
|
||||
"net6.0": {
|
||||
"CommandDotNet": {
|
||||
"type": "Direct",
|
||||
"requested": "[7.0.2, )",
|
||||
"resolved": "7.0.2",
|
||||
"contentHash": "sFfqn4T6Ux4AbGnhS+BZvf9iVeP6b9p9bwaMT8nsefVcYH2tC9MHj3AoY+GG0nnBPmFawRqdgud08cBjBdPByQ==",
|
||||
"dependencies": {
|
||||
"Microsoft.CSharp": "4.7.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Logging": {
|
||||
"type": "Direct",
|
||||
"requested": "[6.0.0, )",
|
||||
|
2135
util/MySqlMigrations/Migrations/20230208212115_CipherKeyUpdate.Designer.cs
generated
Normal file
2135
util/MySqlMigrations/Migrations/20230208212115_CipherKeyUpdate.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,25 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Bit.MySqlMigrations.Migrations;
|
||||
|
||||
public partial class CipherKeyUpdate : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Key",
|
||||
table: "Cipher",
|
||||
type: "longtext",
|
||||
nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Key",
|
||||
table: "Cipher");
|
||||
}
|
||||
}
|
@ -1498,6 +1498,9 @@ namespace Bit.MySqlMigrations.Migrations
|
||||
|
||||
b.Property<string>("Folders")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Key")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<Guid?>("OrganizationId")
|
||||
.HasColumnType("char(36)");
|
||||
|
2146
util/PostgresMigrations/Migrations/20230208210621_CipherKeyUpdate.Designer.cs
generated
Normal file
2146
util/PostgresMigrations/Migrations/20230208210621_CipherKeyUpdate.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,24 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Bit.PostgresMigrations.Migrations;
|
||||
|
||||
public partial class CipherKeyUpdate : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Key",
|
||||
table: "Cipher",
|
||||
type: "text",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Key",
|
||||
table: "Cipher");
|
||||
}
|
||||
}
|
@ -1509,6 +1509,9 @@ namespace Bit.PostgresMigrations.Migrations
|
||||
|
||||
b.Property<string>("Folders")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Key")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<Guid?>("OrganizationId")
|
||||
.HasColumnType("uuid");
|
||||
|
@ -190,7 +190,12 @@ public class Program
|
||||
var vaultConnectionString = Helpers.GetValueFromEnvFile("global",
|
||||
"globalSettings__sqlServer__connectionString");
|
||||
var migrator = new DbMigrator(vaultConnectionString, null);
|
||||
migrator.MigrateMsSqlDatabaseWithRetries(false);
|
||||
|
||||
var log = false;
|
||||
|
||||
migrator.MigrateMsSqlDatabaseWithRetries(log);
|
||||
|
||||
migrator.MigrateMsSqlDatabaseWithRetries(log, true, MigratorConstants.TransitionMigrationsFolderName);
|
||||
}
|
||||
|
||||
private static bool ValidateInstallation()
|
||||
|
2133
util/SqliteMigrations/Migrations/20230208210629_CipherKeyUpdate.Designer.cs
generated
Normal file
2133
util/SqliteMigrations/Migrations/20230208210629_CipherKeyUpdate.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,24 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Bit.SqliteMigrations.Migrations;
|
||||
|
||||
public partial class CipherKeyUpdate : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Key",
|
||||
table: "Cipher",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Key",
|
||||
table: "Cipher");
|
||||
}
|
||||
}
|
@ -1496,6 +1496,9 @@ namespace Bit.SqliteMigrations.Migrations
|
||||
|
||||
b.Property<string>("Folders")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Key")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<Guid?>("OrganizationId")
|
||||
.HasColumnType("TEXT");
|
||||
|
Reference in New Issue
Block a user