diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c207620bae..bf9778651a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -263,7 +263,7 @@ jobs: - name: Scan Docker image id: container-scan - uses: anchore/scan-action@62b74fb7bb810d2c45b1865f47a77655621862a5 # v7.2.3 + uses: anchore/scan-action@0d444ed77d83ee2ba7f5ced0d90d640a1281d762 # v7.3.0 with: image: ${{ steps.image-tags.outputs.primary_tag }} fail-build: false diff --git a/Directory.Build.props b/Directory.Build.props index c4c7f342fa..d223304ada 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -13,6 +13,10 @@ true + + false + false + diff --git a/bitwarden_license/src/Scim/Scim.csproj b/bitwarden_license/src/Scim/Scim.csproj index 7d1ea317b2..d3858e1225 100644 --- a/bitwarden_license/src/Scim/Scim.csproj +++ b/bitwarden_license/src/Scim/Scim.csproj @@ -1,4 +1,5 @@  + bitwarden-Scim diff --git a/bitwarden_license/src/Sso/Sso.csproj b/bitwarden_license/src/Sso/Sso.csproj index 2a1c14ae5a..709e8c2c4a 100644 --- a/bitwarden_license/src/Sso/Sso.csproj +++ b/bitwarden_license/src/Sso/Sso.csproj @@ -1,4 +1,5 @@  + bitwarden-Sso diff --git a/global.json b/global.json index 4cbe3f083a..970250aec9 100644 --- a/global.json +++ b/global.json @@ -6,6 +6,6 @@ "msbuild-sdks": { "Microsoft.Build.Traversal": "4.1.0", "Microsoft.Build.Sql": "1.0.0", - "Bitwarden.Server.Sdk": "1.2.0" + "Bitwarden.Server.Sdk": "1.4.0" } } diff --git a/src/Admin/Admin.csproj b/src/Admin/Admin.csproj index b815ddea82..5733589466 100644 --- a/src/Admin/Admin.csproj +++ b/src/Admin/Admin.csproj @@ -1,4 +1,5 @@ + bitwarden-Admin diff --git a/src/Admin/Controllers/UsersController.cs b/src/Admin/Controllers/UsersController.cs index f42b22b098..eb7f1e8c04 100644 --- a/src/Admin/Controllers/UsersController.cs +++ b/src/Admin/Controllers/UsersController.cs @@ -86,7 +86,7 @@ public class UsersController : Controller return RedirectToAction("Index"); } - var ciphers = await _cipherRepository.GetManyByUserIdAsync(id); + var ciphers = await _cipherRepository.GetManyByUserIdAsync(id, withOrganizations: false); var isTwoFactorEnabled = await _twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(user); var verifiedDomain = await _userService.IsClaimedByAnyOrganizationAsync(user.Id); diff --git a/src/Admin/Program.cs b/src/Admin/Program.cs index 006a8223b2..80a1ae058c 100644 --- a/src/Admin/Program.cs +++ b/src/Admin/Program.cs @@ -8,7 +8,7 @@ public class Program { Host .CreateDefaultBuilder(args) - .ConfigureCustomAppConfiguration(args) + .UseBitwardenSdk() .ConfigureWebHostDefaults(webBuilder => { webBuilder.ConfigureKestrel(o => diff --git a/src/Api/Api.csproj b/src/Api/Api.csproj index d25b989d11..c4a286f33c 100644 --- a/src/Api/Api.csproj +++ b/src/Api/Api.csproj @@ -1,4 +1,6 @@  + + bitwarden-Api false diff --git a/src/Api/Program.cs b/src/Api/Program.cs index bf924af47f..baeaab9fdb 100644 --- a/src/Api/Program.cs +++ b/src/Api/Program.cs @@ -8,7 +8,7 @@ public class Program { Host .CreateDefaultBuilder(args) - .ConfigureCustomAppConfiguration(args) + .UseBitwardenSdk() .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); diff --git a/src/Api/Tools/Controllers/ImportCiphersController.cs b/src/Api/Tools/Controllers/ImportCiphersController.cs index 8b3ec5e26c..bebf7cbf29 100644 --- a/src/Api/Tools/Controllers/ImportCiphersController.cs +++ b/src/Api/Tools/Controllers/ImportCiphersController.cs @@ -74,11 +74,6 @@ public class ImportCiphersController : Controller throw new BadRequestException("You cannot import this much data at once."); } - if (model.Ciphers.Any(c => c.ArchivedDate.HasValue)) - { - throw new BadRequestException("You cannot import archived items into an organization."); - } - var orgId = new Guid(organizationId); var collections = model.Collections.Select(c => c.ToCollection(orgId)).ToList(); diff --git a/src/Core/Billing/Constants/StripeConstants.cs b/src/Core/Billing/Constants/StripeConstants.cs index e9c34d7e06..524a819b28 100644 --- a/src/Core/Billing/Constants/StripeConstants.cs +++ b/src/Core/Billing/Constants/StripeConstants.cs @@ -70,10 +70,6 @@ public static class StripeConstants public const string InvoiceApproved = "invoice_approved"; public const string OrganizationId = "organizationId"; public const string PayPalTransactionId = "btPayPalTransactionId"; - public const string PreviousAdditionalStorage = "previous_additional_storage"; - public const string PreviousPeriodEndDate = "previous_period_end_date"; - public const string PreviousPremiumPriceId = "previous_premium_price_id"; - public const string PreviousPremiumUserId = "previous_premium_user_id"; public const string ProviderId = "providerId"; public const string Region = "region"; public const string RetiredBraintreeCustomerId = "btCustomerId_old"; diff --git a/src/Core/Billing/Premium/Commands/UpgradePremiumToOrganizationCommand.cs b/src/Core/Billing/Premium/Commands/UpgradePremiumToOrganizationCommand.cs index 81bc5c9e2c..9e573fac53 100644 --- a/src/Core/Billing/Premium/Commands/UpgradePremiumToOrganizationCommand.cs +++ b/src/Core/Billing/Premium/Commands/UpgradePremiumToOrganizationCommand.cs @@ -2,7 +2,6 @@ using Bit.Core.Billing.Commands; using Bit.Core.Billing.Constants; using Bit.Core.Billing.Enums; -using Bit.Core.Billing.Extensions; using Bit.Core.Billing.Pricing; using Bit.Core.Billing.Services; using Bit.Core.Entities; @@ -96,9 +95,6 @@ public class UpgradePremiumToOrganizationCommand( var storageItem = currentSubscription.Items.Data.FirstOrDefault(i => i.Price.Id == usersPremiumPlan.Storage.StripePriceId); - // Capture the previous additional storage quantity for potential revert - var previousAdditionalStorage = storageItem?.Quantity ?? 0; - if (storageItem != null) { subscriptionItemOptions.Add(new SubscriptionItemOptions @@ -133,14 +129,10 @@ public class UpgradePremiumToOrganizationCommand( var subscriptionUpdateOptions = new SubscriptionUpdateOptions { Items = subscriptionItemOptions, - ProrationBehavior = StripeConstants.ProrationBehavior.None, + ProrationBehavior = StripeConstants.ProrationBehavior.CreateProrations, Metadata = new Dictionary { [StripeConstants.MetadataKeys.OrganizationId] = organizationId.ToString(), - [StripeConstants.MetadataKeys.PreviousPremiumPriceId] = usersPremiumPlan.Seat.StripePriceId, - [StripeConstants.MetadataKeys.PreviousPeriodEndDate] = currentSubscription.GetCurrentPeriodEnd()?.ToString("O") ?? string.Empty, - [StripeConstants.MetadataKeys.PreviousAdditionalStorage] = previousAdditionalStorage.ToString(), - [StripeConstants.MetadataKeys.PreviousPremiumUserId] = user.Id.ToString(), [StripeConstants.MetadataKeys.UserId] = string.Empty // Remove userId to unlink subscription from User } }; diff --git a/src/Core/Constants.cs b/src/Core/Constants.cs index 499254bc31..8fb213245a 100644 --- a/src/Core/Constants.cs +++ b/src/Core/Constants.cs @@ -160,7 +160,6 @@ public static class FeatureFlagKeys public const string Otp6Digits = "pm-18612-otp-6-digits"; public const string PM24579_PreventSsoOnExistingNonCompliantUsers = "pm-24579-prevent-sso-on-existing-non-compliant-users"; public const string DisableAlternateLoginMethods = "pm-22110-disable-alternate-login-methods"; - public const string MJMLBasedEmailTemplates = "mjml-based-email-templates"; public const string PM2035PasskeyUnlock = "pm-2035-passkey-unlock"; public const string MjmlWelcomeEmailTemplates = "pm-21741-mjml-welcome-email"; public const string OrganizationConfirmationEmail = "pm-28402-update-confirmed-to-org-email-template"; diff --git a/src/Core/MailTemplates/Handlebars/Auth/SendAccessEmailOtpEmail.html.hbs b/src/Core/MailTemplates/Handlebars/Auth/SendAccessEmailOtpEmail.html.hbs index 5bf1f24218..7d30fdcbe4 100644 --- a/src/Core/MailTemplates/Handlebars/Auth/SendAccessEmailOtpEmail.html.hbs +++ b/src/Core/MailTemplates/Handlebars/Auth/SendAccessEmailOtpEmail.html.hbs @@ -1,28 +1,691 @@ -{{#>FullHtmlLayout}} - - - - - - - - - - - - - -
- Verify your email to access this Bitwarden Send. -
-
- Your verification code is: {{Token}} -
-
- This code can only be used once and expires in 5 minutes. After that you'll need to verify your email again. -
-
-
- {{TheDate}} at {{TheTime}} {{TimeZone}} -
-{{/FullHtmlLayout}} \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+ + + + + + + +
+ + + + + + + + +
+ + + + + +
+ + + + + + + +
+ + +
+ + + + + + + + + + + + + +
+ + + + + + + +
+ + + +
+ +
+ +

+ Verify your email to access this Bitwarden Send +

+ +
+ +
+ + + +
+ + + + + + + + + +
+ + + + + + + +
+ + + +
+ +
+ +
+ + +
+ +
+ + + + + +
+ + +
+ +
+ + + + + + + + + +
+ + + + + + + +
+ + + +
+ + + + + + + +
+ + +
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
+ +
Your verification code is:
+ +
+ +
{{Token}}
+ +
+ +
+ +
+ +
This code expires in {{Expiry}} minutes. After that, you'll need + to verify your email again.
+ +
+ +
+ +
+ + +
+ +
+ + + + + +
+ + + + + + + +
+ + +
+ + + + + + + +
+ + + + + + + + + +
+ +

+ Bitwarden Send transmits sensitive, temporary information to + others easily and securely. Learn more about + Bitwarden Send + or + sign up + to try it today. +

+ +
+ +
+ +
+ + +
+ +
+ + + +
+ +
+ + + + + + + + + +
+ + + + + + + +
+ + + +
+ + + + + + + +
+ + +
+ + + + + + + + + +
+ +

+ Learn more about Bitwarden +

+ Find user guides, product documentation, and videos on the + Bitwarden Help Center.
+ +
+ +
+ + + +
+ + + + + + + + + +
+ +
+ + +
+ +
+ + + +
+ +
+ + + + + + + + + +
+ + + + + + + +
+ + +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + +
+ + + + + + +
+ + + +
+
+ + + + + + + + + + +
+ + + + + + +
+ + + +
+
+ + + + + + + + + + +
+ + + + + + +
+ + + +
+
+ + + + + + + + + + +
+ + + + + + +
+ + + +
+
+ + + + + + + + + + +
+ + + + + + +
+ + + +
+
+ + + + + + + + + + +
+ + + + + + +
+ + + +
+
+ + + + + + + + + + +
+ + + + + + +
+ + + +
+
+ + + +
+ +

+ © 2025 Bitwarden Inc. 1 N. Calle Cesar Chavez, Suite 102, Santa + Barbara, CA, USA +

+

+ Always confirm you are on a trusted Bitwarden domain before logging + in:
+ bitwarden.com | + Learn why we include this +

+ +
+ +
+ + +
+ +
+ + + + + +
+ + + + \ No newline at end of file diff --git a/src/Core/MailTemplates/Handlebars/Auth/SendAccessEmailOtpEmail.text.hbs b/src/Core/MailTemplates/Handlebars/Auth/SendAccessEmailOtpEmail.text.hbs index f83008c30b..7c9c1db527 100644 --- a/src/Core/MailTemplates/Handlebars/Auth/SendAccessEmailOtpEmail.text.hbs +++ b/src/Core/MailTemplates/Handlebars/Auth/SendAccessEmailOtpEmail.text.hbs @@ -3,7 +3,7 @@ Verify your email to access this Bitwarden Send. Your verification code is: {{Token}} -This code can only be used once and expires in 5 minutes. After that you'll need to verify your email again. +This code can only be used once and expires in {{Expiry}} minutes. After that you'll need to verify your email again. -Date : {{TheDate}} at {{TheTime}} {{TimeZone}} -{{/BasicTextLayout}} \ No newline at end of file +Bitwarden Send transmits sensitive, temporary information to others easily and securely. Learn more about Bitwarden Send or sign up to try it today. +{{/BasicTextLayout}} diff --git a/src/Core/MailTemplates/Handlebars/Auth/SendAccessEmailOtpEmailv2.html.hbs b/src/Core/MailTemplates/Handlebars/Auth/SendAccessEmailOtpEmailv2.html.hbs deleted file mode 100644 index 7d30fdcbe4..0000000000 --- a/src/Core/MailTemplates/Handlebars/Auth/SendAccessEmailOtpEmailv2.html.hbs +++ /dev/null @@ -1,691 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - -
- - - - - - - -
- - - - - - - - -
- - - - - -
- - - - - - - -
- - -
- - - - - - - - - - - - - -
- - - - - - - -
- - - -
- -
- -

- Verify your email to access this Bitwarden Send -

- -
- -
- - - -
- - - - - - - - - -
- - - - - - - -
- - - -
- -
- -
- - -
- -
- - - - - -
- - -
- -
- - - - - - - - - -
- - - - - - - -
- - - -
- - - - - - - -
- - -
- - - - - - - -
- - - - - - - - - - - - - - - - - - - - - -
- -
Your verification code is:
- -
- -
{{Token}}
- -
- -
- -
- -
This code expires in {{Expiry}} minutes. After that, you'll need - to verify your email again.
- -
- -
- -
- - -
- -
- - - - - -
- - - - - - - -
- - -
- - - - - - - -
- - - - - - - - - -
- -

- Bitwarden Send transmits sensitive, temporary information to - others easily and securely. Learn more about - Bitwarden Send - or - sign up - to try it today. -

- -
- -
- -
- - -
- -
- - - -
- -
- - - - - - - - - -
- - - - - - - -
- - - -
- - - - - - - -
- - -
- - - - - - - - - -
- -

- Learn more about Bitwarden -

- Find user guides, product documentation, and videos on the - Bitwarden Help Center.
- -
- -
- - - -
- - - - - - - - - -
- -
- - -
- -
- - - -
- -
- - - - - - - - - -
- - - - - - - -
- - -
- - - - - - - - - - - - - -
- - - - - - - - - - - - -
- - - - - - -
- - - -
-
- - - - - - - - - - -
- - - - - - -
- - - -
-
- - - - - - - - - - -
- - - - - - -
- - - -
-
- - - - - - - - - - -
- - - - - - -
- - - -
-
- - - - - - - - - - -
- - - - - - -
- - - -
-
- - - - - - - - - - -
- - - - - - -
- - - -
-
- - - - - - - - - - -
- - - - - - -
- - - -
-
- - - -
- -

- © 2025 Bitwarden Inc. 1 N. Calle Cesar Chavez, Suite 102, Santa - Barbara, CA, USA -

-

- Always confirm you are on a trusted Bitwarden domain before logging - in:
- bitwarden.com | - Learn why we include this -

- -
- -
- - -
- -
- - - - - -
- - - - \ No newline at end of file diff --git a/src/Core/MailTemplates/Handlebars/Auth/SendAccessEmailOtpEmailv2.text.hbs b/src/Core/MailTemplates/Handlebars/Auth/SendAccessEmailOtpEmailv2.text.hbs deleted file mode 100644 index 7c9c1db527..0000000000 --- a/src/Core/MailTemplates/Handlebars/Auth/SendAccessEmailOtpEmailv2.text.hbs +++ /dev/null @@ -1,9 +0,0 @@ -{{#>BasicTextLayout}} -Verify your email to access this Bitwarden Send. - -Your verification code is: {{Token}} - -This code can only be used once and expires in {{Expiry}} minutes. After that you'll need to verify your email again. - -Bitwarden Send transmits sensitive, temporary information to others easily and securely. Learn more about Bitwarden Send or sign up to try it today. -{{/BasicTextLayout}} diff --git a/src/Core/Platform/Mail/HandlebarsMailService.cs b/src/Core/Platform/Mail/HandlebarsMailService.cs index e92fa34daa..910285244d 100644 --- a/src/Core/Platform/Mail/HandlebarsMailService.cs +++ b/src/Core/Platform/Mail/HandlebarsMailService.cs @@ -209,26 +209,6 @@ public class HandlebarsMailService : IMailService } public async Task SendSendEmailOtpEmailAsync(string email, string token, string subject) - { - var message = CreateDefaultMessage(subject, email); - var requestDateTime = DateTime.UtcNow; - var model = new DefaultEmailOtpViewModel - { - Token = token, - TheDate = requestDateTime.ToLongDateString(), - TheTime = requestDateTime.ToShortTimeString(), - TimeZone = _utcTimeZoneDisplay, - WebVaultUrl = _globalSettings.BaseServiceUri.VaultWithHash, - SiteName = _globalSettings.SiteName, - }; - await AddMessageContentAsync(message, "Auth.SendAccessEmailOtpEmail", model); - message.MetaData.Add("SendGridBypassListManagement", true); - // TODO - PM-25380 change to string constant - message.Category = "SendEmailOtp"; - await _mailDeliveryService.SendEmailAsync(message); - } - - public async Task SendSendEmailOtpEmailv2Async(string email, string token, string subject) { var message = CreateDefaultMessage(subject, email); var requestDateTime = DateTime.UtcNow; @@ -242,7 +222,7 @@ public class HandlebarsMailService : IMailService WebVaultUrl = _globalSettings.BaseServiceUri.VaultWithHash, SiteName = _globalSettings.SiteName, }; - await AddMessageContentAsync(message, "Auth.SendAccessEmailOtpEmailv2", model); + await AddMessageContentAsync(message, "Auth.SendAccessEmailOtpEmail", model); message.MetaData.Add("SendGridBypassListManagement", true); // TODO - PM-25380 change to string constant message.Category = "SendEmailOtp"; diff --git a/src/Core/Platform/Mail/IMailService.cs b/src/Core/Platform/Mail/IMailService.cs index e21e1a010f..e07e4bad29 100644 --- a/src/Core/Platform/Mail/IMailService.cs +++ b/src/Core/Platform/Mail/IMailService.cs @@ -51,17 +51,15 @@ public interface IMailService Task SendChangeEmailAlreadyExistsEmailAsync(string fromEmail, string toEmail); Task SendChangeEmailEmailAsync(string newEmailAddress, string token); Task SendTwoFactorEmailAsync(string email, string accountEmail, string token, string deviceIp, string deviceType, TwoFactorEmailPurpose purpose); - Task SendSendEmailOtpEmailAsync(string email, string token, string subject); /// - /// has a default expiry of 5 minutes so we set the expiry to that value int he view model. + /// has a default expiry of 5 minutes so we set the expiry to that value in the view model. /// Sends OTP code token to the specified email address. - /// will replace when MJML templates are fully accepted. /// /// Email address to send the OTP to /// Otp code token - /// subject line of the email + /// Subject line of the email /// Task - Task SendSendEmailOtpEmailv2Async(string email, string token, string subject); + Task SendSendEmailOtpEmailAsync(string email, string token, string subject); Task SendFailedTwoFactorAttemptEmailAsync(string email, TwoFactorProviderType type, DateTime utcNow, string ip); Task SendNoMasterPasswordHintEmailAsync(string email); Task SendMasterPasswordHintEmailAsync(string email, string hint); diff --git a/src/Core/Platform/Mail/NoopMailService.cs b/src/Core/Platform/Mail/NoopMailService.cs index 7de48e4619..0064058afb 100644 --- a/src/Core/Platform/Mail/NoopMailService.cs +++ b/src/Core/Platform/Mail/NoopMailService.cs @@ -99,11 +99,6 @@ public class NoopMailService : IMailService return Task.FromResult(0); } - public Task SendSendEmailOtpEmailv2Async(string email, string token, string subject) - { - return Task.FromResult(0); - } - public Task SendFailedTwoFactorAttemptEmailAsync(string email, TwoFactorProviderType failedType, DateTime utcNow, string ip) { return Task.FromResult(0); diff --git a/src/Core/Tools/ImportFeatures/ImportCiphersCommand.cs b/src/Core/Tools/ImportFeatures/ImportCiphersCommand.cs index 9300e3c4bb..3f856e96fc 100644 --- a/src/Core/Tools/ImportFeatures/ImportCiphersCommand.cs +++ b/src/Core/Tools/ImportFeatures/ImportCiphersCommand.cs @@ -76,6 +76,12 @@ public class ImportCiphersCommand : IImportCiphersCommand { cipher.Favorites = $"{{\"{cipher.UserId.ToString().ToUpperInvariant()}\":true}}"; } + + if (cipher.UserId.HasValue && cipher.ArchivedDate.HasValue) + { + cipher.Archives = $"{{\"{cipher.UserId.Value.ToString().ToUpperInvariant()}\":\"" + + $"{cipher.ArchivedDate.Value:yyyy-MM-ddTHH:mm:ss.fffffffZ}\"}}"; + } } var userfoldersIds = (await _folderRepository.GetManyByUserIdAsync(importingUserId)).Select(f => f.Id).ToList(); @@ -135,10 +141,16 @@ public class ImportCiphersCommand : IImportCiphersCommand } } - // Init. ids for ciphers foreach (var cipher in ciphers) { + // Init. ids for ciphers cipher.SetNewId(); + + if (cipher.ArchivedDate.HasValue) + { + cipher.Archives = $"{{\"{importingUserId.ToString().ToUpperInvariant()}\":\"" + + $"{cipher.ArchivedDate.Value:yyyy-MM-ddTHH:mm:ss.fffffffZ}\"}}"; + } } var organizationCollectionsIds = (await _collectionRepository.GetManyByOrganizationIdAsync(org.Id)).Select(c => c.Id).ToList(); diff --git a/src/Events/Events.csproj b/src/Events/Events.csproj index dcd66892ed..dc1df1d587 100644 --- a/src/Events/Events.csproj +++ b/src/Events/Events.csproj @@ -1,4 +1,5 @@  + bitwarden-Events diff --git a/src/Events/Program.cs b/src/Events/Program.cs index 1a00549005..78a3cfcdc0 100644 --- a/src/Events/Program.cs +++ b/src/Events/Program.cs @@ -8,7 +8,7 @@ public class Program { Host .CreateDefaultBuilder(args) - .ConfigureCustomAppConfiguration(args) + .UseBitwardenSdk() .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); diff --git a/src/EventsProcessor/EventsProcessor.csproj b/src/EventsProcessor/EventsProcessor.csproj index 2f1aeaef54..9c128aa606 100644 --- a/src/EventsProcessor/EventsProcessor.csproj +++ b/src/EventsProcessor/EventsProcessor.csproj @@ -1,4 +1,5 @@  + bitwarden-EventsProcessor diff --git a/src/Icons/Icons.csproj b/src/Icons/Icons.csproj index 9dc39eab1e..adc1f6d557 100644 --- a/src/Icons/Icons.csproj +++ b/src/Icons/Icons.csproj @@ -1,4 +1,5 @@  + bitwarden-Icons diff --git a/src/Icons/Program.cs b/src/Icons/Program.cs index 80c1b5728e..5ca5723a52 100644 --- a/src/Icons/Program.cs +++ b/src/Icons/Program.cs @@ -8,6 +8,7 @@ public class Program { Host .CreateDefaultBuilder(args) + .UseBitwardenSdk() .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); diff --git a/src/Identity/Identity.csproj b/src/Identity/Identity.csproj index db49f8c856..f31d8c005e 100644 --- a/src/Identity/Identity.csproj +++ b/src/Identity/Identity.csproj @@ -1,4 +1,5 @@  + bitwarden-Identity diff --git a/src/Identity/IdentityServer/RequestValidators/SendAccess/SendEmailOtpRequestValidator.cs b/src/Identity/IdentityServer/RequestValidators/SendAccess/SendEmailOtpRequestValidator.cs index f20fdb6f07..bd2b4f91b4 100644 --- a/src/Identity/IdentityServer/RequestValidators/SendAccess/SendEmailOtpRequestValidator.cs +++ b/src/Identity/IdentityServer/RequestValidators/SendAccess/SendEmailOtpRequestValidator.cs @@ -1,7 +1,6 @@ using System.Security.Claims; using System.Security.Cryptography; using System.Text; -using Bit.Core; using Bit.Core.Auth.Identity; using Bit.Core.Auth.Identity.TokenProviders; using Bit.Core.Services; @@ -13,7 +12,6 @@ using Duende.IdentityServer.Validation; namespace Bit.Identity.IdentityServer.RequestValidators.SendAccess; public class SendEmailOtpRequestValidator( - IFeatureService featureService, IOtpTokenProvider otpTokenProvider, IMailService mailService) : ISendAuthenticationMethodValidator { @@ -66,20 +64,12 @@ public class SendEmailOtpRequestValidator( { return BuildErrorResult(SendAccessConstants.EmailOtpValidatorResults.OtpGenerationFailed); } - if (featureService.IsEnabled(FeatureFlagKeys.MJMLBasedEmailTemplates)) - { - await mailService.SendSendEmailOtpEmailv2Async( - email, - token, - string.Format(SendAccessConstants.OtpEmail.Subject, token)); - } - else - { - await mailService.SendSendEmailOtpEmailAsync( - email, - token, - string.Format(SendAccessConstants.OtpEmail.Subject, token)); - } + + await mailService.SendSendEmailOtpEmailAsync( + email, + token, + string.Format(SendAccessConstants.OtpEmail.Subject, token)); + return BuildErrorResult(SendAccessConstants.EmailOtpValidatorResults.EmailOtpSent); } diff --git a/src/Identity/Program.cs b/src/Identity/Program.cs index 238ad8ce3a..ae284c86f2 100644 --- a/src/Identity/Program.cs +++ b/src/Identity/Program.cs @@ -15,7 +15,7 @@ public class Program { return Host .CreateDefaultBuilder(args) - .ConfigureCustomAppConfiguration(args) + .UseBitwardenSdk() .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); diff --git a/src/Infrastructure.Dapper/Vault/Repositories/CipherRepository.cs b/src/Infrastructure.Dapper/Vault/Repositories/CipherRepository.cs index 48232ef484..ecf6d8e4e7 100644 --- a/src/Infrastructure.Dapper/Vault/Repositories/CipherRepository.cs +++ b/src/Infrastructure.Dapper/Vault/Repositories/CipherRepository.cs @@ -248,7 +248,7 @@ public class CipherRepository : Repository, ICipherRepository new { Ids = ids.ToGuidIdArrayTVP(), UserId = userId }, commandType: CommandType.StoredProcedure); - return results; + return DateTime.SpecifyKind(results, DateTimeKind.Utc); } } @@ -595,7 +595,7 @@ public class CipherRepository : Repository, ICipherRepository new { Ids = ids.ToGuidIdArrayTVP(), UserId = userId }, commandType: CommandType.StoredProcedure); - return results; + return DateTime.SpecifyKind(results, DateTimeKind.Utc); } } @@ -608,7 +608,7 @@ public class CipherRepository : Repository, ICipherRepository new { Ids = ids.ToGuidIdArrayTVP(), UserId = userId }, commandType: CommandType.StoredProcedure); - return results; + return DateTime.SpecifyKind(results, DateTimeKind.Utc); } } @@ -621,7 +621,7 @@ public class CipherRepository : Repository, ICipherRepository new { Ids = ids.ToGuidIdArrayTVP(), OrganizationId = organizationId }, commandType: CommandType.StoredProcedure); - return results; + return DateTime.SpecifyKind(results, DateTimeKind.Utc); } } diff --git a/src/Notifications/Notifications.csproj b/src/Notifications/Notifications.csproj index 4d19f7faf9..76278fdea8 100644 --- a/src/Notifications/Notifications.csproj +++ b/src/Notifications/Notifications.csproj @@ -1,4 +1,5 @@  + bitwarden-Notifications diff --git a/src/Notifications/Program.cs b/src/Notifications/Program.cs index 2792391729..ec7ea67fda 100644 --- a/src/Notifications/Program.cs +++ b/src/Notifications/Program.cs @@ -8,7 +8,7 @@ public class Program { Host .CreateDefaultBuilder(args) - .ConfigureCustomAppConfiguration(args) + .UseBitwardenSdk() .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); diff --git a/test/Api.Test/Tools/Controllers/ImportCiphersControllerTests.cs b/test/Api.Test/Tools/Controllers/ImportCiphersControllerTests.cs index 9ca641a28e..a8465ed0f6 100644 --- a/test/Api.Test/Tools/Controllers/ImportCiphersControllerTests.cs +++ b/test/Api.Test/Tools/Controllers/ImportCiphersControllerTests.cs @@ -806,63 +806,6 @@ public class ImportCiphersControllerTests Arg.Any()); } - [Theory, BitAutoData] - public async Task PostImportOrganization_ThrowsException_WhenAnyCipherIsArchived( - SutProvider sutProvider, - IFixture fixture, - User user - ) - { - var orgId = Guid.NewGuid(); - - sutProvider.GetDependency() - .SelfHosted = false; - sutProvider.GetDependency() - .ImportCiphersLimitation = _organizationCiphersLimitations; - - SetupUserService(sutProvider, user); - - var ciphers = fixture.Build() - .With(_ => _.ArchivedDate, DateTime.UtcNow) - .CreateMany(2).ToArray(); - - var request = new ImportOrganizationCiphersRequestModel - { - Collections = new List().ToArray(), - Ciphers = ciphers, - CollectionRelationships = new List>().ToArray(), - }; - - sutProvider.GetDependency() - .AccessImportExport(Arg.Any()) - .Returns(false); - - sutProvider.GetDependency() - .AuthorizeAsync(Arg.Any(), - Arg.Any>(), - Arg.Is>(reqs => - reqs.Contains(BulkCollectionOperations.ImportCiphers))) - .Returns(AuthorizationResult.Failed()); - - sutProvider.GetDependency() - .AuthorizeAsync(Arg.Any(), - Arg.Any>(), - Arg.Is>(reqs => - reqs.Contains(BulkCollectionOperations.Create))) - .Returns(AuthorizationResult.Success()); - - sutProvider.GetDependency() - .GetManyByOrganizationIdAsync(orgId) - .Returns(new List()); - - var exception = await Assert.ThrowsAsync(async () => - { - await sutProvider.Sut.PostImportOrganization(orgId.ToString(), request); - }); - - Assert.Equal("You cannot import archived items into an organization.", exception.Message); - } - private static void SetupUserService(SutProvider sutProvider, User user) { // This is a workaround for the NSubstitute issue with ambiguous arguments diff --git a/test/Billing.Test/Services/SubscriptionUpdatedHandlerTests.cs b/test/Billing.Test/Services/SubscriptionUpdatedHandlerTests.cs index 2259d846b7..0e1ce8e1f0 100644 --- a/test/Billing.Test/Services/SubscriptionUpdatedHandlerTests.cs +++ b/test/Billing.Test/Services/SubscriptionUpdatedHandlerTests.cs @@ -712,15 +712,15 @@ public class SubscriptionUpdatedHandlerTests Data = [ new SubscriptionItem - { - CurrentPeriodEnd = DateTime.UtcNow.AddDays(10), - Plan = new Plan { Id = "2023-enterprise-org-seat-annually" } - }, - new SubscriptionItem - { - CurrentPeriodEnd = DateTime.UtcNow.AddDays(10), - Plan = new Plan { Id = "secrets-manager-enterprise-seat-annually" } - } + { + CurrentPeriodEnd = DateTime.UtcNow.AddDays(10), + Plan = new Plan { Id = "2023-enterprise-org-seat-annually" } + }, + new SubscriptionItem + { + CurrentPeriodEnd = DateTime.UtcNow.AddDays(10), + Plan = new Plan { Id = "secrets-manager-enterprise-seat-annually" } + } ] }, Customer = new Customer @@ -760,7 +760,7 @@ public class SubscriptionUpdatedHandlerTests Data = [ new SubscriptionItem { Plan = new Stripe.Plan { Id = "secrets-manager-teams-seat-annually" } }, - ] + ] } }) } diff --git a/test/Core.Test/Billing/Premium/Commands/UpgradePremiumToOrganizationCommandTests.cs b/test/Core.Test/Billing/Premium/Commands/UpgradePremiumToOrganizationCommandTests.cs index e686d04009..ea07c7f81d 100644 --- a/test/Core.Test/Billing/Premium/Commands/UpgradePremiumToOrganizationCommandTests.cs +++ b/test/Core.Test/Billing/Premium/Commands/UpgradePremiumToOrganizationCommandTests.cs @@ -392,11 +392,6 @@ public class UpgradePremiumToOrganizationCommandTests "sub_123", Arg.Is(opts => opts.Metadata.ContainsKey(StripeConstants.MetadataKeys.OrganizationId) && - opts.Metadata.ContainsKey(StripeConstants.MetadataKeys.PreviousPremiumPriceId) && - opts.Metadata[StripeConstants.MetadataKeys.PreviousPremiumPriceId] == "premium-annually" && - opts.Metadata.ContainsKey(StripeConstants.MetadataKeys.PreviousPeriodEndDate) && - opts.Metadata.ContainsKey(StripeConstants.MetadataKeys.PreviousAdditionalStorage) && - opts.Metadata[StripeConstants.MetadataKeys.PreviousAdditionalStorage] == "0" && opts.Metadata.ContainsKey(StripeConstants.MetadataKeys.UserId) && opts.Metadata[StripeConstants.MetadataKeys.UserId] == string.Empty)); // Removes userId to unlink from User } @@ -598,8 +593,6 @@ public class UpgradePremiumToOrganizationCommandTests await _stripeAdapter.Received(1).UpdateSubscriptionAsync( "sub_123", Arg.Is(opts => - opts.Metadata.ContainsKey(StripeConstants.MetadataKeys.PreviousAdditionalStorage) && - opts.Metadata[StripeConstants.MetadataKeys.PreviousAdditionalStorage] == "5" && opts.Items.Count == 3 && // 2 deleted (premium + storage) + 1 new seat opts.Items.Count(i => i.Deleted == true) == 2)); } diff --git a/test/Core.Test/Services/HandlebarsMailServiceTests.cs b/test/Core.Test/Services/HandlebarsMailServiceTests.cs index b98c4580f5..4ff0868c7e 100644 --- a/test/Core.Test/Services/HandlebarsMailServiceTests.cs +++ b/test/Core.Test/Services/HandlebarsMailServiceTests.cs @@ -254,21 +254,6 @@ public class HandlebarsMailServiceTests } } - [Fact] - public async Task SendSendEmailOtpEmailAsync_SendsEmail() - { - // Arrange - var email = "test@example.com"; - var token = "aToken"; - var subject = string.Format("Your Bitwarden Send verification code is {0}", token); - - // Act - await _sut.SendSendEmailOtpEmailAsync(email, token, subject); - - // Assert - await _mailDeliveryService.Received(1).SendEmailAsync(Arg.Any()); - } - [Fact] public async Task SendIndividualUserWelcomeEmailAsync_SendsCorrectEmail() { diff --git a/test/Core.Test/Tools/ImportFeatures/ImportCiphersAsyncCommandTests.cs b/test/Core.Test/Tools/ImportFeatures/ImportCiphersAsyncCommandTests.cs index aea06f39a8..f6b1bd200a 100644 --- a/test/Core.Test/Tools/ImportFeatures/ImportCiphersAsyncCommandTests.cs +++ b/test/Core.Test/Tools/ImportFeatures/ImportCiphersAsyncCommandTests.cs @@ -326,4 +326,101 @@ public class ImportCiphersAsyncCommandTests await sutProvider.GetDependency().Received(1).PushSyncVaultAsync(importingUserId); } + + [Theory, BitAutoData] + public async Task ImportIntoIndividualVaultAsync_WithArchivedCiphers_PreservesArchiveStatus( + Guid importingUserId, + List ciphers, + SutProvider sutProvider) + { + var archivedDate = DateTime.UtcNow.AddDays(-1); + ciphers[0].UserId = importingUserId; + ciphers[0].ArchivedDate = archivedDate; + + sutProvider.GetDependency() + .AnyPoliciesApplicableToUserAsync(importingUserId, PolicyType.OrganizationDataOwnership) + .Returns(false); + + sutProvider.GetDependency() + .GetManyByUserIdAsync(importingUserId) + .Returns(new List()); + + var folders = new List(); + var folderRelationships = new List>(); + + await sutProvider.Sut.ImportIntoIndividualVaultAsync(folders, ciphers, folderRelationships, importingUserId); + + await sutProvider.GetDependency() + .Received(1) + .CreateAsync(importingUserId, + Arg.Is>(c => + c[0].Archives != null && + c[0].Archives.Contains(importingUserId.ToString().ToUpperInvariant()) && + c[0].Archives.Contains(archivedDate.ToString("yyyy-MM-ddTHH:mm:ss.fffffffZ"))), + Arg.Any>()); + } + + /* + * Archive functionality is a per-user function. When importing archived ciphers into an organization vault, + * the Archives field should be set for the importing user only. This allows the importing user to see + * items as archived, while other organization members will not see them as archived. + */ + [Theory, BitAutoData] + public async Task ImportIntoOrganizationalVaultAsync_WithArchivedCiphers_SetsArchivesForImportingUserOnly( + Organization organization, + Guid importingUserId, + OrganizationUser importingOrganizationUser, + List collections, + List ciphers, + SutProvider sutProvider) + { + var archivedDate = DateTime.UtcNow.AddDays(-1); + organization.MaxCollections = null; + importingOrganizationUser.OrganizationId = organization.Id; + + foreach (var collection in collections) + { + collection.OrganizationId = organization.Id; + } + + foreach (var cipher in ciphers) + { + cipher.OrganizationId = organization.Id; + } + + ciphers[0].ArchivedDate = archivedDate; + ciphers[0].Archives = null; + + KeyValuePair[] collectionRelationships = { + new(0, 0), + new(1, 1), + new(2, 2) + }; + + sutProvider.GetDependency() + .GetByIdAsync(organization.Id) + .Returns(organization); + + sutProvider.GetDependency() + .GetByOrganizationAsync(organization.Id, importingUserId) + .Returns(importingOrganizationUser); + + sutProvider.GetDependency() + .GetManyByOrganizationIdAsync(organization.Id) + .Returns(new List()); + + await sutProvider.Sut.ImportIntoOrganizationalVaultAsync(collections, ciphers, collectionRelationships, importingUserId); + + await sutProvider.GetDependency() + .Received(1) + .CreateAsync( + Arg.Is>(c => + c[0].ArchivedDate == archivedDate && + c[0].Archives != null && + c[0].Archives.Contains(importingUserId.ToString().ToUpperInvariant()) && + c[0].Archives.Contains(archivedDate.ToString("yyyy-MM-ddTHH:mm:ss.fffffffZ"))), + Arg.Any>(), + Arg.Any>(), + Arg.Any>()); + } } diff --git a/test/Identity.Test/IdentityServer/SendAccess/SendEmailOtpRequestValidatorTests.cs b/test/Identity.Test/IdentityServer/SendAccess/SendEmailOtpRequestValidatorTests.cs index 1815b9207d..d2c7051f69 100644 --- a/test/Identity.Test/IdentityServer/SendAccess/SendEmailOtpRequestValidatorTests.cs +++ b/test/Identity.Test/IdentityServer/SendAccess/SendEmailOtpRequestValidatorTests.cs @@ -270,10 +270,8 @@ public class SendEmailOtpRequestValidatorTests // Arrange var otpTokenProvider = Substitute.For>(); var mailService = Substitute.For(); - var featureService = Substitute.For(); - // Act - var validator = new SendEmailOtpRequestValidator(featureService, otpTokenProvider, mailService); + var validator = new SendEmailOtpRequestValidator(otpTokenProvider, mailService); // Assert Assert.NotNull(validator); diff --git a/test/Infrastructure.IntegrationTest/Infrastructure.IntegrationTest.csproj b/test/Infrastructure.IntegrationTest/Infrastructure.IntegrationTest.csproj index 4822df4c77..262a0fe856 100644 --- a/test/Infrastructure.IntegrationTest/Infrastructure.IntegrationTest.csproj +++ b/test/Infrastructure.IntegrationTest/Infrastructure.IntegrationTest.csproj @@ -9,9 +9,7 @@ - - - + diff --git a/test/IntegrationTestCommon/IntegrationTestCommon.csproj b/test/IntegrationTestCommon/IntegrationTestCommon.csproj index 6fd6369f49..9a2e159b22 100644 --- a/test/IntegrationTestCommon/IntegrationTestCommon.csproj +++ b/test/IntegrationTestCommon/IntegrationTestCommon.csproj @@ -8,7 +8,6 @@ -